From c8d0d7634c8d6f0c92973e8c5b2e38d28ab3a17c Mon Sep 17 00:00:00 2001 From: Joe Pea Date: Sun, 30 Jul 2023 12:03:35 -0700 Subject: [PATCH] feat: allow `LumeElement.defineElement()` to be called repeatedly without errors and it will not try to re-define an already defined element class, allow it to be called multiple times with different `name`s to define the same custom element using alternative tag names, allow a custom registry to be passed in for use cases like ShadowRoot-scoped custom element registries Also improved inline documentation of some features. BREAKING: `LumeElement`'s `static defineElement()` method no longer throws an error when called repeatedly with the same name or no name, which changes runtime behavior if anyone was previously relying on catching this error with `try-catch`. If you previous had logic in a catch block to detect already-defined elements, you can instead use `customElements.get(name)` to perform the check instead of using try-catch. --- src/LumeElement.ts | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/LumeElement.ts b/src/LumeElement.ts index 7e78bdb..0bb5651 100644 --- a/src/LumeElement.ts +++ b/src/LumeElement.ts @@ -23,10 +23,22 @@ const HTMLElement = // tried, but TS has been updated for abstract mixin support. class LumeElement extends HTMLElement { + /** The default tag name of the elements this class is instantiated for. */ static elementName: string = '' - static defineElement(name?: string) { - customElements.define(name || this.elementName, this) + /** + * Define this class for the given element `name`, or using its default name + * (`elementName`) if no `name` given. Defaults to using the global + * `customElements` registry unless another registry is provided (for + * example a ShadowRoot-scoped registry). + */ + static defineElement(name: string = '', registry: CustomElementRegistry = customElements) { + if (registry.get(name)) return + else { + if (name) registry.define(name, this) + // Allow the same element to be defined more than once using alternative names. + else registry.define(this.elementName, class extends this {}) + } } /** Non-decorator users can use this to specify which props are reactive. */ @@ -141,9 +153,28 @@ class LumeElement extends HTMLElement { } } + /** + * If a subclass provides this, it should return DOM. It is called with + * Solid.js `render()`, so it can also contain Solid.js reactivity (signals + * and effects) and templating (DOM-returning reactive JSX or html template + * literals). + */ protected declare template?: Template + + /** + * If provided, this style gets created once per ShadowRoot of each element + * instantiated from this class. The expression can access `this` for string + * interpolation. + */ protected declare css?: string | (() => string) - protected static css?: string | (() => string) + + /** + * If provided, this style gets created a single time for all elements + * instantiated from this class, instead of once per element. If you do not + * need to interpolate values into the string using `this`, then use this + * static property for more performance compared to the instance property. + */ + protected declare static css?: string | (() => string) /** * When `true`, the custom element will have a `ShadowRoot`. Set to `false`