@fusion.dev/helix-headless 0.169.0 → 0.171.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 (41) hide show
  1. package/custom-elements.json +663 -95
  2. package/dist/chat/composer/chat-composer.d.ts +2 -1
  3. package/dist/chat/composer/chat-composer.js +7 -1
  4. package/dist/chat/root/chat-root.d.ts +36 -6
  5. package/dist/chat/root/chat-root.js +53 -5
  6. package/dist/code-block/root/code-block-root.d.ts +20 -2
  7. package/dist/code-block/root/code-block-root.js +29 -8
  8. package/dist/color-picker/root/color-picker-root.d.ts +50 -7
  9. package/dist/color-picker/root/color-picker-root.js +62 -1
  10. package/dist/combobox/root/combobox-root.d.ts +16 -6
  11. package/dist/combobox/root/combobox-root.js +9 -2
  12. package/dist/command/root/command-root.d.ts +16 -1
  13. package/dist/command/root/command-root.js +8 -0
  14. package/dist/data-table/root/data-table-root.d.ts +40 -5
  15. package/dist/data-table/root/data-table-root.js +35 -7
  16. package/dist/filter-input/chip/filter-chip.d.ts +13 -3
  17. package/dist/filter-input/chip/filter-chip.js +5 -0
  18. package/dist/filter-input/menu/filter-menu.js +0 -1
  19. package/dist/filter-input/root/filter-input-root.d.ts +71 -4
  20. package/dist/filter-input/root/filter-input-root.js +87 -2
  21. package/dist/format-date/root/format-date-root.d.ts +3 -1
  22. package/dist/input/root/input-root.d.ts +6 -0
  23. package/dist/input/root/input-root.js +13 -0
  24. package/dist/listbox/root/listbox-root.d.ts +6 -0
  25. package/dist/listbox/root/listbox-root.js +6 -1
  26. package/dist/number-input/root/number-input-root.d.ts +16 -6
  27. package/dist/number-input/root/number-input-root.js +15 -4
  28. package/dist/select/root/select-root.js +2 -0
  29. package/dist/steps/root/steps-root.d.ts +32 -8
  30. package/dist/steps/root/steps-root.js +70 -9
  31. package/dist/steps/step/step.d.ts +9 -1
  32. package/dist/steps/step/step.js +5 -0
  33. package/dist/video-playlist/root/video-playlist-root.d.ts +19 -1
  34. package/dist/video-playlist/root/video-playlist-root.js +14 -3
  35. package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
  36. package/dist/virtual-list/root/virtual-list-root.js +7 -2
  37. package/lint.json +1 -1
  38. package/package.json +5 -5
  39. package/types/jsx.d.ts +510 -126
  40. package/types/vue.d.ts +109 -26
  41. package/types/words.d.ts +12 -2
@@ -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, countLabel, customElement, DelayController, drawAutofill, drawStatus, formatWords, HelixCreateEvent, HelixFilterEvent, HelixFormElement, handedLocale, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, onApple, PopoverController, reportValues, StatusController, submitImplicitly, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, afterLinkFollowed, chosenValues, countLabel, customElement, DelayController, drawAutofill, drawStatus, formatWords, HelixCreateEvent, HelixFilterEvent, HelixFormElement, handedLocale, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, onApple, PopoverController, reportValues, StatusController, submitImplicitly, 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, state } from 'lit/decorators.js';
@@ -69,6 +69,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
69
69
  placement: 'bottom-start',
70
70
  surface: () => this.partNamed('surface'),
71
71
  });
72
+ this.created = new Set();
72
73
  this.handleInput = (event) => {
73
74
  event.stopPropagation();
74
75
  const typed = event.target.value;
@@ -252,7 +253,11 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
252
253
  return this.partNamed('listbox');
253
254
  }
254
255
  get values() {
255
- return this.value === '' ? [] : this.value.split(',');
256
+ const known = new Set(this.created);
257
+ for (const option of optionsAmong(this.knownChildren)) {
258
+ known.add(option.value);
259
+ }
260
+ return chosenValues(this.value, known, this.multiple);
256
261
  }
257
262
  get chosen() {
258
263
  const options = optionsAmong(this.knownChildren);
@@ -296,6 +301,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
296
301
  return;
297
302
  }
298
303
  const next = this.multiple ? [...this.values, wanted].join(',') : wanted;
304
+ this.created.add(wanted);
299
305
  if (!this.requestValue(next)) {
300
306
  return;
301
307
  }
@@ -343,6 +349,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
343
349
  }
344
350
  const named = (this.listbox?.options ?? []).find((option) => option.text.trim().toLowerCase() === typed.toLowerCase());
345
351
  const answer = named?.value ?? typed;
352
+ this.created.add(answer);
346
353
  if (this.multiple) {
347
354
  if (!this.values.includes(answer)) {
348
355
  this.requestValue([...this.values, answer].join(','));
@@ -55,7 +55,8 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
55
55
  * | `hotkey`, Command or Control with K by default | Opens the palette from anywhere on the page, or closes it. With `inline`, puts focus in its filter field. |
56
56
  * | Down, Up | Moves the highlight to the next or the previous command, round from the last to the first. |
57
57
  * | Page Down, Page Up | Moves the highlight by as many commands as the list shows at once. |
58
- * | Home, End, Left, Right | Move the caret in the filter field. |
58
+ * | Home, End | Move the caret in the filter field. With `home-end="list"`, move the highlight to the first or the last command. |
59
+ * | Left, Right | Move the caret in the filter field. |
59
60
  * | Enter | Runs the highlighted command, or follows it where it is a link, and closes the palette. On a command with `opens`, shows its page. |
60
61
  * | Command or Control with Enter | Takes the highlighted command's `alternate` destination, where it has one. |
61
62
  * | Backspace | In an empty filter on a page, goes back to the page before. |
@@ -144,6 +145,14 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
144
145
  * property only, there being no way to write a function in markup.
145
146
  */
146
147
  filterMatch?: ((text: string, filter: string) => boolean) | undefined;
148
+ /**
149
+ * Where Home and End go from the filter field.
150
+ *
151
+ * Absent, they move the caret, as APG's combobox has them. `list` moves the
152
+ * highlight to the first or the last command, as React Aria's Autocomplete
153
+ * does, and Shift with either still selects the text.
154
+ */
155
+ homeEnd?: 'list';
147
156
  /**
148
157
  * The key that opens the palette, written as `mod+k`, `shift+p`, or `k`,
149
158
  * in Kbd's chord spelling: `mod` is Command or Control, `ctrl`, `cmd`,
@@ -178,6 +187,12 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
178
187
  * Accessible name for the overlay, which has no visible heading.
179
188
  */
180
189
  label: string;
190
+ /**
191
+ * How the commands are laid out, as Listbox's `layout`: `grid` draws them in
192
+ * rows and columns, for a palette of icons or emoji, and the arrow keys
193
+ * follow the rows and the columns. Absent is one command under another.
194
+ */
195
+ layout?: 'grid' | 'stack';
181
196
  /**
182
197
  * What the list says when its commands could not be loaded, such as after a
183
198
  * request to a server fails. Empty, null or no attribute is no error, so
@@ -246,6 +246,8 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
246
246
  results-label=${this.resultsLabel}
247
247
  ?loading=${this.loading}
248
248
  .items=${this.items}
249
+ .fieldHomeEnd=${this.homeEnd === 'list'}
250
+ layout=${this.layout ?? nothing}
249
251
  filterable
250
252
  @helix-before-change=${this.handleChange}
251
253
  @helix-filter=${this.handleFilter}
@@ -299,6 +301,9 @@ __decorate([
299
301
  __decorate([
300
302
  property({ attribute: false })
301
303
  ], HelixHeadlessCommand.prototype, "filterMatch", void 0);
304
+ __decorate([
305
+ property({ attribute: 'home-end' })
306
+ ], HelixHeadlessCommand.prototype, "homeEnd", void 0);
302
307
  __decorate([
303
308
  property()
304
309
  ], HelixHeadlessCommand.prototype, "hotkey", void 0);
@@ -312,6 +317,9 @@ __decorate([
312
317
  words,
313
318
  property()
314
319
  ], HelixHeadlessCommand.prototype, "label", void 0);
320
+ __decorate([
321
+ property({ reflect: true })
322
+ ], HelixHeadlessCommand.prototype, "layout", void 0);
315
323
  __decorate([
316
324
  property({ attribute: 'load-error' })
317
325
  ], HelixHeadlessCommand.prototype, "loadError", void 0);
@@ -24,10 +24,15 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
24
24
  *
25
25
  * Sort and filter apply to the whole set, including the rows on other pages.
26
26
  *
27
- * **The cell values do not server-render.** The headings and the row elements
28
- * do. Reading a cell needs the columns, and Lit SSR does not pass the column
29
- * children to this element, so a crawler sees an empty table of the right
30
- * shape. Use `Table` when the served HTML has to carry the values.
27
+ * **Write the first page as rows, so the served HTML carries the values.** The
28
+ * table reads each cell from its column, and a server does not pass the column
29
+ * children to this element, so on its own it serves the headings and no values.
30
+ * Write the first page under the columns as `TableRow` and `TableCell`
31
+ * elements, in the table's sort and page size and formatted as its columns draw
32
+ * them. The table shows your rows until `rows` holds a row, then draws its own
33
+ * in their place and leaves yours in the page, unshown. A development build
34
+ * warns when the two differ. In React, map the first page of the same array in
35
+ * the same server component.
31
36
  *
32
37
  * `selectable` adds a column of checkboxes, keyed on `row-key`:
33
38
  *
@@ -132,7 +137,7 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
132
137
  * @aliases Grid, Result table, Data grid
133
138
  * @documentation https://fusion.dev/helix/components/data-table
134
139
  *
135
- * @slot - The columns, as `TableColumn` elements.
140
+ * @slot - The columns, as `TableColumn` elements, and the first page served as `TableRow` elements, which show until `rows` holds a row.
136
141
  * @slot empty - Content shown when there are no rows to show and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
137
142
  * @slot error - Content shown while `error-text` is set, such as a button that tries again. Falls back to `error-text`, which the live region still says.
138
143
  * @slot footer - Content below the grid. It replaces the built-in pagination.
@@ -610,6 +615,15 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
610
615
  private drawnPage;
611
616
  /** Whether the last render drew the `more` row, which follows the page's rows. */
612
617
  private moreDrawn;
618
+ /**
619
+ * Whether `rows` has held a row, after which the rows the page served are
620
+ * never shown again, whatever `rows` holds later.
621
+ */
622
+ private drewRows;
623
+ /** Whether the last render showed the rows the page served. */
624
+ private serving;
625
+ /** Whether the render before the last showed them, so the change is checked once. */
626
+ private servedLast;
613
627
  /** The letters typed at the grid, which move to the row whose header they start. */
614
628
  private readonly typeahead;
615
629
  /**
@@ -841,6 +855,27 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
841
855
  * what this replaced.
842
856
  */
843
857
  protected get hasRows(): boolean;
858
+ /**
859
+ * The rows the page served: the first page, written as `TableRow` children.
860
+ *
861
+ * By tag, because the page may not register `Table`, and an element that is
862
+ * not defined has no `isTableRow` to find it by.
863
+ */
864
+ private get servedRows();
865
+ /**
866
+ * Whether this render shows the rows the page served in place of drawing its
867
+ * own.
868
+ *
869
+ * **Until `rows` has held a row, and never after.** A server cannot tell
870
+ * whether the page served any, and a render hydrating what one sent has to
871
+ * render the same, so both show the slot that would hold them. In a browser
872
+ * the rows are shown while `rows` is empty, which is how a framework that
873
+ * renders on a server leaves the property until it hydrates. Once the table
874
+ * has drawn its own rows it stops showing the served ones and never removes
875
+ * them: they are the page's markup, and a framework that rendered them owns
876
+ * them.
877
+ */
878
+ private serves;
844
879
  /**
845
880
  * Whether this render can tell a result with no rows from one not yet handed
846
881
  * over.
@@ -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 HelixHeadlessDataTable_1;
8
- import { anchorAt, arranged, commaList, countLabel, customElement, cycle, deepActiveElement, deepContains, everyState, format, formatWords, gridMove, HelixActivateEvent, HelixElement, HelixExpandEvent, HelixFilterEvent, HelixFilteredEvent, HelixLoadMoreEvent, HelixNavigateEvent, HelixReorderEvent, HelixReorderedEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, handedLocale, hostAria, inlineStep, inRowOrder, MixinCollection, markRegion, onAndroid, ordered, pageRows, parse, placeKey, ReorderController, rangeAdd, reportRuntime, requestChange, requestReorder, scrollingAncestor, scrollRoom, selectsAll, Typeahead, toggleEvery, treeArrow, typesCharacter, VirtualizeController, valueAt, window_, words, wordsLocale, writtenList, } from '@fusion.dev/helix-core';
8
+ import { anchorAt, arranged, auditServed, commaList, countLabel, customElement, cycle, deepActiveElement, deepContains, everyState, format, formatWords, gridMove, HelixActivateEvent, HelixElement, HelixExpandEvent, HelixFilterEvent, HelixFilteredEvent, HelixLoadMoreEvent, HelixNavigateEvent, HelixReorderEvent, HelixReorderedEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, handedLocale, hostAria, inlineStep, inRowOrder, MixinCollection, markRegion, onAndroid, ordered, pageRows, parse, placeKey, ReorderController, rangeAdd, reportRuntime, requestChange, requestReorder, scrollingAncestor, scrollRoom, selectsAll, Typeahead, toggleEvery, treeArrow, typesCharacter, VirtualizeController, valueAt, window_, words, wordsLocale, writtenList, } from '@fusion.dev/helix-core';
9
9
  import { caretDown, caretRight, dotsSixVertical } from '@fusion.dev/helix-icons';
10
10
  import { nothing } from 'lit';
11
11
  import { property, state } from 'lit/decorators.js';
@@ -86,6 +86,9 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
86
86
  this.focusWanted = false;
87
87
  this.drawnPage = [];
88
88
  this.moreDrawn = false;
89
+ this.drewRows = false;
90
+ this.serving = false;
91
+ this.servedLast = false;
89
92
  this.typeahead = new Typeahead();
90
93
  this.anchorKey = undefined;
91
94
  this.shiftPressed = false;
@@ -656,7 +659,17 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
656
659
  })).length;
657
660
  }
658
661
  get hasRows() {
659
- return this.view.available > 0 || !this.sourceKnown;
662
+ return this.view.available > 0 || !this.sourceKnown || this.serving;
663
+ }
664
+ get servedRows() {
665
+ return this.knownChildren.filter((child) => /^helix-(headless-)?table-row$/.test(child.localName));
666
+ }
667
+ serves() {
668
+ if (!this.sourceKnown) {
669
+ return true;
670
+ }
671
+ this.drewRows ||= this.rows.length > 0;
672
+ return !this.drewRows && this.servedRows.length > 0;
660
673
  }
661
674
  get sourceKnown() {
662
675
  return this.childrenKnown;
@@ -909,6 +922,9 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
909
922
  }
910
923
  const columns = this.orderedColumns;
911
924
  head.assign(...columns);
925
+ root
926
+ .querySelector('[part="table"] > slot:not([name])')
927
+ ?.assign(...(this.serving ? this.servedRows : []));
912
928
  const rest = [...this.children].filter((child) => !columns.includes(child));
913
929
  for (const [name, wanted] of [
914
930
  ['toolbar', true],
@@ -986,6 +1002,10 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
986
1002
  this.toggleState('empty', !this.loading && !this.hasRows);
987
1003
  this.toggleState('loading', this.loading);
988
1004
  this.assign();
1005
+ if (this.servedLast && !this.serving) {
1006
+ auditServed(this, this.servedRows, [...this.renderRoot.querySelectorAll('[part~="row"]')], this.drawnPage.length);
1007
+ }
1008
+ this.servedLast = this.serving;
989
1009
  this.announceSort();
990
1010
  this.quietControls();
991
1011
  this.placeFocus();
@@ -1186,7 +1206,9 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1186
1206
  }
1187
1207
  render() {
1188
1208
  const { available, matched, page } = this.view;
1189
- this.drawnPage = page;
1209
+ const serving = this.serves();
1210
+ this.serving = serving;
1211
+ this.drawnPage = serving ? [] : page;
1190
1212
  this.moreDrawn = this.loadMore && !this.loading && this.errorText === '' && page.length > 0;
1191
1213
  const rows = this.windowOf(page);
1192
1214
  const kept = this.keptRow(page);
@@ -1196,7 +1218,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1196
1218
  const columns = this.shownColumns;
1197
1219
  const pagination = this.tag('pagination');
1198
1220
  const counted = this.mode === 'server' ? Math.max(this.total, available) : matched;
1199
- const countKnown = counted > 0 || this.sourceKnown;
1221
+ const countKnown = counted > 0 || (this.sourceKnown && !serving);
1200
1222
  this.counts = {
1201
1223
  columns: columns.length ? columns.length + this.lead : undefined,
1202
1224
  rows: countKnown ? counted + 1 + (this.moreDrawn ? 1 : 0) : undefined,
@@ -1210,13 +1232,19 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1210
1232
  @scroll=${this.handleScroll}
1211
1233
  >
1212
1234
  <div part="head" role="row" aria-rowindex="1">
1213
- ${this.rowGrips ? this.renderGripHead(headStop && this.currentColumn === 0) : nothing}${this.selectable
1235
+ ${this.rowGrips && this.sourceKnown
1236
+ ? this.renderGripHead(headStop && this.currentColumn === 0)
1237
+ : nothing}${this.selectable && this.sourceKnown
1214
1238
  ? this.renderSelectAll(headStop && this.currentColumn === this.selectAt)
1215
1239
  : nothing}${this.assignsByName && !this.hydrating
1216
1240
  ? this.orderedColumns.map((_, index) => html `<slot name=${`column-${index + 1}`} @slotchange=${this.handleSlotChange}></slot>`)
1217
- : html `<slot name="columns" @slotchange=${this.handleSlotChange}></slot>`}
1241
+ : html `<slot name="columns" @slotchange=${this.handleSlotChange}></slot>`}${this.sourceKnown ? nothing : html `<slot @slotchange=${this.handleSlotChange}></slot>`}
1218
1242
  </div>
1219
- ${this.renderSpacer('before', kept)}${this.renderRows(rows, page)}${this.renderSpacer('after', kept)}
1243
+ ${serving
1244
+ ? this.sourceKnown
1245
+ ? html `<slot @slotchange=${this.handleSlotChange}></slot>`
1246
+ : nothing
1247
+ : html `${this.renderSpacer('before', kept)}${this.renderRows(rows, page)}${this.renderSpacer('after', kept)}`}
1220
1248
  ${this.renderMore(page)}${this.errorText === '' ? this.renderStatus(filled, 'gridcell') : this.renderError()}
1221
1249
  </div>
1222
1250
  ${this.errorText === '' ? this.renderLive(filled) : html `<span part="live" role="status">${this.errorText}</span>`}
@@ -28,9 +28,10 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
28
28
  * press. `requestChange` documents the pattern.
29
29
  *
30
30
  * **Keyboard.** The remove button is a tab stop of its own, and Enter or Space
31
- * on it asks to remove the filter. In a `FilterInput`, Backspace in the empty
32
- * field asks to remove the last chip, the arrow keys move between the field
33
- * and the chips, and Delete or Backspace on a chip asks to remove it.
31
+ * on it asks to remove the filter. In a `FilterInput`, the field's text is the
32
+ * one tab stop: Backspace in the empty field asks to remove the last chip, the
33
+ * arrow keys move between the field and the chips, and Delete or Backspace on
34
+ * a chip asks to remove it.
34
35
  *
35
36
  * The remove button is named `dismiss-label` followed by the chip's words,
36
37
  * such as "Remove filter Status is not open", so a row of chips says which
@@ -78,6 +79,15 @@ export declare class HelixHeadlessFilterChip extends HelixHeadlessFilterChip_bas
78
79
  * closing a message.
79
80
  */
80
81
  dismissLabel: string;
82
+ /**
83
+ * Whether the remove button is a tab stop of its own.
84
+ *
85
+ * `FilterInput` writes `false` on the chips in its `tags` slot: the field's
86
+ * text is its one tab stop, and the arrow keys move from the text to the
87
+ * chips, as in a `multiple` Combobox. No attribute: it is coordination
88
+ * between elements, as `Tag.dismissTabStop` is.
89
+ */
90
+ dismissTabStop: boolean;
81
91
  /**
82
92
  * The name of the field the filter is on, as shown on the chip.
83
93
  *
@@ -13,6 +13,7 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
13
13
  constructor() {
14
14
  super(...arguments);
15
15
  this.dismissLabel = 'Remove filter';
16
+ this.dismissTabStop = true;
16
17
  this.label = '';
17
18
  this.operatorLabel = '';
18
19
  }
@@ -43,6 +44,7 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
43
44
  part="dismiss"
44
45
  type="button"
45
46
  aria-label=${this.removeName}
47
+ tabindex=${this.dismissTabStop ? nothing : '-1'}
46
48
  @click=${() => this.dismiss()}
47
49
  >
48
50
  <svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true">
@@ -56,6 +58,9 @@ __decorate([
56
58
  words,
57
59
  property({ attribute: 'dismiss-label' })
58
60
  ], HelixHeadlessFilterChip.prototype, "dismissLabel", void 0);
61
+ __decorate([
62
+ property({ attribute: false })
63
+ ], HelixHeadlessFilterChip.prototype, "dismissTabStop", void 0);
59
64
  __decorate([
60
65
  property()
61
66
  ], HelixHeadlessFilterChip.prototype, "label", void 0);
@@ -180,7 +180,6 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
180
180
  part="divider"
181
181
  exportparts="content: divider-content, line: divider-line"
182
182
  slot="submenu"
183
- announced
184
183
  ></${divider}>`
185
184
  : nothing}
186
185
  ${offered.map((option) => html `<${item}
@@ -33,8 +33,9 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
33
33
  * first colon, reads a leading `-` as the negation, and reads a word in
34
34
  * brackets before the colon as the comparison.
35
35
  *
36
- * **Keyboard.** Focus stays in the text. The text names the highlighted
37
- * suggestion as its `aria-activedescendant`.
36
+ * **Keyboard.** Focus stays in the text, which is the field's one tab stop. The
37
+ * text names the highlighted suggestion as its `aria-activedescendant`, and the
38
+ * arrow keys reach the tokens.
38
39
  *
39
40
  * | Key | What it does |
40
41
  * |---|---|
@@ -92,7 +93,8 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
92
93
  * and removing the chip alone would leave its value ticked in the hidden
93
94
  * menu. Its dismissal is treated as the field being filtered by nothing, so
94
95
  * it fires `helix-facet` like every other change, and the chip goes when the
95
- * filter goes.
96
+ * filter goes. Set `chips` and the field adds, updates, and removes those
97
+ * chips itself, from every `helix-facet` you do not cancel.
96
98
  *
97
99
  * @since 0.1.0
98
100
  * @status stable
@@ -105,6 +107,8 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
105
107
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
106
108
  * @slot hint - Help under the control, for content richer than the `hint` text.
107
109
  * @slot label - The label, for content richer than the `label` text.
110
+ * @slot loading - What the panel shows while `loading` is set, for content richer than `loading-text`.
111
+ * @slot no-results - What the panel shows when `filter-suggestions` hides every suggestion, for content richer than `no-results-text`.
108
112
  * @slot start - Content before the text, such as a search icon.
109
113
  * @slot suggestions - Suggestions for what to type next, as `Option`s and `Option.Group`s.
110
114
  * @slot tags - The active filters, as `FilterChip`s or `Tag`s.
@@ -121,17 +125,21 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
121
125
  *
122
126
  * @csspart clear - The button that empties the field.
123
127
  * @csspart control - The input itself.
128
+ * @csspart empty - Shown in the panel when `filter-suggestions` hides every suggestion.
124
129
  * @csspart feedback - The error message under the field.
125
130
  * @csspart field - The box that holds everything.
126
131
  * @csspart hint - The help text under the field, shown while there is no error.
127
132
  * @csspart label - The label above the field.
128
133
  * @csspart lead - The box before the text that holds the `start` slot.
129
134
  * @csspart live - The live region that says how many suggestions the open panel offers. It is rendered from the first time the panel opens with suggestions.
135
+ * @csspart loading - Shown in the panel while the suggestions load.
130
136
  * @csspart rest - The count of tokens that did not fit. It is a `<button>` with `expandable` and a `<span>` without it.
131
137
  * @csspart surface - The floating panel the suggestions sit in.
132
138
  * @csspart tags - The row the tokens sit in.
133
139
  *
134
140
  * @cssstate invalid - The field has an error to show, from the browser's validation or from your `feedback`.
141
+ * @cssstate loading - While `loading` is set.
142
+ * @cssstate no-results - `filter-suggestions` hid every suggestion, and `no-results-text` or the `no-results` slot says so.
135
143
  * @cssstate open - The suggestions are open.
136
144
  */
137
145
  export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_base {
@@ -143,6 +151,20 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
143
151
  protected readonly aria: AriaForwardController;
144
152
  /** Typed, so every keystroke is announced. See `announcesInput`. */
145
153
  protected readonly announcesInput = true;
154
+ /**
155
+ * Whether the field keeps a `FilterChip` for each filter in its `tags` slot,
156
+ * from the `helix-facet` events it hears.
157
+ *
158
+ * Off, you build the chips, which is what you do when your own model holds
159
+ * the filters. On, a filter typed, picked, or changed adds or updates the
160
+ * chip for its field, one with no values takes it away (unless its
161
+ * comparison takes none, such as "is empty"), and removing the chip clears
162
+ * the filter. A `helix-facet` you cancel changes no chip.
163
+ *
164
+ * The chips are elements the field adds to its own children, so leave this
165
+ * off where a framework renders those children and would remove them.
166
+ */
167
+ chips: boolean;
146
168
  /**
147
169
  * Shows a button that empties the field.
148
170
  *
@@ -163,6 +185,27 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
163
185
  * what you slot and you narrow it.
164
186
  */
165
187
  filterSuggestions: boolean;
188
+ /**
189
+ * Whether the suggestions are still loading, such as while a request for the
190
+ * values of a typed key is out.
191
+ *
192
+ * The panel opens on `loading-text`, or the `loading` slot, while the field
193
+ * has focus, and a screen reader hears it. Slot the options and clear this
194
+ * when they arrive.
195
+ */
196
+ loading: boolean;
197
+ /**
198
+ * Text the panel shows while the suggestions load.
199
+ */
200
+ loadingText: string;
201
+ /**
202
+ * Text the panel shows when `filter-suggestions` hides every suggestion, such
203
+ * as "No matching filters".
204
+ *
205
+ * Empty, or no `no-results` slot, and the panel closes instead, as it does
206
+ * when it has nothing to offer.
207
+ */
208
+ noResultsText: string;
166
209
  /**
167
210
  * The text shown while the field is empty.
168
211
  *
@@ -341,6 +384,16 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
341
384
  * else could have said it.
342
385
  */
343
386
  private announceFacet;
387
+ /**
388
+ * Keeps the chip for a filter in step with it, under `chips`.
389
+ *
390
+ * After every listener has had its turn, so a consumer's `preventDefault()`
391
+ * on the same event, on this element or above it, is read: `dispatchEvent`
392
+ * runs them all before the microtask does.
393
+ */
394
+ private readonly handleFacet;
395
+ /** Adds, updates or removes the chip for one filter. See `chips`. */
396
+ private drawChip;
344
397
  /**
345
398
  * A chip asking to go, answered as the filter going.
346
399
  *
@@ -413,6 +466,12 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
413
466
  * is why `settleOpen` is not called from here.
414
467
  */
415
468
  private refreshSuggesting;
469
+ /**
470
+ * Whether `filter-suggestions` has hidden every suggestion there is, where a
471
+ * `no-results-text` or a `no-results` slot says so. The panel stays open on
472
+ * it, and `:state(no-results)` is on the host.
473
+ */
474
+ private get unmatched();
416
475
  /** The panel again, whenever what is in it changes. */
417
476
  private readonly handleSuggestions;
418
477
  /** Focus arriving in the field is a reader asking what they can type. */
@@ -426,7 +485,15 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
426
485
  private readonly handleFocusOut;
427
486
  /** The leading box again, whenever what is in front of the text changes. */
428
487
  private readonly handleLeading;
429
- /** The row again, whenever what is in it changes. */
488
+ /**
489
+ * The row again, whenever what is in it changes.
490
+ *
491
+ * **And the text is the field's one tab stop.** Each token's remove button
492
+ * was a stop of its own, so Tab through a field with five filters took six
493
+ * presses. A `Tag` or `FilterChip` takes `dismissTabStop` for this, as the
494
+ * tags of a `multiple` Combobox do, and the arrow keys still reach every one.
495
+ * A token not yet defined is told once it is.
496
+ */
430
497
  private readonly handleTokens;
431
498
  /** The tokens slotted in `tags`, which the arrow keys reach from the text. */
432
499
  protected tokenElements(): HTMLElement[];