@fusion.dev/helix-headless 0.171.0 → 0.173.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 (67) hide show
  1. package/custom-elements.json +1350 -124
  2. package/dist/button-group/root/button-group-root.d.ts +30 -0
  3. package/dist/button-group/root/button-group-root.js +50 -4
  4. package/dist/canvas/root/canvas-root.d.ts +7 -0
  5. package/dist/canvas/root/canvas-root.js +7 -0
  6. package/dist/chat/composer/chat-composer.d.ts +28 -1
  7. package/dist/chat/composer/chat-composer.js +22 -3
  8. package/dist/chat/root/chat-root.d.ts +38 -2
  9. package/dist/chat/root/chat-root.js +38 -1
  10. package/dist/color-input/root/color-input-root.d.ts +15 -1
  11. package/dist/color-input/root/color-input-root.js +19 -1
  12. package/dist/color-picker/root/color-picker-root.d.ts +5 -1
  13. package/dist/color-picker/root/color-picker-root.js +1 -1
  14. package/dist/copy-button/root/copy-button-root.d.ts +43 -3
  15. package/dist/copy-button/root/copy-button-root.js +49 -3
  16. package/dist/data-table/root/data-table-root.d.ts +5 -1
  17. package/dist/data-table/root/data-table-root.js +1 -0
  18. package/dist/dialog/root/dialog-root.js +1 -0
  19. package/dist/drawer/root/drawer-root.d.ts +4 -0
  20. package/dist/drawer/root/drawer-root.js +1 -0
  21. package/dist/element-events.generated.js +3 -0
  22. package/dist/graph/root/graph-root.d.ts +22 -0
  23. package/dist/graph/root/graph-root.js +21 -5
  24. package/dist/lightbox/root/lightbox-root.js +1 -0
  25. package/dist/list/root/list-root.d.ts +6 -1
  26. package/dist/list/root/list-root.js +3 -2
  27. package/dist/listbox/root/listbox-root.d.ts +2 -1
  28. package/dist/listbox/root/listbox-root.js +3 -2
  29. package/dist/menu/item/menu-item.d.ts +2 -0
  30. package/dist/menu/item/menu-item.js +2 -0
  31. package/dist/menu/root/menu-root.d.ts +73 -12
  32. package/dist/menu/root/menu-root.js +103 -7
  33. package/dist/outline/root/outline-root.d.ts +57 -0
  34. package/dist/outline/root/outline-root.js +98 -29
  35. package/dist/popover/root/popover-root.d.ts +59 -6
  36. package/dist/popover/root/popover-root.js +51 -3
  37. package/dist/select/root/select-root.d.ts +2 -2
  38. package/dist/select/root/select-root.js +2 -2
  39. package/dist/speed-dial/root/speed-dial-root.d.ts +14 -0
  40. package/dist/speed-dial/root/speed-dial-root.js +34 -2
  41. package/dist/splitter/root/splitter-root.d.ts +25 -2
  42. package/dist/splitter/root/splitter-root.js +19 -4
  43. package/dist/tag/group/tag-group.d.ts +8 -1
  44. package/dist/tag/group/tag-group.js +8 -0
  45. package/dist/tag/root/tag-root.d.ts +12 -0
  46. package/dist/tag/root/tag-root.js +12 -0
  47. package/dist/textarea/root/textarea-root.d.ts +8 -0
  48. package/dist/textarea/root/textarea-root.js +16 -0
  49. package/dist/time-picker/root/time-picker-root.d.ts +14 -0
  50. package/dist/time-picker/root/time-picker-root.js +8 -1
  51. package/dist/timeline/item/timeline-item.d.ts +5 -1
  52. package/dist/timeline/item/timeline-item.js +9 -0
  53. package/dist/timeline/root/timeline-root.d.ts +23 -0
  54. package/dist/timeline/root/timeline-root.js +17 -3
  55. package/dist/tooltip/root/tooltip-root.d.ts +10 -0
  56. package/dist/tooltip/root/tooltip-root.js +14 -2
  57. package/dist/virtual-list/reorder/index.d.ts +1 -0
  58. package/dist/virtual-list/reorder/index.js +4 -0
  59. package/dist/virtual-list/reorder/virtual-list-reorder.d.ts +86 -0
  60. package/dist/virtual-list/reorder/virtual-list-reorder.js +193 -0
  61. package/dist/virtual-list/root/virtual-list-root.d.ts +49 -11
  62. package/dist/virtual-list/root/virtual-list-root.js +10 -1
  63. package/lint.json +1 -1
  64. package/package.json +5 -5
  65. package/types/jsx.d.ts +880 -48
  66. package/types/vue.d.ts +174 -19
  67. package/types/words.d.ts +22 -0
@@ -1,4 +1,5 @@
1
- import { type PropertyValues, type TemplateResult } from 'lit';
1
+ import { type Placement } from '@fusion.dev/helix-core';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
2
3
  import { HelixHeadlessIconButton } from '../../icon-button/root/icon-button-root.js';
3
4
  import '../../tooltip/index.js';
4
5
  /**
@@ -31,7 +32,17 @@ import '../../tooltip/index.js';
31
32
  * The button is named `label`, or the host's `aria-label`, and its name
32
33
  * changes to `copied-label` or `error-label` for the moment after a press. The
33
34
  * same words are said in a live region, so a screen reader hears the result,
34
- * and the tooltip shows them.
35
+ * and the tooltip shows them. The tooltip does not also describe the button,
36
+ * since its words are the button's name.
37
+ *
38
+ * **The tooltip.** It shows the name on hover and focus and the result after a
39
+ * press. `tooltip="copy"` shows it only for the result, and `tooltip="none"`
40
+ * draws none, as Web Awesome's copy button offers. `tooltip-placement` is the
41
+ * side it prefers, and it moves to another side where that one does not fit.
42
+ *
43
+ * **Words on the button.** `show-label` draws the name beside the icon, and
44
+ * the result in its place after a press. The button is as wide as the longest
45
+ * of the three, so a press does not resize it.
35
46
  *
36
47
  * @since 0.1.0
37
48
  * @status stable
@@ -50,6 +61,7 @@ import '../../tooltip/index.js';
50
61
  * @csspart live - The live region that says `copied-label` or `error-label` after a press, and `pending-label` when the button becomes pending under focus. It is rendered from the first time it says anything.
51
62
  * @csspart mark - The clipboard icon, and the confirmation icon that replaces it.
52
63
  * @csspart spinner - The ring a pending button turns, an empty element the styled button draws over the icon. It is rendered only while the button is pending.
64
+ * @csspart text - The words beside the icon, under `show-label`. It holds all three, `label`, `copied-label` and `error-label`, with all but the current one hidden.
53
65
  * @csspart tooltip - The tooltip bubble, forwarded from the tooltip inside the button.
54
66
  * @csspart tooltip-arrow - The tooltip's arrow, which points at the button.
55
67
  * @csspart tooltip-arrow-bottom - The arrow while the bubble is below the button, on the bubble's top edge. Set together with `tooltip-arrow`.
@@ -64,7 +76,6 @@ import '../../tooltip/index.js';
64
76
  * @cssstate copied - The moment after a successful copy, while `copied-label` names the button.
65
77
  * @cssstate failed - The moment after a press that put nothing on the clipboard, while `error-label` names the button.
66
78
  *
67
- * @withheld csspart text - Button's words, which this does not render: the label is the icon's name.
68
79
  * @withheld slot end - Button's, which this does not render.
69
80
  * @withheld slot start - Button's, which this does not render.
70
81
  */
@@ -106,6 +117,24 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
106
117
  * world is called Copy. A consumer with a better word still sets it.
107
118
  */
108
119
  label: string;
120
+ /**
121
+ * Draws the name as words beside the icon, and the result in their place
122
+ * after a press. The button stays as wide as the longest of them.
123
+ */
124
+ showLabel: boolean;
125
+ /**
126
+ * When the tooltip shows: `full` on hover and focus and after a press,
127
+ * `copy` only after a press, to show the result, and `none` never, for a
128
+ * button whose result shows another way, such as under `show-label`.
129
+ *
130
+ * Unset is `full`, and the attribute stays absent until you write it.
131
+ */
132
+ tooltip?: 'copy' | 'full' | 'none';
133
+ /**
134
+ * The side the tooltip prefers. It moves to another side where this one
135
+ * does not fit.
136
+ */
137
+ tooltipPlacement: Placement;
109
138
  /**
110
139
  * The text copied to the clipboard.
111
140
  *
@@ -156,6 +185,9 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
156
185
  */
157
186
  protected get forwardsLabel(): boolean;
158
187
  protected willUpdate(changed: PropertyValues): void;
188
+ /** Whether the last render showed a result, so `tooltip="copy"` opens and closes on the change. */
189
+ private showedResult;
190
+ protected updated(changed: PropertyValues): void;
159
191
  /** @internal A copy button never submits, so it is no form's default button. */
160
192
  protected get submitsForm(): boolean;
161
193
  /**
@@ -211,6 +243,14 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
211
243
  protected renderCopiedIcon(): TemplateResult;
212
244
  /** The mark for a press that put nothing on the clipboard, behind a seam of its own. */
213
245
  protected renderFailedIcon(): TemplateResult;
246
+ /**
247
+ * The words beside the icon, under `show-label`, behind a seam of its own.
248
+ *
249
+ * All three are drawn and only the current one shows, so the button is as
250
+ * wide as the longest of them and a press does not resize it. Hidden from a
251
+ * screen reader, which hears the same words as the button's name.
252
+ */
253
+ protected renderText(): TemplateResult | typeof nothing;
214
254
  /**
215
255
  * The bubble the control sits inside, behind a seam.
216
256
  *
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessCopyButton_1;
8
- import { CopyController, customElement, HelixCopyErrorEvent, words } from '@fusion.dev/helix-core';
8
+ import { CopyController, customElement, HelixCopyErrorEvent, words, } from '@fusion.dev/helix-core';
9
9
  import { check, copy, x } from '@fusion.dev/helix-icons';
10
10
  import { nothing } from 'lit';
11
11
  import { property } from 'lit/decorators.js';
@@ -22,8 +22,11 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
22
22
  this.feedbackDuration = 1600;
23
23
  this.from = '';
24
24
  this.label = 'Copy';
25
+ this.showLabel = false;
26
+ this.tooltipPlacement = 'top';
25
27
  this.value = '';
26
28
  this.namedBy = null;
29
+ this.showedResult = false;
27
30
  this.clipboard = new CopyController(this, { after: () => this.feedbackDuration });
28
31
  this.handleCopy = async (event) => {
29
32
  if (this.pending) {
@@ -62,6 +65,24 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
62
65
  this.toggleState('copied', this.copied);
63
66
  this.toggleState('failed', this.failed);
64
67
  }
68
+ updated(changed) {
69
+ super.updated(changed);
70
+ const result = this.copied || this.failed;
71
+ if (result === this.showedResult) {
72
+ return;
73
+ }
74
+ this.showedResult = result;
75
+ if (this.tooltip !== 'copy') {
76
+ return;
77
+ }
78
+ const tip = this.renderRoot.querySelector('[data-copy-tooltip]');
79
+ if (result) {
80
+ tip?.show();
81
+ }
82
+ else {
83
+ tip?.hide();
84
+ }
85
+ }
65
86
  get submitsForm() {
66
87
  return false;
67
88
  }
@@ -94,7 +115,7 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
94
115
  @keydown=${this.notePress}
95
116
  @pointerdown=${this.notePress}
96
117
  >
97
- ${this.renderCopiedIcon()} ${this.renderFailedIcon()} ${this.renderCopyIcon()}${this.renderSpinner()}
118
+ ${this.renderCopiedIcon()} ${this.renderFailedIcon()} ${this.renderCopyIcon()}${this.renderSpinner()}${this.renderText()}
98
119
  </button>`)}${this.renderLive()}`;
99
120
  }
100
121
  renderCopyIcon() {
@@ -112,8 +133,24 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
112
133
  >${HelixHeadlessCopyButton_1.icon(x)}</slot
113
134
  >`;
114
135
  }
136
+ renderText() {
137
+ if (!this.showLabel) {
138
+ return nothing;
139
+ }
140
+ const resting = !this.copied && !this.failed;
141
+ return html `<span part="text" aria-hidden="true"
142
+ ><span ?hidden=${!resting}>${this.namedBy || this.label}</span
143
+ ><span ?hidden=${!this.copied}>${this.copiedLabel}</span
144
+ ><span ?hidden=${!this.failed}>${this.errorLabel}</span
145
+ ></span>`;
146
+ }
115
147
  renderTooltip(control) {
116
- return html `<${this.tag('tooltip')} content=${this.says} persist
148
+ if (this.tooltip === 'none') {
149
+ return control;
150
+ }
151
+ const content = this.tooltip === 'copy' && !this.copied && !this.failed ? '' : this.says;
152
+ return html `<${this.tag('tooltip')} content=${content} persist
153
+ data-copy-tooltip placement=${this.tooltipPlacement}
117
154
  exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom,
118
155
  arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right,
119
156
  arrow-top: tooltip-arrow-top, tooltip, tooltip-bottom, tooltip-left, tooltip-right,
@@ -140,6 +177,15 @@ __decorate([
140
177
  words,
141
178
  property()
142
179
  ], HelixHeadlessCopyButton.prototype, "label", void 0);
180
+ __decorate([
181
+ property({ attribute: 'show-label', reflect: true, type: Boolean })
182
+ ], HelixHeadlessCopyButton.prototype, "showLabel", void 0);
183
+ __decorate([
184
+ property()
185
+ ], HelixHeadlessCopyButton.prototype, "tooltip", void 0);
186
+ __decorate([
187
+ property({ attribute: 'tooltip-placement' })
188
+ ], HelixHeadlessCopyButton.prototype, "tooltipPlacement", void 0);
143
189
  __decorate([
144
190
  property()
145
191
  ], HelixHeadlessCopyButton.prototype, "value", void 0);
@@ -208,7 +208,7 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
208
208
  * @csspart select - The cell that holds a row's checkbox.
209
209
  * @csspart select-all - The heading that holds the checkbox that selects every row.
210
210
  * @csspart select-selected - Added to `select` in a selected row.
211
- * @csspart table - The grid.
211
+ * @csspart table - The grid. While `windowed`, the box that scrolls, which needs only a block size on this tier.
212
212
  * @csspart toggle - The mark in a row header that a press opens and closes its row by, in a tree grid.
213
213
  * @csspart toggle-mark - The caret in `toggle`. It points along the line while the row is closed and down while it is open.
214
214
  * @csspart toggle-open - Added to `toggle` while its row is open.
@@ -586,6 +586,10 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
586
586
  * Off by default. Windowing costs a scroll listener, a measurement per row
587
587
  * drawn, and a `block-size` on the scrolling box, and a table of twenty rows
588
588
  * should pay none of them.
589
+ *
590
+ * While windowed, the `table` part scrolls on this tier: it carries
591
+ * `data-scroller`, and `core` ships `[data-scroller] { overflow: auto }` into
592
+ * every shadow root. You still need to give it a block size.
589
593
  */
590
594
  windowed: boolean;
591
595
  private sortState;
@@ -1228,6 +1228,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1228
1228
  part="table"
1229
1229
  role="rowgroup"
1230
1230
  aria-busy=${this.loading ? 'true' : nothing}
1231
+ ?data-scroller=${this.windowed}
1231
1232
  style=${template ? `--data-table-template: ${template}` : nothing}
1232
1233
  @scroll=${this.handleScroll}
1233
1234
  >
@@ -31,6 +31,7 @@ let HelixHeadlessDialog = class HelixHeadlessDialog extends MixinControllerOpen(
31
31
  '--show': () => this.show(),
32
32
  '--toggle': () => this.toggleOpen(),
33
33
  },
34
+ expanded: () => this.open,
34
35
  });
35
36
  this.persistent = false;
36
37
  this.returned = '';
@@ -74,6 +74,10 @@ declare const HelixHeadlessDrawer_base: (new (...args: any[]) => import("@fusion
74
74
  * <helix-headless-drawer id="nav">…</helix-headless-drawer>
75
75
  * ```
76
76
  *
77
+ * From its first press, a button that opens the drawer this way says whether
78
+ * the drawer is open: a Helix button through `expanded`, and any other
79
+ * element with `aria-expanded`.
80
+ *
77
81
  * @since 0.1.0
78
82
  * @status stable
79
83
  * @category overlays
@@ -19,6 +19,7 @@ let HelixHeadlessDrawer = class HelixHeadlessDrawer extends MixinControllerOpen(
19
19
  '--show': () => this.requestOpen(true),
20
20
  '--toggle': () => this.toggleOpen(),
21
21
  },
22
+ expanded: () => this.open,
22
23
  });
23
24
  this.dismissible = false;
24
25
  this.dismissLabel = 'Close';
@@ -196,6 +196,7 @@ registerElementEvents({
196
196
  'helix-headless-map-terrain': ['helix-map-part-change'],
197
197
  'helix-headless-megamenu': ['helix-before-close', 'helix-before-open', 'toggle'],
198
198
  'helix-headless-menu': [
199
+ 'change',
199
200
  'helix-before-close',
200
201
  'helix-before-open',
201
202
  'helix-filter',
@@ -362,7 +363,9 @@ registerElementEvents({
362
363
  'helix-headless-virtual-list': [
363
364
  'change',
364
365
  'helix-before-change',
366
+ 'helix-before-reorder',
365
367
  'helix-load-more',
366
368
  'helix-range-change',
369
+ 'helix-reorder',
367
370
  ],
368
371
  });
@@ -44,6 +44,7 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
44
44
  * @slot footer - Content below the graph.
45
45
  * @slot fullscreen-icon - Replaces the glyph inside the fullscreen button.
46
46
  * @slot header - Content above the graph.
47
+ * @slot loading - What the panel shows while `loading` is set and the list holds no notes. Defaults to `loading-label`.
47
48
  * @slot pan-down-icon - Replaces the glyph inside the pan down button.
48
49
  * @slot pan-end-icon - Replaces the glyph inside the button that pans toward the end of a line of text.
49
50
  * @slot pan-start-icon - Replaces the glyph inside the button that pans toward the start of a line of text.
@@ -80,6 +81,7 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
80
81
  * @csspart legend-swatch - The mark in a group's color, before its name.
81
82
  * @csspart list - The list of notes, which draws only the rows in view.
82
83
  * @csspart live - What screen readers hear: how many notes there are when a search or the legend changes it, and which way the drawing went after a key or a control moved it.
84
+ * @csspart loading - The box in place of the list while `loading` is set and it holds no notes, holding the `loading` slot.
83
85
  * @csspart note - One note in the list.
84
86
  * @csspart note-cursor - The note the keyboard is on in the list.
85
87
  * @csspart note-linked - A note linked to or from the selected one.
@@ -103,6 +105,7 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
103
105
  * @cssstate empty - The list holds no notes: the graph has none, or a search or the groups hid them all.
104
106
  * @cssstate fullscreen - The graph fills the screen.
105
107
  * @cssstate idle - The user has been still long enough for the header and footer to fade.
108
+ * @cssstate loading - While `loading` is set.
106
109
  * @cssstate local - The drawing shows the selected note's local graph, with `depth`.
107
110
  * @cssstate panel-open - A graph with `no-panel` has the search and the list open over the drawing.
108
111
  * @cssstate ready - The graph has been drawn whole, after `helix-graph-ready`.
@@ -169,6 +172,13 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
169
172
  links: readonly GraphLink[];
170
173
  /** How a note that links to the selected one is described, with `{note}`. */
171
174
  linksToLabel: string;
175
+ /**
176
+ * Marks the graph as busy while its notes arrive: the list is `aria-busy`,
177
+ * and the panel says so where it holds no notes yet.
178
+ */
179
+ loading: boolean;
180
+ /** What the panel says while `loading` is set and the list holds no notes. */
181
+ loadingLabel: string;
172
182
  /** What screen readers hear once the drawing has moved down, after `moved()`. */
173
183
  movedDownLabel: string;
174
184
  /** What screen readers hear once the drawing has moved left, after `moved()`. */
@@ -185,6 +195,18 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
185
195
  * such as at the top of a page, where the drawing takes the whole frame.
186
196
  */
187
197
  noPanel: boolean;
198
+ /**
199
+ * What each note's row in the list shows in place of its name: a function
200
+ * given the note that returns text, an element or a Lit template.
201
+ *
202
+ * For an icon by its kind, or a date under its name, as React Aria's
203
+ * `ListBoxItem` takes children. The row is still named by the note's name,
204
+ * which typeahead and the search find it by, and described by how it stands
205
+ * to the selected note. Return the name with your content where it should
206
+ * still show. A property only, there being no way to write a function in
207
+ * markup.
208
+ */
209
+ noteContent?: ((node: GraphNode) => unknown) | undefined;
188
210
  /** The list's name, read by screen readers, and the name of the button that opens it with `no-panel`. */
189
211
  notesLabel: string;
190
212
  /**
@@ -27,6 +27,8 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
27
27
  this.linkedFromLabel = 'Linked from {note}';
28
28
  this.links = [];
29
29
  this.linksToLabel = 'Links to {note}';
30
+ this.loading = false;
31
+ this.loadingLabel = 'Loading notes';
30
32
  this.movedDownLabel = 'Moved down';
31
33
  this.movedLeftLabel = 'Moved left';
32
34
  this.movedRightLabel = 'Moved right';
@@ -163,6 +165,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
163
165
  super.willUpdate(changed);
164
166
  this.toggleState('fullscreen', this.filled);
165
167
  this.toggleState('idle', this.resting);
168
+ this.toggleState('loading', this.loading);
166
169
  this.toggleState('panel-open', this.noPanel && this.panelOpen);
167
170
  this.toggleState('searching', this.query.trim() !== '');
168
171
  this.toggleState('selected', this.active !== '');
@@ -389,14 +392,15 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
389
392
  aria-selected=${chosen ? 'true' : 'false'}
390
393
  aria-posinset=${index + 1}
391
394
  aria-setsize=${total}
392
- aria-description=${relation || nothing}
395
+ aria-describedby=${relation ? `note-${index}-relation` : nothing}
396
+ aria-label=${this.noteContent ? nameOf(entry.node) : nothing}
393
397
  @click=${() => {
394
398
  this.cursor = index;
395
399
  this.choose(index);
396
400
  }}
397
401
  >
398
- <span part="note-name">${nameOf(entry.node)}</span>
399
- ${relation ? html `<span part="note-relation" aria-hidden="true">${relation}</span>` : nothing}
402
+ <span part="note-name">${this.noteContent ? this.noteContent(entry.node) : nameOf(entry.node)}</span>
403
+ ${relation ? html `<span part="note-relation" id=${`note-${index}-relation`} aria-hidden="true">${relation}</span>` : nothing}
400
404
  </div>`;
401
405
  })}`;
402
406
  }
@@ -458,7 +462,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
458
462
  data-scroller
459
463
  aria-label=${this.notesLabel}
460
464
  aria-activedescendant=${cursor}
461
- aria-keyshortcuts="ArrowUp ArrowDown Home End PageUp PageDown Enter Space"
465
+ aria-busy=${this.loading ? 'true' : nothing}
462
466
  @keydown=${this.handleListKeydown}
463
467
  @scroll=${() => this.virtualize.measure()}
464
468
  >
@@ -467,7 +471,9 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
467
471
  <div part="after" role="presentation" style="block-size: ${after}px"></div>
468
472
  </div>
469
473
  ${this.entries.length === 0
470
- ? html `<div part="empty"><slot name="empty">${this.emptyLabel}</slot></div>`
474
+ ? this.loading
475
+ ? html `<div part="loading"><slot name="loading">${this.loadingLabel}</slot></div>`
476
+ : html `<div part="empty"><slot name="empty">${this.emptyLabel}</slot></div>`
471
477
  : nothing}
472
478
  </div>
473
479
  </div>
@@ -529,6 +535,13 @@ __decorate([
529
535
  words,
530
536
  property({ attribute: 'links-to-label' })
531
537
  ], HelixHeadlessGraph.prototype, "linksToLabel", void 0);
538
+ __decorate([
539
+ property({ reflect: true, type: Boolean })
540
+ ], HelixHeadlessGraph.prototype, "loading", void 0);
541
+ __decorate([
542
+ words,
543
+ property({ attribute: 'loading-label' })
544
+ ], HelixHeadlessGraph.prototype, "loadingLabel", void 0);
532
545
  __decorate([
533
546
  words,
534
547
  property({ attribute: 'moved-down-label' })
@@ -551,6 +564,9 @@ __decorate([
551
564
  __decorate([
552
565
  property({ attribute: 'no-panel', reflect: true, type: Boolean })
553
566
  ], HelixHeadlessGraph.prototype, "noPanel", void 0);
567
+ __decorate([
568
+ property({ attribute: false })
569
+ ], HelixHeadlessGraph.prototype, "noteContent", void 0);
554
570
  __decorate([
555
571
  words,
556
572
  property({ attribute: 'notes-label' })
@@ -91,6 +91,7 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
91
91
  '--show': () => this.show(),
92
92
  '--toggle': () => this.toggleOpen(),
93
93
  },
94
+ expanded: () => this.open,
94
95
  });
95
96
  this.handleSlotChange = () => {
96
97
  this.requestUpdate();
@@ -244,7 +244,8 @@ export declare class HelixHeadlessList extends HelixElement {
244
244
  /**
245
245
  * The selected rows' values, separated by commas with no spaces, such as `a,c`, in a list that selects.
246
246
  *
247
- * Set it to start with rows selected. A row's value is its `value`, or its
247
+ * A press or a key writes it in row order, as Data Table does, whatever
248
+ * order the rows were chosen in. Set it to start with rows selected. A row's value is its `value`, or its
248
249
  * words where it has none.
249
250
  */
250
251
  value: string;
@@ -328,6 +329,10 @@ export declare class HelixHeadlessList extends HelixElement {
328
329
  /**
329
330
  * Selects what a press or a key leaves, asking first. `helix-before-change`
330
331
  * carries the `value` it would leave, and `change` follows once it has moved.
332
+ *
333
+ * In row order, as Data Table and Table write theirs, whatever order the
334
+ * rows were chosen in: a press on the fourth row and then the first is
335
+ * `a,d`.
331
336
  */
332
337
  private choose;
333
338
  /** What `choiceValue` needs of this list for a row. */
@@ -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 { auditName, commaList, customElement, deepActiveElement, drawStatus, focusItemOf, formatWords, HelixActionEvent, HelixElement, HelixLoadMoreEvent, hostAria, inlineStep, KeyboardNavigationController, placeKey, pressedOwnControl, ReorderController, requestReorder, requestValue, rovingStop, StatusController, scrollingAncestor, scrollRoom, selectsAll, typeaheadByWords, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
7
+ import { auditName, commaList, customElement, deepActiveElement, drawStatus, focusItemOf, formatWords, HelixActionEvent, HelixElement, HelixLoadMoreEvent, hostAria, inlineStep, inRowOrder, KeyboardNavigationController, placeKey, pressedOwnControl, ReorderController, requestReorder, requestValue, rovingStop, StatusController, scrollingAncestor, scrollRoom, selectsAll, 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 { HelixHeadlessListItem } from '../item/list-item.js';
@@ -262,7 +262,8 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
262
262
  if (next === null) {
263
263
  return;
264
264
  }
265
- requestValue(this, next, (value) => {
265
+ const ordered = inRowOrder(commaList(next), this.rows.map((row) => this.valueFor(row))).join(',');
266
+ requestValue(this, ordered, (value) => {
266
267
  this.value = value;
267
268
  });
268
269
  }
@@ -394,7 +394,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
394
394
  * sounded the same. Said when the filter changes and the count with it, so
395
395
  * a keystroke that leaves the same options says nothing new. An empty filter
396
396
  * says nothing, being the list as it was given; no matches is `empty-text`,
397
- * which the region says in place of this.
397
+ * which the region says in place of this. A load that ends says the count
398
+ * too, whatever the filter.
398
399
  */
399
400
  private tell;
400
401
  /**
@@ -260,13 +260,14 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
260
260
  if (!this.hasUpdated) {
261
261
  return;
262
262
  }
263
- if (!changed.has('filter')) {
263
+ const loaded = changed.get('loading') === true && !this.loading;
264
+ if (!changed.has('filter') && !loaded) {
264
265
  this.tellActive(changed);
265
266
  return;
266
267
  }
267
268
  const count = this.shown.length;
268
269
  this.announced =
269
- this.filter.trim() && count
270
+ (this.filter.trim() || loaded) && count
270
271
  ? this.resultLabel
271
272
  ? countLabel(count, this.resultLabel, this.resultsLabel, wordsLocale(this))
272
273
  : formatWords(this, 'resultsLabel', { count })
@@ -81,6 +81,8 @@ import '../../kbd/index.js';
81
81
  * @csspart surface - The panel a submenu opens in beside the row, under the menu's `submenus="flyout"`.
82
82
  *
83
83
  * @cssstate checked - The row is checked.
84
+ * @cssstate disabled - The row is disabled.
85
+ * @cssstate expanded - Under `submenus="flyout"`, the row's submenu is open beside it.
84
86
  * @cssstate highlighted - In a menu with `filterable`, the arrow keys or the pointer reached the row while focus stays in the filter field.
85
87
  * @cssstate submenu-mark - Under `submenus="flyout"`, a row in this row's submenu can be ticked or opens a submenu, so the submenu keeps its trailing column.
86
88
  * @cssstate submenu-start - Under `submenus="flyout"`, a row in this row's submenu has content before its label, so the submenu keeps its leading column.
@@ -93,6 +93,8 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
93
93
  super.willUpdate(changed);
94
94
  this.toggleState('checked', this.checked);
95
95
  this.toggleState('highlighted', this.virtual && this.highlighted);
96
+ this.toggleState('disabled', this.disabled);
97
+ this.toggleState('expanded', this.expanded);
96
98
  if (!this.childrenKnown) {
97
99
  return;
98
100
  }