vrugtehagel / yozo

A custom element library for simple, robust and readable components
https://yozo.ooo
5 stars 0 forks source link

Add an API for the internal `CustomStateSet` #18

Open vrugtehagel opened 3 weeks ago

vrugtehagel commented 3 weeks ago

It's be cool if authors could use e.g.

<meta state="is-checked" as="isChecked">

then component users can use :state(is-checked) and .isChecked for component state. This essentially just creates the isChecked property (a boolean), and is linked to whether or not the CustomStateSet includes the string is-checked. Probably also the readonly attribute should be supported, because authors might not always want to allow component users to modify internal state. Potentially also as should be optional, though it's a tad unclear to me as of now what the expected behavior would be; would the property be omitted altogether or would the created property name match the camelCased state name?

See CustomStateSet on MDN. This is relatively widely supported, only Safari is lagging behind even with ElementInternals.

vrugtehagel commented 2 weeks ago

For what it's worth, this can be recreated by doing

<meta property="isChecked">

<script>
const internals = this.attachInternals()

effect(() => {
  if($.isChecked) internals.states.add('is-checked')
  else internals.states.delete('is-checked')
}, update => update())
</script>

but this is a little more verbose than I'd like (especially for something that should be simple), and it is not self-documenting like e.g. <meta attribute> even though it is a user-facing aspect of the custom element.

vrugtehagel commented 2 weeks ago

Potential issue; it is not currently possible to sync the items in internals.states and the individual properties. That is, manually adding or deleting a state through internals.states.add() or internals.states.delete() would completely bypass the live variables and cause them to be out-of-sync; the property would return the wrong thing.

vrugtehagel commented 2 weeks ago

Also, another issue; states are not always boolean. Sometimes you'll want e.g. .visibility = 'visible' | 'hidden' or .color = 'red' | 'green' | 'blue', where only one of the options is in the states set at any given time. This is not appropriately expressed by <meta state="name">.

vrugtehagel commented 2 weeks ago

Could also be an option to define different sets of states that are then mutually exclusive, like

<meta state="visible, hidden" as="visibility">
<meta state="red, green, blue" as="color">

But this is rather hard to parse (i.e. takes a considerable amount of code and is relatively non-generic) and still does not cater for more complex situations. For example, having enabled, disabled and visible, hidden, and have hidden and disabled to be mutually exlusive but not any other combinations.