/** * 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: * create a new web component * create the desired type inside * pass all attributes to the child component * store is at as `node` inside the parent * * 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 function inheritHtmlElement(tagName: K) { return class Inherited extends HTMLElement { protected node: HTMLElementTagNameMap[K]; connectedCallback(autoAddNode?: boolean) { 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); } // Atuomatically add node to DOM if autoAddNode is true or unspecified if (autoAddNode === undefined || autoAddNode) { this.appendChild(this.node); } } }; }