@fusion.dev/helix-headless 0.162.0 → 0.163.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 (44) hide show
  1. package/custom-elements.json +342 -15
  2. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +3 -1
  3. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -1
  4. package/dist/canvas/index.d.ts +1 -1
  5. package/dist/canvas/root/canvas-document.d.ts +33 -0
  6. package/dist/canvas/root/canvas-root.d.ts +50 -2
  7. package/dist/canvas/root/canvas-root.js +98 -3
  8. package/dist/chat/composer/chat-composer.js +1 -1
  9. package/dist/chat/root/chat-root.d.ts +21 -5
  10. package/dist/chat/root/chat-root.js +21 -9
  11. package/dist/checkbox-group/root/checkbox-group-root.d.ts +3 -1
  12. package/dist/checkbox-group/root/checkbox-group-root.js +2 -1
  13. package/dist/combobox/root/combobox-root.d.ts +8 -1
  14. package/dist/combobox/root/combobox-root.js +5 -1
  15. package/dist/list/root/list-root.d.ts +3 -1
  16. package/dist/list/root/list-root.js +2 -1
  17. package/dist/listbox/root/listbox-root.d.ts +2 -1
  18. package/dist/listbox/root/listbox-root.js +2 -1
  19. package/dist/map/root/map-root.d.ts +8 -1
  20. package/dist/map/root/map-root.js +12 -0
  21. package/dist/menu/item/menu-item.d.ts +38 -7
  22. package/dist/menu/item/menu-item.js +20 -9
  23. package/dist/menu/root/menu-root.d.ts +10 -1
  24. package/dist/menu/root/menu-root.js +8 -3
  25. package/dist/notifications/group/notification-group.d.ts +4 -1
  26. package/dist/notifications/group/notification-group.js +5 -3
  27. package/dist/notifications/root/notifications-root.d.ts +7 -2
  28. package/dist/notifications/root/notifications-root.js +2 -1
  29. package/dist/radio/group/radio-group.d.ts +2 -1
  30. package/dist/radio/group/radio-group.js +2 -1
  31. package/dist/select/root/select-root.d.ts +8 -1
  32. package/dist/select/root/select-root.js +5 -1
  33. package/dist/steps/root/steps-root.d.ts +7 -1
  34. package/dist/steps/root/steps-root.js +6 -1
  35. package/dist/tag/group/tag-group.d.ts +3 -1
  36. package/dist/tag/group/tag-group.js +2 -1
  37. package/dist/toggle/group/toggle-group.d.ts +2 -1
  38. package/dist/toggle/group/toggle-group.js +4 -1
  39. package/dist/tree/root/tree-root.d.ts +2 -1
  40. package/dist/tree/root/tree-root.js +4 -1
  41. package/lint.json +1 -1
  42. package/package.json +5 -5
  43. package/types/jsx.d.ts +162 -24
  44. package/types/vue.d.ts +40 -7
@@ -10,16 +10,22 @@ import '../../kbd/index.js';
10
10
  * - `checked` makes the row a choice.
11
11
  * - With neither, the row is a command.
12
12
  *
13
- * The role follows the kind. A row with `checked` is `menuitemcheckbox` with
14
- * `aria-checked`, and `exclusive` makes it `menuitemradio`, for a set where
15
- * only one can be on. A row that opens a page keeps `menuitem` and sets
16
- * `aria-haspopup`.
13
+ * The role follows the kind. A row with `checkable` or `checked` is
14
+ * `menuitemcheckbox` with `aria-checked`, and an `exclusive` row is
15
+ * `menuitemradio`, for a set where only one can be on. A row that opens a page
16
+ * keeps `menuitem` and sets `aria-haspopup`.
17
17
  *
18
18
  * Choosing a choice row checks or unchecks it before `helix-select` is sent,
19
19
  * so a listener reads the new `checked`. An `exclusive` row turns on and turns
20
20
  * off the `exclusive` rows next to it. Cancel `helix-before-toggle` to keep a
21
21
  * row as it is and set `checked` yourself.
22
22
  *
23
+ * Space on a choice row changes it and leaves the menu open, so a reader can
24
+ * set several in a row, as the APG's menu does. Enter and a press change it and
25
+ * close the menu. `close-on-select="false"` on the row keeps the menu open after
26
+ * any choice of it, and `close-on-select` on the row closes it after a Space as
27
+ * well.
28
+ *
23
29
  * The menu sets whether the row's page is showing as a property, never as an
24
30
  * attribute. An attribute written onto your element after it upgrades causes a
25
31
  * hydration mismatch.
@@ -82,7 +88,8 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
82
88
  * as a checkbox tells a reader its siblings are commands, which is the one
83
89
  * thing they are not.
84
90
  *
85
- * `checked` implies it, so a row that is on never has to say both.
91
+ * `checked` and `exclusive` imply it, so a row that is on, or one of a set,
92
+ * never has to say both.
86
93
  *
87
94
  * Choosing the row checks or unchecks it, after a `helix-before-toggle` that
88
95
  * you can cancel.
@@ -95,6 +102,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
95
102
  * state you start the row in, and `:state(checked)` carries the current one.
96
103
  */
97
104
  checked: boolean;
105
+ /**
106
+ * Whether choosing the row closes the menu.
107
+ *
108
+ * Unset takes the menu's `close-on-select`, which is on, and keeps the menu
109
+ * open after a Space on a choice row. Write `close-on-select="false"` for a
110
+ * row that changes something the reader is watching, such as a zoom step.
111
+ */
112
+ closeOnSelect?: boolean;
98
113
  /**
99
114
  * Whether the row is disabled, so it cannot be chosen.
100
115
  */
@@ -116,8 +131,7 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
116
131
  * it checked. `menuitemradio` tells a reader which kind of set they are in,
117
132
  * where `menuitemcheckbox` says every row is independent.
118
133
  *
119
- * Meaningless without `checkable`, since a row that cannot be ticked is not
120
- * one of a set of anything.
134
+ * It makes the row a choice, so it needs no `checkable` beside it.
121
135
  */
122
136
  exclusive: boolean;
123
137
  /**
@@ -192,6 +206,17 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
192
206
  * protected. Not a consumer's to read or set.
193
207
  */
194
208
  readonly isMenuItem = true;
209
+ /**
210
+ * @internal Whether the last choice of this row asked the menu to stay open:
211
+ * a Space on a choice row, or `close-on-select="false"`. Read by the menu
212
+ * while it hears the row's `helix-select`.
213
+ */
214
+ keepsOpen: boolean;
215
+ /**
216
+ * @internal Whether the row is a choice: it can be ticked, or is one of a
217
+ * set. Read by the menu to reserve the trailing column.
218
+ */
219
+ get choice(): boolean;
195
220
  /**
196
221
  * Whether anything was slotted into `submenu`.
197
222
  *
@@ -342,8 +367,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
342
367
  private tick;
343
368
  /** The other `exclusive` rows in this row's run of siblings. */
344
369
  private exclusivePeers;
370
+ /**
371
+ * Chooses the row. `space` is whether a Space chose it, which keeps the menu
372
+ * open on a choice row unless the row says otherwise.
373
+ */
345
374
  private choose;
346
375
  private readonly handleClick;
376
+ /** Whether the click on its way is the one a Space on a linked row sent. */
377
+ private spacePressed;
347
378
  /**
348
379
  * Enter and Space choose the row, which a `<div>` would not do on its own.
349
380
  *
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { ariaChord, auditName, customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, isRtl, onApple, PopoverController, requestChange, slotFilled, } from '@fusion.dev/helix-core';
7
+ import { ariaChord, auditName, customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, isRtl, onApple, onUnlessFalse, PopoverController, requestChange, slotFilled, } from '@fusion.dev/helix-core';
8
8
  import { caretRight, check } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -29,6 +29,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
29
29
  this.target = '';
30
30
  this.value = '';
31
31
  this.isMenuItem = true;
32
+ this.keepsOpen = false;
32
33
  this.submenu = false;
33
34
  this.start = false;
34
35
  this.end = false;
@@ -58,8 +59,10 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
58
59
  surface: () => (this.flyout ? this.partNamed('surface') : null),
59
60
  });
60
61
  this.handleClick = () => {
61
- this.choose();
62
+ this.choose(this.spacePressed);
63
+ this.spacePressed = false;
62
64
  };
65
+ this.spacePressed = false;
63
66
  this.handleKeydown = (event) => {
64
67
  if (event.key !== 'Enter' && event.key !== ' ') {
65
68
  return;
@@ -70,12 +73,17 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
70
73
  }
71
74
  event.preventDefault();
72
75
  if (link) {
76
+ this.spacePressed = true;
73
77
  link.click();
78
+ this.spacePressed = false;
74
79
  return;
75
80
  }
76
- this.handleClick();
81
+ this.choose(event.key === ' ');
77
82
  };
78
83
  }
84
+ get choice() {
85
+ return this.checkable || this.checked || this.exclusive;
86
+ }
79
87
  get hasSubmenu() {
80
88
  return this.submenu;
81
89
  }
@@ -93,9 +101,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
93
101
  const rows = this.knownChildren.filter((child) => child.slot === 'submenu' && child.isMenuItem);
94
102
  this.toggleState('submenu-start', this.flyout && rows.some((row) => row.querySelector(':scope > [slot="start"]') !== null));
95
103
  this.toggleState('submenu-mark', this.flyout &&
96
- rows.some((row) => row.checkable ||
97
- row.checked ||
98
- [...row.children].some((child) => child.slot === 'submenu')));
104
+ rows.some((row) => row.choice || [...row.children].some((child) => child.slot === 'submenu')));
99
105
  }
100
106
  firstUpdated(changed) {
101
107
  super.firstUpdated(changed);
@@ -152,7 +158,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
152
158
  return this.active;
153
159
  }
154
160
  tick() {
155
- if (!(this.checkable || this.checked) || this.hasSubmenu) {
161
+ if (!this.choice || this.hasSubmenu) {
156
162
  return;
157
163
  }
158
164
  const next = this.exclusive ? true : !this.checked;
@@ -188,10 +194,12 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
188
194
  }
189
195
  return peers;
190
196
  }
191
- choose() {
197
+ choose(space = false) {
192
198
  if (this.disabled) {
193
199
  return;
194
200
  }
201
+ const spaceKeeps = space && this.choice && !this.hasSubmenu;
202
+ this.keepsOpen = this.closeOnSelect === undefined ? spaceKeeps : !this.closeOnSelect;
195
203
  this.tick();
196
204
  this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
197
205
  }
@@ -221,7 +229,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
221
229
  }
222
230
  const submenu = this.hasSubmenu;
223
231
  const linked = this.href !== '' && !this.disabled;
224
- const ticks = this.checkable || this.checked;
232
+ const ticks = this.choice;
225
233
  const role = ticks ? (this.exclusive ? 'menuitemradio' : 'menuitemcheckbox') : 'menuitem';
226
234
  const checkedState = ticks ? (this.checked ? 'true' : 'false') : nothing;
227
235
  const keys = this.shortcut === '' ? nothing : ariaChord(this.shortcut, !this.hydrating && onApple());
@@ -281,6 +289,9 @@ __decorate([
281
289
  __decorate([
282
290
  property({ type: Boolean })
283
291
  ], HelixHeadlessMenuItem.prototype, "checked", void 0);
292
+ __decorate([
293
+ property({ attribute: 'close-on-select', converter: onUnlessFalse })
294
+ ], HelixHeadlessMenuItem.prototype, "closeOnSelect", void 0);
284
295
  __decorate([
285
296
  property({ reflect: true, type: Boolean })
286
297
  ], HelixHeadlessMenuItem.prototype, "disabled", void 0);
@@ -40,7 +40,8 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
40
40
  * Keys: Enter, Space or Down Arrow on the trigger opens the menu on its first
41
41
  * row, and Up Arrow on its last. In the menu, Up and Down Arrow move between
42
42
  * rows, Home and End go to the first and the last, a typed letter moves to
43
- * the next row whose name starts with it, Enter and Space choose, Right Arrow
43
+ * the next row whose name starts with it, Enter and Space choose (Space on a
44
+ * choice row changes it and leaves the menu open), Right Arrow
44
45
  * opens a row's submenu and Left Arrow goes back, the two swapping on a
45
46
  * right-to-left page. Left Arrow on the top page does nothing. Escape closes
46
47
  * the menu and returns focus to the trigger. Tab closes it and moves on.
@@ -109,6 +110,14 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
109
110
  * The text of the row that returns from a submenu.
110
111
  */
111
112
  backLabel: string;
113
+ /**
114
+ * Whether choosing a row closes the menu.
115
+ *
116
+ * On by default, and a Space on a choice row leaves the menu open either way.
117
+ * Write `close-on-select="false"` to keep the menu open after every choice, as
118
+ * Ark UI's `closeOnSelect` does. A row's own `close-on-select` wins over this.
119
+ */
120
+ closeOnSelect: boolean;
112
121
  /**
113
122
  * Whether the menu is the trigger's context menu: it opens where the reader
114
123
  * asks for one, with a right-click, a long press, Shift+F10 or the menu key.
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, anchorElement, customElement, DrilldownController, deepActiveElement, deepContains, focusTarget, HelixElement, inlineStep, KeyboardNavigationController, linkDescription, MixinCollection, MixinControllerOpen, PopoverController, scrollItemIntoView, sideOf, slottedWords, tellDisclosure, typeaheadBy, unlinkDescription, words, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, anchorElement, customElement, DrilldownController, deepActiveElement, deepContains, focusTarget, HelixElement, inlineStep, KeyboardNavigationController, linkDescription, MixinCollection, MixinControllerOpen, onUnlessFalse, PopoverController, scrollItemIntoView, sideOf, slottedWords, tellDisclosure, typeaheadBy, unlinkDescription, words, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -31,6 +31,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
31
31
  constructor() {
32
32
  super(...arguments);
33
33
  this.backLabel = 'Back';
34
+ this.closeOnSelect = true;
34
35
  this.context = false;
35
36
  this.label = '';
36
37
  this.longPressLabel = 'Long press or press Alt+Down to show the menu';
@@ -119,8 +120,9 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
119
120
  this.enter(chosen);
120
121
  return;
121
122
  }
123
+ const keep = chosen.keepsOpen || (chosen.closeOnSelect === undefined && !this.closeOnSelect);
122
124
  queueMicrotask(() => {
123
- if (!event.defaultPrevented) {
125
+ if (!(event.defaultPrevented || keep)) {
124
126
  this.hide();
125
127
  }
126
128
  });
@@ -538,7 +540,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
538
540
  markColumns() {
539
541
  const rows = this.flies ? this.topRows : this.drilldown.rows;
540
542
  this.toggleState('start', rows.some((row) => row.querySelector(':scope > [slot="start"]') !== null));
541
- this.toggleState('mark', rows.some((row) => row.checkable || row.checked || this.drilldown.hasPage(row)));
543
+ this.toggleState('mark', rows.some((row) => row.choice || this.drilldown.hasPage(row)));
542
544
  }
543
545
  mark(item, onPage) {
544
546
  const page = this.drilldown.page;
@@ -608,6 +610,9 @@ __decorate([
608
610
  words,
609
611
  property({ attribute: 'back-label' })
610
612
  ], HelixHeadlessMenu.prototype, "backLabel", void 0);
613
+ __decorate([
614
+ property({ attribute: 'close-on-select', converter: onUnlessFalse })
615
+ ], HelixHeadlessMenu.prototype, "closeOnSelect", void 0);
611
616
  __decorate([
612
617
  property({ reflect: true, type: Boolean })
613
618
  ], HelixHeadlessMenu.prototype, "context", void 0);
@@ -1,4 +1,4 @@
1
- import { ChildrenController, HelixElement } from '@fusion.dev/helix-core';
1
+ import { ChildrenController, HelixElement, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import type { HelixHeadlessNotification } from '../notification/notification.js';
4
4
  declare const HelixHeadlessNotificationGroup_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
@@ -140,7 +140,10 @@ export declare class HelixHeadlessNotificationGroup extends HelixHeadlessNotific
140
140
  * the group is in is asked to update as well, since it counts the group's
141
141
  * rows and does not watch inside the group.
142
142
  */
143
+ private readonly recount;
143
144
  protected readonly childWatch: ChildrenController;
145
+ /** Hears a row upgrade after it arrived, which neither the slot nor `childWatch` reports. */
146
+ protected readonly upgradeWatch: UpgradeController;
144
147
  private readonly handleToggle;
145
148
  protected render(): TemplateResult;
146
149
  }
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { assignSlots, ChildrenController, customElement, HelixElement, hostAria, MixinControllerOpen, } from '@fusion.dev/helix-core';
7
+ import { assignSlots, ChildrenController, customElement, HelixElement, hostAria, MixinControllerOpen, UpgradeController, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  const TRIGGER_ID = 'trigger';
@@ -16,10 +16,12 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
16
16
  this.collapsible = false;
17
17
  this.label = '';
18
18
  this.isNotificationGroup = true;
19
- this.childWatch = new ChildrenController(this, () => {
19
+ this.recount = () => {
20
20
  this.requestUpdate();
21
21
  this.parentElement?.requestUpdate?.();
22
- });
22
+ };
23
+ this.childWatch = new ChildrenController(this, this.recount);
24
+ this.upgradeWatch = new UpgradeController(this, this.recount);
23
25
  this.handleToggle = () => {
24
26
  this.toggleOpen();
25
27
  };
@@ -1,4 +1,4 @@
1
- import { ChildrenController, HelixElement } from '@fusion.dev/helix-core';
1
+ import { ChildrenController, HelixElement, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  import type { HelixHeadlessNotificationGroup } from '../group/notification-group.js';
4
4
  import type { HelixHeadlessNotification } from '../notification/notification.js';
@@ -239,6 +239,11 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
239
239
  * a group is the group's to hear, and the group asks the feed to count again.
240
240
  */
241
241
  protected readonly childWatch: ChildrenController;
242
+ /**
243
+ * Hears a row upgrade after it arrived. A row is known by its marker, which
244
+ * an element that has not upgraded lacks, so the row was assigned nowhere.
245
+ */
246
+ protected readonly upgradeWatch: UpgradeController;
242
247
  /**
243
248
  * An item's own read event, heard on the way past, so the count follows.
244
249
  *
@@ -328,7 +333,7 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
328
333
  * moment the markup is parsed. The rows themselves come from `items`, which
329
334
  * does need them upgraded, and that is the honest limit rather than a claim
330
335
  * this had made the other way: a row that upgrades late is counted on the next
331
- * update, which its own `slotchange` asks for.
336
+ * update, which `UpgradeController` asks for when its definition arrives.
332
337
  */
333
338
  private markMedia;
334
339
  /**
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { assignSlots, ChildrenController, customElement, DelayController, deepContains, focusableElements, formatWords, HelixElement, HelixLoadMoreEvent, HelixReadEvent, hostAria, MixinCollection, markRegion, rovingStop, whenHydrated, words, } from '@fusion.dev/helix-core';
7
+ import { assignSlots, ChildrenController, customElement, DelayController, deepContains, focusableElements, formatWords, HelixElement, HelixLoadMoreEvent, HelixReadEvent, hostAria, MixinCollection, markRegion, rovingStop, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { feedStep } from '../feed-keys.js';
@@ -24,6 +24,7 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
24
24
  this.requestUpdate();
25
25
  };
26
26
  this.childWatch = new ChildrenController(this, this.handleSlotChange);
27
+ this.upgradeWatch = new UpgradeController(this, this.handleSlotChange);
27
28
  this.handleRead = () => {
28
29
  queueMicrotask(() => this.requestUpdate());
29
30
  };
@@ -1,4 +1,4 @@
1
- import { DisabledCascadeController, HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
1
+ import { DisabledCascadeController, HelixFormElement, KeyboardNavigationController, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import { HelixHeadlessRadio } from '../root/radio-root.js';
4
4
  declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
@@ -123,6 +123,7 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
123
123
  */
124
124
  get items(): HelixHeadlessRadio[];
125
125
  private observer;
126
+ protected readonly upgradeWatch: UpgradeController;
126
127
  /**
127
128
  * Announces before it applies, so a listener can refuse the change.
128
129
  *
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, DisabledCascadeController, HelixFormElement, hostAria, KeyboardNavigationController, MixinControllerValue, MixinField, words, } from '@fusion.dev/helix-core';
7
+ import { customElement, DisabledCascadeController, HelixFormElement, hostAria, KeyboardNavigationController, MixinControllerValue, MixinField, UpgradeController, words, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessRadio } from '../root/radio-root.js';
@@ -22,6 +22,7 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
22
22
  this.cascade = new DisabledCascadeController(this, {
23
23
  items: () => this.items,
24
24
  });
25
+ this.upgradeWatch = new UpgradeController(this, undefined, { subtree: true });
25
26
  this.handleSelection = (event) => {
26
27
  event.stopPropagation();
27
28
  if (this.readOnly) {
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, type DataOptionItem, HelixFormElement, PopoverController } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, type DataOptionItem, HelixFormElement, PopoverController, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import { type HelixHeadlessOption } from '../../option/root/option-root.js';
4
4
  import '../../listbox/index.js';
@@ -251,6 +251,13 @@ export declare class HelixHeadlessSelect extends HelixHeadlessSelect_base {
251
251
  * be reached from the server and are left plain.
252
252
  */
253
253
  private get listbox();
254
+ /**
255
+ * Hears an option upgrade after it arrived. The options are this element's
256
+ * children, which the listbox reaches through a slot and does not watch, so
257
+ * both read them again: the listbox to select the one `value` names, and this
258
+ * element for the words on its trigger.
259
+ */
260
+ protected readonly upgradeWatch: UpgradeController;
254
261
  protected readonly panel: PopoverController;
255
262
  /**
256
263
  * What the trigger says: the chosen options' words, or the placeholder.
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, afterLinkFollowed, chosenValues, customElement, dataOptions, drawAutofill, drawStatus, HelixFormElement, hostAria, hostWords, isRtl, MixinControllerOpen, MixinControllerValue, MixinField, onApple, PopoverController, reportValues, StatusController, sideOf, typesCharacter, words, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, afterLinkFollowed, chosenValues, customElement, dataOptions, drawAutofill, drawStatus, HelixFormElement, hostAria, hostWords, isRtl, MixinControllerOpen, MixinControllerValue, MixinField, onApple, PopoverController, reportValues, StatusController, sideOf, typesCharacter, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { caretDown } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -36,6 +36,10 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
36
36
  this.placeholder = 'Choose';
37
37
  this.position = 'floating';
38
38
  this.valueMissingMessage = 'Choose one to continue.';
39
+ this.upgradeWatch = new UpgradeController(this, () => {
40
+ this.listbox?.requestUpdate();
41
+ this.requestUpdate();
42
+ }, { subtree: true });
39
43
  this.panel = new PopoverController(this, {
40
44
  anchor: () => this.partNamed('trigger'),
41
45
  boundary: () => [
@@ -150,7 +150,13 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
150
150
  protected readonly keys: KeyboardNavigationController;
151
151
  /** The steps, in the order the consumer wrote them. */
152
152
  private get steps();
153
- /** The index of the stage the reader is on, or `-1`. */
153
+ /**
154
+ * The index of the stage the reader is on, or `-1`.
155
+ *
156
+ * Read from `active` once anything has set it, because a move writes
157
+ * `active` at once and each step's `current` only on the next update: two
158
+ * calls to `next()` in one task then move two stages.
159
+ */
154
160
  private get at();
155
161
  /**
156
162
  * Moves to the next stage that can be gone to, as Ark UI's `goToNextStep`
@@ -58,7 +58,12 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
58
58
  return [...this.querySelectorAll(':scope > *')].filter((one) => 'current' in one);
59
59
  }
60
60
  get at() {
61
- return this.steps.findIndex((step) => step.current);
61
+ const steps = this.steps;
62
+ if (this.active === '') {
63
+ return steps.findIndex((step) => step.current);
64
+ }
65
+ const item = this.resolveActive(steps);
66
+ return item ? steps.indexOf(item) : -1;
62
67
  }
63
68
  next() {
64
69
  return this.step(1);
@@ -1,4 +1,4 @@
1
- import { HelixElement, KeyboardNavigationController, type SelectionMode } from '@fusion.dev/helix-core';
1
+ import { HelixElement, KeyboardNavigationController, type SelectionMode, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import { HelixHeadlessTag } from '../root/tag-root.js';
4
4
  /** How many tags a group lets a reader select: core's `SelectionMode`. */
@@ -179,6 +179,8 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
179
179
  */
180
180
  private readonly handleDismiss;
181
181
  private readonly handleSlotchange;
182
+ /** Reads a tag again once it upgrades, so it takes its role and its place. */
183
+ protected readonly upgradeWatch: UpgradeController;
182
184
  private readonly handleFocusin;
183
185
  connectedCallback(): void;
184
186
  disconnectedCallback(): void;
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { commaList, customElement, deepActiveElement, deepContains, focusItemOf, HelixElement, hostAria, KeyboardNavigationController, pressedValue, prunedValue, requestValue, rovingStop, slottedWords, typeaheadByWords, whenHydrated, words, } from '@fusion.dev/helix-core';
7
+ import { commaList, customElement, deepActiveElement, deepContains, focusItemOf, HelixElement, hostAria, KeyboardNavigationController, pressedValue, prunedValue, requestValue, rovingStop, slottedWords, typeaheadByWords, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessTag } from '../root/tag-root.js';
@@ -126,6 +126,7 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
126
126
  this.sync();
127
127
  this.requestUpdate();
128
128
  };
129
+ this.upgradeWatch = new UpgradeController(this, this.handleSlotchange);
129
130
  this.handleFocusin = (event) => {
130
131
  const tag = this.tagOf(event);
131
132
  if (tag && tag !== this.last) {
@@ -1,4 +1,4 @@
1
- import { DisabledCascadeController, HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
1
+ import { DisabledCascadeController, HelixFormElement, KeyboardNavigationController, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessToggleGroup_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
@@ -215,6 +215,7 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
215
215
  */
216
216
  private get items();
217
217
  private observer;
218
+ protected readonly upgradeWatch: UpgradeController;
218
219
  private readonly handleSelection;
219
220
  /**
220
221
  * Whether the group holds an answer the form can submit: a value an item
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { commaList, customElement, DisabledCascadeController, HelixFormElement, hostAria, IndicatorController, KeyboardNavigationController, MixinControllerValue, MixinField, reportValues, rovingStop, toggleValue, toolbarAround, } from '@fusion.dev/helix-core';
7
+ import { commaList, customElement, DisabledCascadeController, HelixFormElement, hostAria, IndicatorController, KeyboardNavigationController, MixinControllerValue, MixinField, reportValues, rovingStop, toggleValue, toolbarAround, UpgradeController, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessToggle } from '../root/toggle-root.js';
@@ -35,6 +35,9 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
35
35
  this.cascade = new DisabledCascadeController(this, {
36
36
  items: () => this.items,
37
37
  });
38
+ this.upgradeWatch = new UpgradeController(this, () => this.handleItems(), {
39
+ subtree: true,
40
+ });
38
41
  this.handleSelection = (event) => {
39
42
  queueMicrotask(() => {
40
43
  if (event.defaultPrevented) {
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixElement, UpgradeController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessTree_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & typeof HelixElement;
4
4
  /**
@@ -209,6 +209,7 @@ export declare class HelixHeadlessTree extends HelixHeadlessTree_base {
209
209
  /** Whether many items can be checked. */
210
210
  private get multiple();
211
211
  private watcher;
212
+ protected readonly upgradeWatch: UpgradeController;
212
213
  /** The item that last had focus, so its removal can hand focus on. */
213
214
  private focused;
214
215
  /** The item last checked or unchecked, where a range Shift+Space checks starts. */
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, auditName, closestInstance, commaList, customElement, deepActiveElement, formatWords, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, ReorderController, requestReorder, scrollItemIntoView, selectsAll, slottedWords, Typeahead, treeArrow, typesCharacter, whenHydrated, words, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, auditName, closestInstance, commaList, customElement, deepActiveElement, formatWords, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, ReorderController, requestReorder, scrollItemIntoView, selectsAll, slottedWords, Typeahead, treeArrow, typesCharacter, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { fromGrip } from '../grip.js';
@@ -53,6 +53,9 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
53
53
  });
54
54
  this.pickedUpLabel = 'Picked up {name}';
55
55
  this.reorderable = false;
56
+ this.upgradeWatch = new UpgradeController(this, () => this.syncItems(), {
57
+ subtree: true,
58
+ });
56
59
  this.handleFocusIn = (event) => {
57
60
  this.focused = event
58
61
  .composedPath()