{"id":27103298,"url":"https://github.com/abstractn/abs-component","last_synced_at":"2026-04-15T21:31:19.145Z","repository":{"id":204991782,"uuid":"711363431","full_name":"Abstractn/abs-component","owner":"Abstractn","description":"Simple HTML-to-JS component association system","archived":false,"fork":false,"pushed_at":"2024-05-24T10:49:54.000Z","size":58,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-07T23:36:43.021Z","etag":null,"topics":["component","development","frontend","html","javascript","lightweight","simple","typescript","utility"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Abstractn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-29T02:47:04.000Z","updated_at":"2024-11-12T11:39:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"e4cf62ba-5299-4f77-85e2-576d7725895b","html_url":"https://github.com/Abstractn/abs-component","commit_stats":null,"previous_names":["abstractn/abs-component"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Abstractn/abs-component","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abstractn%2Fabs-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abstractn%2Fabs-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abstractn%2Fabs-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abstractn%2Fabs-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Abstractn","download_url":"https://codeload.github.com/Abstractn/abs-component/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abstractn%2Fabs-component/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31861267,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component","development","frontend","html","javascript","lightweight","simple","typescript","utility"],"created_at":"2025-04-06T16:52:48.825Z","updated_at":"2026-04-15T21:31:19.125Z","avatar_url":"https://github.com/Abstractn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Abs-Component\n\n[![npm version](https://badgen.net/npm/v/abs-component)](https://www.npmjs.com/package/abs-component) [![Install size](https://packagephobia.com/badge?p=abs-component)](https://packagephobia.com/result?p=abs-component)\n\n\n## Introduction:\n\nThis module allows you to define classes and associate an instance of them to an HTML node so that these nodes can behave as autonomous components.\n\nIts implementation remains rather simple and the main goal in mind is to give users a basic way of organizing the scripts for different elements in a page, especially in small projects such as static websites.\n\n\n## Development:\n\n\u003e Ignore this if you don't intend to contribute on the project\n\nMake sure you have the Node version 15 or greater.\n\nA simple `npm install` will get everything needed for development.\n\nEdit files under `/src` folder.\n\nOnce tested, commited and pushed in a development branch it will be merged over to main branch (through pull request if needed) and a new dist build will be generated with `npm run build`.\n\nThe build is then pushed to remote together with package version increase and publish.\n\nDevelopment branch can then be realligned with main branch.\n\n\n## Installation:\n\n#### - As module\nInstall the module with your preferred package manager by typing the following in your terminal:\n```\nnpm install -D abs-component\n```\nthen import the module in your script\n```typescript\nimport { AbsComponentManager } from 'abs-component';\n```\n\n#### - In browser\n\nAll available files are the following:\n```bash\n# Typescript\nhttps://cdn.jsdelivr.net/npm/abs-component/dist/abs-component.ts\n\n# Javascript\nhttps://cdn.jsdelivr.net/npm/abs-component/dist/abs-component.js\n\n# Javascript without exports\nhttps://cdn.jsdelivr.net/npm/abs-component/dist/abs-component.nx.js\n```\n\nTo include it for browser usage inser this in your `\u003chead\u003e`:\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/abs-component@1/dist/abs-component.nx.js\"\u003e\u003c/script\u003e\n```\n\n\u003e Remove `@1` after the package name from the link to load the latest version or specify the version by changing the number\n\n\n## Component from the template side:\n\nIn HTML components are defined by simply applying a data-attribute to the desired node.\nThis attribute can be configured as preferred, else it's `data-abs-component` by default.\n\n```html\n\u003cdiv data-abs-component=\"MyComponent\"\u003e\u003c/div\u003e\n```\n\n\n## Component from the logic side:\n\nBe it Typescript or Javascript (the only difference is simply that TS has explicit interfaces available), the base structure of a component class is the same.\n\n```javascript\n// add `implements AbsComponent` for Typescript\nclass MyComponent {\n  \n  // The constructor receives an HTMLElement parameter\n  // that is the DOM node reference\n  // that the component is associated to\n\n  // add `public` keyword to `node` for Typescript\n  // recommended to add `readonly` keyword too to `node` for Typescript\n  constructor (node) {}\n\n  // All components are recommended to define an `init` function\n  // but it's not required if know what to do\n  init() {}\n\n  // Then usually but optionally there is a `ready` function\n  // (see the component lifecyle later in the README)\n  ready() {\n    this.myCustomMethod();\n  }\n\n  // And optionally, for unique use cases, a `destroy` function\n  // NOTE: as of now components can only be destroyed manually\n  destroy() {}\n\n  // After these basic definitions you can start adding\n  // additional methods to run custom logic\n\n  myCustomMethod() {\n    // For obtaining node references\n    // contained inside the current component\n    // you can use `this.node` as a starting node\n    // to run a query selection\n    const myComponentSubnode = this.node.querySelector('.my-component-subnode');\n  }\n}\n```\n\n\n## Usage example:\n\n1) Firstly create an instance of the module class optionally passing to the constructor a config object with your preferred options.\n\n```typescript\nimport { AbsComponentManager } from 'abs-component';\nimport { AbsComponentManagerConfig } from 'abs-component';\n\nconst absComponentManagerConfig: AbsComponentManagerConfig = {};\nconst absComponentManager = new AbsComponentManager(absComponentManagerConfig);\n```\n\n2) Define a component class\n\n(I will build a switch button component to showcase a realistic yet simple example)\n\n```typescript\nimport { AbsComponent } from 'abs-component';\n\ninterface ButtonComponent {\n  onButtonNode: HTMLElement;\n  offButtonNode: HTMLElement;\n  state: boolean;\n  BUTTON_ACTIVE_CLASS: string;\n}\n\nclass SwitchComponent implements AbsComponent {\n  constructor(public readonly node: HTMLElement) {}\n\n  init(): void {\n    this.onButtonNode = this.node.querySelector('button.on') as HTMLElement;\n    this.offButtonNode = this.node.querySelector('button.off') as HTMLElement;\n    this.state = false;\n    this.BUTTON_ACTIVE_CLASS = 'active';\n  }\n\n  ready(): void {\n    this.setButtonEvents();\n  }\n\n  setButtonEvents(): void {\n    this.onButtonNode.addEventListener('click', () =\u003e {\n      if(this.state !== true) {\n        this.node.querySelector(`.${this.BUTTON_ACTIVE_CLASS}`).classList.remove(this.BUTTON_ACTIVE_CLASS);\n        this.onButtonNode.classList.add(this.BUTTON_ACTIVE_CLASS);\n        this.state = true;\n        this.reportState();\n      }\n    });\n    this.offButtonNode.addEventListener('click', () =\u003e {\n      if(this.state !== false) {\n        this.node.querySelector(`.${this.BUTTON_ACTIVE_CLASS}`).classList.remove(this.BUTTON_ACTIVE_CLASS);\n        this.offButtonNode.classList.add(this.BUTTON_ACTIVE_CLASS);\n        this.state = false;\n        this.reportState();\n      }\n    });\n  }\n\n  reportState(): void {\n    console.log(this.state);\n  }\n}\n```\n\nand its template will look something like this:\n\n```html\n\u003cstyle\u003e\n  button.on.active,\n  button.off.active {\n    color: red;\n  }\n\u003c/style\u003e\n\n\u003cdiv class=\"switch\" data-abs-component=\"switch-component\"\u003e\n  \u003cbutton class=\"on\"\u003eON\u003c/button\u003e\n  \u003cbutton class=\"off\"\u003eOFF\u003c/button\u003e\n\u003c/div\u003e\n```\n\n3) Register your newly defined component, passing both the attribute value and the class\n\n```typescript\nabsComponentManager.registerComponent('switch-component', SwitchComponent);\n```\n\n4) After all components are defined and registered, initialize them all together with a single method\n\n```typescript\nabsComponentManager.initComponents();\n```\n\n\u003e NOTE: do not confuse `initComponents` (plural) with `initComponent` (singular). This last one allows you to initialize a single component. It can be useful if components are printed at runtime.\n\n\nNow we will have our HTML elements completely autonomous with their self-contained logic ready to be interacted with.\n\n\n## Module lifecycle\n\nThe component initialization will select all HTML nodes having the component name data-attribute and call their `init()` method sequentially.\nAfter all components have been inited the same will be done calling their `ready()` method.\n\n\u003e NOTE: if you want a component to interact with another writing code inside the `init()` method, one of the components may not exist yet depending on their order in the page. It is suggested to write inside `init()` for internal declarations and component preparation while the proper logic should go inside `ready()`;\n\n```mermaid\nflowchart TB\n    a[define a component class] --\u003e b[register the component]\n    b[register the component] --\u003e a[define a component class]\n    b[register the component] --\u003e c[initialize components]\n    c[initialize components] -- the order depends on how components are placed in DOM --\u003e d([all components' `init` called sequentially])\n    d([all components' `init` called sequentially]) --\u003e e([all components' `ready` called sequentially])\n```\n\n## Component interactions\n\nThere is currently no well-structured way for components to communicate with each other but the module still offers a bare-minimum possibility.\n\nThe component manager offers a read only `components` object that looks like a record of names of registered components containing an array of how many instances of each component is present in the page.\n\n```typescript\nconsole.log(absComponentManager.components);\n\n// log output\n{\n  'switch-component': [\n    {\n      init: Function,\n      ready: Function,\n      setButtonEvents: Function,\n      reportState: Function,\n      onButtonNode: HTMLElement,\n      offButtonNode: HTMLElement,\n      state: boolean,\n      BUTTON_ACTIVE_CLASS: string\n    }\n  ]\n}\n```\n\nTaking the previous example of a switch component we could say that a second component or any other script can access `SwitchComponent`'s parameters and methods using the following:\n\n```typescript\n// this is the component class\nabsComponentManager.components['switch-component'][0];\n\n// the defined parameter containing `'active'`\nabsComponentManager.components['switch-component'][0].BUTTON_ACTIVE_CLASS;\n```\n\n\u003e NOTE: depending on how the page behaves this method of accessing other componets may result unreliable as when a component is destroyed their class is removed from the array inside the `components` record list. If you have previously assigned a local reference to an external component then this reference will not be valid anymore.\n\nAn **additional and safer** method of obtaining a class reference of other components is using the `getComponentByNode` methods exposed by the component manager class.\n\nIf you have the exact node of the interested component, this function will run a search within the `components` object and return the class associated to that node.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabstractn%2Fabs-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabstractn%2Fabs-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabstractn%2Fabs-component/lists"}