@fusion.dev/helix-headless 0.44.0 → 0.46.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.
Files changed (32) hide show
  1. package/custom-elements.json +104 -19
  2. package/dist/carousel/root/carousel-root.d.ts +1 -1
  3. package/dist/carousel/root/carousel-root.js +13 -3
  4. package/dist/chat/message/chat-message.d.ts +14 -1
  5. package/dist/chat/message/chat-message.js +9 -2
  6. package/dist/checkbox-group/root/checkbox-group-root.js +3 -0
  7. package/dist/color-picker/root/color-picker-root.d.ts +19 -0
  8. package/dist/color-picker/root/color-picker-root.js +4 -0
  9. package/dist/drilldown/root/drilldown-root.js +5 -1
  10. package/dist/file-input/root/file-input-root.d.ts +45 -3
  11. package/dist/file-input/root/file-input-root.js +37 -25
  12. package/dist/filter-input/chip/filter-chip.d.ts +1 -1
  13. package/dist/filter-input/field/filter-field.d.ts +1 -1
  14. package/dist/filter-input/menu/filter-menu.d.ts +1 -1
  15. package/dist/filter-input/operator/filter-operator.d.ts +1 -1
  16. package/dist/filter-input/root/filter-input-root.d.ts +1 -1
  17. package/dist/listbox/root/listbox-root.js +3 -0
  18. package/dist/option/group/option-group.d.ts +24 -1
  19. package/dist/option/group/option-group.js +8 -0
  20. package/dist/option/root/option-root.d.ts +1 -1
  21. package/dist/radio/group/radio-group.d.ts +10 -1
  22. package/dist/radio/group/radio-group.js +7 -8
  23. package/dist/radio/root/radio-root.d.ts +18 -1
  24. package/dist/radio/root/radio-root.js +5 -1
  25. package/dist/skip-link/root/skip-link-root.d.ts +1 -1
  26. package/dist/steps/root/steps-root.js +3 -0
  27. package/dist/tabs/root/tabs-root.js +3 -0
  28. package/dist/toggle/group/toggle-group.js +3 -0
  29. package/dist/tree/root/tree-root.js +3 -1
  30. package/package.json +5 -5
  31. package/types/jsx.d.ts +94 -14
  32. package/types/vue.d.ts +22 -4
@@ -43,7 +43,7 @@ import type { HelixHeadlessFilterOperator } from '../operator/filter-operator.js
43
43
  * what fills a chip then is whatever the consumer types.
44
44
  *
45
45
  * @since 0.23.0
46
- * @status experimental
46
+ * @status stable
47
47
  * @category forms
48
48
  * @aliases Facet, Dimension
49
49
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -50,7 +50,7 @@ import '../../menu/index.js';
50
50
  * rather than inventing one. Refusing `helix-facet` refuses the change.
51
51
  *
52
52
  * @since 0.25.0
53
- * @status experimental
53
+ * @status stable
54
54
  * @category forms
55
55
  * @aliases FacetPicker, FilterBuilder
56
56
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -23,7 +23,7 @@ import { nothing, type PropertyValues } from 'lit';
23
23
  * consumer bug and the field reports it rather than guessing.
24
24
  *
25
25
  * @since 0.23.0
26
- * @status experimental
26
+ * @status stable
27
27
  * @category forms
28
28
  * @aliases Comparator, Predicate
29
29
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -83,7 +83,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
83
83
  * and the chip goes because the filter went.
84
84
  *
85
85
  * @since 0.1.0
86
- * @status experimental
86
+ * @status stable
87
87
  * @category forms
88
88
  * @aliases SearchField, FacetInput, TokenInput
89
89
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -77,6 +77,9 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
77
77
  });
78
78
  this.handleSelection = (event) => {
79
79
  event.stopPropagation();
80
+ if (event.defaultPrevented) {
81
+ return;
82
+ }
80
83
  this.activeValue = event.detail.value;
81
84
  this.choose(event.detail.value);
82
85
  };
@@ -13,7 +13,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
13
13
  * an option is in without the heading becoming something to land on.
14
14
  *
15
15
  * @since 0.1.0
16
- * @status experimental
16
+ * @status stable
17
17
  * @aliases OptGroup, Section
18
18
  * @documentation https://fusion.dev/helix/components/option
19
19
  *
@@ -29,6 +29,29 @@ export declare class HelixHeadlessOptionGroup extends HelixElement {
29
29
  */
30
30
  label: string;
31
31
  protected willUpdate(changed: PropertyValues): void;
32
+ protected updated(changed: PropertyValues): void;
33
+ /**
34
+ * Names the run after the heading it drew, whatever the heading is made of.
35
+ *
36
+ * **The string alone was not the name of the heading, it was the name of one
37
+ * way of writing it.** `label` reached `ariaLabel` and the slot reached the
38
+ * screen, so a consumer who took the documented `label` slot for a heading
39
+ * richer than a string got a `role="group"` with no accessible name at all,
40
+ * and the words they wrote were the one thing a reader could not hear: the
41
+ * heading carries `aria-hidden`, because the name was supposed to be on the
42
+ * host already. Set both and they disagreed, the host saying the string and
43
+ * the panel showing the slot.
44
+ *
45
+ * So the host points at the heading, which is the one thing that is always
46
+ * what a reader sees. `ElementInternals` and not an IDREF, because the host
47
+ * is in the light DOM and the heading is in this shadow root, which is the
48
+ * boundary `aria-labelledby` cannot cross. `MixinField.linkHost` does the
49
+ * same for a field's label and records the measurement.
50
+ *
51
+ * Client only. Element references need the nodes to exist, and the server has
52
+ * the string.
53
+ */
54
+ private nameFromHeading;
32
55
  protected render(): TemplateResult;
33
56
  }
34
57
  declare global {
@@ -16,6 +16,14 @@ let HelixHeadlessOptionGroup = class HelixHeadlessOptionGroup extends HelixEleme
16
16
  super.willUpdate(changed);
17
17
  hostAria(this.internals, { label: this.label, role: 'group' });
18
18
  }
19
+ updated(changed) {
20
+ super.updated(changed);
21
+ this.nameFromHeading();
22
+ }
23
+ nameFromHeading() {
24
+ const heading = this.partNamed('label');
25
+ hostAria(this.internals, { labelledBy: heading ? [heading] : [] });
26
+ }
19
27
  render() {
20
28
  return html `<span part="label" aria-hidden="true">
21
29
  <slot name="label">${this.label}</slot>
@@ -41,7 +41,7 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
41
41
  * question and differs between a select and a command palette.
42
42
  *
43
43
  * @since 0.1.0
44
- * @status experimental
44
+ * @status stable
45
45
  * @category forms
46
46
  * @aliases Item, Choice
47
47
  * @documentation https://fusion.dev/helix/components/option
@@ -26,7 +26,7 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
26
26
  * anything is chosen.
27
27
  *
28
28
  * @since 0.1.0
29
- * @status experimental
29
+ * @status stable
30
30
  * @aliases RadioButtons, Choice group
31
31
  * @documentation https://fusion.dev/helix/components/radio
32
32
  *
@@ -67,6 +67,15 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
67
67
  *
68
68
  * The item has not moved by the time this runs, unlike a native input, so
69
69
  * refusing is simply not writing the value: there is nothing to put back.
70
+ *
71
+ * **The radio's own `helix-select` is a request, and the argument that it was
72
+ * a notification is the one this settles.** That argument ran: the group's
73
+ * veto is `helix-before-change` a step later, so the item could be read as
74
+ * announcing a choice the group then decides about. What it leaves out is who
75
+ * can reach which event. `helix-before-change` is dispatched on the group, so
76
+ * refusing it needs a listener on the group, and a consumer ruling out one
77
+ * answer binds to that answer. Two gates in two positions is the shape, and
78
+ * the item's is the one that was doing nothing.
70
79
  */
71
80
  private readonly handleSelection;
72
81
  /** Writes the answer onto the items, and nothing onto the host. */
@@ -17,6 +17,9 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
17
17
  });
18
18
  this.handleSelection = (event) => {
19
19
  event.stopPropagation();
20
+ if (event.defaultPrevented) {
21
+ return;
22
+ }
20
23
  const next = event.detail.value;
21
24
  if (next === this.value) {
22
25
  return;
@@ -29,17 +32,13 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
29
32
  }
30
33
  place() {
31
34
  const items = this.items;
35
+ const answer = items.find((item) => item.value === this.value);
32
36
  for (const item of items) {
33
- item.selected = item.value === this.value;
37
+ item.selected = item === answer;
34
38
  }
35
- const entered = this.value === '' && items.length > 0 ? items[0] : undefined;
39
+ const entry = answer ? undefined : items.find((item) => !item.disabled);
36
40
  for (const item of items) {
37
- if (item === entered) {
38
- item.setAttribute('tabindex', '0');
39
- }
40
- else {
41
- item.removeAttribute('tabindex');
42
- }
41
+ item.focusable = item === entry;
43
42
  }
44
43
  }
45
44
  sync() {
@@ -20,7 +20,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
20
20
  * be a second source of truth for which one is on.
21
21
  *
22
22
  * @since 0.1.0
23
- * @status experimental
23
+ * @status stable
24
24
  * @category forms
25
25
  * @aliases Option, Choice
26
26
  * @documentation https://fusion.dev/helix/components/radio
@@ -75,6 +75,23 @@ export declare class HelixHeadlessRadio extends HelixElement {
75
75
  */
76
76
  get defaultSelected(): boolean;
77
77
  set defaultSelected(next: boolean);
78
+ /**
79
+ * Whether this choice holds the group's tab stop while nothing is chosen.
80
+ *
81
+ * The stop rides on the selection, and an unanswered group has no selection,
82
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
83
+ * enter the set at all.
84
+ *
85
+ * **Written by the group, with no attribute, and it reaches the control
86
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
87
+ * is markup a consumer rendered and their server never sent, which React
88
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
89
+ * the element rather than on the `<button role="radio">` inside it, so a
90
+ * screen reader meets an unnamed box where a native group would have given
91
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
92
+ * same reason.
93
+ */
94
+ focusable: boolean;
78
95
  /**
79
96
  * Whether this is the current choice.
80
97
  *
@@ -11,6 +11,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.disabled = false;
14
+ this.focusable = false;
14
15
  this.selected = false;
15
16
  this.value = '';
16
17
  }
@@ -40,7 +41,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
40
41
  role="radio"
41
42
  aria-checked=${this.selected ? 'true' : 'false'}
42
43
  ?disabled=${this.disabled}
43
- tabindex=${this.selected ? '0' : '-1'}
44
+ tabindex=${this.selected || this.focusable ? '0' : '-1'}
44
45
  @click=${() => this.select()}
45
46
  >
46
47
  <span part="box"><span part="mark" aria-hidden="true"></span></span>
@@ -52,6 +53,9 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
52
53
  __decorate([
53
54
  property({ reflect: true, type: Boolean })
54
55
  ], HelixHeadlessRadio.prototype, "disabled", void 0);
56
+ __decorate([
57
+ property({ attribute: false })
58
+ ], HelixHeadlessRadio.prototype, "focusable", void 0);
55
59
  __decorate([
56
60
  property({ type: Boolean })
57
61
  ], HelixHeadlessRadio.prototype, "selected", void 0);
@@ -32,7 +32,7 @@ import { type TemplateResult } from 'lit';
32
32
  * broken.
33
33
  *
34
34
  * @since 0.1.0
35
- * @status experimental
35
+ * @status stable
36
36
  * @category navigation
37
37
  * @aliases Skip navigation, Skip to content
38
38
  * @documentation https://fusion.dev/helix/components/skip-link
@@ -12,6 +12,9 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
12
12
  super(...arguments);
13
13
  this.label = 'Steps';
14
14
  this.handleSelection = (event) => {
15
+ if (event.defaultPrevented) {
16
+ return;
17
+ }
15
18
  this.requestActive(event.detail.value);
16
19
  };
17
20
  this.handleSlotChange = () => {
@@ -26,6 +26,9 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
26
26
  this.label = 'Tabs';
27
27
  this.manual = false;
28
28
  this.handleSelection = (event) => {
29
+ if (event.defaultPrevented) {
30
+ return;
31
+ }
29
32
  this.requestActive(event.detail.value);
30
33
  };
31
34
  this.handleSlotChange = () => {
@@ -22,6 +22,9 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
22
22
  items: () => this.items,
23
23
  });
24
24
  this.handleSelection = (event) => {
25
+ if (event.defaultPrevented) {
26
+ return;
27
+ }
25
28
  this.requestValue(event.detail.value);
26
29
  };
27
30
  }
@@ -23,7 +23,9 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
23
23
  super(...arguments);
24
24
  this.label = 'Tree';
25
25
  this.handleSelection = (event) => {
26
- this.requestActive(event.detail.value);
26
+ if (!event.defaultPrevented) {
27
+ this.requestActive(event.detail.value);
28
+ }
27
29
  this.syncItems();
28
30
  };
29
31
  this.handleKeydown = (event) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.44.0",
4
+ "version": "0.46.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,14 +46,14 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.44.0",
50
- "@fusion.dev/helix-icons": "0.44.0",
49
+ "@fusion.dev/helix-core": "0.46.0",
50
+ "@fusion.dev/helix-icons": "0.46.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@custom-elements-manifest/analyzer": "^0.11.0",
55
- "@fusion.dev/helix-cem": "0.44.0",
56
- "@fusion.dev/helix-testing": "0.44.0",
55
+ "@fusion.dev/helix-cem": "0.46.0",
56
+ "@fusion.dev/helix-testing": "0.46.0",
57
57
  "@fusion.dev/scripts": "0.0.0-dev",
58
58
  "@happy-dom/global-registrator": "^20.0.0",
59
59
  "@lit-labs/ssr": "^4.1.0",
package/types/jsx.d.ts CHANGED
@@ -1861,7 +1861,7 @@ server markup never had, which React reports as a mismatch. The one-across
1861
1861
  case is the absent attribute, and the styles read it that way. */
1862
1862
  perView?: (HelixHeadlessCarousel["perView"]) | undefined;
1863
1863
 
1864
- /** 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`. */
1864
+ /** Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`. */
1865
1865
  "onhelix-select"?: ((e: HelixHeadlessCarouselHelixSelectElementEvent) => void) | undefined;
1866
1866
  };
1867
1867
 
@@ -1961,7 +1961,7 @@ its attribute on upgrade, and the host then carries something a consumer's
1961
1961
  server markup never had, which React reports as a mismatch. The one-across
1962
1962
  case is the absent attribute, and the styles read it that way. */
1963
1963
  "prop:perView"?: (HelixHeadlessCarousel["perView"]) | undefined;
1964
- /** 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`. */
1964
+ /** Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`. */
1965
1965
  "on:helix-select"?: ((e: HelixHeadlessCarouselHelixSelectElementEvent) => void) | undefined;
1966
1966
 
1967
1967
  /** Set the innerHTML of the element */
@@ -2097,10 +2097,13 @@ Reflected, because every one of the four is a visual state and a stylesheet
2097
2097
  has no other way to know. Absent is a settled message with nothing to
2098
2098
  report, which is what most of a transcript is, and it is the absence of the
2099
2099
  attribute rather than a value of it: there is no initializer, so nothing is
2100
- reflected until something happens. */
2100
+ reflected until something happens.
2101
+
2102
+ **The turn writes this once, on a retry nobody refused**, and `handleRetry`
2103
+ holds the reason. Everything else that moves it is the consumer's. */
2101
2104
  state?: (HelixHeadlessChatMessage["state"]) | undefined;
2102
2105
 
2103
- /** Fired when the reader asks to send a failed message again, and cancelable. */
2106
+ /** Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were. */
2104
2107
  "onhelix-send"?: ((e: HelixHeadlessChatMessageHelixSendElementEvent) => void) | undefined;
2105
2108
  };
2106
2109
 
@@ -2137,9 +2140,12 @@ Reflected, because every one of the four is a visual state and a stylesheet
2137
2140
  has no other way to know. Absent is a settled message with nothing to
2138
2141
  report, which is what most of a transcript is, and it is the absence of the
2139
2142
  attribute rather than a value of it: there is no initializer, so nothing is
2140
- reflected until something happens. */
2143
+ reflected until something happens.
2144
+
2145
+ **The turn writes this once, on a retry nobody refused**, and `handleRetry`
2146
+ holds the reason. Everything else that moves it is the consumer's. */
2141
2147
  "prop:state"?: (HelixHeadlessChatMessage["state"]) | undefined;
2142
- /** Fired when the reader asks to send a failed message again, and cancelable. */
2148
+ /** Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were. */
2143
2149
  "on:helix-send"?: ((e: HelixHeadlessChatMessageHelixSendElementEvent) => void) | undefined;
2144
2150
 
2145
2151
  /** Set the innerHTML of the element */
@@ -6195,7 +6201,7 @@ Here rather than on each control. */
6195
6201
 
6196
6202
  /** Announced once the value has moved. */
6197
6203
  onchange?: ((e: HelixHeadlessFileInputElementEvent) => void) | undefined;
6198
- /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves. */
6204
+ /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included. */
6199
6205
  "onhelix-before-change"?: ((
6200
6206
  e: HelixHeadlessFileInputHelixBeforeChangeElementEvent,
6201
6207
  ) => void) | undefined;
@@ -6258,7 +6264,7 @@ Here rather than on each control. */
6258
6264
  "prop:name"?: (HelixHeadlessFileInput["name"]) | undefined;
6259
6265
  /** Announced once the value has moved. */
6260
6266
  "on:change"?: ((e: HelixHeadlessFileInputElementEvent) => void) | undefined;
6261
- /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves. */
6267
+ /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included. */
6262
6268
  "on:helix-before-change"?: ((
6263
6269
  e: HelixHeadlessFileInputHelixBeforeChangeElementEvent,
6264
6270
  ) => void) | undefined;
@@ -11715,6 +11721,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
11715
11721
  Splitting them means moving what every stylesheet selects on, which is a
11716
11722
  change of its own rather than a property being added. */
11717
11723
  defaultSelected?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
11724
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
11725
+
11726
+ The stop rides on the selection, and an unanswered group has no selection,
11727
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
11728
+ enter the set at all.
11729
+
11730
+ **Written by the group, with no attribute, and it reaches the control
11731
+ rather than the host.** A `tabindex` on the host is two faults at once: it
11732
+ is markup a consumer rendered and their server never sent, which React
11733
+ reports as a mismatch and does not patch up, and it puts the tab stop on
11734
+ the element rather than on the `<button role="radio">` inside it, so a
11735
+ screen reader meets an unnamed box where a native group would have given
11736
+ them an unchecked radio. `Toggle.Root` carries the same property for the
11737
+ same reason. */
11738
+ focusable?: (HelixHeadlessRadio["focusable"]) | undefined;
11718
11739
 
11719
11740
  /** Fired when this choice is made. Cancelable: refusing it takes the decision and nothing moves. */
11720
11741
  "onhelix-select"?: ((e: HelixHeadlessRadioHelixSelectElementEvent) => void) | undefined;
@@ -11755,6 +11776,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
11755
11776
  Splitting them means moving what every stylesheet selects on, which is a
11756
11777
  change of its own rather than a property being added. */
11757
11778
  "prop:defaultSelected"?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
11779
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
11780
+
11781
+ The stop rides on the selection, and an unanswered group has no selection,
11782
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
11783
+ enter the set at all.
11784
+
11785
+ **Written by the group, with no attribute, and it reaches the control
11786
+ rather than the host.** A `tabindex` on the host is two faults at once: it
11787
+ is markup a consumer rendered and their server never sent, which React
11788
+ reports as a mismatch and does not patch up, and it puts the tab stop on
11789
+ the element rather than on the `<button role="radio">` inside it, so a
11790
+ screen reader meets an unnamed box where a native group would have given
11791
+ them an unchecked radio. `Toggle.Root` carries the same property for the
11792
+ same reason. */
11793
+ "prop:focusable"?: (HelixHeadlessRadio["focusable"]) | undefined;
11758
11794
  /** Fired when this choice is made. Cancelable: refusing it takes the decision and nothing moves. */
11759
11795
  "on:helix-select"?: ((e: HelixHeadlessRadioHelixSelectElementEvent) => void) | undefined;
11760
11796
 
@@ -19148,7 +19184,7 @@ export type CustomElements = {
19148
19184
  *
19149
19185
  * Events that will be emitted by the component.
19150
19186
  *
19151
- * - `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`.
19187
+ * - `helix-select`: Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`.
19152
19188
  *
19153
19189
  * ## Slots
19154
19190
  *
@@ -19336,6 +19372,9 @@ export type CustomElements = {
19336
19372
  * report, which is what most of a transcript is, and it is the absence of the
19337
19373
  * attribute rather than a value of it: there is no initializer, so nothing is
19338
19374
  * reflected until something happens.
19375
+ *
19376
+ * **The turn writes this once, on a retry nobody refused**, and `handleRetry`
19377
+ * holds the reason. Everything else that moves it is the consumer's.
19339
19378
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
19340
19379
  *
19341
19380
  * **Named `whenSettled` because `settled` is taken four times over**, by
@@ -19361,7 +19400,7 @@ export type CustomElements = {
19361
19400
  *
19362
19401
  * Events that will be emitted by the component.
19363
19402
  *
19364
- * - `helix-send`: Fired when the reader asks to send a failed message again, and cancelable.
19403
+ * - `helix-send`: Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were.
19365
19404
  *
19366
19405
  * ## Slots
19367
19406
  *
@@ -23208,7 +23247,7 @@ export type CustomElements = {
23208
23247
  * Events that will be emitted by the component.
23209
23248
  *
23210
23249
  * - `change`: Announced once the value has moved.
23211
- * - `helix-before-change`: Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves.
23250
+ * - `helix-before-change`: Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included.
23212
23251
  * - `input`: Announced once the value has moved.
23213
23252
  *
23214
23253
  * ## Slots
@@ -23239,6 +23278,11 @@ export type CustomElements = {
23239
23278
  * picker come back out the same way.
23240
23279
  * - `focus(options?: FocusOptions) => void`: Puts focus on the input, the way a label pointing at one would.
23241
23280
  * - `formResetCallback() => void`: Empties it, which is what a form reset means for a file input.
23281
+ *
23282
+ * Not through `commit`, and that is the point of the split: a reset is the
23283
+ * form's decision and there is nothing here for a listener to refuse. What it
23284
+ * owes is the report and the platform's own announcement, which is what
23285
+ * `settle` is.
23242
23286
  * - `formDisabledCallback(disabled: boolean) => void`: The platform's notice that an ancestor fieldset changed.
23243
23287
  *
23244
23288
  * **The argument is the effective state, and taking it whole is what latched
@@ -29054,6 +29098,20 @@ export type CustomElements = {
29054
29098
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
29055
29099
  * Splitting them means moving what every stylesheet selects on, which is a
29056
29100
  * change of its own rather than a property being added. (property only)
29101
+ * - `focusable`: Whether this choice holds the group's tab stop while nothing is chosen.
29102
+ *
29103
+ * The stop rides on the selection, and an unanswered group has no selection,
29104
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
29105
+ * enter the set at all.
29106
+ *
29107
+ * **Written by the group, with no attribute, and it reaches the control
29108
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
29109
+ * is markup a consumer rendered and their server never sent, which React
29110
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
29111
+ * the element rather than on the `<button role="radio">` inside it, so a
29112
+ * screen reader meets an unnamed box where a native group would have given
29113
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
29114
+ * same reason. (property only)
29057
29115
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
29058
29116
  *
29059
29117
  * **Named `whenSettled` because `settled` is taken four times over**, by
@@ -36868,7 +36926,7 @@ export type CustomElementsSolidJs = {
36868
36926
  *
36869
36927
  * Events that will be emitted by the component.
36870
36928
  *
36871
- * - `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`.
36929
+ * - `helix-select`: Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`.
36872
36930
  *
36873
36931
  * ## Slots
36874
36932
  *
@@ -37060,6 +37118,9 @@ export type CustomElementsSolidJs = {
37060
37118
  * report, which is what most of a transcript is, and it is the absence of the
37061
37119
  * attribute rather than a value of it: there is no initializer, so nothing is
37062
37120
  * reflected until something happens.
37121
+ *
37122
+ * **The turn writes this once, on a retry nobody refused**, and `handleRetry`
37123
+ * holds the reason. Everything else that moves it is the consumer's.
37063
37124
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
37064
37125
  *
37065
37126
  * **Named `whenSettled` because `settled` is taken four times over**, by
@@ -37085,7 +37146,7 @@ export type CustomElementsSolidJs = {
37085
37146
  *
37086
37147
  * Events that will be emitted by the component.
37087
37148
  *
37088
- * - `helix-send`: Fired when the reader asks to send a failed message again, and cancelable.
37149
+ * - `helix-send`: Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were.
37089
37150
  *
37090
37151
  * ## Slots
37091
37152
  *
@@ -40981,7 +41042,7 @@ export type CustomElementsSolidJs = {
40981
41042
  * Events that will be emitted by the component.
40982
41043
  *
40983
41044
  * - `change`: Announced once the value has moved.
40984
- * - `helix-before-change`: Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves.
41045
+ * - `helix-before-change`: Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included.
40985
41046
  * - `input`: Announced once the value has moved.
40986
41047
  *
40987
41048
  * ## Slots
@@ -41012,6 +41073,11 @@ export type CustomElementsSolidJs = {
41012
41073
  * picker come back out the same way.
41013
41074
  * - `focus(options?: FocusOptions) => void`: Puts focus on the input, the way a label pointing at one would.
41014
41075
  * - `formResetCallback() => void`: Empties it, which is what a form reset means for a file input.
41076
+ *
41077
+ * Not through `commit`, and that is the point of the split: a reset is the
41078
+ * form's decision and there is nothing here for a listener to refuse. What it
41079
+ * owes is the report and the platform's own announcement, which is what
41080
+ * `settle` is.
41015
41081
  * - `formDisabledCallback(disabled: boolean) => void`: The platform's notice that an ancestor fieldset changed.
41016
41082
  *
41017
41083
  * **The argument is the effective state, and taking it whole is what latched
@@ -46914,6 +46980,20 @@ export type CustomElementsSolidJs = {
46914
46980
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
46915
46981
  * Splitting them means moving what every stylesheet selects on, which is a
46916
46982
  * change of its own rather than a property being added. (property only)
46983
+ * - `focusable`: Whether this choice holds the group's tab stop while nothing is chosen.
46984
+ *
46985
+ * The stop rides on the selection, and an unanswered group has no selection,
46986
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
46987
+ * enter the set at all.
46988
+ *
46989
+ * **Written by the group, with no attribute, and it reaches the control
46990
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
46991
+ * is markup a consumer rendered and their server never sent, which React
46992
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
46993
+ * the element rather than on the `<button role="radio">` inside it, so a
46994
+ * screen reader meets an unnamed box where a native group would have given
46995
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
46996
+ * same reason. (property only)
46917
46997
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
46918
46998
  *
46919
46999
  * **Named `whenSettled` because `settled` is taken four times over**, by
package/types/vue.d.ts CHANGED
@@ -1009,7 +1009,7 @@ case is the absent attribute, and the styles read it that way. */
1009
1009
  };
1010
1010
 
1011
1011
  export type HelixHeadlessCarouselVueEvents = {
1012
- /** 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`. */
1012
+ /** Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`. */
1013
1013
  "helix-select": HelixHeadlessCarouselHelixSelectElementEvent;
1014
1014
  };
1015
1015
 
@@ -1102,12 +1102,15 @@ Reflected, because every one of the four is a visual state and a stylesheet
1102
1102
  has no other way to know. Absent is a settled message with nothing to
1103
1103
  report, which is what most of a transcript is, and it is the absence of the
1104
1104
  attribute rather than a value of it: there is no initializer, so nothing is
1105
- reflected until something happens. */
1105
+ reflected until something happens.
1106
+
1107
+ **The turn writes this once, on a retry nobody refused**, and `handleRetry`
1108
+ holds the reason. Everything else that moves it is the consumer's. */
1106
1109
  state?: (HelixHeadlessChatMessage["state"]) | undefined;
1107
1110
  };
1108
1111
 
1109
1112
  export type HelixHeadlessChatMessageVueEvents = {
1110
- /** Fired when the reader asks to send a failed message again, and cancelable. */
1113
+ /** Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were. */
1111
1114
  "helix-send": HelixHeadlessChatMessageHelixSendElementEvent;
1112
1115
  };
1113
1116
 
@@ -3211,7 +3214,7 @@ Here rather than on each control. */
3211
3214
  export type HelixHeadlessFileInputVueEvents = {
3212
3215
  /** Announced once the value has moved. */
3213
3216
  change: HelixHeadlessFileInputElementEvent;
3214
- /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves. */
3217
+ /** Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included. */
3215
3218
  "helix-before-change": HelixHeadlessFileInputHelixBeforeChangeElementEvent;
3216
3219
  /** Announced once the value has moved. */
3217
3220
  input: HelixHeadlessFileInputElementEvent;
@@ -6021,6 +6024,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
6021
6024
  Splitting them means moving what every stylesheet selects on, which is a
6022
6025
  change of its own rather than a property being added. */
6023
6026
  defaultSelected?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
6027
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
6028
+
6029
+ The stop rides on the selection, and an unanswered group has no selection,
6030
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
6031
+ enter the set at all.
6032
+
6033
+ **Written by the group, with no attribute, and it reaches the control
6034
+ rather than the host.** A `tabindex` on the host is two faults at once: it
6035
+ is markup a consumer rendered and their server never sent, which React
6036
+ reports as a mismatch and does not patch up, and it puts the tab stop on
6037
+ the element rather than on the `<button role="radio">` inside it, so a
6038
+ screen reader meets an unnamed box where a native group would have given
6039
+ them an unchecked radio. `Toggle.Root` carries the same property for the
6040
+ same reason. */
6041
+ focusable?: (HelixHeadlessRadio["focusable"]) | undefined;
6024
6042
  };
6025
6043
 
6026
6044
  export type HelixHeadlessRadioVueEvents = {