UNPKG

@surface/custom-element

Version:

Provides support of directives and data binding on custom elements.

52 lines (51 loc) 2.66 kB
import { DisposableMetadata, HookableMetadata } from "@surface/core"; import { disposeTree, stringToCSSStyleSheet } from "../common.js"; import CustomElement from "../custom-element.js"; import StaticMetadata from "../metadata/static-metadata.js"; import TemplateParser from "../parsers/template-parser.js"; import TemplateProcessor from "../processors/template-processor.js"; import { globalCustomDirectives } from "../singletons.js"; /** * Defines a new custom element. * @param tagname tag name to be registered. * @param options definition options. */ export default function element(tagname, options) { return (target) => { if (target.prototype instanceof CustomElement) { const staticMetadata = StaticMetadata.from(target); const templateElement = document.createElement("template"); templateElement.innerHTML = options?.template ?? "<slot></slot>"; const descriptor = TemplateParser.parseReference(tagname, templateElement); if (options?.style) { const styles = Array.isArray(options.style) ? options.style : [options.style]; staticMetadata.styles.push(...styles.map(stringToCSSStyleSheet)); } staticMetadata.template = templateElement; staticMetadata.descriptor = descriptor; const handler = { construct: (target, args, newTarget) => { const instance = Reflect.construct(target, args, newTarget); HookableMetadata.from(target).finish(instance); const context = { directives: new Map([...globalCustomDirectives, ...Object.entries(options?.directives ?? {})]), host: instance, root: instance.shadowRoot, scope: { host: instance }, templateDescriptor: StaticMetadata.from(target).descriptor, }; const disposable = TemplateProcessor.process(context); DisposableMetadata.from(instance).add(disposable); DisposableMetadata.from(instance).add({ dispose: () => disposeTree(instance.shadowRoot) }); return instance; }, }; HookableMetadata.from(target).hooked = true; const proxy = new Proxy(target, handler); window.customElements.define(tagname, proxy, options); return proxy; } window.customElements.define(tagname, target, options); return target; }; }