@fusion.dev/helix-headless 0.1.0 → 0.3.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 (127) hide show
  1. package/custom-elements.json +234 -234
  2. package/dist/accordion/item/accordion-item.js +0 -108
  3. package/dist/accordion/root/accordion-root.js +0 -33
  4. package/dist/announcement/root/announcement-root.js +1 -76
  5. package/dist/app-header/root/app-header-root.js +0 -84
  6. package/dist/avatar/group/avatar-group.js +0 -77
  7. package/dist/avatar/root/avatar-root.js +0 -72
  8. package/dist/badge/root/badge-root.js +0 -67
  9. package/dist/bar-chart/root/bar-chart-root.js +3 -263
  10. package/dist/blockquote/root/blockquote-root.js +0 -46
  11. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -72
  12. package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -52
  13. package/dist/button/root/button-root.js +0 -215
  14. package/dist/button-group/root/button-group-root.js +0 -30
  15. package/dist/callout/root/callout-root.js +0 -52
  16. package/dist/card/root/card-root.js +0 -41
  17. package/dist/carousel/root/carousel-root.d.ts +2 -2
  18. package/dist/carousel/root/carousel-root.js +2 -362
  19. package/dist/chat/composer/chat-composer.js +0 -131
  20. package/dist/chat/message/chat-message.js +0 -137
  21. package/dist/chat/root/chat-root.js +0 -193
  22. package/dist/checkbox/root/checkbox-root.js +0 -113
  23. package/dist/checkbox-group/root/checkbox-group-root.js +1 -157
  24. package/dist/code/root/code-root.js +0 -26
  25. package/dist/code-block/root/code-block-root.js +0 -100
  26. package/dist/color-input/root/color-input-root.js +0 -217
  27. package/dist/color-picker/color.js +0 -69
  28. package/dist/color-picker/root/color-picker-root.js +0 -272
  29. package/dist/combobox/root/combobox-root.js +0 -428
  30. package/dist/command/root/command-root.js +0 -177
  31. package/dist/copy-button/root/copy-button-root.js +0 -134
  32. package/dist/data-table/root/data-table-root.js +0 -336
  33. package/dist/data-table/root/order.js +0 -100
  34. package/dist/date-input/root/date-input-root.js +1 -398
  35. package/dist/date-picker/root/date-picker-root.js +0 -358
  36. package/dist/dialog/root/dialog-root.js +0 -189
  37. package/dist/divider/root/divider-root.js +0 -71
  38. package/dist/donut-chart/root/donut-chart-root.js +0 -214
  39. package/dist/drawer/root/drawer-root.js +0 -116
  40. package/dist/drilldown/item/drilldown-item.js +0 -82
  41. package/dist/drilldown/root/drilldown-root.js +0 -91
  42. package/dist/element-events.generated.js +0 -5
  43. package/dist/empty/root/empty-root.js +0 -35
  44. package/dist/file-input/root/file-input-root.js +0 -232
  45. package/dist/format-date/root/format-date-root.js +0 -95
  46. package/dist/heatmap/root/buckets.js +0 -57
  47. package/dist/heatmap/root/heatmap-root.js +0 -313
  48. package/dist/icon/root/icon-root.js +0 -48
  49. package/dist/icon-button/root/icon-button-root.js +0 -30
  50. package/dist/image/root/image-root.js +0 -174
  51. package/dist/index.js +0 -9
  52. package/dist/input/root/input-root.js +0 -228
  53. package/dist/kbd/root/kbd-root.js +0 -117
  54. package/dist/legend/root/legend-root.js +0 -73
  55. package/dist/line-chart/root/line-chart-root.js +0 -172
  56. package/dist/list/item/list-item.js +0 -92
  57. package/dist/list/root/list-root.js +0 -26
  58. package/dist/listbox/root/listbox-root.js +2 -429
  59. package/dist/loader/root/loader-root.js +0 -40
  60. package/dist/megamenu/root/megamenu-root.js +0 -284
  61. package/dist/menu/item/menu-item.js +1 -221
  62. package/dist/menu/root/menu-root.js +1 -171
  63. package/dist/meter/root/meter-root.js +1 -152
  64. package/dist/navigation/root/navigation-root.js +0 -56
  65. package/dist/notifications/notification/notification.js +0 -159
  66. package/dist/notifications/root/notifications-root.js +0 -148
  67. package/dist/number-input/root/number-input-root.js +1 -263
  68. package/dist/option/group/option-group.js +0 -26
  69. package/dist/option/root/option-root.js +0 -157
  70. package/dist/outline/root/outline-root.js +0 -292
  71. package/dist/pagination/root/pagination-root.js +0 -361
  72. package/dist/pagination/size/pagination-size.js +0 -104
  73. package/dist/pagination/summary/pagination-summary.js +0 -90
  74. package/dist/pin-input/root/pin-input-root.js +0 -276
  75. package/dist/popover/root/popover-root.js +0 -103
  76. package/dist/progress/root/progress-root.js +1 -133
  77. package/dist/qr-code/root/qr-code-root.js +0 -96
  78. package/dist/quote/root/quote-root.js +0 -25
  79. package/dist/radio/group/radio-group.js +0 -81
  80. package/dist/radio/root/radio-root.js +0 -85
  81. package/dist/rating/root/rating-root.js +0 -220
  82. package/dist/scroll-area/root/scroll-area-root.js +0 -100
  83. package/dist/select/root/select-root.js +2 -218
  84. package/dist/skeleton/root/skeleton-root.js +0 -20
  85. package/dist/slider/root/slider-root.js +1 -267
  86. package/dist/sparkline/root/sparkline-root.js +3 -164
  87. package/dist/speed-dial/item/speed-dial-item.js +0 -52
  88. package/dist/speed-dial/root/speed-dial-root.js +1 -116
  89. package/dist/splitter/root/splitter-root.js +0 -489
  90. package/dist/stat/group/stat-group.js +0 -18
  91. package/dist/stat/root/stat-root.js +0 -23
  92. package/dist/steps/root/steps-root.js +0 -62
  93. package/dist/steps/step/step.js +0 -131
  94. package/dist/switch/root/switch-root.js +0 -80
  95. package/dist/table/cell/table-cell.js +0 -23
  96. package/dist/table/column/table-column.js +0 -158
  97. package/dist/table/root/table-root.js +0 -82
  98. package/dist/table/row/table-row.js +0 -21
  99. package/dist/tabs/panel/tab-panel.js +0 -49
  100. package/dist/tabs/root/tabs-root.js +0 -98
  101. package/dist/tabs/tab/tab.js +0 -59
  102. package/dist/tag/root/tag-root.js +0 -31
  103. package/dist/textarea/root/textarea-root.js +0 -162
  104. package/dist/time-input/root/time-input-root.js +0 -283
  105. package/dist/time-picker/root/time-picker-root.js +0 -326
  106. package/dist/timeline/item/timeline-item.js +0 -66
  107. package/dist/timeline/root/timeline-root.js +0 -41
  108. package/dist/toast/item/toast-item.js +0 -165
  109. package/dist/toast/root/toast-root.js +0 -51
  110. package/dist/toggle/group/toggle-group.js +0 -150
  111. package/dist/toggle/root/toggle-root.js +0 -78
  112. package/dist/toolbar/root/toolbar-root.js +0 -116
  113. package/dist/tooltip/root/tooltip-root.js +0 -427
  114. package/dist/tour/root/tour-root.js +0 -130
  115. package/dist/tour/stop/tour-stop.js +1 -127
  116. package/dist/tree/item/tree-item.js +0 -198
  117. package/dist/tree/root/tree-root.js +0 -97
  118. package/dist/trend/root/trend-root.js +0 -53
  119. package/dist/video/root/video-root.d.ts +8 -12
  120. package/dist/video/root/video-root.js +5 -756
  121. package/dist/video-playlist/item/video-playlist-item.js +1 -90
  122. package/dist/video-playlist/menu/video-playlist-menu.js +1 -132
  123. package/dist/video-playlist/root/video-playlist-root.js +0 -113
  124. package/dist/virtual-list/root/virtual-list-root.js +0 -105
  125. package/jsx.d.ts +1408 -2576
  126. package/package.json +6 -6
  127. package/vue.d.ts +1 -1
@@ -11,130 +11,20 @@ import { property } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  import { html } from 'lit/static-html.js';
13
13
  import '../../listbox/index.js';
14
- /**
15
- * @summary A closed list that opens to be chosen from.
16
- *
17
- * @description
18
- * **A trigger and a `Listbox`, not a listbox reimplemented behind a button.**
19
- * The list this opens is the same element a consumer can use on its own, with
20
- * the same keyboard, the same filtering and the same multi-select rules. What
21
- * Select adds is the closing: a trigger that says what is chosen, a surface
22
- * that floats, and somewhere for focus to go back to.
23
- *
24
- * The options are the consumer's, in the consumer's light DOM, slotted through
25
- * into the listbox. That is why the listbox walks through slots to find them:
26
- * from its point of view the options belong to something else.
27
- *
28
- * **What it does not do is duplicate the value.** `Select` owns it, hands it
29
- * down, and takes changes back; the listbox is the thing being operated and not
30
- * a second place the answer lives.
31
- *
32
- * @since 0.1.0
33
- * @status experimental
34
- * @category forms
35
- * @aliases Dropdown, Picker
36
- * @documentation https://helix.fusion.dev/components/select
37
- *
38
- * @slot - The options, and any groups of them.
39
- * @slot end - After it, for a count, a unit, or a clear button.
40
- * @slot hint - Richer than a string.
41
- * @slot indicator - The mark on the trigger saying this opens, usually a caret.
42
- * @slot label - Richer than a string.
43
- * @slot start - Before the chosen value, for an icon or a prefix.
44
- *
45
- * @event {Event} change - Announced once the value has moved.
46
- * @event {HelixBeforeChangeEvent} helix-before-change - Relayed from the listbox when the answer changes. Cancelable: refusing it takes the decision and nothing moves.
47
- * @event {HelixBeforeCloseEvent} helix-before-close - Fired before it closes. Cancelable: refusing it takes the decision and nothing moves.
48
- * @event {HelixBeforeOpenEvent} helix-before-open - Fired before the list opens. Cancelable: refusing it takes the decision and nothing moves.
49
- * @event {Event} input - Announced once the value has moved.
50
- * @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
51
- *
52
- * @csspart feedback - What is wrong with it.
53
- * @csspart hint - Help under the trigger.
54
- * @csspart indicator - The caret saying the control opens. Replaced by slotting one.
55
- * @csspart label - The question, over the trigger.
56
- * @csspart listbox - The list inside it.
57
- * @csspart listbox-empty - Shown when a filter has hidden everything.
58
- * @csspart listbox-feedback - What is wrong with it.
59
- * @csspart listbox-filter-icon - The mark beside that field.
60
- * @csspart listbox-filter-mark - The glyph beside the filter field, where none is slotted.
61
- * @csspart listbox-hint - Help under the list.
62
- * @csspart listbox-items - The list itself.
63
- * @csspart listbox-label - The question, over the list.
64
- * @csspart listbox-loading - Shown while the options are still on their way.
65
- * @csspart listbox-search - The region holding the field that narrows the list.
66
- * @csspart listbox-search-field - The field itself.
67
- * @csspart surface - The floating panel.
68
- * @csspart trigger - The closed control, which says what is chosen.
69
- * @csspart value - The chosen option's text inside the trigger.
70
- */
71
14
  let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(MixinControllerValue(MixinField(HelixFormElement))) {
72
15
  constructor() {
73
16
  super(...arguments);
74
- /* A consumer's `aria-describedby` names ids in their own tree and the control
75
- is in this shadow root, so the reference has to be carried down as an
76
- element rather than copied as an id. See `AriaForwardController`: measured
77
- in Chrome's own tree, the host held the description and the control the
78
- reader is actually on was exposed without it. */
79
17
  this.aria = new AriaForwardController(this, {
80
18
  target: () => this.renderRoot?.querySelector('[part="trigger"]') ?? null,
81
19
  });
82
- /**
83
- * What it says where there are none.
84
- */
85
20
  this.emptyText = 'No matches';
86
- /**
87
- * Whether the list can be narrowed by typing.
88
- *
89
- * Passed to the listbox rather than acted on here, because the listbox owns
90
- * the filtering, the empty state and the keyboard.
91
- */
92
21
  this.filterable = false;
93
- /**
94
- * Whether the options are still on their way.
95
- *
96
- * Passed straight to the list, which is where the rows are and so where the
97
- * message belongs. Two components saying "Loading" about one request is a
98
- * consumer wiring the same flag twice.
99
- */
100
22
  this.loading = false;
101
- /**
102
- * What the list says while they are on their way.
103
- */
104
23
  this.loadingText = 'Loading';
105
- /**
106
- * Whether more than one option can be the answer.
107
- */
108
24
  this.multiple = false;
109
- /**
110
- * Shown on the trigger while nothing is chosen.
111
- */
112
25
  this.placeholder = 'Choose';
113
- /* The surface floats *and is in the top layer*.
114
- *
115
- * Without `showPopover` the panel is an ordinary positioned element and the
116
- * first ancestor with `overflow: hidden` clips it, so a select inside a
117
- * scrolling card is cut off where a menu in the same place is not. The two
118
- * are the same shape (floating panel, trigger, dismissal), which is what
119
- * `PopoverController` is, and having one of them is what stops the third copy
120
- * being subtly different.
121
- *
122
- * Held off the trigger by the controller rather than by a margin on the
123
- * panel. A margin is inside the box the controller measures, so it would be
124
- * counted once in the placement and again in the layout, and the gap would
125
- * change as the panel flipped above the trigger for want of room. */
126
26
  this.panel = new PopoverController(this, {
127
27
  anchor: () => this.renderRoot?.querySelector('[part="trigger"]'),
128
- /* The surface *and* the trigger.
129
- *
130
- * A control is not always one element: the button that opens this panel is
131
- * part of it, and a press on it is not a press outside. Scoped to the
132
- * surface alone, `pointerdown` on the trigger closes the panel and the
133
- * trigger's own `toggleOpen` reopens it on the `click` that follows, so
134
- * the panel can never be closed by the button that opened it.
135
- *
136
- * `DismissController` documents this exact failure and takes a list for
137
- * it. */
138
28
  boundary: () => [
139
29
  this.renderRoot?.querySelector('[part="surface"]'),
140
30
  this.renderRoot?.querySelector('[part="trigger"]'),
@@ -145,44 +35,14 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
145
35
  surface: () => this.renderRoot?.querySelector('[part="surface"]'),
146
36
  });
147
37
  this.handleChange = (event) => {
148
- /* From the event, not from the listbox.
149
- *
150
- * The event carries the value being *proposed*, and it is dispatched before
151
- * it is applied, which is what makes it refusable. Reading the listbox here
152
- * reads the value it still holds: one choice behind, and on the first
153
- * choice nothing at all.
154
- *
155
- * And the listbox never applies it anyway. The select passes `.value` down,
156
- * which is a consumer declaring ownership, so the listbox inside the panel
157
- * is controlled by the select and correctly waits to be told. That is the
158
- * right arrangement, and it makes reading the listbox's own value the one
159
- * thing that can never work. */
160
38
  const next = event.detail.value;
161
- /* Nothing happens on a refusal.
162
- *
163
- * Without this return the panel closes and a native `change` goes out
164
- * whether or not the change was taken, so a consumer calling
165
- * `preventDefault()` on `helix-change` keeps the old value, watches the
166
- * select shut as though it had been chosen, and tells the form it moved. */
167
39
  if (!this.requestValue(next)) {
168
40
  event.stopPropagation();
169
41
  return;
170
42
  }
171
- /* Closed on a choice, unless more than one can be made: a multi-select that
172
- shut after each pick would be a control the reader has to reopen to use
173
- again. */
174
43
  if (!this.multiple) {
175
44
  this.close();
176
45
  }
177
- /* The listbox's request is stopped and this element's own has already gone
178
- out, from `requestValue` above: two elements announcing one choice is
179
- what a consumer would have to deduplicate.
180
-
181
- And no `change` is dispatched here. `requestValue` announces `input` and
182
- `change` through the mixin, the way every other discrete control does, so
183
- a second one from this line is a second `change` per choice: measured on
184
- the styled tier at three, one from the mixin, one from here, and one from
185
- the listbox before it was stopped. */
186
46
  event.stopPropagation();
187
47
  };
188
48
  this.handleTriggerKeydown = (event) => {
@@ -192,14 +52,6 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
192
52
  }
193
53
  };
194
54
  }
195
- /**
196
- * Which option is chosen, as an index, or `-1` where none is.
197
- *
198
- * Read off the listbox this renders, which owns the options and is where the
199
- * same two properties are implemented. Delegating rather than recomputing is
200
- * what keeps a composed control and the control inside it from answering
201
- * differently: they are one chooser to a consumer.
202
- */
203
55
  get selectedIndex() {
204
56
  return this.listbox?.selectedIndex ?? -1;
205
57
  }
@@ -209,32 +61,12 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
209
61
  listbox.selectedIndex = at;
210
62
  }
211
63
  }
212
- /** Which kind of chooser this is, in the platform's two words. */
213
64
  get type() {
214
65
  return this.multiple ? 'select-multiple' : 'select-one';
215
66
  }
216
- /**
217
- * The list inside the surface, or nothing where there is no DOM to hold one.
218
- *
219
- * The optional chain is the server. `renderRoot` is a property Lit's element
220
- * shim does not carry, and this getter is reached from `display`, which is
221
- * read during `render`: without it the select throws on every server render
222
- * and cannot be served at all.
223
- *
224
- * The other reads of `renderRoot` here are inside callbacks the controllers
225
- * invoke, and a controller only runs on a connected element, so they cannot
226
- * be reached from the server and are left plain.
227
- */
228
67
  get listbox() {
229
68
  return this.renderRoot?.querySelector('[part="listbox"]') ?? null;
230
69
  }
231
- /**
232
- * What the trigger says: the chosen options' words, or the placeholder.
233
- *
234
- * Read off the options rather than stored, because the words are the
235
- * consumer's content and storing a copy is storing something that goes stale
236
- * the moment they change it.
237
- */
238
70
  get display() {
239
71
  const chosen = new Set(this.value === '' ? [] : this.value.split(','));
240
72
  const words = (this.listbox?.options ?? [])
@@ -242,17 +74,13 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
242
74
  .map((option) => option.text);
243
75
  return words.length > 0 ? words.join(', ') : this.placeholder;
244
76
  }
245
- /** Closes the list and puts focus back where it came from. */
246
77
  close() {
247
78
  if (!this.open) {
248
79
  return;
249
80
  }
250
81
  this.requestOpen(false);
251
- // Back to the trigger, because focus is inside a panel that is going away
252
- // and a reader who loses it lands at the top of the document.
253
82
  this.renderRoot.querySelector('[part="trigger"]')?.focus();
254
83
  }
255
- /** Reports the answer and the verdict on it. */
256
84
  sync() {
257
85
  this.internals.setFormValue(this.value === '' ? null : this.value);
258
86
  this.reportRequired(this.value === '', 'Choose one to continue.', 'trigger');
@@ -266,42 +94,16 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
266
94
  this.sync();
267
95
  this.linkHost();
268
96
  if (this.open) {
269
- /* Driven from `updated` rather than from a `show()` method, because the
270
- open state is the property and a consumer may set it directly. The
271
- controller is idempotent, so arriving here twice for the same open
272
- costs nothing. */
273
97
  this.panel.show();
274
- /* Into the list, which is where the keyboard has to be for the arrows to
275
- mean anything. The listbox delegates focus, so this lands on the list
276
- inside it rather than stopping at the listbox element, which is not
277
- itself focusable.
278
- *
279
- * Only on the update that opened it. `updated` runs for any change while
280
- * the panel is up, a filter keystroke or a value arriving, and moving
281
- * focus on each of those is a focus thief. */
282
98
  if (changed.has('open')) {
283
99
  this.listbox?.focus();
284
100
  }
285
101
  }
286
102
  else {
287
103
  this.panel.hide();
288
- /* And the filter goes with it. Without this a filterable select reopens
289
- still narrowed by the old needle, with `hidden` still written on the
290
- options it ruled out, so the second use of the control starts from the
291
- first one's leftovers. Closing is where a search ends. */
292
104
  this.listbox?.requestFilter('');
293
105
  }
294
106
  }
295
- /**
296
- * The list the trigger opens, behind a seam the styled tier can move.
297
- *
298
- * A method rather than a fragment inline, so a tier above can render its own
299
- * list, wrap this one, or pass it a property the base does not know about,
300
- * without reimplementing the whole template. The tag is interpolated rather
301
- * than written, so `tier` resolves it: this renders the base listbox, and a
302
- * styled subclass that sets `tier` renders the styled one from the same
303
- * template.
304
- */
305
107
  renderListbox() {
306
108
  return html `<${this.tag('listbox')}
307
109
  empty-text=${this.emptyText}
@@ -328,15 +130,6 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
328
130
  <slot @slotchange=${() => this.requestUpdate()}></slot>
329
131
  </${this.tag('listbox')}>`;
330
132
  }
331
- /**
332
- * The caret saying the control opens, behind a seam.
333
- *
334
- * Slot fallback, so a consumer replaces it by slotting; and a method, so the
335
- * tier above replaces it by overriding. Those answer different people: the
336
- * slot is for the page and the method is for whoever is building the styled
337
- * layer, and a fallback with only the first is one a tier can restyle and
338
- * cannot change.
339
- */
340
133
  renderIndicator() {
341
134
  return html `<slot name="indicator">
342
135
  <svg part="indicator" fill="currentColor" viewBox=${caretDown.viewBox} aria-hidden="true">
@@ -359,18 +152,9 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
359
152
  >
360
153
  <slot name="start"></slot>
361
154
  <span part="value">${this.display}</span>
362
- ${
363
- /* Before the indicator, not after it. The caret is the affordance
364
- saying the control opens, so it stays the last thing in the trigger
365
- whatever else is put in front of it. */
366
- ''}
155
+ ${''}
367
156
  <slot name="end"></slot>
368
- ${
369
- /* The slot has a fallback: without one a select written without a
370
- hand-slotted icon draws no mark at all saying it opens. A caret is
371
- the mark, and slotting anything replaces it, which is this
372
- library's rule for a content property. */
373
- ''}
157
+ ${''}
374
158
  ${this.renderIndicator()}
375
159
  </button>
376
160
 
@@ -6,26 +6,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
- /**
10
- * @summary Stands in for content that has not arrived.
11
- *
12
- * @description
13
- * Carries `aria-busy` and a polite live region, so a reader using a screen
14
- * reader is told the region is filling rather than told nothing and left to
15
- * wonder. Both go through `ElementInternals`, which writes no attribute.
16
- *
17
- * Holds no `shape`. Whether a placeholder is a line, a block or a circle is
18
- * presentation, and putting it here would give the headless tier an appearance
19
- * property, which is the one thing that tier is for not having.
20
- *
21
- * @since 0.1.0
22
- * @status stable
23
- * @category feedback
24
- * @aliases Placeholder, Shimmer, Ghost
25
- * @documentation https://helix.fusion.dev/components/skeleton
26
- *
27
- * @csspart skeleton - The placeholder surface.
28
- */
29
9
  let HelixHeadlessSkeleton = class HelixHeadlessSkeleton extends HelixElement {
30
10
  willUpdate(changed) {
31
11
  super.willUpdate(changed);