@fusion.dev/helix-headless 0.103.3 → 0.104.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 (107) hide show
  1. package/custom-elements.json +9253 -880
  2. package/dist/announcement/root/announcement-root.d.ts +9 -2
  3. package/dist/announcement/root/announcement-root.js +9 -6
  4. package/dist/badge/root/badge-root.js +2 -3
  5. package/dist/canvas/edge/canvas-edge.d.ts +112 -0
  6. package/dist/canvas/edge/canvas-edge.js +66 -0
  7. package/dist/canvas/index.d.ts +4 -0
  8. package/dist/canvas/index.js +3 -0
  9. package/dist/canvas/node/canvas-node.d.ts +211 -0
  10. package/dist/canvas/node/canvas-node.js +148 -0
  11. package/dist/canvas/root/canvas-document.d.ts +215 -0
  12. package/dist/canvas/root/canvas-document.js +284 -0
  13. package/dist/canvas/root/canvas-root.d.ts +912 -0
  14. package/dist/canvas/root/canvas-root.js +1814 -0
  15. package/dist/carousel/root/carousel-root.d.ts +53 -16
  16. package/dist/carousel/root/carousel-root.js +46 -11
  17. package/dist/chat/root/chat-root.d.ts +4 -0
  18. package/dist/chat/root/chat-root.js +11 -7
  19. package/dist/checkbox/root/checkbox-root.js +1 -0
  20. package/dist/checkbox-group/root/checkbox-group-root.d.ts +10 -0
  21. package/dist/checkbox-group/root/checkbox-group-root.js +5 -0
  22. package/dist/color-input/root/color-input-root.js +21 -5
  23. package/dist/color-picker/root/color-picker-root.d.ts +55 -0
  24. package/dist/color-picker/root/color-picker-root.js +61 -12
  25. package/dist/combobox/root/combobox-root.d.ts +21 -2
  26. package/dist/combobox/root/combobox-root.js +23 -9
  27. package/dist/command/root/command-root.d.ts +1 -0
  28. package/dist/command/root/command-root.js +1 -1
  29. package/dist/data-table/root/data-table-root.d.ts +43 -42
  30. package/dist/data-table/root/data-table-root.js +35 -34
  31. package/dist/date-input/root/date-input-root.d.ts +27 -0
  32. package/dist/date-input/root/date-input-root.js +38 -9
  33. package/dist/date-picker/root/date-picker-root.d.ts +28 -16
  34. package/dist/date-picker/root/date-picker-root.js +17 -15
  35. package/dist/drilldown/root/drilldown-root.d.ts +1 -0
  36. package/dist/drilldown/root/drilldown-root.js +5 -2
  37. package/dist/element-events.generated.js +15 -0
  38. package/dist/empty/root/empty-root.d.ts +12 -7
  39. package/dist/empty/root/empty-root.js +13 -10
  40. package/dist/file-input/root/file-input-root.d.ts +5 -1
  41. package/dist/file-input/root/file-input-root.js +6 -4
  42. package/dist/filter-input/menu/filter-menu.d.ts +1 -0
  43. package/dist/filter-input/menu/filter-menu.js +1 -1
  44. package/dist/filter-input/root/filter-input-root.js +5 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +1 -0
  47. package/dist/input/root/input-root.js +1 -1
  48. package/dist/lightbox/root/lightbox-root.d.ts +9 -1
  49. package/dist/lightbox/root/lightbox-root.js +17 -7
  50. package/dist/listbox/root/listbox-root.d.ts +16 -14
  51. package/dist/listbox/root/listbox-root.js +24 -15
  52. package/dist/map/root/map-root.d.ts +6 -3
  53. package/dist/map/root/map-root.js +8 -2
  54. package/dist/menu/root/menu-root.d.ts +1 -0
  55. package/dist/menu/root/menu-root.js +1 -1
  56. package/dist/notifications/group/notification-group.js +5 -4
  57. package/dist/notifications/notification/notification.d.ts +10 -10
  58. package/dist/notifications/notification/notification.js +3 -3
  59. package/dist/notifications/root/notifications-root.d.ts +1 -0
  60. package/dist/notifications/root/notifications-root.js +1 -0
  61. package/dist/number-input/root/number-input-root.d.ts +17 -0
  62. package/dist/number-input/root/number-input-root.js +22 -6
  63. package/dist/object-inspector/root/object-inspector-root.d.ts +10 -0
  64. package/dist/object-inspector/root/object-inspector-root.js +10 -2
  65. package/dist/outline/root/outline-root.d.ts +15 -0
  66. package/dist/outline/root/outline-root.js +12 -7
  67. package/dist/pagination/root/pagination-root.d.ts +18 -18
  68. package/dist/pagination/root/pagination-root.js +5 -5
  69. package/dist/pagination/size/pagination-size.d.ts +10 -9
  70. package/dist/pagination/size/pagination-size.js +4 -4
  71. package/dist/pagination/summary/pagination-summary.js +6 -5
  72. package/dist/pin-input/root/pin-input-root.d.ts +5 -0
  73. package/dist/pin-input/root/pin-input-root.js +10 -6
  74. package/dist/rating/root/rating-root.js +3 -2
  75. package/dist/select/root/select-root.d.ts +1 -0
  76. package/dist/select/root/select-root.js +8 -3
  77. package/dist/slider/root/slider-root.d.ts +12 -0
  78. package/dist/slider/root/slider-root.js +13 -1
  79. package/dist/splitter/root/splitter-root.d.ts +44 -35
  80. package/dist/splitter/root/splitter-root.js +27 -22
  81. package/dist/switch/root/switch-root.js +1 -0
  82. package/dist/table/column/table-column.d.ts +17 -0
  83. package/dist/table/column/table-column.js +16 -4
  84. package/dist/table/root/table-root.d.ts +1 -0
  85. package/dist/table/root/table-root.js +1 -1
  86. package/dist/textarea/root/textarea-root.js +1 -1
  87. package/dist/time-input/root/time-input-root.d.ts +29 -0
  88. package/dist/time-input/root/time-input-root.js +31 -10
  89. package/dist/time-picker/root/time-picker-root.d.ts +14 -0
  90. package/dist/time-picker/root/time-picker-root.js +16 -14
  91. package/dist/toast/item/toast-item.d.ts +12 -11
  92. package/dist/toast/item/toast-item.js +11 -5
  93. package/dist/toolbar/root/toolbar-root.js +2 -16
  94. package/dist/tooltip/root/tooltip-root.d.ts +1 -0
  95. package/dist/tooltip/root/tooltip-root.js +6 -0
  96. package/dist/tour/root/tour-root.d.ts +10 -10
  97. package/dist/tour/root/tour-root.js +5 -5
  98. package/dist/tree/root/tree-root.d.ts +1 -0
  99. package/dist/tree/root/tree-root.js +1 -1
  100. package/dist/video/root/video-root.d.ts +84 -0
  101. package/dist/video/root/video-root.js +116 -41
  102. package/dist/video-playlist/menu/video-playlist-menu.d.ts +1 -0
  103. package/dist/video-playlist/menu/video-playlist-menu.js +4 -3
  104. package/package.json +5 -5
  105. package/types/jsx.d.ts +5419 -716
  106. package/types/svelte.d.ts +3 -0
  107. package/types/vue.d.ts +1607 -213
@@ -12,8 +12,9 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
12
12
  * what filters it, not in what the list is.
13
13
  *
14
14
  * **`value` is comma separated when `multiple` is set**, the way
15
- * `Checkbox.Group`'s is. It stays a string because that is what a form submits
16
- * and what `MixinControllerValue` holds.
15
+ * `Checkbox.Group`'s is, and a string because that is what `MixinControllerValue`
16
+ * holds. The form is sent one entry per answer, as a native `<select multiple>`
17
+ * sends it, so a server never has to know to split on a comma.
17
18
  *
18
19
  * **Chosen and active are two different things.** Chosen is the answer; active
19
20
  * is where the keyboard is. Only the second moves with the arrows, and
@@ -42,6 +43,7 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
42
43
  * @csspart hint - Help under the list.
43
44
  * @csspart items - The list itself.
44
45
  * @csspart label - The question, over the list.
46
+ * @csspart live - The empty and loading lines again, in a region a screen reader hears when they change. Visually hidden in the styled tier.
45
47
  * @csspart loading - Shown while the options are still on their way.
46
48
  * @csspart search - The region holding the field that narrows the list.
47
49
  * @csspart search-field - The field itself.
@@ -59,22 +61,10 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
59
61
  * keystrokes as the listbox. A consumer who wants autofill can still ask.
60
62
  */
61
63
  autocomplete: string;
62
- /**
63
- * What the list is narrowed to, if anything.
64
- *
65
- * Filtering hides rather than removes, so nothing about the value changes and
66
- * the list is still the consumer's own elements in their own order. Reading
67
- * gives what is being filtered on; writing declares it theirs, and
68
- * `default-filter` seeds one the list owns.
69
- */
70
- get filter(): string;
71
- set filter(next: string);
72
64
  /**
73
65
  * What to start narrowed to, where nothing declared it.
74
66
  */
75
67
  defaultFilter: string;
76
- /** What the list is narrowed to, whoever set it. */
77
- private filterState;
78
68
  /**
79
69
  * What is shown when a filter has hidden every option.
80
70
  *
@@ -84,6 +74,18 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
84
74
  * never given anything.
85
75
  */
86
76
  emptyText: string;
77
+ /** What the list is narrowed to, whoever set it. */
78
+ private filterState;
79
+ /**
80
+ * What the list is narrowed to, if anything.
81
+ *
82
+ * Filtering hides rather than removes, so nothing about the value changes and
83
+ * the list is still the consumer's own elements in their own order. Reading
84
+ * gives what is being filtered on; writing declares it theirs, and
85
+ * `default-filter` seeds one the list owns.
86
+ */
87
+ get filter(): string;
88
+ set filter(next: string);
87
89
  /**
88
90
  * Whether the list renders a field for narrowing itself.
89
91
  *
@@ -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, customElement, DisabledCascadeController, HelixFilterEvent, HelixFormElement, hostAria, MixinCollection, MixinControllerValue, MixinField, requestChange, scrollItemIntoView, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, DisabledCascadeController, HelixFilterEvent, HelixFormElement, hostAria, MixinCollection, MixinControllerValue, MixinField, reportValues, requestChange, scrollItemIntoView, } from '@fusion.dev/helix-core';
8
8
  import { magnifyingGlass } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -63,8 +63,8 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
63
63
  });
64
64
  this.autocomplete = 'off';
65
65
  this.defaultFilter = '';
66
- this.filterState = undefined;
67
66
  this.emptyText = 'No matches';
67
+ this.filterState = undefined;
68
68
  this.filterable = false;
69
69
  this.filterLabel = 'Filter';
70
70
  this.multiple = false;
@@ -286,7 +286,12 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
286
286
  }
287
287
  }
288
288
  sync() {
289
- this.internals.setFormValue(this.value === '' ? null : this.value);
289
+ if (this.multiple) {
290
+ reportValues(this.internals, this.name, this.value === '' ? [] : this.value.split(','));
291
+ }
292
+ else {
293
+ this.internals.setFormValue(this.value === '' ? null : this.value);
294
+ }
290
295
  this.reportRequired(this.value === '', 'Choose one to continue.', 'items');
291
296
  }
292
297
  connectedCallback() {
@@ -303,8 +308,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
303
308
  }
304
309
  willUpdate(changed) {
305
310
  super.willUpdate(changed);
306
- hostAria(this.internals, { label: this.label, role: 'listbox' });
307
- this.internals.ariaMultiSelectable = this.multiple ? 'true' : null;
311
+ hostAria(this.internals, { role: '' });
308
312
  if (this.childrenKnown) {
309
313
  this.place();
310
314
  }
@@ -324,7 +328,6 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
324
328
  super.updated(changed);
325
329
  this.followActive();
326
330
  this.sync();
327
- this.linkHost();
328
331
  this.linkActive();
329
332
  }
330
333
  requestFilter(next) {
@@ -354,6 +357,11 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
354
357
  </slot>`;
355
358
  }
356
359
  render() {
360
+ const shown = this.counted && !this.loading
361
+ ? this.filter === ''
362
+ ? this.options.length > 0
363
+ : this.hasMatches
364
+ : true;
357
365
  return html `${this.renderGroupLabel()}
358
366
  ${this.filterable
359
367
  ? html `<div part="search">
@@ -378,32 +386,33 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
378
386
  <div
379
387
  part="items"
380
388
  id="items"
389
+ role="listbox"
381
390
  aria-busy=${this.loading ? 'true' : nothing}
391
+ aria-describedby=${this.describedBy ?? nothing}
392
+ aria-labelledby=${this.labeled ? this.labelId : nothing}
393
+ aria-multiselectable=${this.multiple ? 'true' : nothing}
394
+ aria-required=${this.required ? 'true' : nothing}
382
395
  ${''}
383
396
  tabindex=${this.isDisabled ? -1 : 0}
384
397
  >
385
398
  <slot @slotchange=${() => this.place()}></slot>
386
- ${this.renderStatus(this.counted && !this.loading
387
- ? this.filter === ''
388
- ? this.options.length > 0
389
- : this.hasMatches
390
- : true)}
399
+ ${this.renderStatus(shown)}
391
400
  </div>
392
- ${this.renderHint()}${this.renderFeedback()}`;
401
+ ${this.renderLive(shown)}${this.renderHint()}${this.renderFeedback()}`;
393
402
  }
394
403
  };
395
404
  __decorate([
396
405
  property()
397
406
  ], HelixHeadlessListbox.prototype, "autocomplete", void 0);
398
- __decorate([
399
- property()
400
- ], HelixHeadlessListbox.prototype, "filter", null);
401
407
  __decorate([
402
408
  property({ attribute: 'default-filter' })
403
409
  ], HelixHeadlessListbox.prototype, "defaultFilter", void 0);
404
410
  __decorate([
405
411
  property({ attribute: 'empty-text' })
406
412
  ], HelixHeadlessListbox.prototype, "emptyText", void 0);
413
+ __decorate([
414
+ property()
415
+ ], HelixHeadlessListbox.prototype, "filter", null);
407
416
  __decorate([
408
417
  property({ attribute: 'filterable', reflect: true, type: Boolean })
409
418
  ], HelixHeadlessListbox.prototype, "filterable", void 0);
@@ -1,4 +1,4 @@
1
- import { HelixElement, type ViewportControlsPlacement } from '@fusion.dev/helix-core';
1
+ import { HelixElement, type ViewportControlsPlacement, type ViewportPanDetail, type ViewportZoomDetail } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * What `ViewportControlsPlacement` is called for a map.
@@ -37,7 +37,7 @@ declare const HelixHeadlessMap_base: (new (...args: any[]) => import("@fusion.de
37
37
  * A frame that moved the map would have to know whose map it was.
38
38
  *
39
39
  * @since 0.100.0
40
- * @status stable
40
+ * @status experimental
41
41
  * @category media
42
42
  * @aliases Map frame, Map container, Geo
43
43
  * @documentation https://fusion.dev/helix/components/map
@@ -83,6 +83,9 @@ export declare class HelixHeadlessMap extends HelixHeadlessMap_base {
83
83
  * at every encounter.
84
84
  */
85
85
  label: string;
86
+ /** The events this family names, the surface naming none of its own. */
87
+ protected panEvent(detail: ViewportPanDetail): Event;
88
+ protected zoomEvent(detail: ViewportZoomDetail): Event;
86
89
  /**
87
90
  * What the group of controls is called.
88
91
  *
@@ -90,7 +93,7 @@ export declare class HelixHeadlessMap extends HelixHeadlessMap_base {
90
93
  * the group hears what it drives rather than "view controls", which names the
91
94
  * mechanism.
92
95
  */
93
- protected get controlsLabel(): string;
96
+ protected get restingControlsLabel(): string;
94
97
  protected willUpdate(changed: PropertyValues): void;
95
98
  /**
96
99
  * The frame, with each region in a box of its own.
@@ -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, hostAria, MixinViewport, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, HelixMapPanEvent, HelixMapZoomEvent, hostAria, MixinViewport, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement) {
@@ -12,7 +12,13 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
12
12
  super(...arguments);
13
13
  this.label = '';
14
14
  }
15
- get controlsLabel() {
15
+ panEvent(detail) {
16
+ return new HelixMapPanEvent(detail);
17
+ }
18
+ zoomEvent(detail) {
19
+ return new HelixMapZoomEvent(detail);
20
+ }
21
+ get restingControlsLabel() {
16
22
  return 'Map controls';
17
23
  }
18
24
  willUpdate(changed) {
@@ -40,6 +40,7 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
40
40
  * @csspart back-mark - The glyph on the back control of a submenu.
41
41
  * @csspart empty - Shown when there are no items to show.
42
42
  * @csspart header - The bar above a submenu, holding the way back and its name.
43
+ * @csspart live - The empty and loading lines again, in a region a screen reader hears when they change. Visually hidden in the styled tier.
43
44
  * @csspart loading - Shown while the items are still on their way.
44
45
  * @csspart surface - The floating panel the menu is drawn on.
45
46
  * @csspart title - The name of the submenu that is showing.
@@ -204,7 +204,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
204
204
  </div>`}
205
205
  <slot @slotchange=${(event) => this.countRows(event)}></slot>
206
206
  ${this.renderStatus(this.hasRows)}
207
- </div>`;
207
+ </div>${this.renderLive(this.hasRows)}`;
208
208
  }
209
209
  };
210
210
  __decorate([
@@ -9,6 +9,7 @@ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  const TRIGGER_ID = 'trigger';
11
11
  const ITEMS_ID = 'items';
12
+ const LABEL_ID = 'label';
12
13
  let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extends MixinControllerOpen(HelixElement) {
13
14
  constructor() {
14
15
  super(...arguments);
@@ -34,7 +35,7 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
34
35
  }
35
36
  willUpdate(changed) {
36
37
  super.willUpdate(changed);
37
- hostAria(this.internals, this.label === '' ? { role: 'group' } : { label: this.label, role: 'group' });
38
+ hostAria(this.internals, { role: 'listitem' });
38
39
  }
39
40
  firstUpdated(changed) {
40
41
  super.firstUpdated(changed);
@@ -76,14 +77,14 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
76
77
  type="button"
77
78
  @click=${this.handleToggle}
78
79
  >
79
- <span part="label"><slot name="label">${this.label}</slot></span>
80
+ <span part="label" id=${LABEL_ID}><slot name="label">${this.label}</slot></span>
80
81
  ${count}
81
82
  </button>`
82
- : html `<span part="label"><slot name="label">${this.label}</slot></span>
83
+ : html `<span part="label" id=${LABEL_ID}><slot name="label">${this.label}</slot></span>
83
84
  ${count}`}
84
85
  <span part="actions"><slot name="actions"></slot></span>
85
86
  </div>
86
- <div id=${ITEMS_ID} part="items">
87
+ <div id=${ITEMS_ID} part="items" role="list" aria-labelledby=${LABEL_ID}>
87
88
  <slot></slot>
88
89
  </div>`;
89
90
  }
@@ -77,6 +77,16 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
77
77
  * empty string would give.
78
78
  */
79
79
  at: string;
80
+ /**
81
+ * State to open with, where nothing declared one.
82
+ */
83
+ defaultRead: boolean;
84
+ /**
85
+ * Locale for the timestamp.
86
+ *
87
+ * Empty is the runtime's own.
88
+ */
89
+ locale: string;
80
90
  /**
81
91
  * Whether the reader has seen it.
82
92
  *
@@ -94,16 +104,6 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
94
104
  */
95
105
  get read(): boolean;
96
106
  set read(next: boolean);
97
- /**
98
- * State to open with, where nothing declared one.
99
- */
100
- defaultRead: boolean;
101
- /**
102
- * Locale for the timestamp.
103
- *
104
- * Empty is the runtime's own.
105
- */
106
- locale: string;
107
107
  /**
108
108
  * @internal Read by the list to identify its children across the light DOM,
109
109
  * which is cheaper than `instanceof` and is why this is public rather than
@@ -120,15 +120,15 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
120
120
  __decorate([
121
121
  property()
122
122
  ], HelixHeadlessNotification.prototype, "at", void 0);
123
- __decorate([
124
- property({ type: Boolean })
125
- ], HelixHeadlessNotification.prototype, "read", null);
126
123
  __decorate([
127
124
  property({ attribute: 'default-read', type: Boolean })
128
125
  ], HelixHeadlessNotification.prototype, "defaultRead", void 0);
129
126
  __decorate([
130
127
  property()
131
128
  ], HelixHeadlessNotification.prototype, "locale", void 0);
129
+ __decorate([
130
+ property({ type: Boolean })
131
+ ], HelixHeadlessNotification.prototype, "read", null);
132
132
  HelixHeadlessNotification = __decorate([
133
133
  customElement('helix-headless-notification')
134
134
  ], HelixHeadlessNotification);
@@ -41,6 +41,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
41
41
  * @csspart footer - The region below the list.
42
42
  * @csspart header - The region above the list.
43
43
  * @csspart list - The list itself.
44
+ * @csspart live - The empty and loading lines again, in a region a screen reader hears when they change. Visually hidden in the styled tier.
44
45
  * @csspart loading - Shown while the feed is still on its way.
45
46
  * @csspart mark-all - The control that marks everything read.
46
47
  *
@@ -139,6 +139,7 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
139
139
  <slot name="items" @slotchange=${this.handleSlotChange}></slot>
140
140
  ${this.renderStatus(hasItems)}
141
141
  </div>
142
+ ${this.renderLive(hasItems)}
142
143
  <div part="footer"><slot name="footer"></slot></div>`;
143
144
  }
144
145
  };
@@ -69,6 +69,15 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
69
69
  * consumer to ask for it.
70
70
  */
71
71
  autocomplete: string;
72
+ /**
73
+ * What a reader is told when the field holds something that is not a number.
74
+ *
75
+ * **Every sentence a control raises in the browser's bubble is a property**,
76
+ * named for the validity flag it reports, so a form in another language
77
+ * speaks it: `feedback` replaces the message under the field and does not
78
+ * reach the bubble. Absent is the control's own sentence, in English.
79
+ */
80
+ badInputMessage?: string;
72
81
  /**
73
82
  * Accessible name for the control that steps down.
74
83
  */
@@ -103,6 +112,14 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
103
112
  * Not a label: it goes when typing starts.
104
113
  */
105
114
  placeholder: string;
115
+ /**
116
+ * What a reader is told when the number is above `max`, with `{max}` filled in.
117
+ */
118
+ rangeOverflowMessage?: string;
119
+ /**
120
+ * What a reader is told when the number is below `min`, with `{min}` filled in.
121
+ */
122
+ rangeUnderflowMessage?: string;
106
123
  /**
107
124
  * Whether the field refuses input while still submitting its value.
108
125
  */
@@ -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, valueAsNumber as asNumber, customElement, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
8
8
  import { minus, plus } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -193,17 +193,24 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
193
193
  return;
194
194
  }
195
195
  const parsed = this.numeric;
196
- if (this.required && this.value.trim() === '') {
197
- this.internals.setValidity({ valueMissing: true }, 'Please fill in this field.', control);
196
+ if (this.readOnly) {
197
+ this.internals.setValidity({});
198
+ }
199
+ else if (this.required && this.value.trim() === '') {
200
+ this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage || 'Please fill in this field.', control);
198
201
  }
199
202
  else if (this.value.trim() !== '' && parsed === null) {
200
- this.internals.setValidity({ badInput: true }, 'Please enter a number.', control);
203
+ this.internals.setValidity({ badInput: true }, this.badInputMessage || 'Please enter a number.', control);
201
204
  }
202
205
  else if (parsed !== null && this.min !== undefined && parsed < this.min) {
203
- this.internals.setValidity({ rangeUnderflow: true }, `Value must be ${this.min} or more.`, control);
206
+ this.internals.setValidity({ rangeUnderflow: true }, fillTemplate(this.rangeUnderflowMessage || 'Value must be {min} or more.', {
207
+ min: this.min,
208
+ }), control);
204
209
  }
205
210
  else if (parsed !== null && this.max !== undefined && parsed > this.max) {
206
- this.internals.setValidity({ rangeOverflow: true }, `Value must be ${this.max} or less.`, control);
211
+ this.internals.setValidity({ rangeOverflow: true }, fillTemplate(this.rangeOverflowMessage || 'Value must be {max} or less.', {
212
+ max: this.max,
213
+ }), control);
207
214
  }
208
215
  else {
209
216
  this.internals.setValidity({}, '', control);
@@ -300,6 +307,9 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
300
307
  __decorate([
301
308
  property()
302
309
  ], HelixHeadlessNumberInput.prototype, "autocomplete", void 0);
310
+ __decorate([
311
+ property({ attribute: 'bad-input-message' })
312
+ ], HelixHeadlessNumberInput.prototype, "badInputMessage", void 0);
303
313
  __decorate([
304
314
  property({ attribute: 'decrement-label' })
305
315
  ], HelixHeadlessNumberInput.prototype, "decrementLabel", void 0);
@@ -318,6 +328,12 @@ __decorate([
318
328
  __decorate([
319
329
  property()
320
330
  ], HelixHeadlessNumberInput.prototype, "placeholder", void 0);
331
+ __decorate([
332
+ property({ attribute: 'range-overflow-message' })
333
+ ], HelixHeadlessNumberInput.prototype, "rangeOverflowMessage", void 0);
334
+ __decorate([
335
+ property({ attribute: 'range-underflow-message' })
336
+ ], HelixHeadlessNumberInput.prototype, "rangeUnderflowMessage", void 0);
321
337
  __decorate([
322
338
  property({ attribute: 'readonly', reflect: true, type: Boolean })
323
339
  ], HelixHeadlessNumberInput.prototype, "readOnly", void 0);
@@ -94,6 +94,16 @@ interface Row {
94
94
  * @cssproperty --object-inspector-level - How deep a row is, from 0 at the root, for indenting it.
95
95
  */
96
96
  export declare class HelixHeadlessObjectInspector extends HelixElement {
97
+ /**
98
+ * What the copy control is called on a row with a key, with the key as
99
+ * `{name}`.
100
+ *
101
+ * **Every word the inspector speaks is a property**, so a page in another
102
+ * language hears its own rather than English around its own keys.
103
+ */
104
+ copyLabel: string;
105
+ /** What the copy control is called where there is no key, on the value itself. */
106
+ copyValueLabel: string;
97
107
  /**
98
108
  * How many levels start open.
99
109
  *
@@ -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 HelixHeadlessObjectInspector_1;
8
- import { CopyController, customElement, elementById, HelixElement, inlineStep, scrollItemIntoView, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
8
+ import { CopyController, customElement, elementById, fillTemplate, HelixElement, inlineStep, scrollItemIntoView, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
9
9
  import { caretDown, caretRight } from '@fusion.dev/helix-icons';
10
10
  import { html, nothing } from 'lit';
11
11
  import { property, state } from 'lit/decorators.js';
@@ -18,6 +18,8 @@ const child = (path, key) => `${path}/${key.replaceAll('~', '~0').replaceAll('/'
18
18
  let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends HelixElement {
19
19
  constructor() {
20
20
  super(...arguments);
21
+ this.copyLabel = 'Copy {name}';
22
+ this.copyValueLabel = 'Copy value';
21
23
  this.defaultDepth = 1;
22
24
  this.for = '';
23
25
  this.label = '';
@@ -291,7 +293,7 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
291
293
  tooltip-arrow-left: copy-tooltip-arrow-left, tooltip-arrow-right: copy-tooltip-arrow-right,
292
294
  tooltip-arrow-top: copy-tooltip-arrow-top, tooltip-bottom: copy-tooltip-bottom,
293
295
  tooltip-left: copy-tooltip-left, tooltip-right: copy-tooltip-right, tooltip-top: copy-tooltip-top"
294
- label=${row?.key === undefined ? 'Copy value' : `Copy ${row.key}`}
296
+ label=${row?.key === undefined ? this.copyValueLabel : fillTemplate(this.copyLabel, { name: row.key })}
295
297
  .value=${this.copyable(row)}
296
298
  ></${this.tag('copy-button')}>`;
297
299
  }
@@ -321,6 +323,12 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
321
323
  </div>`;
322
324
  }
323
325
  };
326
+ __decorate([
327
+ property({ attribute: 'copy-label' })
328
+ ], HelixHeadlessObjectInspector.prototype, "copyLabel", void 0);
329
+ __decorate([
330
+ property({ attribute: 'copy-value-label' })
331
+ ], HelixHeadlessObjectInspector.prototype, "copyValueLabel", void 0);
324
332
  __decorate([
325
333
  property({ attribute: 'default-depth', type: Number })
326
334
  ], HelixHeadlessObjectInspector.prototype, "defaultDepth", void 0);
@@ -79,6 +79,11 @@ export declare class HelixHeadlessOutline extends HelixElement {
79
79
  get current(): string;
80
80
  /**
81
81
  * What `current` reports, which only `mark` writes.
82
+ *
83
+ * **A field, not state.** Nothing renders it: the links carry `aria-current`
84
+ * and the mark is placed by measuring them, so a render on its change drew
85
+ * the same template again. It also made the first measurement, which can only
86
+ * happen after the first render, schedule a second one from `firstUpdated`.
82
87
  */
83
88
  private reading;
84
89
  private observer;
@@ -146,6 +151,8 @@ export declare class HelixHeadlessOutline extends HelixElement {
146
151
  private landing;
147
152
  /** The guard behind `scrollend`, in case it never comes. */
148
153
  private landed;
154
+ /** What `scrollend` calls when the scroll a click started lands, while it is awaited. */
155
+ private arrive;
149
156
  /**
150
157
  * The two things an `IntersectionObserver` cannot see, and one listener for
151
158
  * both.
@@ -221,6 +228,14 @@ export declare class HelixHeadlessOutline extends HelixElement {
221
228
  */
222
229
  private readonly handleSlotChange;
223
230
  disconnectedCallback(): void;
231
+ /**
232
+ * Stops waiting for a scroll to land, both halves of the wait.
233
+ *
234
+ * The `scrollend` listener is on the document, so it is taken off as well as
235
+ * the timer: left on, every click that did not wait for the last landing
236
+ * added one more.
237
+ */
238
+ private unland;
224
239
  connectedCallback(): void;
225
240
  protected firstUpdated(changed: PropertyValues): void;
226
241
  protected updated(changed: PropertyValues): void;
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
- import { property, state } from 'lit/decorators.js';
9
+ import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
@@ -43,13 +43,13 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
43
43
  }
44
44
  this.landing = section;
45
45
  this.markTo(section);
46
- clearTimeout(this.landed);
46
+ this.unland();
47
47
  const arrive = () => {
48
- clearTimeout(this.landed);
48
+ this.unland();
49
49
  this.landing = undefined;
50
- document.removeEventListener('scrollend', arrive, true);
51
50
  this.mark();
52
51
  };
52
+ this.arrive = arrive;
53
53
  document.addEventListener('scrollend', arrive, { capture: true, once: true });
54
54
  this.landed = setTimeout(arrive, 1200);
55
55
  if (this.behavior === 'auto') {
@@ -115,6 +115,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
115
115
  const next = active?.target.id ?? '';
116
116
  if (next !== this.reading) {
117
117
  this.reading = next;
118
+ this.indicator.place();
118
119
  this.dispatchEvent(new HelixActivateEvent({ value: next }));
119
120
  }
120
121
  }
@@ -168,8 +169,15 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
168
169
  }
169
170
  this.observer?.disconnect();
170
171
  this.observer = undefined;
172
+ this.unland();
173
+ }
174
+ unland() {
171
175
  clearTimeout(this.landed);
172
176
  this.landed = undefined;
177
+ if (this.arrive) {
178
+ document.removeEventListener('scrollend', this.arrive, true);
179
+ this.arrive = undefined;
180
+ }
173
181
  }
174
182
  connectedCallback() {
175
183
  super.connectedCallback();
@@ -213,9 +221,6 @@ __decorate([
213
221
  __decorate([
214
222
  property({ type: Number })
215
223
  ], HelixHeadlessOutline.prototype, "offset", void 0);
216
- __decorate([
217
- state()
218
- ], HelixHeadlessOutline.prototype, "reading", void 0);
219
224
  HelixHeadlessOutline = __decorate([
220
225
  customElement('helix-headless-outline')
221
226
  ], HelixHeadlessOutline);
@@ -97,24 +97,6 @@ export declare class HelixHeadlessPagination extends HelixElement {
97
97
  * written onto a host at upgrade that a server's output never had.
98
98
  */
99
99
  ends: boolean;
100
- /**
101
- * The page on show, one-based.
102
- *
103
- * An accessor pair, the same shape as the calendar's `month`. Reading gives
104
- * the page on show; writing sets it, and `default-page` seeds one the element
105
- * keeps until somebody does.
106
- *
107
- * **Clamped on the way out, because "the page on show" is what it says it
108
- * gives.** Returning whatever was written means `default-page="99"` on a
109
- * ten-page result renders page 10, marks page 10 with `aria-current`, and
110
- * reports 99, so a consumer syncing their query from this reads a page that
111
- * does not exist while the control in front of them says otherwise. Not a
112
- * far-fetched value: it is a bookmarked link after the result shrank.
113
- *
114
- * Rounded here too, so `2.7` reads as the 2 it renders.
115
- */
116
- get page(): number;
117
- set page(next: number);
118
100
  /**
119
101
  * Accessible name for the control that jumps to the first page.
120
102
  */
@@ -157,6 +139,24 @@ export declare class HelixHeadlessPagination extends HelixElement {
157
139
  * Accessible name for the control that moves forward.
158
140
  */
159
141
  nextLabel: string;
142
+ /**
143
+ * The page on show, one-based.
144
+ *
145
+ * An accessor pair, the same shape as the calendar's `month`. Reading gives
146
+ * the page on show; writing sets it, and `default-page` seeds one the element
147
+ * keeps until somebody does.
148
+ *
149
+ * **Clamped on the way out, because "the page on show" is what it says it
150
+ * gives.** Returning whatever was written means `default-page="99"` on a
151
+ * ten-page result renders page 10, marks page 10 with `aria-current`, and
152
+ * reports 99, so a consumer syncing their query from this reads a page that
153
+ * does not exist while the control in front of them says otherwise. Not a
154
+ * far-fetched value: it is a bookmarked link after the result shrank.
155
+ *
156
+ * Rounded here too, so `2.7` reads as the 2 it renders.
157
+ */
158
+ get page(): number;
159
+ set page(next: number);
160
160
  /**
161
161
  * Accessible name for one page, with `{page}` and `{pages}` filled in.
162
162
  */