UNPKG

bausteine

Version:
197 lines (196 loc) 7.62 kB
import { render, internals } from "hydro-js"; export default function createWebComponent(name, template, { extendsFrom, reflects = false } = {}) { const extendsFromClass = (extendsFrom ? MyHTMLElementTagNameMap[extendsFrom] : window.HTMLElement); if (!window.customElements.get(name)) { window.customElements.define(name, class hydroElement extends extendsFromClass { #observer; #props; #unmount; static disabledFeatures = ["shadow"]; constructor() { super(); if (reflects) { this.#observer = new window.MutationObserver((mutationList) => this.attributeChangedCallback(mutationList)); } } connectedCallback() { const current = new window.DocumentFragment(); current.append(...this.childNodes); const props = {}; let first; if (internals.allNodeChanges.has(this)) { internals.allNodeChanges.get(this).reduce((acc, curr) => { const proxy = curr.at(3); const key = curr.at(2); first = internals.hydroToReactive.get(proxy) || proxy; acc[key] = first; return acc; }, props); } this.#props = Object.values(props).every((prop) => prop === first) ? first : props; this.#unmount = render(template({ root: this, props: this.#props }), this.appendChild(document.createElement("span")), false); for (const slot of this.querySelectorAll("slot")) { const replacer = current.querySelector(`[slot="${slot.getAttribute("name")}"]`); if (replacer) { render(replacer, slot, false); } } for (const unusedSlot of current.querySelectorAll("[slot]")) { unusedSlot.remove(); } const defaultSlot = this.querySelector("slot:not([name])"); if (defaultSlot && current.childNodes.length) { render(current, defaultSlot, false); } this.#observer?.observe(this, { attributes: true, attributeOldValue: true, }); } // Inform about external changes attributeChangedCallback(mutationList) { for (const mutation of mutationList) { const key = mutation.attributeName; const elemValue = this.getAttribute(key); // Trigger for manual change if (elemValue === null) { this.#props[key].setter(internals.boolAttrList.includes(key) ? false : null); } else if (elemValue === "") { this.#props[key].setter(true); } else { this.#props[key].setter(elemValue); } } } disconnectedCallback() { this.#observer?.disconnect(); this.#unmount(); } // eslint-disable-next-line @typescript-eslint/no-explicit-any observe(name, fn) { this.#props[name]?.observe(name, fn); } }, extendsFrom ? { extends: extendsFrom } : undefined); } } const MyHTMLElementTagNameMap = { a: window.HTMLAnchorElement, abbr: window.HTMLElement, address: window.HTMLElement, area: window.HTMLAreaElement, article: window.HTMLElement, aside: window.HTMLElement, audio: window.HTMLAudioElement, b: window.HTMLElement, base: window.HTMLBaseElement, bdi: window.HTMLElement, bdo: window.HTMLElement, blockquote: window.HTMLQuoteElement, body: window.HTMLBodyElement, br: window.HTMLBRElement, button: window.HTMLButtonElement, canvas: window.HTMLCanvasElement, caption: window.HTMLTableCaptionElement, cite: window.HTMLElement, code: window.HTMLElement, col: window.HTMLTableColElement, colgroup: window.HTMLTableColElement, data: window.HTMLDataElement, // Safari bug datalist: Object.prototype.hasOwnProperty.call(window, "HTMLDataListElement") ? window.HTMLDataListElement : window.HTMLElement, dd: window.HTMLElement, del: window.HTMLModElement, details: window.HTMLDetailsElement, dfn: window.HTMLElement, dialog: window.HTMLDialogElement, div: window.HTMLDivElement, dl: window.HTMLDListElement, dt: window.HTMLElement, em: window.HTMLElement, embed: window.HTMLEmbedElement, fieldset: window.HTMLFieldSetElement, figcaption: window.HTMLElement, figure: window.HTMLElement, footer: window.HTMLElement, form: window.HTMLFormElement, h1: window.HTMLHeadingElement, h2: window.HTMLHeadingElement, h3: window.HTMLHeadingElement, h4: window.HTMLHeadingElement, h5: window.HTMLHeadingElement, h6: window.HTMLHeadingElement, head: window.HTMLHeadElement, header: window.HTMLElement, hgroup: window.HTMLElement, hr: window.HTMLHRElement, html: window.HTMLHtmlElement, i: window.HTMLElement, iframe: window.HTMLIFrameElement, img: window.HTMLImageElement, input: window.HTMLInputElement, ins: window.HTMLModElement, kbd: window.HTMLElement, label: window.HTMLLabelElement, legend: window.HTMLLegendElement, li: window.HTMLLIElement, link: window.HTMLLinkElement, main: window.HTMLElement, map: window.HTMLMapElement, mark: window.HTMLElement, menu: window.HTMLMenuElement, meta: window.HTMLMetaElement, meter: window.HTMLMeterElement, nav: window.HTMLElement, noscript: window.HTMLElement, object: window.HTMLObjectElement, ol: window.HTMLOListElement, optgroup: window.HTMLOptGroupElement, option: window.HTMLOptionElement, output: window.HTMLOutputElement, p: window.HTMLParagraphElement, picture: window.HTMLPictureElement, pre: window.HTMLPreElement, progress: window.HTMLProgressElement, q: window.HTMLQuoteElement, rp: window.HTMLElement, rt: window.HTMLElement, ruby: window.HTMLElement, s: window.HTMLElement, samp: window.HTMLElement, script: window.HTMLScriptElement, search: window.HTMLElement, section: window.HTMLElement, select: window.HTMLSelectElement, slot: window.HTMLSlotElement, small: window.HTMLElement, source: window.HTMLSourceElement, span: window.HTMLSpanElement, strong: window.HTMLElement, style: window.HTMLStyleElement, sub: window.HTMLElement, summary: window.HTMLElement, sup: window.HTMLElement, table: window.HTMLTableElement, tbody: window.HTMLTableSectionElement, td: window.HTMLTableCellElement, template: window.HTMLTemplateElement, textarea: window.HTMLTextAreaElement, tfoot: window.HTMLTableSectionElement, th: window.HTMLTableCellElement, thead: window.HTMLTableSectionElement, time: window.HTMLTimeElement, title: window.HTMLTitleElement, tr: window.HTMLTableRowElement, track: window.HTMLTrackElement, u: window.HTMLElement, ul: window.HTMLUListElement, var: window.HTMLElement, video: window.HTMLVideoElement, wbr: window.HTMLElement, };