@fusion.dev/helix-headless 0.215.3 → 0.216.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 (113) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +1 -1
  3. package/dist/area-chart/root/area-chart-root.d.ts +1 -0
  4. package/dist/area-chart/root/area-chart-root.js +1 -1
  5. package/dist/bar-chart/root/bar-chart-root.d.ts +1 -0
  6. package/dist/bar-chart/root/bar-chart-root.js +1 -1
  7. package/dist/button/root/button-root.d.ts +3 -1
  8. package/dist/button/root/button-root.js +2 -1
  9. package/dist/chat/composer/chat-composer.d.ts +18 -1
  10. package/dist/chat/composer/chat-composer.js +8 -3
  11. package/dist/checkbox/root/checkbox-root.d.ts +3 -1
  12. package/dist/checkbox/root/checkbox-root.js +2 -1
  13. package/dist/code-block/highlight.d.ts +9 -0
  14. package/dist/code-block/highlight.js +39 -0
  15. package/dist/code-block/highlighter.d.ts +44 -0
  16. package/dist/code-block/highlighter.js +44 -0
  17. package/dist/code-block/index.d.ts +1 -0
  18. package/dist/code-block/index.js +1 -0
  19. package/dist/code-block/keyed-lines.d.ts +23 -0
  20. package/dist/code-block/keyed-lines.js +75 -0
  21. package/dist/code-block/root/code-block-root.d.ts +6 -23
  22. package/dist/code-block/root/code-block-root.js +8 -25
  23. package/dist/code-editor/index.d.ts +1 -0
  24. package/dist/code-editor/index.js +1 -0
  25. package/dist/code-editor/root/code-editor-root.d.ts +367 -0
  26. package/dist/code-editor/root/code-editor-root.js +746 -0
  27. package/dist/code-input/index.d.ts +1 -0
  28. package/dist/code-input/index.js +1 -0
  29. package/dist/code-input/root/code-input-root.d.ts +119 -0
  30. package/dist/code-input/root/code-input-root.js +142 -0
  31. package/dist/combobox/root/combobox-root.d.ts +6 -1
  32. package/dist/combobox/root/combobox-root.js +4 -2
  33. package/dist/command/root/command-root.d.ts +61 -11
  34. package/dist/command/root/command-root.js +88 -3
  35. package/dist/donut-chart/root/donut-chart-root.d.ts +1 -0
  36. package/dist/donut-chart/root/donut-chart-root.js +1 -1
  37. package/dist/element-events.generated.js +36 -3
  38. package/dist/filter-input/root/filter-input-root.d.ts +9 -1
  39. package/dist/filter-input/root/filter-input-root.js +7 -3
  40. package/dist/heatmap/root/heatmap-root.d.ts +1 -0
  41. package/dist/heatmap/root/heatmap-root.js +1 -1
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.js +4 -0
  44. package/dist/input/root/input-root.d.ts +14 -2
  45. package/dist/input/root/input-root.js +11 -3
  46. package/dist/kbd/root/kbd-root.js +9 -2
  47. package/dist/line-chart/root/line-chart-root.d.ts +1 -0
  48. package/dist/line-chart/root/line-chart-root.js +1 -1
  49. package/dist/listbox/root/listbox-root.d.ts +8 -1
  50. package/dist/listbox/root/listbox-root.js +7 -4
  51. package/dist/markdown/blocks.d.ts +66 -0
  52. package/dist/markdown/blocks.js +326 -0
  53. package/dist/markdown/index.d.ts +6 -0
  54. package/dist/markdown/index.js +5 -0
  55. package/dist/markdown/inline.d.ts +57 -0
  56. package/dist/markdown/inline.js +483 -0
  57. package/dist/markdown/root/markdown-root.d.ts +99 -0
  58. package/dist/markdown/root/markdown-root.js +93 -0
  59. package/dist/markdown/stream.d.ts +47 -0
  60. package/dist/markdown/stream.js +62 -0
  61. package/dist/markdown/tree.d.ts +57 -0
  62. package/dist/markdown/tree.js +144 -0
  63. package/dist/markdown/writer.d.ts +33 -0
  64. package/dist/markdown/writer.js +139 -0
  65. package/dist/menu/root/menu-root.d.ts +53 -0
  66. package/dist/menu/root/menu-root.js +92 -15
  67. package/dist/pagination/size/pagination-size.d.ts +3 -0
  68. package/dist/pagination/size/pagination-size.js +2 -1
  69. package/dist/scatter-chart/root/scatter-chart-root.d.ts +1 -0
  70. package/dist/scatter-chart/root/scatter-chart-root.js +1 -1
  71. package/dist/select/root/select-root.d.ts +6 -1
  72. package/dist/select/root/select-root.js +4 -2
  73. package/dist/shortcut-overlay/index.d.ts +1 -0
  74. package/dist/shortcut-overlay/index.js +1 -0
  75. package/dist/shortcut-overlay/root/shortcut-overlay-root.d.ts +128 -0
  76. package/dist/shortcut-overlay/root/shortcut-overlay-root.js +275 -0
  77. package/dist/slider/root/slider-root.d.ts +3 -1
  78. package/dist/slider/root/slider-root.js +2 -1
  79. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +1 -0
  80. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +1 -1
  81. package/dist/spatial-heatmap/root/spatial-heatmap-root.d.ts +1 -0
  82. package/dist/spatial-heatmap/root/spatial-heatmap-root.js +1 -1
  83. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +1 -0
  84. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +1 -1
  85. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +1 -0
  86. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +1 -1
  87. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +1 -0
  88. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +1 -1
  89. package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +1 -0
  90. package/dist/spatial-treemap/root/spatial-treemap-root.js +1 -1
  91. package/dist/switch/root/switch-root.d.ts +3 -1
  92. package/dist/switch/root/switch-root.js +2 -1
  93. package/dist/tabs/tab/tab.d.ts +3 -1
  94. package/dist/tabs/tab/tab.js +2 -1
  95. package/dist/textarea/root/textarea-root.d.ts +20 -4
  96. package/dist/textarea/root/textarea-root.js +31 -23
  97. package/dist/time-input/root/time-input-root.d.ts +6 -0
  98. package/dist/time-input/root/time-input-root.js +4 -2
  99. package/dist/time-picker/root/time-picker-root.d.ts +6 -0
  100. package/dist/time-picker/root/time-picker-root.js +4 -2
  101. package/dist/toast/root/toast-root.d.ts +64 -0
  102. package/dist/toast/root/toast-root.js +128 -3
  103. package/dist/toggle/root/toggle-root.d.ts +3 -1
  104. package/dist/toggle/root/toggle-root.js +2 -1
  105. package/dist/virtual-list/root/virtual-list-root.d.ts +18 -2
  106. package/dist/virtual-list/root/virtual-list-root.js +103 -5
  107. package/examples.json +1 -1
  108. package/lint.json +1 -1
  109. package/package.json +6 -6
  110. package/types/jsx.d.ts +5315 -596
  111. package/types/svelte.d.ts +4 -0
  112. package/types/vue.d.ts +1085 -4
  113. package/types/words.d.ts +491 -0
@@ -0,0 +1,275 @@
1
+ import { __decorate } from "tslib";
2
+ import { chordParts, customElement, HelixElement, keyClaimed, keyNames, listControls, listShortcuts, matchesChord, onApple, timeConverter, words, } from '@fusion.dev/helix-core';
3
+ import { css, nothing } from 'lit';
4
+ import { property, state } from 'lit/decorators.js';
5
+ import { html } from 'lit/static-html.js';
6
+ import '../../dialog/index.js';
7
+ const HELD = {
8
+ alt: () => 'Alt',
9
+ ctrl: () => 'Control',
10
+ mod: (apple) => (apple ? 'Meta' : 'Control'),
11
+ shift: () => 'Shift',
12
+ };
13
+ const printed = (keys, apple) => {
14
+ const names = keyNames(chordParts(keys), apple);
15
+ return names.keys.join(names.separator);
16
+ };
17
+ let HelixHeadlessShortcutOverlay = class HelixHeadlessShortcutOverlay extends HelixElement {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.delay = 600;
21
+ this.emptyLabel = 'No shortcuts on this screen.';
22
+ this.hold = 'mod';
23
+ this.label = 'Keyboard shortcuts';
24
+ this.sheetKeys = '?';
25
+ this.badges = [];
26
+ this.sheetOpen = false;
27
+ this.rows = [];
28
+ this.cancel = () => {
29
+ this.hide();
30
+ };
31
+ this.handleKeydown = (event) => {
32
+ const apple = onApple();
33
+ if (event.key === HELD[this.hold](apple)) {
34
+ if (!event.repeat && this.timer === undefined && !this.showing) {
35
+ this.timer = setTimeout(() => {
36
+ this.timer = undefined;
37
+ this.show();
38
+ }, this.delay);
39
+ }
40
+ return;
41
+ }
42
+ this.hide();
43
+ if (this.sheetKeys !== '' && !keyClaimed(event) && matchesChord(event, this.sheetKeys, apple)) {
44
+ event.preventDefault();
45
+ this.showSheet();
46
+ }
47
+ };
48
+ this.handleKeyup = (event) => {
49
+ if (event.key === HELD[this.hold](onApple())) {
50
+ this.hide();
51
+ }
52
+ };
53
+ }
54
+ static { this.styles = css `
55
+ :host {
56
+ display: contents;
57
+ }
58
+
59
+ [part='badges'] {
60
+ background: none;
61
+ border: 0;
62
+ inset: 0;
63
+ margin: 0;
64
+ overflow: visible;
65
+ padding: 0;
66
+ pointer-events: none;
67
+ }
68
+
69
+ [part='badge'] {
70
+ left: 0;
71
+ position: fixed;
72
+ top: 0;
73
+ white-space: nowrap;
74
+ }
75
+
76
+ [part='badge'][data-compact] [part='badge-name'] {
77
+ display: none;
78
+ }
79
+ `; }
80
+ static { this.words = 'helix-shortcut-overlay'; }
81
+ get showing() {
82
+ return this.badges.length > 0;
83
+ }
84
+ connectedCallback() {
85
+ super.connectedCallback();
86
+ document.addEventListener('keydown', this.handleKeydown, true);
87
+ document.addEventListener('keyup', this.handleKeyup, true);
88
+ document.addEventListener('pointerdown', this.cancel, true);
89
+ document.addEventListener('scroll', this.cancel, true);
90
+ window.addEventListener('blur', this.cancel);
91
+ window.addEventListener('resize', this.cancel);
92
+ }
93
+ disconnectedCallback() {
94
+ super.disconnectedCallback();
95
+ document.removeEventListener('keydown', this.handleKeydown, true);
96
+ document.removeEventListener('keyup', this.handleKeyup, true);
97
+ document.removeEventListener('pointerdown', this.cancel, true);
98
+ document.removeEventListener('scroll', this.cancel, true);
99
+ window.removeEventListener('blur', this.cancel);
100
+ window.removeEventListener('resize', this.cancel);
101
+ this.cancel();
102
+ }
103
+ show() {
104
+ const apple = onApple();
105
+ const width = window.innerWidth;
106
+ const height = window.innerHeight;
107
+ const badges = [];
108
+ for (const control of listControls()) {
109
+ const box = control.element.getBoundingClientRect();
110
+ const [keys] = control.keys;
111
+ if (!keys || box.bottom < 0 || box.top > height || box.right < 0 || box.left > width) {
112
+ continue;
113
+ }
114
+ badges.push({
115
+ keys: printed(keys, apple),
116
+ left: box.left + box.width / 2,
117
+ name: control.name,
118
+ top: box.bottom,
119
+ });
120
+ }
121
+ this.badges = badges;
122
+ this.toggleState('showing', badges.length > 0);
123
+ }
124
+ hide() {
125
+ clearTimeout(this.timer);
126
+ this.timer = undefined;
127
+ if (this.badges.length > 0) {
128
+ this.badges = [];
129
+ this.toggleState('showing', false);
130
+ }
131
+ }
132
+ showSheet() {
133
+ this.hide();
134
+ this.rows = this.sheetRows();
135
+ this.sheetOpen = true;
136
+ }
137
+ hideSheet() {
138
+ this.sheetOpen = false;
139
+ }
140
+ sheetRows() {
141
+ const apple = onApple();
142
+ const controls = listControls().filter((control) => control.keys.length > 0);
143
+ const owned = new Set(controls.map((control) => control.element));
144
+ const rows = controls.flatMap((control) => control.keys.map((keys) => ({ keys: printed(keys, apple), name: control.name })));
145
+ for (const shortcut of listShortcuts()) {
146
+ if (shortcut.target && owned.has(shortcut.target)) {
147
+ continue;
148
+ }
149
+ rows.push({ keys: printed(shortcut.keys, apple), name: shortcut.label });
150
+ }
151
+ const seen = new Set();
152
+ return rows.filter((row) => {
153
+ const key = `${row.keys}\u0000${row.name}`;
154
+ if (seen.has(key)) {
155
+ return false;
156
+ }
157
+ seen.add(key);
158
+ return true;
159
+ });
160
+ }
161
+ updated(changed) {
162
+ super.updated(changed);
163
+ const layer = this.partNamed('badges');
164
+ if (changed.has('badges') &&
165
+ layer?.showPopover &&
166
+ this.badges.length > 0 &&
167
+ !layer.matches(':popover-open')) {
168
+ layer.showPopover();
169
+ }
170
+ if (changed.has('badges')) {
171
+ this.arrange();
172
+ }
173
+ if (changed.has('badges') &&
174
+ this.badges.length > 0 &&
175
+ globalThis.document?.fonts?.status === 'loading') {
176
+ void document.fonts.ready.then(() => {
177
+ if (this.badges.length > 0) {
178
+ this.show();
179
+ }
180
+ });
181
+ }
182
+ }
183
+ arrange() {
184
+ const placed = [];
185
+ const margin = 4;
186
+ const width = window.innerWidth;
187
+ const height = window.innerHeight;
188
+ const drawn = [...(this.renderRoot.querySelectorAll('[part~="badge"]') ?? [])];
189
+ drawn.forEach((element, index) => {
190
+ const badge = this.badges[index];
191
+ if (!badge) {
192
+ return;
193
+ }
194
+ const box = (compact, top) => {
195
+ element.toggleAttribute('data-compact', compact);
196
+ const { height: tall, width: wide } = element.getBoundingClientRect();
197
+ const left = Math.round(Math.min(Math.max(badge.left - wide / 2, margin), width - wide - margin));
198
+ const at = Math.round(Math.min(Math.max(top, margin), height - tall - margin));
199
+ return { bottom: at + tall, left, right: left + wide, top: at };
200
+ };
201
+ const hits = (one) => placed.filter((other) => one.left < other.right &&
202
+ one.right > other.left &&
203
+ one.top < other.bottom &&
204
+ one.bottom > other.top);
205
+ const under = badge.top + margin;
206
+ let place = box(false, under);
207
+ if (hits(place).length > 0) {
208
+ place = box(true, under);
209
+ }
210
+ for (let tries = 0; tries < 4 && hits(place).length > 0; tries += 1) {
211
+ const below = Math.max(...hits(place).map((other) => other.bottom));
212
+ place = box(true, below + 2);
213
+ }
214
+ element.style.left = `${place.left}px`;
215
+ element.style.top = `${place.top}px`;
216
+ placed.push(place);
217
+ });
218
+ }
219
+ render() {
220
+ const dialog = this.tag('dialog');
221
+ return html `${this.badges.length > 0
222
+ ? html `<div part="badges" popover="manual" aria-hidden="true">
223
+ ${this.badges.map((badge) => html `<span part="badge"
224
+ ><kbd part="badge-keys">${badge.keys}</kbd> <span part="badge-name">${badge.name}</span></span
225
+ >`)}
226
+ </div>`
227
+ : nothing}<${dialog}
228
+ part="sheet"
229
+ exportparts="body: sheet-body, dismiss: sheet-dismiss, dismiss-mark: sheet-dismiss-mark,
230
+ footer: sheet-footer, header: sheet-header, surface: sheet-surface"
231
+ label=${this.label}
232
+ .open=${this.sheetOpen}
233
+ @close=${() => {
234
+ this.sheetOpen = false;
235
+ }}
236
+ >
237
+ <h2 slot="header">${this.label}</h2>
238
+ ${this.rows.length === 0
239
+ ? html `<p part="empty">${this.emptyLabel}</p>`
240
+ : html `<dl part="list">
241
+ ${this.rows.map((row) => html `<div part="row"><dt part="name">${row.name}</dt><dd part="keys"><kbd>${row.keys}</kbd></dd></div>`)}
242
+ </dl>`}
243
+ </${dialog}>`;
244
+ }
245
+ };
246
+ __decorate([
247
+ property({ converter: timeConverter('delay', 600) })
248
+ ], HelixHeadlessShortcutOverlay.prototype, "delay", void 0);
249
+ __decorate([
250
+ words,
251
+ property({ attribute: 'empty-label' })
252
+ ], HelixHeadlessShortcutOverlay.prototype, "emptyLabel", void 0);
253
+ __decorate([
254
+ property()
255
+ ], HelixHeadlessShortcutOverlay.prototype, "hold", void 0);
256
+ __decorate([
257
+ words,
258
+ property()
259
+ ], HelixHeadlessShortcutOverlay.prototype, "label", void 0);
260
+ __decorate([
261
+ property({ attribute: 'sheet-keys' })
262
+ ], HelixHeadlessShortcutOverlay.prototype, "sheetKeys", void 0);
263
+ __decorate([
264
+ state()
265
+ ], HelixHeadlessShortcutOverlay.prototype, "badges", void 0);
266
+ __decorate([
267
+ state()
268
+ ], HelixHeadlessShortcutOverlay.prototype, "sheetOpen", void 0);
269
+ __decorate([
270
+ state()
271
+ ], HelixHeadlessShortcutOverlay.prototype, "rows", void 0);
272
+ HelixHeadlessShortcutOverlay = __decorate([
273
+ customElement('helix-headless-shortcut-overlay')
274
+ ], HelixHeadlessShortcutOverlay);
275
+ export { HelixHeadlessShortcutOverlay };
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, ListedController } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
@@ -106,6 +106,8 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
106
106
  * @cssstate invalid - The slider has an error to show, from the browser's validation or from your `feedback`.
107
107
  */
108
108
  export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
109
+ /** On the page's list of controls, where a command palette and a shortcut overlay find it. */
110
+ protected readonly listed: ListedController;
109
111
  /**
110
112
  * The key this family's words are registered under, its styled tag:
111
113
  * `registerWords('de', { 'helix-slider': { rangeText: '{start} bis {end}' } })`.
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { AriaForwardController, customElement, formatNumber, formatWords, HelixFormElement, isRtl, MixinControllerValue, MixinField, numberConverter, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
2
+ import { AriaForwardController, customElement, formatNumber, formatWords, HelixFormElement, isRtl, ListedController, MixinControllerValue, MixinField, numberConverter, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
3
3
  import { html, nothing } from 'lit';
4
4
  import { property, state } from 'lit/decorators.js';
5
5
  const SLIDER_KEYS = new Set([
@@ -15,6 +15,7 @@ const SLIDER_KEYS = new Set([
15
15
  let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue(MixinField(HelixFormElement)) {
16
16
  constructor() {
17
17
  super(...arguments);
18
+ this.listed = new ListedController(this, 'focus', () => this.internals?.ariaKeyShortcuts);
18
19
  this.aria = new AriaForwardController(this, {
19
20
  target: () => this.partNamed('control'),
20
21
  });
@@ -86,6 +86,7 @@ declare const HelixHeadlessSpatialBarChart_base: typeof HelixElement & (new (...
86
86
  * @csspart plot - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
87
87
  * @csspart ring - The outline around the bar the pointer or the keys rest on, painted after it, so the bars in front of it cover it.
88
88
  * @csspart ring-behind - The same outline, faint and dashed, drawn over every bar, so the part of the bar the pointer or the keys rest on that others hide still shows.
89
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
89
90
  * @csspart summary - The number of bars, series, and categories, and the range of values, written as a sentence for screen readers, which describes the plot. Hide it visually.
90
91
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
91
92
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
@@ -154,7 +154,7 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
154
154
  ${readable ? this.renderLive(field, at) : nothing}
155
155
  ${this.renderTooltip(cell?.value === undefined ? undefined : cell.row)}
156
156
  </div>
157
- ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
157
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
158
158
  }
159
159
  renderFallback(field, at) {
160
160
  const view = boxView(field.axes, this.camera, [], this.noBox);
@@ -107,6 +107,7 @@ type DrawnCorners = {
107
107
  * @csspart left-face - The side of a prism facing the light more: from the isometric view, the side toward the next row, drawn at its lower left. Left out where the view shows no side.
108
108
  * @csspart right-face - The side of a prism facing the light less: from the isometric view, the side toward the next column, drawn at its lower right. Left out where the view shows one side or none.
109
109
  * @csspart row - A column of the strip, which a grid reads as a row, under `selection-mode`. It holds the column's grid cells, which draw nothing: the prisms are painted apart, in the order the view needs.
110
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
110
111
  * @csspart strip - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
111
112
  * @csspart top-face - The top of a prism, which carries the reading's tone.
112
113
  *
@@ -216,7 +216,7 @@ let HelixHeadlessSpatialHeatmap = class HelixHeadlessSpatialHeatmap extends Heli
216
216
  <span part="end"><slot name="end"></slot></span>
217
217
  </div>${readable ? this.renderOverview(cells) : nothing}${this.status.spoken
218
218
  ? html `<span part="selection" role="status">${this.status.said}</span>`
219
- : nothing}${this.renderTable()}`;
219
+ : nothing}${this.renderSonify()}${this.renderTable()}`;
220
220
  }
221
221
  renderPrism(prism, active, picked, grid, tone) {
222
222
  return svg `<g
@@ -83,6 +83,7 @@ declare const HelixHeadlessSpatialRibbonChart_base: typeof HelixElement & (new (
83
83
  * @csspart plot - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
84
84
  * @csspart ribbon - One ribbon, its faces in its series' color.
85
85
  * @csspart ring - The ring around the reading the pointer or the keys rest on, drawn over the ribbons.
86
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
86
87
  * @csspart summary - The number of readings, series, and places, and the range of values, written as a sentence for screen readers, which describes the plot. Hide it visually.
87
88
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
88
89
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
@@ -150,7 +150,7 @@ let HelixHeadlessSpatialRibbonChart = class HelixHeadlessSpatialRibbonChart exte
150
150
  ${readable ? this.renderLive(field, at) : nothing}
151
151
  ${this.renderTooltip(cell?.value === undefined ? undefined : cell.row)}
152
152
  </div>
153
- ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
153
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
154
154
  }
155
155
  renderFallback(field, at) {
156
156
  const view = boxView(field.axes, this.camera, [], this.noBox);
@@ -81,6 +81,7 @@ declare const HelixHeadlessSpatialScatterChart_base: typeof HelixElement & (new
81
81
  * @csspart point - One point of the chart's own drawing.
82
82
  * @csspart point-active - The point the pointer or the keys rest on, in the chart's own drawing. Set alongside `point`.
83
83
  * @csspart ring - The ring around the point the pointer or the keys rest on, drawn over every point.
84
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
84
85
  * @csspart summary - The three measures and the number of points, written as a sentence for screen readers, which describes the plot. Hide it visually.
85
86
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
86
87
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
@@ -152,7 +152,7 @@ let HelixHeadlessSpatialScatterChart = class HelixHeadlessSpatialScatterChart ex
152
152
  ${this.renderLive(points, at)}
153
153
  ${this.renderTooltip(points[at]?.row)}
154
154
  </div>
155
- ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
155
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
156
156
  }
157
157
  renderFallback(field, at) {
158
158
  const view = boxView(field.axes, this.camera, [], this.noBox);
@@ -82,6 +82,7 @@ declare const HelixHeadlessSpatialSurfaceChart_base: typeof HelixElement & (new
82
82
  * @csspart mark - The invisible point the tooltip is anchored to, where the renderer draws the reading.
83
83
  * @csspart plot - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
84
84
  * @csspart ring - The ring around the reading the pointer or the keys rest on, drawn over the surface.
85
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
85
86
  * @csspart summary - The measures, the number of readings, and the range of heights, written as a sentence for screen readers, which describes the plot. Hide it visually.
86
87
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
87
88
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
@@ -159,7 +159,7 @@ let HelixHeadlessSpatialSurfaceChart = class HelixHeadlessSpatialSurfaceChart ex
159
159
  ${readable ? this.renderLive(field, at) : nothing}
160
160
  ${this.renderTooltip(field.cells[at]?.row)}
161
161
  </div>
162
- ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
162
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
163
163
  }
164
164
  renderFallback(field, at) {
165
165
  const view = boxView(field.axes, this.camera, [], this.noBox);
@@ -86,6 +86,7 @@ declare const HelixHeadlessSpatialTreemap_base: typeof HelixElement & (new (...a
86
86
  * @csspart plot - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
87
87
  * @csspart ring - The outline around the block the pointer or the keys rest on, painted after it, so the blocks in front of it cover it.
88
88
  * @csspart ring-behind - The same outline, faint and dashed, drawn over every block, so the part of the block the pointer or the keys rest on that others hide still shows.
89
+ * @csspart sonify - The button that plays the chart as sound, under `sonify`.
89
90
  * @csspart summary - The number of items and groups and the range of their sizes, written as a sentence for screen readers, which describes the plot. Hide it visually.
90
91
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
91
92
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
@@ -152,7 +152,7 @@ let HelixHeadlessSpatialTreemap = class HelixHeadlessSpatialTreemap extends Mixi
152
152
  ${readable ? this.renderLive(field, at) : nothing}
153
153
  ${this.renderTooltip(field.blocks[at]?.row)}
154
154
  </div>
155
- ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
155
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
156
156
  }
157
157
  renderFallback(field, at) {
158
158
  const view = boxView(field.axes, this.camera, [], this.noBox);
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixFormElement, HostClickController } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, HostClickController, ListedController } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessSwitch_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerCheckedInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
@@ -58,6 +58,8 @@ declare const HelixHeadlessSwitch_base: (new (...args: any[]) => import("@fusion
58
58
  * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
59
59
  */
60
60
  export declare class HelixHeadlessSwitch extends HelixHeadlessSwitch_base {
61
+ /** On the page's list of controls, where a command palette and a shortcut overlay find it. */
62
+ protected readonly listed: ListedController;
61
63
  /**
62
64
  * The key this family's words are registered under, its styled tag:
63
65
  * `registerWords('de', { 'helix-switch': { valueMissingMessage: '...' } })`.
@@ -1,10 +1,11 @@
1
1
  import { __decorate } from "tslib";
2
- import { AriaForwardController, customElement, HelixFormElement, HostClickController, MixinControllerChecked, MixinField, markRegion, words, } from '@fusion.dev/helix-core';
2
+ import { AriaForwardController, customElement, HelixFormElement, HostClickController, ListedController, MixinControllerChecked, MixinField, markRegion, words, } from '@fusion.dev/helix-core';
3
3
  import { html, nothing } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
5
  let HelixHeadlessSwitch = class HelixHeadlessSwitch extends MixinControllerChecked(MixinField(HelixFormElement)) {
6
6
  constructor() {
7
7
  super(...arguments);
8
+ this.listed = new ListedController(this, 'press', () => this.internals?.ariaKeyShortcuts);
8
9
  this.aria = new AriaForwardController(this, {
9
10
  label: true,
10
11
  target: () => this.partNamed('control'),
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { HelixElement, ListedController } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessTab_base: (new (...args: any[]) => import("@fusion.dev/helix-core").DismissibleElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinSelectableItemInterface) & typeof HelixElement;
4
4
  /**
@@ -41,6 +41,8 @@ declare const HelixHeadlessTab_base: (new (...args: any[]) => import("@fusion.de
41
41
  * @interactive button
42
42
  */
43
43
  export declare class HelixHeadlessTab extends HelixHeadlessTab_base {
44
+ /** On the page's list of controls, where a command palette and a shortcut overlay find it. */
45
+ protected readonly listed: ListedController;
44
46
  /**
45
47
  * The key this family's words are registered under, its styled tag:
46
48
  * `registerWords('de', { 'helix-tab': { dismissLabel: 'Schließen' } })`.
@@ -1,11 +1,12 @@
1
1
  import { __decorate } from "tslib";
2
- import { customElement, glyphPath, HelixElement, MixinDismissible, MixinSelectableItem, plainClick, reflectElements, words, } from '@fusion.dev/helix-core';
2
+ import { customElement, glyphPath, HelixElement, ListedController, MixinDismissible, MixinSelectableItem, plainClick, reflectElements, words, } from '@fusion.dev/helix-core';
3
3
  import { x } from '@fusion.dev/helix-icons';
4
4
  import { html, nothing } from 'lit';
5
5
  import { property } from 'lit/decorators.js';
6
6
  let HelixHeadlessTab = class HelixHeadlessTab extends MixinDismissible(MixinSelectableItem(HelixElement)) {
7
7
  constructor() {
8
8
  super(...arguments);
9
+ this.listed = new ListedController(this, 'press', () => this.internals?.ariaKeyShortcuts);
9
10
  this.itemPart = 'tab';
10
11
  this.controls = undefined;
11
12
  this.dismissLabel = 'Close';
@@ -1,6 +1,6 @@
1
- import { AriaForwardController, HelixFormElement, type OwnRule } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, ListedController, type OwnRule } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
- declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
3
+ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusion.dev/helix-core").VoiceElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
5
5
  * @summary A multi-line text field.
6
6
  *
@@ -46,7 +46,9 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
46
46
  *
47
47
  * @event {Event} change - Sent from the textarea when the user finishes an edit.
48
48
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired as the text changes, and cancelable.
49
+ * @event {HelixBeforeListenEvent} helix-before-listen - The dictation button was pressed. Cancelable: cancel it to keep the field from listening.
49
50
  * @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
51
+ * @event {HelixListenEvent} helix-listen - Dictation settled on a phrase and put it in the value, after the field's own `input`.
50
52
  * @event {Event} input - Fired after the value changes.
51
53
  *
52
54
  * @csspart clear - The button that empties the field, under `clearable`.
@@ -55,11 +57,18 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
55
57
  * @csspart field - The box that holds the textarea.
56
58
  * @csspart hint - Help under the field, while nothing is wrong.
57
59
  * @csspart label - The label above the field.
60
+ * @csspart voice - The dictation button, under `voice`.
61
+ * @csspart voice-listening - The dictation button while it listens. Set alongside `voice`.
62
+ * @csspart voice-status - What a screen reader hears as dictation starts and stops, drawn for no one else.
58
63
  *
59
64
  * @cssstate blank - The field holds no text.
60
65
  * @cssstate invalid - The control has an error to show, from the browser's validation, `validate`, or your `feedback`.
66
+ * @cssstate listening - While the field listens for dictation. The element sets `--dictation-level` on itself meanwhile, from 0 to 1, for a meter.
61
67
  */
62
68
  export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
69
+ protected static markRules: string[];
70
+ /** On the page's list of controls, where a command palette and a shortcut overlay find it. */
71
+ protected readonly listed: ListedController;
63
72
  /**
64
73
  * The key this family's words are registered under, its styled tag:
65
74
  * `registerWords('de', { 'helix-textarea': { clearLabel: 'Leeren' } })`.
@@ -140,7 +149,8 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
140
149
  readOnly: boolean;
141
150
  /** Focus is delegated to the control, so the host is never the target. */
142
151
  static shadowRootOptions: ShadowRootInit;
143
- private get control();
152
+ /** The textarea, for a family built on this one to read. */
153
+ protected get control(): HTMLTextAreaElement | null;
144
154
  /**
145
155
  * Every keystroke, cancelable, and put back where it is refused.
146
156
  *
@@ -162,7 +172,7 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
162
172
  * Reset to `auto` first, because `scrollHeight` of an element already sized to
163
173
  * its content is that size, so without the reset the box can only ever grow.
164
174
  */
165
- private grow;
175
+ protected grow(): void;
166
176
  /**
167
177
  * Re-measures when the box changes rather than only when a property does.
168
178
  *
@@ -269,6 +279,12 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
269
279
  wrap: 'hard' | 'off' | 'soft';
270
280
  /** The cross on the clear button, behind a seam a tier can move. */
271
281
  protected renderClearIcon(): TemplateResult;
282
+ /**
283
+ * The `<textarea>` itself, behind a seam a family built on this one wraps:
284
+ * Code Input draws its highlighted text under it.
285
+ */
286
+ protected renderControl(): TemplateResult;
287
+ protected voiceField(): HTMLInputElement | HTMLTextAreaElement | null;
272
288
  protected render(): TemplateResult;
273
289
  }
274
290
  declare global {
@@ -1,12 +1,13 @@
1
1
  import { __decorate } from "tslib";
2
- import { AriaForwardController, applyOwnRule, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, numberConverter, restoreValue, words, } from '@fusion.dev/helix-core';
2
+ import { AriaForwardController, applyOwnRule, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, isRtl, ListedController, MixinControllerValue, MixinField, MixinVoice, numberConverter, restoreValue, SPOKEN_RULE, words, } from '@fusion.dev/helix-core';
3
3
  import { x } from '@fusion.dev/helix-icons';
4
4
  import { html, nothing } from 'lit';
5
5
  import { property } from 'lit/decorators.js';
6
6
  import { grownHeight, lineMetrics } from '../grow.js';
7
- let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerValue(MixinField(HelixFormElement)) {
7
+ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinVoice(MixinControllerValue(MixinField(HelixFormElement))) {
8
8
  constructor() {
9
9
  super(...arguments);
10
+ this.listed = new ListedController(this, 'focus', () => this.internals?.ariaKeyShortcuts);
10
11
  this.aria = new AriaForwardController(this, {
11
12
  target: () => this.partNamed('control'),
12
13
  });
@@ -44,6 +45,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
44
45
  this.rows = 3;
45
46
  this.wrap = 'soft';
46
47
  }
48
+ static { this.markRules = [SPOKEN_RULE]; }
47
49
  static { this.words = 'helix-textarea'; }
48
50
  static { this.shadowRootOptions = {
49
51
  ...HelixFormElement.shadowRootOptions,
@@ -186,30 +188,36 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
186
188
  renderClearIcon() {
187
189
  return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
188
190
  }
191
+ renderControl() {
192
+ return html `<textarea
193
+ part="control"
194
+ id=${this.controlId}
195
+ .value=${this.value}
196
+ rows=${this.rows}
197
+ cols=${this.cols ?? nothing}
198
+ wrap=${this.wrap}
199
+ name=${this.name || nothing}
200
+ placeholder=${this.placeholder || nothing}
201
+ autocomplete=${this.autocomplete || nothing}
202
+ minlength=${this.minLength ?? nothing}
203
+ maxlength=${this.maxLength ?? nothing}
204
+ aria-describedby=${this.describedBy ?? nothing}
205
+ aria-invalid=${this.invalidity ?? nothing}
206
+ ?disabled=${this.isDisabled}
207
+ ?readonly=${this.readOnly}
208
+ ?required=${this.required}
209
+ @input=${this.handleInput}
210
+ @change=${this.handleChange}
211
+ ></textarea>`;
212
+ }
213
+ voiceField() {
214
+ return this.partNamed('control');
215
+ }
189
216
  render() {
190
217
  return html `${this.renderLabel()}
191
218
  <div part="field">
192
219
  <slot name="start"></slot>
193
- <textarea
194
- part="control"
195
- id=${this.controlId}
196
- .value=${this.value}
197
- rows=${this.rows}
198
- cols=${this.cols ?? nothing}
199
- wrap=${this.wrap}
200
- name=${this.name || nothing}
201
- placeholder=${this.placeholder || nothing}
202
- autocomplete=${this.autocomplete || nothing}
203
- minlength=${this.minLength ?? nothing}
204
- maxlength=${this.maxLength ?? nothing}
205
- aria-describedby=${this.describedBy ?? nothing}
206
- aria-invalid=${this.invalidity ?? nothing}
207
- ?disabled=${this.isDisabled}
208
- ?readonly=${this.readOnly}
209
- ?required=${this.required}
210
- @input=${this.handleInput}
211
- @change=${this.handleChange}
212
- ></textarea>
220
+ ${this.renderControl()}
213
221
  <slot name="end"></slot>
214
222
  ${this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly
215
223
  ? html `<button
@@ -220,7 +228,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
220
228
  >
221
229
  ${this.renderClearIcon()}
222
230
  </button>`
223
- : nothing}
231
+ : nothing}${this.renderVoice()}
224
232
  </div>
225
233
  ${this.renderHint()}${this.renderFeedback()}`;
226
234
  }