@fusion.dev/helix-headless 0.41.1 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10397,7 +10397,7 @@
10397
10397
  "type": {
10398
10398
  "text": "HelixSelectEvent"
10399
10399
  },
10400
- "description": "Fired when the slide in view changes, with its index. Cancelable: refusing it takes the decision and nothing moves.",
10400
+ "description": "Fired when the slide in view changes, with its index. Cancelable where the carousel is about to move: refusing a control or an indicator takes the decision and nothing moves. A drag has already moved the track, so that one announces after the fact and refusing it does nothing. See `handleScroll`.",
10401
10401
  "name": "helix-select",
10402
10402
  "reactProp": "onhelix-select"
10403
10403
  }
@@ -30667,7 +30667,7 @@
30667
30667
  "helix": {
30668
30668
  "category": "forms",
30669
30669
  "since": "0.1.0",
30670
- "status": "experimental",
30670
+ "status": "stable",
30671
30671
  "documentation": "https://fusion.dev/helix/components/date-picker",
30672
30672
  "aliases": [
30673
30673
  "Calendar",
@@ -42521,7 +42521,7 @@
42521
42521
  "helix": {
42522
42522
  "category": "typography",
42523
42523
  "since": "0.1.0",
42524
- "status": "experimental",
42524
+ "status": "stable",
42525
42525
  "documentation": "https://fusion.dev/helix/components/format-date",
42526
42526
  "aliases": [
42527
42527
  "RelativeTime",
@@ -42533,7 +42533,7 @@
42533
42533
  "part": "Root",
42534
42534
  "examples": [
42535
42535
  {
42536
- "description": "The same readings on the headless tier, where the value is the element's own text.",
42536
+ "description": "The same readings on the headless tier, with every rule written by hand.",
42537
42537
  "slug": "preview"
42538
42538
  }
42539
42539
  ],
@@ -46085,7 +46085,7 @@
46085
46085
  "helix": {
46086
46086
  "category": "media",
46087
46087
  "since": "0.1.0",
46088
- "status": "experimental",
46088
+ "status": "stable",
46089
46089
  "documentation": "https://fusion.dev/helix/components/image",
46090
46090
  "aliases": [
46091
46091
  "Picture",
@@ -63024,13 +63024,7 @@
63024
63024
  "type": {
63025
63025
  "text": "boolean"
63026
63026
  },
63027
- "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so what is captured here is what\nthe markup said at upgrade, before anything could overwrite it: read the\nattribute later and the listbox's own writes answer instead.\n\n**The deeper half is 302 and is not this.** State reflecting under the\ndeclaration's name is the fault `TreeItem` already avoids by mirroring to\n`data-selected`, and four families here still write `selected` back into a\nconsumer's markup. Splitting them means moving what every stylesheet selects\non, which is a change of its own rather than a property being added."
63028
- },
63029
- {
63030
- "kind": "field",
63031
- "name": "seeded",
63032
- "privacy": "private",
63033
- "description": "What the markup said, read once at construction for the reason above."
63027
+ "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so the declaration is the\nattribute and the state is the property.\n\n**The attribute, read when it is asked for, and never a snapshot taken in\nthe constructor.** A field initializer runs there, and an element the HTML\nparser created has none of its attributes yet at that moment: measured on\nthis element, on `Radio` and on `Toggle`, `<helix-option value=\"a\"\nselected>` reported `defaultSelected` as `false` both from parsed markup\nand from `createElement` followed by `setAttribute`, which is every way a\nconsumer writes one. The snapshot could only ever be true for an element\nalready in the document when `customElements.define` ran.\n\nNothing overwrites it either, which is what makes reading it late correct\nrather than merely later: `selected` does not reflect, for the reason\n`willUpdate` records, so the listbox's writes never reach the attribute and\nwhat it says is what the markup said.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
63034
63028
  },
63035
63029
  {
63036
63030
  "kind": "field",
@@ -71504,13 +71498,7 @@
71504
71498
  "type": {
71505
71499
  "text": "boolean"
71506
71500
  },
71507
- "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so what is captured here is what\nthe markup said at upgrade, before anything could overwrite it: read the\nattribute later and the listbox's own writes answer instead.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
71508
- },
71509
- {
71510
- "kind": "field",
71511
- "name": "seeded",
71512
- "privacy": "private",
71513
- "description": "What the markup said, read once at construction for the reason above."
71501
+ "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so the declaration is the\nattribute and the state is the property.\n\n**The attribute, read when it is asked for, and never a snapshot taken in\nthe constructor.** A field initializer runs there, and an element the HTML\nparser created has none of its attributes yet at that moment: measured on\nthis element, on `Option` and on `Toggle`, `<helix-radio value=\"a\"\nselected>` reported `defaultSelected` as `false` both from parsed markup\nand from `createElement` followed by `setAttribute`, which is every way a\nconsumer writes one. The snapshot could only ever be true for an element\nalready in the document when `customElements.define` ran.\n\nNothing overwrites it either, which is what makes reading it late correct\nrather than merely later: `selected` does not reflect, for the reason\n`willUpdate` records, so the group's writes never reach the attribute and\nwhat it says is what the markup said.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
71514
71502
  },
71515
71503
  {
71516
71504
  "kind": "field",
@@ -77355,7 +77343,7 @@
77355
77343
  "declarations": [
77356
77344
  {
77357
77345
  "kind": "class",
77358
- "description": "A real `<input type=\"range\">`, so the drag, the arrow keys, Home and End, the\npage keys and the touch target are the platform's. The last is the one nobody\nnotices: a native range has a hit area larger than its thumb, sized by the\nplatform for a finger.\n\n**The value is a string, like every other form control's.** A range submits\ntext and `MixinControllerValue` holds text, so the number is a reading of the\nvalue. `valueAsNumber` is there for arithmetic.\n\n**Marks are drawn rather than left to `<datalist>`**, whose ticks cannot be\nstyled and which Firefox does not render for a range at all. A mark is\ndecoration; what a reader can land on is `step`, which the input enforces\nwhether or not anything is drawn. `markers` is a count rather than a flag, so\na slider from zero to a hundred cannot silently ask for a hundred and one.\n\n**A range is two of them.** There is no native two-thumb range, so two are\nstacked on one track and each thumb keeps its own.",
77346
+ "description": "A real `<input type=\"range\">`, so the drag, the arrow keys, Home and End, the\npage keys and the touch target are the platform's. The last is the one nobody\nnotices: a native range has a hit area larger than its thumb, sized by the\nplatform for a finger.\n\n**The value is a string, like every other form control's.** A range submits\ntext and `MixinControllerValue` holds text, so the number is a reading of the\nvalue. `valueAsNumber` is there for arithmetic.\n\n**Marks are drawn rather than left to `<datalist>`**, whose ticks cannot be\nstyled and which Firefox does not render for a range at all. A mark is\ndecoration; what a reader can land on is `step`, which the input enforces\nwhether or not anything is drawn. `markers` is a count rather than a flag, so\na slider from zero to a hundred cannot silently ask for a hundred and one.\n\n**A range is two of them.** There is no native two-thumb range, so two are\nstacked on one track and each thumb keeps its own.\n\n**`required` arrives with the field mixin and cannot fail here**, which is\nworth saying rather than leaving a reader to find. A range is never empty:\nthe platform shows its own midpoint whatever is set, and `willUpdate` seeds\nthat midpoint into `defaultValue` so the value agrees with the handle from the\nfirst render. HTML does not apply `required` to a range either, so it stays\noff the control and the marker beside the label is all it does.",
77359
77347
  "name": "HelixHeadlessSlider",
77360
77348
  "cssProperties": [
77361
77349
  {
@@ -77363,7 +77351,7 @@
77363
77351
  "name": "--slider-fraction"
77364
77352
  },
77365
77353
  {
77366
- "description": "Where the filled span starts, as a percentage of the track.",
77354
+ "description": "Where a range's filled span starts, as a fraction of the track, 0 to 1.",
77367
77355
  "name": "--slider-from"
77368
77356
  },
77369
77357
  {
@@ -77371,7 +77359,7 @@
77371
77359
  "name": "--slider-marker-at"
77372
77360
  },
77373
77361
  {
77374
- "description": "Where the filled span ends, as a percentage of the track.",
77362
+ "description": "Where a range's filled span ends, as a fraction of the track, 0 to 1.",
77375
77363
  "name": "--slider-to"
77376
77364
  }
77377
77365
  ],
@@ -78720,7 +78708,7 @@
78720
78708
  "helix": {
78721
78709
  "category": "forms",
78722
78710
  "since": "0.1.0",
78723
- "status": "experimental",
78711
+ "status": "stable",
78724
78712
  "documentation": "https://fusion.dev/helix/components/slider",
78725
78713
  "aliases": [
78726
78714
  "Range",
@@ -97186,9 +97174,28 @@
97186
97174
  {
97187
97175
  "description": "The group's items.",
97188
97176
  "name": ""
97177
+ },
97178
+ {
97179
+ "description": "Whatever sits after the track on its own line, such as a count or a reset.",
97180
+ "name": "end"
97181
+ },
97182
+ {
97183
+ "description": "Whatever sits before the track on its own line, such as an icon or a caption.",
97184
+ "name": "start"
97189
97185
  }
97190
97186
  ],
97191
97187
  "members": [
97188
+ {
97189
+ "kind": "method",
97190
+ "name": "adopt",
97191
+ "privacy": "private",
97192
+ "return": {
97193
+ "type": {
97194
+ "text": "void"
97195
+ }
97196
+ },
97197
+ "description": "Takes the markup's own answer where nothing has set a value.\n\n**The case a server renders**, and `Tabs` records the same fault from the\nother side. This element cannot choose on the server, having no children to\ncount, so a consumer whose first paint is right wrote `selected` on the\nitem: that serves `aria-checked=\"true\"`, and `sync` then cleared it one\nframe later, because `value` is `''` and nothing matches it. The shipped\n`disabled` example is exactly that markup, and drew both of its groups with\nnothing chosen.\n\n**Onto `defaultValue`, never onto `value`**, which is the same call\n`Slider` makes about the midpoint it seeds: what the markup declared is a\nfact about the markup, so it is where a reset returns to rather than\nsomething the reader chose. It also makes the group report the answer it is\nshowing, which marking the item alone would not: a `required` group would\notherwise tell a reader a value is missing while they are looking at one.\n\nA written `value` still wins, a declared property being a seed and a\ncorrection, and a group with neither still marks nothing at all.\n\n**The property and never `defaultSelected`, which is the attribute.** React\nassigns a JSX prop as a property where the custom element has one, and\nwrites no attribute at all, so keying on the attribute answers for markup\nand not for JSX. Measured by the parity check on the shipped `disabled`\nexample, whose two syntaxes are the same tree: 192 pixels apart, the HTML\nmarking `List` and the TSX marking nothing. The attribute seeds the\nproperty at upgrade, so reading the property covers both.\n\nRead before `sync` writes to it, which is the only moment it still holds\nwhat the consumer declared: after that it holds this group's answer, and\n`value` is no longer empty, so this returns above.\n\n**Called from `willUpdate` and from the slot, never from `sync`**, which\nruns in `updated`: a reactive property set there schedules a second update,\nand Lit's development build warns about exactly that once per group. From\n`willUpdate` the first render already carries the value, which is the pass\nthat has to be right. `children` is undefined on the server, whose element\nshim carries attributes and no tree, and that is the same guard\n`MixinCollection` records for the same reason."
97198
+ },
97192
97199
  {
97193
97200
  "kind": "method",
97194
97201
  "name": "sync",
@@ -98278,7 +98285,7 @@
98278
98285
  "summary": "A row of options with the selection marked by a moving indicator.",
98279
98286
  "helix": {
98280
98287
  "since": "0.1.0",
98281
- "status": "experimental",
98288
+ "status": "stable",
98282
98289
  "documentation": "https://fusion.dev/helix/components/toggle",
98283
98290
  "aliases": [
98284
98291
  "SegmentedControl",
@@ -98376,13 +98383,7 @@
98376
98383
  "type": {
98377
98384
  "text": "boolean"
98378
98385
  },
98379
- "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so what is captured here is what\nthe markup said at upgrade, before anything could overwrite it: read the\nattribute later and the listbox's own writes answer instead.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
98380
- },
98381
- {
98382
- "kind": "field",
98383
- "name": "seeded",
98384
- "privacy": "private",
98385
- "description": "What the markup said, read once at construction for the reason above."
98386
+ "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so the declaration is the\nattribute and the state is the property.\n\n**The attribute, read when it is asked for, and never a snapshot taken in\nthe constructor.** A field initializer runs there, and an element the HTML\nparser created has none of its attributes yet at that moment: measured on\nthis element, on `Option` and on `Radio`, `<helix-toggle value=\"light\"\nselected>` reported `defaultSelected` as `false` both from parsed markup\nand from `createElement` followed by `setAttribute`, which is every way a\nconsumer writes one. The snapshot could only ever be true for an element\nalready in the document when `customElements.define` ran.\n\nNothing overwrites it either, which is what makes reading it late correct\nrather than merely later: `selected` does not reflect, for the reason\n`willUpdate` records, so the group's writes never reach the attribute and\nwhat it says is what the markup said.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
98386
98387
  },
98387
98388
  {
98388
98389
  "kind": "field",
@@ -98825,7 +98826,7 @@
98825
98826
  "helix": {
98826
98827
  "category": "actions",
98827
98828
  "since": "0.1.0",
98828
- "status": "experimental",
98829
+ "status": "stable",
98829
98830
  "documentation": "https://fusion.dev/helix/components/toggle",
98830
98831
  "aliases": [
98831
98832
  "Segment",
@@ -104775,7 +104776,7 @@
104775
104776
  "helix": {
104776
104777
  "category": "media",
104777
104778
  "since": "0.1.0",
104778
- "status": "experimental",
104779
+ "status": "stable",
104779
104780
  "documentation": "https://fusion.dev/helix/components/video-playlist",
104780
104781
  "tier": "headless",
104781
104782
  "group": "BaseVideoPlaylist",
@@ -105342,7 +105343,7 @@
105342
105343
  "helix": {
105343
105344
  "category": "media",
105344
105345
  "since": "0.1.0",
105345
- "status": "experimental",
105346
+ "status": "stable",
105346
105347
  "documentation": "https://fusion.dev/helix/components/video-playlist",
105347
105348
  "aliases": [
105348
105349
  "Episode Menu",
@@ -106005,7 +106006,7 @@
106005
106006
  "helix": {
106006
106007
  "category": "media",
106007
106008
  "since": "0.1.0",
106008
- "status": "experimental",
106009
+ "status": "stable",
106009
106010
  "documentation": "https://fusion.dev/helix/components/video-playlist",
106010
106011
  "aliases": [
106011
106012
  "Playlist",
@@ -108310,7 +108311,7 @@
108310
108311
  "helix": {
108311
108312
  "category": "media",
108312
108313
  "since": "0.1.0",
108313
- "status": "experimental",
108314
+ "status": "stable",
108314
108315
  "documentation": "https://fusion.dev/helix/components/video",
108315
108316
  "aliases": [
108316
108317
  "Player",
@@ -27,7 +27,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
27
27
  *
28
28
  * @slot - The slides.
29
29
  *
30
- * @event {HelixSelectEvent} helix-select - Fired when the slide in view changes, with its index. Cancelable: refusing it takes the decision and nothing moves.
30
+ * @event {HelixSelectEvent} helix-select - Fired when the slide in view changes, with its index. Cancelable where the carousel is about to move: refusing a control or an indicator takes the decision and nothing moves. A drag has already moved the track, so that one announces after the fact and refusing it does nothing. See `handleScroll`.
31
31
  *
32
32
  * @csspart controls - The row of controls.
33
33
  * @csspart indicator - One of them, and a way to reach that slide.
@@ -264,8 +264,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
264
264
  @click=${() => this.handleSwatch(preset)}
265
265
  ></button>`;
266
266
  })}
267
- </div>`}
268
- </div>`;
267
+ </div>`}`;
269
268
  }
270
269
  };
271
270
  __decorate([
@@ -21,7 +21,7 @@ declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fu
21
21
  * write the same comma-separated `value`.
22
22
  *
23
23
  * @since 0.1.0
24
- * @status experimental
24
+ * @status stable
25
25
  * @category forms
26
26
  * @aliases Calendar, Date grid, Month view
27
27
  * @documentation https://fusion.dev/helix/components/date-picker
@@ -30,7 +30,7 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
30
30
  * "yesterday".
31
31
  *
32
32
  * @since 0.1.0
33
- * @status experimental
33
+ * @status stable
34
34
  * @category typography
35
35
  * @aliases RelativeTime, Timestamp, TimeAgo
36
36
  * @documentation https://fusion.dev/helix/components/format-date
@@ -15,7 +15,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
15
15
  * `Skeleton` would make this tier depend on a component it does not ship.
16
16
  *
17
17
  * @since 0.1.0
18
- * @status experimental
18
+ * @status stable
19
19
  * @category media
20
20
  * @aliases Picture, Img, Figure
21
21
  * @documentation https://fusion.dev/helix/components/image
@@ -101,20 +101,30 @@ export declare class HelixHeadlessOption extends HelixElement {
101
101
  *
102
102
  * `<option selected>` reflects to `defaultSelected` and `option.selected` is
103
103
  * live state that does not reflect, and those are two different facts. This
104
- * element has one `selected` doing both jobs, so what is captured here is what
105
- * the markup said at upgrade, before anything could overwrite it: read the
106
- * attribute later and the listbox's own writes answer instead.
107
- *
108
- * **The deeper half is 302 and is not this.** State reflecting under the
109
- * declaration's name is the fault `TreeItem` already avoids by mirroring to
110
- * `data-selected`, and four families here still write `selected` back into a
111
- * consumer's markup. Splitting them means moving what every stylesheet selects
112
- * on, which is a change of its own rather than a property being added.
104
+ * element has one `selected` doing both jobs, so the declaration is the
105
+ * attribute and the state is the property.
106
+ *
107
+ * **The attribute, read when it is asked for, and never a snapshot taken in
108
+ * the constructor.** A field initializer runs there, and an element the HTML
109
+ * parser created has none of its attributes yet at that moment: measured on
110
+ * this element, on `Radio` and on `Toggle`, `<helix-option value="a"
111
+ * selected>` reported `defaultSelected` as `false` both from parsed markup
112
+ * and from `createElement` followed by `setAttribute`, which is every way a
113
+ * consumer writes one. The snapshot could only ever be true for an element
114
+ * already in the document when `customElements.define` ran.
115
+ *
116
+ * Nothing overwrites it either, which is what makes reading it late correct
117
+ * rather than merely later: `selected` does not reflect, for the reason
118
+ * `willUpdate` records, so the listbox's writes never reach the attribute and
119
+ * what it says is what the markup said.
120
+ *
121
+ * **The deeper half is not this.** State reflecting under the declaration's
122
+ * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
123
+ * Splitting them means moving what every stylesheet selects on, which is a
124
+ * change of its own rather than a property being added.
113
125
  */
114
126
  get defaultSelected(): boolean;
115
127
  set defaultSelected(next: boolean);
116
- /** What the markup said, read once at construction for the reason above. */
117
- private seeded;
118
128
  /**
119
129
  * A second line under the words, for what the choice actually is.
120
130
  *
@@ -17,17 +17,16 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
17
17
  this.active = false;
18
18
  this.alternate = '';
19
19
  this.alternateLabel = '';
20
- this.seeded = this.hasAttribute('selected');
21
20
  this.disabled = false;
22
21
  this.label = '';
23
22
  this.selected = false;
24
23
  this.value = '';
25
24
  }
26
25
  get defaultSelected() {
27
- return this.seeded;
26
+ return this.hasAttribute('selected');
28
27
  }
29
28
  set defaultSelected(next) {
30
- this.seeded = next;
29
+ this.toggleAttribute('selected', next);
31
30
  }
32
31
  get text() {
33
32
  return [...this.childNodes]
@@ -51,9 +51,22 @@ export declare class HelixHeadlessRadio extends HelixElement {
51
51
  *
52
52
  * `<option selected>` reflects to `defaultSelected` and `option.selected` is
53
53
  * live state that does not reflect, and those are two different facts. This
54
- * element has one `selected` doing both jobs, so what is captured here is what
55
- * the markup said at upgrade, before anything could overwrite it: read the
56
- * attribute later and the listbox's own writes answer instead.
54
+ * element has one `selected` doing both jobs, so the declaration is the
55
+ * attribute and the state is the property.
56
+ *
57
+ * **The attribute, read when it is asked for, and never a snapshot taken in
58
+ * the constructor.** A field initializer runs there, and an element the HTML
59
+ * parser created has none of its attributes yet at that moment: measured on
60
+ * this element, on `Option` and on `Toggle`, `<helix-radio value="a"
61
+ * selected>` reported `defaultSelected` as `false` both from parsed markup
62
+ * and from `createElement` followed by `setAttribute`, which is every way a
63
+ * consumer writes one. The snapshot could only ever be true for an element
64
+ * already in the document when `customElements.define` ran.
65
+ *
66
+ * Nothing overwrites it either, which is what makes reading it late correct
67
+ * rather than merely later: `selected` does not reflect, for the reason
68
+ * `willUpdate` records, so the group's writes never reach the attribute and
69
+ * what it says is what the markup said.
57
70
  *
58
71
  * **The deeper half is not this.** State reflecting under the declaration's
59
72
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
@@ -62,8 +75,6 @@ export declare class HelixHeadlessRadio extends HelixElement {
62
75
  */
63
76
  get defaultSelected(): boolean;
64
77
  set defaultSelected(next: boolean);
65
- /** What the markup said, read once at construction for the reason above. */
66
- private seeded;
67
78
  /**
68
79
  * Whether this is the current choice.
69
80
  *
@@ -11,15 +11,14 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.disabled = false;
14
- this.seeded = this.hasAttribute('selected');
15
14
  this.selected = false;
16
15
  this.value = '';
17
16
  }
18
17
  get defaultSelected() {
19
- return this.seeded;
18
+ return this.hasAttribute('selected');
20
19
  }
21
20
  set defaultSelected(next) {
22
- this.seeded = next;
21
+ this.toggleAttribute('selected', next);
23
22
  }
24
23
  focusItem() {
25
24
  this.partNamed('item')?.focus();
@@ -23,8 +23,15 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
23
23
  * **A range is two of them.** There is no native two-thumb range, so two are
24
24
  * stacked on one track and each thumb keeps its own.
25
25
  *
26
+ * **`required` arrives with the field mixin and cannot fail here**, which is
27
+ * worth saying rather than leaving a reader to find. A range is never empty:
28
+ * the platform shows its own midpoint whatever is set, and `willUpdate` seeds
29
+ * that midpoint into `defaultValue` so the value agrees with the handle from the
30
+ * first render. HTML does not apply `required` to a range either, so it stays
31
+ * off the control and the marker beside the label is all it does.
32
+ *
26
33
  * @since 0.1.0
27
- * @status experimental
34
+ * @status stable
28
35
  * @category forms
29
36
  * @aliases Range, Range input
30
37
  * @documentation https://fusion.dev/helix/components/slider
@@ -44,9 +51,9 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
44
51
  * @csspart output - The current value, shown beside it.
45
52
  *
46
53
  * @cssproperty --slider-fraction - The value as a fraction of the range, 0 to 1.
47
- * @cssproperty --slider-from - Where the filled span starts, as a percentage of the track.
54
+ * @cssproperty --slider-from - Where a range's filled span starts, as a fraction of the track, 0 to 1.
48
55
  * @cssproperty --slider-marker-at - Where one marker sits, as a percentage of the track.
49
- * @cssproperty --slider-to - Where the filled span ends, as a percentage of the track.
56
+ * @cssproperty --slider-to - Where a range's filled span ends, as a fraction of the track, 0 to 1.
50
57
  *
51
58
  * @cssstate invalid - While there is feedback to show: the platform's verdict, or the consumer's `feedback`.
52
59
  */
@@ -16,11 +16,13 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
16
16
  * it has somewhere to be, which is the harmless direction.
17
17
  *
18
18
  * @since 0.1.0
19
- * @status experimental
19
+ * @status stable
20
20
  * @aliases SegmentedControl, Segmented, Navigation, Switcher
21
21
  * @documentation https://fusion.dev/helix/components/toggle
22
22
  *
23
23
  * @slot - The group's items.
24
+ * @slot end - Whatever sits after the track on its own line, such as a count or a reset.
25
+ * @slot start - Whatever sits before the track on its own line, such as an icon or a caption.
24
26
  *
25
27
  * @event {Event} change - Announced once the value has moved.
26
28
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the answer moves, and cancelable. The only way to refuse one.
@@ -38,6 +40,48 @@ declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@f
38
40
  * @cssstate invalid - While there is feedback to show: the platform's verdict, or the consumer's `feedback`.
39
41
  */
40
42
  export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_base {
43
+ /**
44
+ * Takes the markup's own answer where nothing has set a value.
45
+ *
46
+ * **The case a server renders**, and `Tabs` records the same fault from the
47
+ * other side. This element cannot choose on the server, having no children to
48
+ * count, so a consumer whose first paint is right wrote `selected` on the
49
+ * item: that serves `aria-checked="true"`, and `sync` then cleared it one
50
+ * frame later, because `value` is `''` and nothing matches it. The shipped
51
+ * `disabled` example is exactly that markup, and drew both of its groups with
52
+ * nothing chosen.
53
+ *
54
+ * **Onto `defaultValue`, never onto `value`**, which is the same call
55
+ * `Slider` makes about the midpoint it seeds: what the markup declared is a
56
+ * fact about the markup, so it is where a reset returns to rather than
57
+ * something the reader chose. It also makes the group report the answer it is
58
+ * showing, which marking the item alone would not: a `required` group would
59
+ * otherwise tell a reader a value is missing while they are looking at one.
60
+ *
61
+ * A written `value` still wins, a declared property being a seed and a
62
+ * correction, and a group with neither still marks nothing at all.
63
+ *
64
+ * **The property and never `defaultSelected`, which is the attribute.** React
65
+ * assigns a JSX prop as a property where the custom element has one, and
66
+ * writes no attribute at all, so keying on the attribute answers for markup
67
+ * and not for JSX. Measured by the parity check on the shipped `disabled`
68
+ * example, whose two syntaxes are the same tree: 192 pixels apart, the HTML
69
+ * marking `List` and the TSX marking nothing. The attribute seeds the
70
+ * property at upgrade, so reading the property covers both.
71
+ *
72
+ * Read before `sync` writes to it, which is the only moment it still holds
73
+ * what the consumer declared: after that it holds this group's answer, and
74
+ * `value` is no longer empty, so this returns above.
75
+ *
76
+ * **Called from `willUpdate` and from the slot, never from `sync`**, which
77
+ * runs in `updated`: a reactive property set there schedules a second update,
78
+ * and Lit's development build warns about exactly that once per group. From
79
+ * `willUpdate` the first render already carries the value, which is the pass
80
+ * that has to be right. `children` is undefined on the server, whose element
81
+ * shim carries attributes and no tree, and that is the same guard
82
+ * `MixinCollection` records for the same reason.
83
+ */
84
+ private adopt;
41
85
  /** Marks the chosen item, separately from placing the indicator. */
42
86
  private sync;
43
87
  private readonly indicator;
@@ -25,6 +25,15 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
25
25
  this.requestValue(event.detail.value);
26
26
  };
27
27
  }
28
+ adopt() {
29
+ if (this.value !== '' || this.children === undefined) {
30
+ return;
31
+ }
32
+ const declared = this.items.find((item) => item.selected);
33
+ if (declared) {
34
+ this.defaultValue = declared.value;
35
+ }
36
+ }
28
37
  sync() {
29
38
  const active = this.items.find((item) => item.value === this.value);
30
39
  const entry = active ? undefined : this.items.find((item) => !item.disabled);
@@ -57,6 +66,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
57
66
  }
58
67
  willUpdate(changed) {
59
68
  super.willUpdate(changed);
69
+ this.adopt();
60
70
  hostAria(this.internals, { label: this.label, role: 'radiogroup' });
61
71
  this.internals.setFormValue(this.value === '' ? null : this.value);
62
72
  }
@@ -74,6 +84,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
74
84
  <div part="indicator" aria-hidden="true"></div>
75
85
  <slot
76
86
  @slotchange=${() => {
87
+ this.adopt();
77
88
  this.sync();
78
89
  this.indicator.place();
79
90
  }}
@@ -11,7 +11,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
11
11
  * without radio semantics announcing destinations as choices.
12
12
  *
13
13
  * @since 0.1.0
14
- * @status experimental
14
+ * @status stable
15
15
  * @category actions
16
16
  * @aliases Segment, NavItem, Option
17
17
  * @documentation https://fusion.dev/helix/components/toggle
@@ -34,9 +34,22 @@ export declare class HelixHeadlessToggle extends HelixElement {
34
34
  *
35
35
  * `<option selected>` reflects to `defaultSelected` and `option.selected` is
36
36
  * live state that does not reflect, and those are two different facts. This
37
- * element has one `selected` doing both jobs, so what is captured here is what
38
- * the markup said at upgrade, before anything could overwrite it: read the
39
- * attribute later and the listbox's own writes answer instead.
37
+ * element has one `selected` doing both jobs, so the declaration is the
38
+ * attribute and the state is the property.
39
+ *
40
+ * **The attribute, read when it is asked for, and never a snapshot taken in
41
+ * the constructor.** A field initializer runs there, and an element the HTML
42
+ * parser created has none of its attributes yet at that moment: measured on
43
+ * this element, on `Option` and on `Radio`, `<helix-toggle value="light"
44
+ * selected>` reported `defaultSelected` as `false` both from parsed markup
45
+ * and from `createElement` followed by `setAttribute`, which is every way a
46
+ * consumer writes one. The snapshot could only ever be true for an element
47
+ * already in the document when `customElements.define` ran.
48
+ *
49
+ * Nothing overwrites it either, which is what makes reading it late correct
50
+ * rather than merely later: `selected` does not reflect, for the reason
51
+ * `willUpdate` records, so the group's writes never reach the attribute and
52
+ * what it says is what the markup said.
40
53
  *
41
54
  * **The deeper half is not this.** State reflecting under the declaration's
42
55
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
@@ -45,8 +58,6 @@ export declare class HelixHeadlessToggle extends HelixElement {
45
58
  */
46
59
  get defaultSelected(): boolean;
47
60
  set defaultSelected(next: boolean);
48
- /** What the markup said, read once at construction for the reason above. */
49
- private seeded;
50
61
  /**
51
62
  * Whether this item holds the group's tab stop while nothing is chosen.
52
63
  *
@@ -11,16 +11,15 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.disabled = false;
14
- this.seeded = this.hasAttribute('selected');
15
14
  this.focusable = false;
16
15
  this.selected = false;
17
16
  this.value = '';
18
17
  }
19
18
  get defaultSelected() {
20
- return this.seeded;
19
+ return this.hasAttribute('selected');
21
20
  }
22
21
  set defaultSelected(next) {
23
- this.seeded = next;
22
+ this.toggleAttribute('selected', next);
24
23
  }
25
24
  focusItem() {
26
25
  this.partNamed('item')?.focus();
@@ -32,7 +32,7 @@ import '../../menu/index.js';
32
32
  * changes.
33
33
  *
34
34
  * @since 0.1.0
35
- * @status experimental
35
+ * @status stable
36
36
  * @category media
37
37
  * @aliases Player, Media, Movie
38
38
  * @documentation https://fusion.dev/helix/components/video
@@ -23,7 +23,7 @@ import { type TemplateResult } from 'lit';
23
23
  * the player is on.
24
24
  *
25
25
  * @since 0.1.0
26
- * @status experimental
26
+ * @status stable
27
27
  * @category media
28
28
  * @documentation https://fusion.dev/helix/components/video-playlist
29
29
  *
@@ -16,7 +16,7 @@ import '../../menu/index.js';
16
16
  * `Video`'s `controls` slot is for.
17
17
  *
18
18
  * @since 0.1.0
19
- * @status experimental
19
+ * @status stable
20
20
  * @category media
21
21
  * @aliases Episode Menu, Queue Menu, Up Next
22
22
  * @documentation https://fusion.dev/helix/components/video-playlist
@@ -29,7 +29,7 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
29
29
  * here: the reader already pressed play once.
30
30
  *
31
31
  * @since 0.1.0
32
- * @status experimental
32
+ * @status stable
33
33
  * @category media
34
34
  * @aliases Playlist, Queue, Up next
35
35
  * @documentation https://fusion.dev/helix/components/video-playlist
@@ -14,6 +14,9 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
14
14
  this.chased = false;
15
15
  this.handleSelect = (event) => {
16
16
  event.stopImmediatePropagation();
17
+ if (event.defaultPrevented) {
18
+ return;
19
+ }
17
20
  this.chased = true;
18
21
  this.requestActive(event.detail.value);
19
22
  };