- From: Lea Verou <notifications@github.com>
- Date: Wed, 13 Sep 2023 11:13:02 -0700
- To: WICG/webcomponents <webcomponents@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <WICG/webcomponents/issues/1029@github.com>
This proposal introduces an API for defining custom attributes for both built-ins and custom elements, and design discussion for an API that can be used to define more complex enhancements that involve multiple attributes, methods, JS-only properties etc. This came out of the [TPAC extending built-ins breakout](https://github.com/w3c/tpac2023-breakouts/issues/44), and some follow-up discussions with @keithamus. ## Defining attributes: The `Attribute` class ### Use cases - Augmenting existing elements (either built-ins or custom elements) with custom attributes that trigger custom behaviors - Easier definition of a web component's *own* attributes, as this automatically takes care of property-attribute reflection, typing, default values etc, one of the biggest pain points of defining WCs without helpers ### Prior art - [Original custom attributes proposal (naming only)](https://github.com/whatwg/html/issues/2271) - [@lume's custom attributes proposal](https://github.com/lume/custom-attributes) - [LitElement properties](https://lit.dev/docs/components/properties/) - [VueJS props](https://vuejs.org/guide/components/props.html#prop-validation) ### Needs - Lifecycle hooks: connected, disconnected, changed - Default values - Optional typing ### API sketch * Subclasses that extend a base Attribute class * Static `HTMLElement.attributeRegistry` property which is an AttributeRegistry object. Attributes can be registered generically on `HTMLElement` to be available everywhere, or on specific classes (built-in or custom element classes). * For non custom elements, attribute names MUST contain a hyphen and not match a blacklist (`aria-*`, SVG attributes etc). Or maybe this should be a validation restriction, and not actually enforced by the API? * Static `attributes` property for custom elements so that the definition can be in one place (or would `attributeRegistry` be enough?) ```js class ListAttribute extends Attribute { ownerElement; // element this is attached to value; // current value connectedCallback() { /* ... */ } disconnectedCallback() { /* ... */ } // Called whenever the attribute's value changes changedCallback() { /* ... */ } static dataType = AttributeType.IDREF; // Optional default value static defaultValue = null; } // Usage on built-ins or existing custom elements: HTMLInputElement.attributeRegistry.define("ac-list", ListAttribute); // Usage on new custom elements: class MyInput extends HTMLElement { // Or maybe an array? static attributes = { "ac-list": ListAttribute, // Creates an Attribute object behind the scenes: value: { dataType: AttributeType.NUMBER, defaultValue: 0 } } } // This also works: MyInput.attributeRegistry.define("ac-list", ListAttribute, { propertyName: "autocompleteList" // if we want to override the default acList }); ``` ### Types In v0 types could only be predefined `AttributeType` objects, in the future these should be [constructible](https://www.w3.org/TR/design-principles/#constructors) (all they need is a `parse()` and `stringify()` function, and maybe an optional `defaultValue` to act as a base default value). ### Open Questions - Should `Attribute` extend `EventTarget`? Should it fire events in addition to / instead of some of the lifecycle hooks? - Should lifecycle hooks be called when the attribute is added, or when the element is connected/disconnected? ## Complex Enhancements Complex enhancements include: - Multiple attributes (references to `Attribute` objects) - Methods - Properties and accessors (that don't correspond to attributes) This can be fleshed out at a later time, since `Attribute` already deals with a lot of use cases. That could give us time to get more data about what is needed. ### Prior art * [Element Behaviors](https://github.com/lume/element-behaviors) * [Element custom enhancements](https://github.com/WICG/webcomponents/issues/1000) ### Needs & Design discussion #### Referencing: How to associate enhancements with elements? Ideas: - `has` attribute that takes a list of identifiers (like [Element behaviors](https://github.com/lume/element-behaviors)) - Associating enhancements with a selector would probably afford maximum flexibility and allows the association to happen automatically (e.g. for an Enhancement implementing htmx, the "activation selector" could be `[hx-get], [hx-post], [hx-swap], ...`, without an additional `has="htmx"` being required eveyrwhere that these attributes are used. - Imperative association could allow us to explore functionality without committing to a particular scheme. Note that individual attributes can still automatically activate behaviors, though it's a bit clunky: ```js class MyAttribute extends Attribute { connectedCallback () { this.ownerElement.behaviors.add(Htmx); } disconnectedCallback () { let hasOthers = this.ownerElement.getAttributeNames().some(n => n.startsWith("hx-"); if (!hasOthers) this.ownerElement.behaviors.delete(Htmx); } } ``` #### Flexibility Should enhancements allow the same separation of names and functionality as custom elements and attributes? Given that they include multiple attributes, how would the association happen? I'm leaning towards that they'd also *name* the attributes they are including (since they are naming properties, methods etc already anyway). ```js // Should have the same syntax as HTMLElement.attributes above attributes: { "v-if": VIf "v-else": VElse, "v-on": { value: VOn, propertyName: "vOnEvent" } ... } ``` -- Reply to this email directly or view it on GitHub: https://github.com/WICG/webcomponents/issues/1029 You are receiving this because you are subscribed to this thread. Message ID: <WICG/webcomponents/issues/1029@github.com>
Received on Wednesday, 13 September 2023 18:13:09 UTC