@fusion.dev/helix-headless 0.149.1 → 0.151.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 (69) hide show
  1. package/custom-elements.json +1211 -110
  2. package/dist/area-chart/root/area-chart-root.d.ts +36 -0
  3. package/dist/area-chart/root/area-chart-root.js +17 -2
  4. package/dist/bar-chart/root/bar-chart-root.d.ts +28 -4
  5. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  6. package/dist/button/root/button-root.d.ts +5 -0
  7. package/dist/button/root/button-root.js +3 -0
  8. package/dist/callout/root/callout-root.d.ts +20 -0
  9. package/dist/callout/root/callout-root.js +19 -4
  10. package/dist/chat/composer/chat-composer.d.ts +30 -0
  11. package/dist/chat/composer/chat-composer.js +47 -2
  12. package/dist/chat/root/chat-root.d.ts +10 -0
  13. package/dist/chat/root/chat-root.js +14 -0
  14. package/dist/color-input/root/color-input-root.d.ts +1 -0
  15. package/dist/color-picker/root/color-picker-root.js +1 -0
  16. package/dist/combobox/root/combobox-root.d.ts +12 -1
  17. package/dist/combobox/root/combobox-root.js +8 -0
  18. package/dist/copy-button/root/copy-button-root.d.ts +4 -3
  19. package/dist/copy-button/root/copy-button-root.js +5 -1
  20. package/dist/data-table/root/data-table-root.d.ts +34 -2
  21. package/dist/data-table/root/data-table-root.js +63 -2
  22. package/dist/date-input/root/date-input-root.d.ts +1 -0
  23. package/dist/divider/root/divider-root.d.ts +8 -6
  24. package/dist/divider/root/divider-root.js +10 -2
  25. package/dist/element-events.generated.js +5 -1
  26. package/dist/filter-input/chip/filter-chip.d.ts +2 -1
  27. package/dist/filter-input/root/filter-input-root.d.ts +5 -0
  28. package/dist/filter-input/root/filter-input-root.js +7 -0
  29. package/dist/kbd/root/kbd-root.d.ts +7 -1
  30. package/dist/kbd/root/kbd-root.js +8 -4
  31. package/dist/line-chart/root/line-chart-root.d.ts +36 -0
  32. package/dist/line-chart/root/line-chart-root.js +15 -2
  33. package/dist/number-input/root/number-input-root.d.ts +6 -0
  34. package/dist/number-input/root/number-input-root.js +14 -3
  35. package/dist/option/root/option-root.d.ts +17 -8
  36. package/dist/option/root/option-root.js +18 -4
  37. package/dist/radio/group/radio-group.d.ts +8 -1
  38. package/dist/radio/group/radio-group.js +15 -1
  39. package/dist/scatter-chart/root/scatter-chart-root.d.ts +18 -0
  40. package/dist/scatter-chart/root/scatter-chart-root.js +38 -15
  41. package/dist/slider/root/slider-root.d.ts +8 -0
  42. package/dist/slider/root/slider-root.js +6 -3
  43. package/dist/table/column/table-column.js +3 -2
  44. package/dist/table/grid-controller.d.ts +14 -2
  45. package/dist/table/grid-controller.js +27 -1
  46. package/dist/table/root/table-root.d.ts +30 -4
  47. package/dist/table/root/table-root.js +45 -7
  48. package/dist/table/row/table-row.d.ts +12 -2
  49. package/dist/table/row/table-row.js +8 -2
  50. package/dist/tag/group/tag-group.d.ts +22 -1
  51. package/dist/tag/group/tag-group.js +63 -3
  52. package/dist/tag/root/tag-root.d.ts +9 -0
  53. package/dist/tag/root/tag-root.js +5 -1
  54. package/dist/time-input/root/time-input-root.d.ts +1 -0
  55. package/dist/toggle/group/toggle-group.d.ts +6 -0
  56. package/dist/toggle/group/toggle-group.js +16 -1
  57. package/dist/toggle/root/toggle-root.d.ts +21 -3
  58. package/dist/toggle/root/toggle-root.js +22 -5
  59. package/dist/video/root/video-root.d.ts +6 -0
  60. package/dist/video/root/video-root.js +5 -1
  61. package/dist/video-playlist/root/video-playlist-root.d.ts +4 -0
  62. package/dist/video-playlist/root/video-playlist-root.js +4 -1
  63. package/dist/virtual-list/root/virtual-list-root.d.ts +23 -1
  64. package/dist/virtual-list/root/virtual-list-root.js +21 -2
  65. package/lint.json +1 -1
  66. package/package.json +5 -5
  67. package/types/jsx.d.ts +808 -156
  68. package/types/vue.d.ts +223 -59
  69. package/types/words.d.ts +4 -2
@@ -31,6 +31,9 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
31
31
  * | Escape | Closes the list and puts the chosen words back. On a closed field, a second Escape clears a single choice. |
32
32
  * | Tab | Closes the list and moves on. |
33
33
  * | Backspace | With `multiple`, on an empty field, removes the last tag. |
34
+ * | Left | With `multiple`, with the caret at the start of the text, moves to the last tag's remove button. Right on a right-to-left page. |
35
+ * | Left, Right on a tag | Move between the tags, and past the last back to the text. |
36
+ * | Delete, Backspace on a tag | Removes the tag. |
34
37
  * | Space, Home, End, letters | Edit the text. |
35
38
  *
36
39
  * **ARIA.** The input is the `combobox`, named by the label, with
@@ -46,7 +49,8 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
46
49
  * Set `autocomplete` to let a browser's autofill choose an option.
47
50
  *
48
51
  * With `multiple`, each chosen option is a tag in the field with its own remove
49
- * control. The panel stays open and the filter clears after each choice, so the
52
+ * control. The text is the field's one tab stop, and the arrow keys reach the
53
+ * tags from it. The panel stays open and the filter clears after each choice, so the
50
54
  * next choice starts from the whole list.
51
55
  *
52
56
  * With `creatable`, the combobox offers a create row and fires `helix-create`.
@@ -584,6 +588,13 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
584
588
  * tag does.
585
589
  */
586
590
  protected removeLastToken(): void;
591
+ /** The tags, which the arrow keys reach from the text. */
592
+ protected tokenElements(): HTMLElement[];
593
+ /**
594
+ * Focus goes to the text field, the one tab stop, and never to a tag's remove
595
+ * button ahead of it, where Enter or Space would take a choice away.
596
+ */
597
+ protected get focusTarget(): HTMLElement | null;
587
598
  /** One tag asking to be taken out of the set. */
588
599
  private readonly handleDismiss;
589
600
  /**
@@ -446,6 +446,12 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
446
446
  this.drop(last);
447
447
  }
448
448
  }
449
+ tokenElements() {
450
+ return [...this.renderRoot.querySelectorAll('[part="tag"]')];
451
+ }
452
+ get focusTarget() {
453
+ return this.partNamed('control') ?? super.focusTarget;
454
+ }
449
455
  sync() {
450
456
  if (this.multiple) {
451
457
  reportValues(this.internals, this.name, this.value === '' ? [] : this.value.split(','));
@@ -574,6 +580,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
574
580
  data-value=${option.value}
575
581
  dismiss-label=${formatWords(this, 'removeLabel', { name: option.text })}
576
582
  ?dismissible=${!this.isDisabled && !this.readOnly}
583
+ .dismissTabStop=${false}
577
584
  @helix-dismiss=${this.handleDismiss}
578
585
  >${option.text}</${this.tag('tag')}
579
586
  >`;
@@ -619,6 +626,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
619
626
  aria-expanded=${this.open ? 'true' : 'false'}
620
627
  aria-describedby=${this.describedBy ?? nothing}
621
628
  aria-invalid=${this.invalidity ?? nothing}
629
+ aria-required=${this.required ? 'true' : nothing}
622
630
  placeholder=${(this.chosen.length > 0 || this.unresolved ? '' : this.placeholder) || nothing}
623
631
  ?disabled=${this.isDisabled}
624
632
  ?readonly=${this.readOnly}
@@ -29,8 +29,9 @@ import '../../tooltip/index.js';
29
29
  * **Keyboard.** Enter and Space copy, as they press a native button.
30
30
  *
31
31
  * The button is named `label`, or the host's `aria-label`, and its name
32
- * changes to `copied-label` or `error-label` for the moment after a press,
33
- * which is what a screen reader on it hears. The tooltip shows the same words.
32
+ * changes to `copied-label` or `error-label` for the moment after a press. The
33
+ * same words are said in a live region, so a screen reader hears the result,
34
+ * and the tooltip shows them.
34
35
  *
35
36
  * @since 0.1.0
36
37
  * @status stable
@@ -46,7 +47,7 @@ import '../../tooltip/index.js';
46
47
  * @event {HelixCopyErrorEvent} helix-copy-error - Fired when a press put nothing on the clipboard: the browser did not allow the write, or there was nothing to copy.
47
48
  *
48
49
  * @csspart button - The inner button.
49
- * @csspart live - The live region that says `pending-label` when the button becomes pending under focus. It is rendered from the first time the button is pending.
50
+ * @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.
50
51
  * @csspart mark - The clipboard icon, and the confirmation icon that replaces it.
51
52
  * @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.
52
53
  * @csspart tooltip - The tooltip bubble, forwarded from the tooltip inside the button.
@@ -37,7 +37,11 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
37
37
  const copying = this.clipboard.copy(this.text);
38
38
  this.announcePress(event);
39
39
  const wrote = await copying;
40
- if (!wrote && this.isConnected) {
40
+ if (!this.isConnected) {
41
+ return;
42
+ }
43
+ this.sayStatus(wrote ? this.copiedLabel : this.errorLabel);
44
+ if (!wrote) {
41
45
  this.dispatchEvent(new HelixCopyErrorEvent());
42
46
  }
43
47
  };
@@ -40,6 +40,10 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
40
40
  * and shows a dash while only some are chosen.
41
41
  * - Shift with a press on a checkbox selects every row between it and the row
42
42
  * last chosen.
43
+ * - `selection-behavior="replace"` makes a press or a move select the row
44
+ * alone, as a file list does. Control or Command with a press adds or takes
45
+ * out one row, and Shift with a press selects every row from the one last
46
+ * chosen.
43
47
  *
44
48
  * To act on a single row, such as opening it, put a link or a button in a cell.
45
49
  *
@@ -56,7 +60,8 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
56
60
  * | Enter, F2 | Moves into the cell's controls. Escape moves back to the cell, and Tab moves between the controls of one cell. |
57
61
  * | Enter, Space on a sortable heading | Sorts by the column. |
58
62
  * | Enter, Space on the `load-more` row | Sends `helix-load-more`. |
59
- * | Space on a row | Selects the row, in a `selectable` table. |
63
+ * | Space on a row | Selects the row, in a `selectable` table. Under `selection-behavior="replace"`, selects it alone, and Control or Command with Space adds or takes out the row. |
64
+ * | Down, Up, Page Down, Page Up under `selection-behavior="replace"` | Selects the row it moves to alone. Control or Command with the key moves without selecting. |
60
65
  * | Shift with Down or Up | Selects the rows it moves across, in a `selectable` table. |
61
66
  * | Shift with Space | Selects every row from the one last chosen, in a `selectable` table. |
62
67
  * | Control or Command with A | Selects every row, in a `selectable` table. |
@@ -123,7 +128,9 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
123
128
  * @documentation https://fusion.dev/helix/components/data-table
124
129
  *
125
130
  * @slot - The columns, as `TableColumn` elements.
131
+ * @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.
126
132
  * @slot footer - Content below the grid. It replaces the built-in pagination.
133
+ * @slot loading - Content shown while the rows load, such as a spinner. Falls back to `loading-text`, which the live region still says.
127
134
  * @slot toolbar - Content above the grid, such as a search field or actions.
128
135
  *
129
136
  * @event {HelixActivateEvent} helix-activate - Fired after the page changes, with the new page as a string in `detail.value`.
@@ -216,6 +223,8 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
216
223
  * `registerWords('de', { 'helix-data-table': { nextLabel: 'Nächste Seite' } })`.
217
224
  */
218
225
  static words: string;
226
+ /** The empty and loading lines are the `empty` and `loading` slots. */
227
+ protected statusSlots: boolean;
219
228
  /**
220
229
  * The word for an ascending sort, in a `sorted-label` that writes `{order}`.
221
230
  *
@@ -490,6 +499,15 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
490
499
  * {# wybranych} other {# wybranego}}`.
491
500
  */
492
501
  selectedLabel: string;
502
+ /**
503
+ * What a key or a press on a row does to the rows already selected, in a
504
+ * `selectable` table. `toggle`, the default, keeps them: Space adds or takes
505
+ * out the row. `replace` selects the row alone, as a file list does: Down and
506
+ * Up carry the selection with them, and a press on a row selects it. Control
507
+ * or Command with a press or Space adds or takes out one row, and with a move
508
+ * goes without selecting. The checkboxes add or take out one row either way.
509
+ */
510
+ selectionBehavior: 'replace' | 'toggle';
493
511
  /**
494
512
  * The name of a row's checkbox. `{row}` is replaced with the row's value in
495
513
  * the first column that has a field, or with its key.
@@ -954,7 +972,7 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
954
972
  */
955
973
  toggleSort(field: string): boolean;
956
974
  /**
957
- * The columns into the header row, everything else into the toolbar and footer.
975
+ * The columns into the header row, everything else into the slot it names.
958
976
  *
959
977
  * Manual assignment, so a consumer's own markup is never written to.
960
978
  */
@@ -1124,6 +1142,20 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
1124
1142
  * request carries only the state it asks for.
1125
1143
  */
1126
1144
  private readonly notePress;
1145
+ /** Whether a press or a move on a row selects it alone. */
1146
+ private get replaces();
1147
+ /**
1148
+ * Selects the row at `row` on the page alone, and makes it where a range
1149
+ * runs from. Asks nothing where it is already the only one selected.
1150
+ */
1151
+ private chooseOnly;
1152
+ /**
1153
+ * A press on a row under `selection-behavior="replace"`: it selects the row
1154
+ * alone, Control or Command adds or takes out the row, and Shift selects
1155
+ * every row from the one last chosen. A press on a control in a cell, or on
1156
+ * the checkbox or grip column, is the control's.
1157
+ */
1158
+ private readonly handleRowClick;
1127
1159
  /**
1128
1160
  * A click on a sortable heading, anywhere but its grip or its edge.
1129
1161
  *
@@ -30,6 +30,7 @@ const isControl = (element) => element.matches(CONTROL) || element.shadowRoot?.d
30
30
  let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollection(HelixElement) {
31
31
  constructor() {
32
32
  super(...arguments);
33
+ this.statusSlots = true;
33
34
  this.ascendingLabel = 'ascending';
34
35
  this.canceledLabel = 'Canceled';
35
36
  this.defaultExpanded = '';
@@ -64,6 +65,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
64
65
  this.selectable = false;
65
66
  this.selectAllLabel = 'Select every row';
66
67
  this.selectedLabel = '{count} selected';
68
+ this.selectionBehavior = 'toggle';
67
69
  this.selectRowLabel = 'Select {row}';
68
70
  this.sortedLabel = '{direction, select, ascending {Sorted by {column}, ascending} descending {Sorted by {column}, descending} other {Sorted by {column}}}';
69
71
  this.total = 0;
@@ -178,6 +180,30 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
178
180
  this.notePress = (event) => {
179
181
  this.shiftPressed = event.shiftKey;
180
182
  };
183
+ this.handleRowClick = (event) => {
184
+ if (!this.replaces || event.defaultPrevented) {
185
+ return;
186
+ }
187
+ const place = this.placeOf(event);
188
+ if (!place || place.row < 0 || place.row >= this.drawnPage.length || place.column < this.lead) {
189
+ return;
190
+ }
191
+ const origin = event.composedPath()[0];
192
+ if (origin instanceof Node && this.controlsIn(place).some((one) => deepContains(one, origin))) {
193
+ return;
194
+ }
195
+ if (event.shiftKey && this.anchorKey !== undefined) {
196
+ this.selectRange(place.row);
197
+ return;
198
+ }
199
+ if (event.ctrlKey || event.metaKey) {
200
+ const data = this.drawnPage[place.row];
201
+ const key = this.keyOf(data, this.rowIndex(place.row) - 2);
202
+ this.chooseRow(key, !this.chosen.has(key));
203
+ return;
204
+ }
205
+ this.chooseOnly(place.row);
206
+ };
181
207
  this.handleHeadClick = (event) => {
182
208
  if (this.partIn(event, 'grip') || this.partIn(event, 'resizer')) {
183
209
  return;
@@ -251,14 +277,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
251
277
  }
252
278
  const { column, row } = to;
253
279
  event.preventDefault();
280
+ const onRow = row >= 0 && row < this.drawnPage.length;
254
281
  if (event.shiftKey &&
255
282
  this.selectable &&
256
283
  (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&
257
284
  place.row >= 0 &&
258
- row >= 0 &&
259
- row < this.drawnPage.length) {
285
+ onRow) {
260
286
  this.selectRange(row, place.row);
261
287
  }
288
+ else if (this.replaces &&
289
+ onRow &&
290
+ row !== place.row &&
291
+ !event.shiftKey &&
292
+ !event.ctrlKey &&
293
+ !event.metaKey) {
294
+ this.chooseOnly(row);
295
+ }
262
296
  if (row !== place.row || column !== place.column) {
263
297
  this.moveTo(row, column);
264
298
  }
@@ -878,6 +912,8 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
878
912
  for (const [name, wanted] of [
879
913
  ['toolbar', true],
880
914
  ['footer', true],
915
+ ['empty', true],
916
+ ['loading', true],
881
917
  ]) {
882
918
  const slot = root.querySelector(`slot[name="${name}"]`);
883
919
  if (slot?.assign && wanted) {
@@ -1201,6 +1237,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1201
1237
  connectedCallback() {
1202
1238
  super.connectedCallback();
1203
1239
  this.addEventListener('click', this.handleHeadClick);
1240
+ this.addEventListener('click', this.handleRowClick);
1204
1241
  this.addEventListener('pointerdown', this.notePress, true);
1205
1242
  this.addEventListener('keydown', this.notePress, true);
1206
1243
  this.addEventListener('focusin', this.handleFocusIn);
@@ -1212,6 +1249,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1212
1249
  disconnectedCallback() {
1213
1250
  super.disconnectedCallback();
1214
1251
  this.removeEventListener('click', this.handleHeadClick);
1252
+ this.removeEventListener('click', this.handleRowClick);
1215
1253
  this.removeEventListener('pointerdown', this.notePress, true);
1216
1254
  this.removeEventListener('keydown', this.notePress, true);
1217
1255
  this.removeEventListener('focusin', this.handleFocusIn);
@@ -1220,6 +1258,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1220
1258
  this.removeEventListener('keydown', this.handleGripKey);
1221
1259
  this.removeEventListener('helix-before-resize', this.handleResize);
1222
1260
  }
1261
+ get replaces() {
1262
+ return this.selectable && this.selectionBehavior === 'replace';
1263
+ }
1264
+ chooseOnly(row) {
1265
+ const data = this.drawnPage[row];
1266
+ if (!data) {
1267
+ return;
1268
+ }
1269
+ const key = this.keyOf(data, this.rowIndex(row) - 2);
1270
+ this.anchorKey = key;
1271
+ const now = this.chosen;
1272
+ if (now.size === 1 && now.has(key)) {
1273
+ return;
1274
+ }
1275
+ this.requestSelected(new Set([key]));
1276
+ }
1223
1277
  cellAt(row, column) {
1224
1278
  const offset = this.lead;
1225
1279
  if (row === -1) {
@@ -1402,6 +1456,10 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
1402
1456
  event.preventDefault();
1403
1457
  this.selectRange(place.row);
1404
1458
  }
1459
+ else if (this.replaces && row && !event.ctrlKey && !event.metaKey) {
1460
+ event.preventDefault();
1461
+ this.chooseOnly(place.row);
1462
+ }
1405
1463
  else if (this.selectable && row) {
1406
1464
  event.preventDefault();
1407
1465
  const key = this.keyOf(row, this.rowIndex(place.row) - 2);
@@ -1595,6 +1653,9 @@ __decorate([
1595
1653
  words,
1596
1654
  property({ attribute: 'selected-label' })
1597
1655
  ], HelixHeadlessDataTable.prototype, "selectedLabel", void 0);
1656
+ __decorate([
1657
+ property({ attribute: 'selection-behavior' })
1658
+ ], HelixHeadlessDataTable.prototype, "selectionBehavior", void 0);
1598
1659
  __decorate([
1599
1660
  words,
1600
1661
  property({ attribute: 'select-row-label' })
@@ -35,6 +35,7 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
35
35
  *
36
36
  * @slot feedback - The error message under the field.
37
37
  * @slot hint - The help text under the field.
38
+ * @slot label - The label, for content richer than the `label` text.
38
39
  * @slot next-icon - The icon on the calendar's next month control.
39
40
  * @slot panel-footer - Content in the panel under the calendar, such as a note or an action.
40
41
  * @slot panel-header - Content in the panel above the calendar, such as a heading or a shortcut.
@@ -12,9 +12,10 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
12
  * **The line is a separator**, as `<hr>` is, and as Radix Separator, React
13
13
  * Aria Separator, and Web Awesome's divider are. It is named by any content
14
14
  * set into it, so a screen reader says "Or, separator" where you wrote "Or" on
15
- * the line. Set `decorative` on a rule that separates nothing, such as one
16
- * under a heading or between the facts of a byline, and it leaves the
17
- * accessibility tree.
15
+ * the line, and the words are read once, as that name. Set words into a
16
+ * separator and no controls, which are hidden with them. Set `decorative` on
17
+ * a rule that separates nothing, such as one under a heading or between the
18
+ * facts of a byline, and it leaves the accessibility tree.
18
19
  *
19
20
  * Use `orientation="vertical"` between items on one line. A vertical divider
20
21
  * stretches to fit its flex parent and sets no height of its own.
@@ -44,9 +45,10 @@ export declare class HelixHeadlessDivider extends HelixElement {
44
45
  * Whether screen readers announce the line as a separator, which they now do
45
46
  * by default.
46
47
  *
47
- * It dates from when the line was decorative by default. It is still read,
48
- * and it wins over `decorative`, so a divider written with it stays a
49
- * separator.
48
+ * @deprecated Remove it: a divider is a separator unless `decorative` is
49
+ * set. It dates from when the line was decorative by default, and it is
50
+ * still read and wins over `decorative`, so a divider written with it stays
51
+ * a separator.
50
52
  */
51
53
  announced: boolean;
52
54
  /**
@@ -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, HelixElement, markRegion, toolbarAround, toolbarAxis, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, markRegion, reportRuntime, toolbarAround, toolbarAxis, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
@@ -38,6 +38,14 @@ let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
38
38
  updated(changed) {
39
39
  super.updated(changed);
40
40
  this.markEmpty();
41
+ if (changed.has('announced') && this.announced && changed.get('announced') === undefined) {
42
+ reportRuntime({
43
+ kind: 'deprecated',
44
+ message: `<${this.localName} announced> is deprecated: remove it. A divider is a separator ` +
45
+ 'unless `decorative` is set.',
46
+ tag: this.localName,
47
+ });
48
+ }
41
49
  }
42
50
  render() {
43
51
  const separator = this.announced || !this.decorative;
@@ -47,7 +55,7 @@ let HelixHeadlessDivider = class HelixHeadlessDivider extends HelixElement {
47
55
  aria-labelledby=${separator ? 'content' : nothing}
48
56
  aria-orientation=${separator ? this.drawn : nothing}
49
57
  ></div>
50
- <span id="content" part="content"><slot @slotchange=${() => this.markEmpty()}></slot></span>
58
+ <span id="content" part="content" aria-hidden=${separator ? 'true' : nothing}><slot @slotchange=${() => this.markEmpty()}></slot></span>
51
59
  <div part="line" role="presentation"></div>`;
52
60
  }
53
61
  };
@@ -3,7 +3,9 @@ registerElementEvents({
3
3
  'helix-headless-accordion-item': ['helix-before-close', 'helix-before-open', 'toggle'],
4
4
  'helix-headless-announcement': ['helix-dismiss'],
5
5
  'helix-headless-app-header': ['helix-scrolled'],
6
+ 'helix-headless-area-chart': ['helix-reading-change', 'helix-reading-press'],
6
7
  'helix-headless-avatar': ['error', 'load'],
8
+ 'helix-headless-bar-chart': ['helix-reading-change', 'helix-reading-press'],
7
9
  'helix-headless-button': ['helix-press'],
8
10
  'helix-headless-button-group': ['change', 'helix-before-change'],
9
11
  'helix-headless-callout': ['helix-dismiss'],
@@ -132,6 +134,7 @@ registerElementEvents({
132
134
  'helix-before-open',
133
135
  'toggle',
134
136
  ],
137
+ 'helix-headless-line-chart': ['helix-reading-change', 'helix-reading-press'],
135
138
  'helix-headless-list': [
136
139
  'change',
137
140
  'helix-action',
@@ -208,6 +211,7 @@ registerElementEvents({
208
211
  'helix-preview',
209
212
  'input',
210
213
  ],
214
+ 'helix-headless-scatter-chart': ['helix-reading-change', 'helix-reading-press'],
211
215
  'helix-headless-scroll-area': ['scroll', 'scrollend'],
212
216
  'helix-headless-select': [
213
217
  'change',
@@ -262,7 +266,7 @@ registerElementEvents({
262
266
  'helix-headless-time-picker': ['change', 'helix-before-change', 'input'],
263
267
  'helix-headless-toast': ['helix-dismiss'],
264
268
  'helix-headless-toast-item': ['helix-dismiss'],
265
- 'helix-headless-toggle': ['helix-select'],
269
+ 'helix-headless-toggle': ['change', 'helix-select'],
266
270
  'helix-headless-toggle-group': ['change', 'helix-before-change', 'helix-select', 'input'],
267
271
  'helix-headless-tooltip': ['helix-before-close', 'helix-before-open', 'toggle'],
268
272
  'helix-headless-tour': [
@@ -29,7 +29,8 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
29
29
  *
30
30
  * **Keyboard.** The remove button is a tab stop of its own, and Enter or Space
31
31
  * on it asks to remove the filter. In a `FilterInput`, Backspace in the empty
32
- * field asks to remove the last chip.
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.
33
34
  *
34
35
  * The remove button is named `dismiss-label` followed by the chip's words,
35
36
  * such as "Remove filter Status is not open", so a row of chips says which
@@ -43,6 +43,9 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
43
43
  * | Home, End | Highlights the first or the last suggestion while the panel is open. |
44
44
  * | Escape | Closes the panel and stops there, so a dialog around the field stays open. |
45
45
  * | Backspace | In an empty field, asks the last token to go. |
46
+ * | Left | With the caret at the start of the text, moves to the last token's remove button. Right on a right-to-left page. |
47
+ * | Left, Right on a token | Move between the tokens, and past the last back to the text. |
48
+ * | Delete, Backspace on a token | Asks the token to go. |
46
49
  *
47
50
  * **Typed filters.** The field parses only the fields you declare. Write
48
51
  * `FilterField` elements, usually inside the `FilterMenu` slotted into `start`,
@@ -425,6 +428,8 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
425
428
  private readonly handleLeading;
426
429
  /** The row again, whenever what is in it changes. */
427
430
  private readonly handleTokens;
431
+ /** The tokens slotted in `tags`, which the arrow keys reach from the text. */
432
+ protected tokenElements(): HTMLElement[];
428
433
  /**
429
434
  * Asks the last token to go, rather than taking it.
430
435
  *
@@ -84,6 +84,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
84
84
  }
85
85
  };
86
86
  this.handleKeydown = (event) => {
87
+ if (event.isComposing) {
88
+ return;
89
+ }
87
90
  if (this.tokenKeydown(event, this.value)) {
88
91
  return;
89
92
  }
@@ -310,6 +313,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
310
313
  this.suggesting = options.length > 0;
311
314
  this.activeAt = this.activeAt < options.length ? this.activeAt : -1;
312
315
  }
316
+ tokenElements() {
317
+ return (this.tokens?.assignedElements({ flatten: true }) ?? []);
318
+ }
313
319
  removeLastToken() {
314
320
  const last = this.tokens
315
321
  ?.assignedElements({ flatten: true })
@@ -368,6 +374,7 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
368
374
  const active = this.open ? options[this.activeAt] : undefined;
369
375
  for (const [at, option] of options.entries()) {
370
376
  option.active = this.open && at === this.activeAt;
377
+ option.selectionFollowsActive = true;
371
378
  }
372
379
  const control = this.control;
373
380
  if (control && 'ariaActiveDescendantElement' in control) {
@@ -23,6 +23,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
23
23
  * the shortcut with the control. A chord the page wrote there itself is left
24
24
  * alone, and the key takes back only what it added.
25
25
  *
26
+ * A chord is drawn with the platform's modifiers first, in its order, so one
27
+ * `keys` value is right everywhere: `mod+shift+p` is `⇧⌘P` on an Apple
28
+ * keyboard and `Ctrl+Shift+P` elsewhere. Where it is drawn in glyphs, it is
29
+ * read in words, such as `Shift Command P`.
30
+ *
26
31
  * `caps` draws a chord as a key per key: each is a `key` part and the `+`
27
32
  * between two a `separator` part, so a sheet can draw a cap for each.
28
33
  * `key-labels` renames any key, on every platform, such as
@@ -141,7 +146,8 @@ export declare class HelixHeadlessKbd extends HelixElement {
141
146
  /**
142
147
  * The chord under `caps`, a part for each key and for each `+` between two,
143
148
  * so a sheet can draw a cap per key. Written with no space between, so a
144
- * chord reads as the one label it was.
149
+ * chord reads as the one label it was. Where the chord is said in words,
150
+ * the parts are hidden from a screen reader, which reads the words.
145
151
  */
146
152
  protected renderChord(): TemplateResult;
147
153
  }
@@ -135,12 +135,16 @@ let HelixHeadlessKbd = class HelixHeadlessKbd extends HelixElement {
135
135
  if (this.keys === '') {
136
136
  return html `<kbd part="kbd"><slot></slot></kbd>`;
137
137
  }
138
- const { keys, separator } = this.label;
139
- return html `<kbd part="kbd">${this.caps ? this.renderChord() : keys.join(separator)}</kbd>`;
138
+ const { keys, separator, spoken } = this.label;
139
+ if (spoken === '') {
140
+ return html `<kbd part="kbd">${this.caps ? this.renderChord() : keys.join(separator)}</kbd>`;
141
+ }
142
+ return html `<kbd part="kbd">${this.caps ? this.renderChord() : html `<span aria-hidden="true">${keys.join(separator)}</span>`}<span data-spoken>${spoken}</span></kbd>`;
140
143
  }
141
144
  renderChord() {
142
- const { keys, separator } = this.label;
143
- return html `${keys.map((key, index) => html `${index > 0 && separator !== '' ? html `<span part="separator">${separator}</span>` : nothing}<span part="key">${key}</span>`)}`;
145
+ const { keys, separator, spoken } = this.label;
146
+ const hidden = spoken === '' ? nothing : 'true';
147
+ return html `${keys.map((key, index) => html `${index > 0 && separator !== '' ? html `<span part="separator" aria-hidden=${hidden}>${separator}</span>` : nothing}<span part="key" aria-hidden=${hidden}>${key}</span>`)}`;
144
148
  }
145
149
  };
146
150
  __decorate([
@@ -27,6 +27,9 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
27
27
  * `<svg>` that fills its box scales with the box, so the same `font-size` can
28
28
  * render at 10px in a narrow chart and 43px in a wide one.
29
29
  *
30
+ * `value-axis` draws the scale's numbers beside the plot, at the levels
31
+ * `gridlines` rules, written in `locale`.
32
+ *
30
33
  * The table is the accessible representation, and the drawing is hidden from
31
34
  * screen readers, who get the numbers. The `summary` part names the chart, from
32
35
  * `label` or the table's caption, and gives each series' latest reading.
@@ -37,13 +40,22 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
37
40
  * a reading. The tooltip follows, the `live` part says the reading in words,
38
41
  * and Escape puts the tooltip away.
39
42
  *
43
+ * **A tap shows the reading under it**, and keeps it until a tap lands
44
+ * elsewhere. `helix-reading-change` says each move of the reading, by pointer,
45
+ * tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
46
+ * pressed with Enter or Space, for a page that opens what a reading stands for.
47
+ *
40
48
  * @since 0.1.0
41
49
  * @status stable
42
50
  * @category charts
43
51
  * @aliases Time Series, Trend Chart, Multi-line Chart
44
52
  * @documentation https://fusion.dev/helix/components/line-chart
45
53
  *
54
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
55
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
56
+ *
46
57
  * @csspart area - The wash under one line, where `area` is set.
58
+ * @csspart axis - The numbers of the scale beside the plot, under `value-axis`.
47
59
  * @csspart band - The band around the reading under the pointer.
48
60
  * @csspart baseline - The rule at zero, where zero is inside the range.
49
61
  * @csspart gridline - One rule across the plot, under `gridlines`.
@@ -60,6 +72,7 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
60
72
  * @csspart summary - The chart's name and each series' latest reading, written as a sentence for screen readers. Hide it visually.
61
73
  * @csspart svg - The drawing surface the lines and areas sit on.
62
74
  * @csspart terminus - The mark on one series' last reading.
75
+ * @csspart tick - One number on the axis, placed with `inset-block-start` at its level.
63
76
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
64
77
  * @csspart tooltip-anchor - The `Tooltip` element itself.
65
78
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
@@ -79,6 +92,7 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
79
92
  *
80
93
  * @cssproperty --chart-terminus-x - Where a series' last reading sits horizontally, as a percentage. Written by the component.
81
94
  * @cssproperty --chart-terminus-y - Where a series' last reading sits vertically, as a percentage. Written by the component.
95
+ * @cssproperty --line-chart-axis - The width of the axis, in `ch`, set from the widest number on it. Written by the component on the axis.
82
96
  */
83
97
  export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base {
84
98
  /**
@@ -86,6 +100,13 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
86
100
  * `registerWords('de', { 'helix-line-chart': { positionLabel: '{position} von {count}' } })`.
87
101
  */
88
102
  static words: string;
103
+ /**
104
+ * Sends the reading's moves and presses as `helix-reading-change` and
105
+ * `helix-reading-press`, which the pointer hears. Here and not in a
106
+ * constructor, where the manifest's analyzer reads the assignment as a
107
+ * member of this class.
108
+ */
109
+ connectedCallback(): void;
89
110
  /**
90
111
  * Draws a wash under each line, down to the floor of the plot. The washes
91
112
  * overlap and do not add up, so for parts that make a total, use
@@ -202,6 +223,15 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
202
223
  * layers; a single `mask-image` would only ever cut the last one.
203
224
  */
204
225
  terminus?: 'none' | 'ring' | 'square' | 'triangle';
226
+ /**
227
+ * Draws the scale's numbers beside the plot, the `axis` part, at the levels
228
+ * `gridlines` rules, so a reading can be measured off the drawing as well
229
+ * as read from the tooltip.
230
+ *
231
+ * Absent draws none: a line read for its shape needs no numbers beside it,
232
+ * and the tooltip and the table hold every reading.
233
+ */
234
+ valueAxis: boolean;
205
235
  /** The domain every series is drawn against, which the tooltip also needs. */
206
236
  private range;
207
237
  /**
@@ -223,6 +253,12 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
223
253
  */
224
254
  private ends;
225
255
  protected render(): TemplateResult;
256
+ /**
257
+ * The scale's numbers beside the plot, under `value-axis`, written in the
258
+ * chart's locale. A seam, so a subclass can write the numbers its own way,
259
+ * such as a currency.
260
+ */
261
+ protected renderAxis(y: (value: number) => number, domain: [number, number]): TemplateResult;
226
262
  /**
227
263
  * What a reader who cannot see the lines is told: the chart's name and the
228
264
  * latest reading of each series.