Add register decorator for web components and a better inheriting system for html elements

This commit is contained in:
2024-10-17 23:14:54 +02:00
parent cac185634d
commit 4165f8d4af
4 changed files with 48 additions and 33 deletions

View File

@ -1,3 +1,15 @@
/**
* Class decorator to register components easily
* It's a wrapper around window.customElements.define
* What's nice about it is that you don't separate the component registration
* and the class definition
**/
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
return (component: CustomElementConstructor) => {
window.customElements.define(name, component, options);
};
}
/**
* Safari doesn't support inheriting from HTML tags on web components
* The technique is to:
@ -6,28 +18,33 @@
* pass all attributes to the child component
* store is at as `node` inside the parent
*
* To use this, you must use the tag name twice, once for creating the class
* and the second time while calling super to pass it to the constructor
* Since we can't use the generic type to instantiate the node, we create a generator function
*
* ```js
* class MyClass extends inheritHtmlElement("select") {
* // do whatever
* }
* ```
**/
export class InheritedComponent<
K extends keyof HTMLElementTagNameMap,
> extends HTMLElement {
node: HTMLElementTagNameMap[K];
export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) {
return class Inherited extends HTMLElement {
protected node: HTMLElementTagNameMap[K];
constructor(tagName: K) {
super();
this.node = document.createElement(tagName);
const attributes: Attr[] = []; // We need to make a copy to delete while iterating
for (const attr of this.attributes) {
if (attr.name in this.node) {
attributes.push(attr);
constructor() {
super();
this.node = document.createElement(tagName);
const attributes: Attr[] = []; // We need to make a copy to delete while iterating
for (const attr of this.attributes) {
if (attr.name in this.node) {
attributes.push(attr);
}
}
}
for (const attr of attributes) {
this.removeAttributeNode(attr);
this.node.setAttributeNode(attr);
for (const attr of attributes) {
this.removeAttributeNode(attr);
this.node.setAttributeNode(attr);
}
this.appendChild(this.node);
}
this.appendChild(this.node);
}
};
}