@fusion.dev/helix-headless 0.154.0 → 0.156.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 (104) hide show
  1. package/custom-elements.json +3213 -179
  2. package/dist/accordion/item/accordion-item.d.ts +15 -0
  3. package/dist/accordion/item/accordion-item.js +12 -1
  4. package/dist/accordion/root/accordion-root.d.ts +2 -1
  5. package/dist/accordion/root/accordion-root.js +1 -0
  6. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
  7. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
  8. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
  9. package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
  10. package/dist/button-group/root/button-group-root.d.ts +2 -0
  11. package/dist/button-group/root/button-group-root.js +1 -0
  12. package/dist/canvas/node/canvas-node.js +1 -1
  13. package/dist/canvas/root/canvas-root.d.ts +17 -2
  14. package/dist/canvas/root/canvas-root.js +23 -6
  15. package/dist/chat/composer/chat-composer.js +1 -1
  16. package/dist/chat/root/chat-root.d.ts +14 -1
  17. package/dist/chat/root/chat-root.js +14 -4
  18. package/dist/color-input/root/color-input-root.d.ts +9 -3
  19. package/dist/color-input/root/color-input-root.js +3 -1
  20. package/dist/color-picker/channels.d.ts +5 -3
  21. package/dist/color-picker/channels.js +79 -21
  22. package/dist/color-picker/root/color-picker-root.d.ts +58 -5
  23. package/dist/color-picker/root/color-picker-root.js +75 -13
  24. package/dist/combobox/root/combobox-root.d.ts +10 -0
  25. package/dist/combobox/root/combobox-root.js +18 -3
  26. package/dist/command/root/command-root.d.ts +116 -1
  27. package/dist/command/root/command-root.js +124 -15
  28. package/dist/data-table/root/data-table-root.d.ts +25 -0
  29. package/dist/data-table/root/data-table-root.js +13 -3
  30. package/dist/date-input/root/date-input-root.d.ts +33 -1
  31. package/dist/date-input/root/date-input-root.js +9 -2
  32. package/dist/date-picker/root/date-picker-root.d.ts +43 -13
  33. package/dist/date-picker/root/date-picker-root.js +54 -31
  34. package/dist/element-events.generated.js +3 -1
  35. package/dist/graph/root/graph-root.d.ts +13 -0
  36. package/dist/graph/root/graph-root.js +16 -0
  37. package/dist/list/root/list-root.d.ts +74 -1
  38. package/dist/list/root/list-root.js +92 -4
  39. package/dist/list/selection.js +3 -3
  40. package/dist/map/root/map-root.d.ts +6 -0
  41. package/dist/map/root/map-root.js +8 -1
  42. package/dist/navigation/root/navigation-root.d.ts +26 -0
  43. package/dist/navigation/root/navigation-root.js +58 -1
  44. package/dist/number-input/root/number-input-root.d.ts +63 -6
  45. package/dist/number-input/root/number-input-root.js +96 -11
  46. package/dist/object-inspector/root/object-inspector-root.d.ts +36 -1
  47. package/dist/object-inspector/root/object-inspector-root.js +26 -2
  48. package/dist/option/root/option-root.d.ts +32 -0
  49. package/dist/option/root/option-root.js +44 -5
  50. package/dist/outline/root/outline-root.d.ts +6 -2
  51. package/dist/outline/root/outline-root.js +7 -3
  52. package/dist/select/root/select-root.d.ts +29 -3
  53. package/dist/select/root/select-root.js +34 -9
  54. package/dist/slider/root/slider-root.d.ts +71 -9
  55. package/dist/slider/root/slider-root.js +129 -42
  56. package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
  57. package/dist/speed-dial/item/speed-dial-item.js +48 -7
  58. package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
  59. package/dist/speed-dial/root/speed-dial-root.js +50 -5
  60. package/dist/splitter/root/splitter-root.d.ts +22 -0
  61. package/dist/splitter/root/splitter-root.js +36 -7
  62. package/dist/steps/reach.d.ts +5 -1
  63. package/dist/steps/reach.js +2 -2
  64. package/dist/steps/root/steps-root.d.ts +37 -2
  65. package/dist/steps/root/steps-root.js +49 -7
  66. package/dist/steps/step/step.d.ts +6 -0
  67. package/dist/steps/step/step.js +4 -0
  68. package/dist/table/root/table-root.d.ts +20 -0
  69. package/dist/table/root/table-root.js +13 -3
  70. package/dist/tabs/root/tabs-root.d.ts +37 -0
  71. package/dist/tabs/root/tabs-root.js +92 -2
  72. package/dist/tabs/tab/tab.d.ts +38 -2
  73. package/dist/tabs/tab/tab.js +45 -4
  74. package/dist/tag/group/tag-group.d.ts +29 -3
  75. package/dist/tag/group/tag-group.js +42 -11
  76. package/dist/time-input/root/time-input-root.d.ts +32 -1
  77. package/dist/time-input/root/time-input-root.js +9 -2
  78. package/dist/time-picker/root/time-picker-root.d.ts +63 -1
  79. package/dist/time-picker/root/time-picker-root.js +58 -7
  80. package/dist/timeline/item/timeline-item.d.ts +25 -0
  81. package/dist/timeline/item/timeline-item.js +27 -2
  82. package/dist/toggle/root/toggle-root.d.ts +2 -0
  83. package/dist/toggle/root/toggle-root.js +1 -1
  84. package/dist/tree/item/tree-item.d.ts +10 -0
  85. package/dist/tree/item/tree-item.js +12 -0
  86. package/dist/tree/root/tree-root.d.ts +40 -1
  87. package/dist/tree/root/tree-root.js +120 -4
  88. package/dist/tree/selection.d.ts +6 -0
  89. package/dist/tree/selection.js +11 -0
  90. package/dist/video/root/keys.d.ts +6 -2
  91. package/dist/video/root/keys.js +18 -1
  92. package/dist/video/root/video-root.d.ts +60 -1
  93. package/dist/video/root/video-root.js +70 -2
  94. package/dist/video-playlist/item/video-playlist-item.d.ts +15 -1
  95. package/dist/video-playlist/item/video-playlist-item.js +7 -0
  96. package/dist/video-playlist/root/video-playlist-root.d.ts +43 -4
  97. package/dist/video-playlist/root/video-playlist-root.js +69 -2
  98. package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
  99. package/dist/virtual-list/root/virtual-list-root.js +3 -0
  100. package/lint.json +1 -1
  101. package/package.json +5 -5
  102. package/types/jsx.d.ts +3885 -383
  103. package/types/vue.d.ts +1045 -107
  104. package/types/words.d.ts +146 -1
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type TemplateResult } from 'lit';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary One entry in a video playlist.
5
5
  *
@@ -34,6 +34,8 @@ import { type TemplateResult } from 'lit';
34
34
  * @csspart label - The name and the content after it.
35
35
  * @csspart media - The column at the start of the row.
36
36
  * @csspart thumbnail - The image of `poster`, shown when the `media` slot is empty.
37
+ *
38
+ * @cssstate current - The entry that is playing.
37
39
  */
38
40
  export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
39
41
  /**
@@ -78,6 +80,11 @@ export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
78
80
  * where there is no slot to ask. `Option.text` reads its own the same way.
79
81
  */
80
82
  get text(): string;
83
+ /**
84
+ * Focuses the entry's button, which is where the playlist's arrow keys send
85
+ * focus. The host itself takes no focus.
86
+ */
87
+ focus(options?: FocusOptions): void;
81
88
  private readonly choose;
82
89
  /**
83
90
  * What stands for the row: a thumbnail where there is one, and nothing where
@@ -88,6 +95,13 @@ export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
88
95
  * slots `media` for the same reasons and neither has to know about the other.
89
96
  */
90
97
  protected renderMedia(): TemplateResult;
98
+ /**
99
+ * Mirrors the playing entry where a stylesheet can select on the host,
100
+ * `helix-video-playlist-item:state(current)`. A custom state, because the
101
+ * playlist writes `current`, and an attribute it wrote would be markup a
102
+ * server never sent.
103
+ */
104
+ protected willUpdate(changed: PropertyValues): void;
91
105
  protected render(): TemplateResult;
92
106
  }
93
107
  declare global {
@@ -29,11 +29,18 @@ let HelixHeadlessVideoPlaylistItem = class HelixHeadlessVideoPlaylistItem extend
29
29
  .join('')
30
30
  .trim();
31
31
  }
32
+ focus(options) {
33
+ this.renderRoot?.querySelector('button')?.focus(options);
34
+ }
32
35
  renderMedia() {
33
36
  return html `<slot name="media">
34
37
  ${this.poster === '' ? nothing : html `<img part="thumbnail" alt="" src=${this.poster} />`}
35
38
  </slot>`;
36
39
  }
40
+ willUpdate(changed) {
41
+ super.willUpdate(changed);
42
+ this.toggleState('current', this.current);
43
+ }
37
44
  render() {
38
45
  return html `<div part="container" role="listitem">
39
46
  <button
@@ -35,10 +35,17 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
35
35
  * your name, and only the title follows the entry.
36
36
  *
37
37
  * **Keyboard.** Each entry is a button in the tab order, and Enter or Space
38
- * plays it. The current entry carries `aria-current`. While focus is on the
39
- * player, the player's own keys work, and `Video` lists them. Its previous
40
- * and next controls move through the entries in the same order the playlist
41
- * does.
38
+ * plays it. The arrow keys move focus to the entry before or after, and Home
39
+ * and End to the first and the last, passing over disabled entries. The
40
+ * current entry carries `aria-current`. While focus is on the player, the
41
+ * player's own keys work, and `Video` lists them, and Shift with N or P plays
42
+ * the next or the previous entry. The player's previous and next controls
43
+ * move through the entries in the same order the playlist does.
44
+ *
45
+ * **What a screen reader hears.** A list named by `label`, with a button for
46
+ * each entry. When the playlist moves to another entry, by a press, by the
47
+ * player's controls, or at the end of a clip, a live region says
48
+ * `playing-label`, "Now playing" and the entry's name.
42
49
  *
43
50
  * @since 0.1.0
44
51
  * @status stable
@@ -54,7 +61,10 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
54
61
  * @event {HelixEndEvent} helix-end - The last entry finished playing, and the playlist stopped there. Not sent while `repeat` is set.
55
62
  *
56
63
  * @csspart items - The list of entries.
64
+ * @csspart live - A live region that says which entry plays after the playlist moves.
57
65
  * @csspart player - The region that holds the player.
66
+ *
67
+ * @cssstate ended - The last entry finished and the playlist stopped there, until an entry plays again.
58
68
  */
59
69
  export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlaylist_base {
60
70
  /**
@@ -66,6 +76,17 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
66
76
  * The list's name, read by screen readers.
67
77
  */
68
78
  label: string;
79
+ /**
80
+ * What the live region says when the playlist moves to another entry.
81
+ * `{title}` is the entry's name.
82
+ *
83
+ * **A move a reader did not make is said.** At the end of a clip the
84
+ * playlist starts the next one, and the only other trace of that is the
85
+ * `aria-current` on a button the reader is not on.
86
+ */
87
+ playingLabel: string;
88
+ /** The live region, rendered from the first move. */
89
+ private readonly status;
69
90
  /**
70
91
  * What happens when an entry ends with no next one to go to. `one` plays the
71
92
  * same entry again, and `all` goes from the last entry back to the first.
@@ -168,6 +189,24 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
168
189
  */
169
190
  previous(): void;
170
191
  private readonly handleSelect;
192
+ /**
193
+ * The arrow keys, Home and End on an entry move focus to another one.
194
+ *
195
+ * **The entries the keys reach are the enabled ones**, because a disabled
196
+ * button cannot take focus and a key that landed on one would leave focus
197
+ * where it was. Up and Left go back, Down and Right go on, and Left and
198
+ * Right swap on a right-to-left page, so a list laid out in a row moves the
199
+ * way it reads.
200
+ */
201
+ private readonly handleKeydown;
202
+ /** The player playing again, after the end or anywhere, means the list has not ended. */
203
+ private readonly handlePlay;
204
+ /**
205
+ * Shift with N plays the next entry and Shift with P the previous one, while
206
+ * focus is on the player, the keys a reader knows from the common players.
207
+ * Not inside a field the player holds, where they are letters.
208
+ */
209
+ private takePlayerKey;
171
210
  private readonly handleNext;
172
211
  private readonly handlePrevious;
173
212
  /**
@@ -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, HelixEndEvent, MixinControllerActive, words, } from '@fusion.dev/helix-core';
7
+ import { customElement, drawStatus, formatWords, HelixElement, HelixEndEvent, inlineStep, MixinControllerActive, StatusController, words, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessVideoPlaylistItem } from '../item/video-playlist-item.js';
@@ -13,6 +13,8 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
13
13
  constructor() {
14
14
  super(...arguments);
15
15
  this.label = 'Playlist';
16
+ this.playingLabel = 'Now playing {title}';
17
+ this.status = new StatusController(this);
16
18
  this.shuffle = false;
17
19
  this.chased = false;
18
20
  this.handleSelect = (event) => {
@@ -23,6 +25,39 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
23
25
  this.chased = true;
24
26
  this.requestActive(event.detail.value);
25
27
  };
28
+ this.handleKeydown = (event) => {
29
+ const from = event.target;
30
+ if (this.takePlayerKey(event)) {
31
+ return;
32
+ }
33
+ if (!(from instanceof HelixHeadlessVideoPlaylistItem) ||
34
+ event.altKey ||
35
+ event.ctrlKey ||
36
+ event.metaKey ||
37
+ event.shiftKey) {
38
+ return;
39
+ }
40
+ const reachable = this.items.filter((one) => !one.disabled);
41
+ const at = reachable.indexOf(from);
42
+ const step = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : inlineStep(this, event.key);
43
+ const to = event.key === 'Home'
44
+ ? 0
45
+ : event.key === 'End'
46
+ ? reachable.length - 1
47
+ : step !== 0 && at !== -1
48
+ ? Math.min(Math.max(at + step, 0), reachable.length - 1)
49
+ : undefined;
50
+ if (to === undefined) {
51
+ return;
52
+ }
53
+ event.preventDefault();
54
+ reachable[to]?.focus();
55
+ };
56
+ this.handlePlay = (event) => {
57
+ if (event.target === this.player) {
58
+ this.toggleState('ended', false);
59
+ }
60
+ };
26
61
  this.handleNext = () => {
27
62
  this.next();
28
63
  };
@@ -38,6 +73,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
38
73
  return;
39
74
  }
40
75
  if (this.following(1).next === undefined) {
76
+ this.toggleState('ended', true);
41
77
  this.dispatchEvent(new HelixEndEvent());
42
78
  return;
43
79
  }
@@ -91,19 +127,43 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
91
127
  previous() {
92
128
  this.step(-1);
93
129
  }
130
+ takePlayerKey(event) {
131
+ const player = this.player;
132
+ if (!player ||
133
+ event.target !== player ||
134
+ event.defaultPrevented ||
135
+ !event.shiftKey ||
136
+ event.altKey ||
137
+ event.ctrlKey ||
138
+ event.metaKey ||
139
+ (event.key !== 'N' && event.key !== 'P')) {
140
+ return false;
141
+ }
142
+ const origin = event.composedPath()[0];
143
+ if (origin?.tagName === 'INPUT' || origin?.isContentEditable) {
144
+ return false;
145
+ }
146
+ event.preventDefault();
147
+ this.step(event.key === 'N' ? 1 : -1);
148
+ return true;
149
+ }
94
150
  connectedCallback() {
95
151
  super.connectedCallback();
96
152
  this.addEventListener('helix-select', this.handleSelect);
153
+ this.addEventListener('keydown', this.handleKeydown);
97
154
  this.addEventListener('helix-next', this.handleNext);
98
155
  this.addEventListener('helix-previous', this.handlePrevious);
99
156
  this.addEventListener('ended', this.handleEnded, true);
157
+ this.addEventListener('play', this.handlePlay, true);
100
158
  }
101
159
  disconnectedCallback() {
102
160
  super.disconnectedCallback();
103
161
  this.removeEventListener('helix-select', this.handleSelect);
162
+ this.removeEventListener('keydown', this.handleKeydown);
104
163
  this.removeEventListener('helix-next', this.handleNext);
105
164
  this.removeEventListener('helix-previous', this.handlePrevious);
106
165
  this.removeEventListener('ended', this.handleEnded, true);
166
+ this.removeEventListener('play', this.handlePlay, true);
107
167
  }
108
168
  willUpdate(changed) {
109
169
  super.willUpdate(changed);
@@ -123,6 +183,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
123
183
  return;
124
184
  }
125
185
  if (player.src !== current.src) {
186
+ this.toggleState('ended', false);
126
187
  player.src = current.src;
127
188
  player.poster = current.poster;
128
189
  player.mediaTitle = current.text;
@@ -130,6 +191,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
130
191
  player.label = current.text;
131
192
  }
132
193
  if (this.chased) {
194
+ this.status.say(formatWords(this, 'playingLabel', { title: current.text }));
133
195
  void player.updateComplete?.then(() => player.play()).catch(() => { });
134
196
  }
135
197
  }
@@ -139,13 +201,18 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
139
201
  return html `<div part="player"><slot name="player"></slot></div>
140
202
  <div part="items" role="list" aria-label=${this.label}>
141
203
  <slot></slot>
142
- </div>`;
204
+ </div>
205
+ ${drawStatus(this.status)}`;
143
206
  }
144
207
  };
145
208
  __decorate([
146
209
  words,
147
210
  property()
148
211
  ], HelixHeadlessVideoPlaylist.prototype, "label", void 0);
212
+ __decorate([
213
+ words,
214
+ property({ attribute: 'playing-label' })
215
+ ], HelixHeadlessVideoPlaylist.prototype, "playingLabel", void 0);
149
216
  __decorate([
150
217
  property()
151
218
  ], HelixHeadlessVideoPlaylist.prototype, "repeat", void 0);
@@ -97,6 +97,11 @@ export interface VirtualListRange {
97
97
  * shows nothing, since a list whose items have not arrived yet has not said
98
98
  * it has none.
99
99
  *
100
+ * The list takes the `empty` state while it has read its items and holds none,
101
+ * and the `loading` state while `loading` is set, so your stylesheet can
102
+ * change the box around it for either. A list with no name is reported once
103
+ * in a development build. Name it with `label` or `aria-label`.
104
+ *
100
105
  * **Row height.** Rows are placed by `estimate` until they are drawn. Then the
101
106
  * list reads the drawn rows' height once and places every row by it, so a
102
107
  * wrong estimate still reaches the last item. Set `measured` for rows of
@@ -131,6 +136,9 @@ export interface VirtualListRange {
131
136
  * @csspart empty - The row shown when there are no items and none are loading, where `empty-label` or the `empty` slot gives it content.
132
137
  * @csspart loading - The row after the last item while `loading` is set.
133
138
  * @csspart viewport - The scrolling box. On this tier, it needs only a block size.
139
+ *
140
+ * @cssstate empty - While the list has read its items and holds none.
141
+ * @cssstate loading - While `loading` is set.
134
142
  */
135
143
  export declare class HelixHeadlessVirtualList extends HelixElement {
136
144
  /**
@@ -33,6 +33,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
33
33
  this.aria = new AriaForwardController(this, {
34
34
  label: true,
35
35
  target: () => this.partNamed('viewport'),
36
+ unnamed: 'Give it a `label`, or an `aria-label` that says what the items are.',
36
37
  });
37
38
  this.virtualize = new VirtualizeController(this, {
38
39
  container: () => this.partNamed('viewport'),
@@ -210,6 +211,8 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
210
211
  }
211
212
  updated(changed) {
212
213
  super.updated?.(changed);
214
+ this.toggleState('empty', this.readFor !== undefined && this.data.length === 0);
215
+ this.toggleState('loading', this.loading);
213
216
  this.virtualize.measure();
214
217
  this.place();
215
218
  this.virtualize.settle();