@fusion.dev/helix-headless 0.155.0 → 0.156.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +1787 -92
- package/dist/canvas/node/canvas-node.js +1 -1
- package/dist/canvas/root/canvas-root.d.ts +17 -2
- package/dist/canvas/root/canvas-root.js +23 -6
- package/dist/chat/composer/chat-composer.js +1 -1
- package/dist/chat/root/chat-root.d.ts +14 -1
- package/dist/chat/root/chat-root.js +14 -4
- package/dist/combobox/root/combobox-root.d.ts +10 -0
- package/dist/combobox/root/combobox-root.js +18 -3
- package/dist/command/root/command-root.d.ts +116 -1
- package/dist/command/root/command-root.js +124 -15
- package/dist/data-table/root/data-table-root.d.ts +25 -0
- package/dist/data-table/root/data-table-root.js +13 -3
- package/dist/date-input/root/date-input-root.d.ts +32 -1
- package/dist/date-input/root/date-input-root.js +7 -1
- package/dist/element-events.generated.js +1 -0
- package/dist/graph/root/graph-root.d.ts +13 -0
- package/dist/graph/root/graph-root.js +16 -0
- package/dist/list/root/list-root.d.ts +74 -1
- package/dist/list/root/list-root.js +92 -4
- package/dist/list/selection.js +3 -3
- package/dist/map/root/map-root.d.ts +6 -0
- package/dist/map/root/map-root.js +8 -1
- package/dist/number-input/root/number-input-root.d.ts +63 -6
- package/dist/number-input/root/number-input-root.js +96 -11
- package/dist/object-inspector/root/object-inspector-root.d.ts +36 -1
- package/dist/object-inspector/root/object-inspector-root.js +26 -2
- package/dist/option/root/option-root.d.ts +32 -0
- package/dist/option/root/option-root.js +44 -5
- package/dist/outline/root/outline-root.d.ts +6 -2
- package/dist/outline/root/outline-root.js +7 -3
- package/dist/table/root/table-root.d.ts +20 -0
- package/dist/table/root/table-root.js +13 -3
- package/dist/time-input/root/time-input-root.d.ts +32 -1
- package/dist/time-input/root/time-input-root.js +9 -2
- package/dist/time-picker/root/time-picker-root.d.ts +63 -1
- package/dist/time-picker/root/time-picker-root.js +58 -7
- package/dist/timeline/item/timeline-item.d.ts +25 -0
- package/dist/timeline/item/timeline-item.js +27 -2
- package/dist/video/root/keys.d.ts +6 -2
- package/dist/video/root/keys.js +18 -1
- package/dist/video/root/video-root.d.ts +60 -1
- package/dist/video/root/video-root.js +70 -2
- package/dist/video-playlist/item/video-playlist-item.d.ts +15 -1
- package/dist/video-playlist/item/video-playlist-item.js +7 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +43 -4
- package/dist/video-playlist/root/video-playlist-root.js +69 -2
- package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
- package/dist/virtual-list/root/virtual-list-root.js +3 -0
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +2901 -232
- package/types/vue.d.ts +824 -58
- package/types/words.d.ts +110 -0
|
@@ -77,7 +77,7 @@ let HelixHeadlessCanvasNode = class HelixHeadlessCanvasNode extends HelixElement
|
|
|
77
77
|
aria-describedby=${this.status || this.meaning || this.links ? 'about' : nothing}
|
|
78
78
|
aria-label=${this.label || nothing}
|
|
79
79
|
aria-labelledby=${this.label || this.group ? nothing : 'content'}
|
|
80
|
-
aria-keyshortcuts=${this.connectable ? 'Enter F2 Space Shift+ArrowRight Shift+ArrowDown Alt+ArrowRight Alt+ArrowDown C Delete' : nothing}
|
|
80
|
+
aria-keyshortcuts=${this.connectable ? 'Enter F2 Space Control+ArrowDown Control+ArrowUp Shift+ArrowRight Shift+ArrowDown Alt+ArrowRight Alt+ArrowDown C Delete' : nothing}
|
|
81
81
|
role=${this.group ? 'group' : 'article'}
|
|
82
82
|
tabindex=${this.current ? '0' : '-1'}
|
|
83
83
|
>
|
|
@@ -46,6 +46,10 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
46
46
|
* - One card holds the board's tab stop.
|
|
47
47
|
* - The arrow keys move through the cards in reading order. The card you land
|
|
48
48
|
* on scrolls into view, and its connections are announced with it.
|
|
49
|
+
* - The card you land on is selected. Control or Command with an arrow key,
|
|
50
|
+
* Home, or End moves without changing the selection, and Space adds the card
|
|
51
|
+
* to the selection or takes it out, so you can select cards that are not
|
|
52
|
+
* next to each other.
|
|
49
53
|
* - Shift with an arrow key moves the card: by 16 units, or to the next
|
|
50
54
|
* multiple of `snap` where it is set. Alt with an arrow key resizes it from
|
|
51
55
|
* its bottom right corner the same way.
|
|
@@ -143,6 +147,8 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
143
147
|
* @csspart zoom-in - The button that zooms in.
|
|
144
148
|
* @csspart zoom-out - The button that zooms out.
|
|
145
149
|
*
|
|
150
|
+
* @cssproperty --canvas-zoom - The board's zoom, which the board writes: 1 at the document's own size, 0.5 at half. The cards inherit it, so a card's stylesheet can draw less when the board is far out, such as a border that stays one pixel wide at `calc(1px / var(--canvas-zoom))`.
|
|
151
|
+
*
|
|
146
152
|
* @cssstate connecting - While you draw a connection.
|
|
147
153
|
* @cssstate direct - The board moves without easing, while you drag it or while it settles into its box.
|
|
148
154
|
* @cssstate empty - While the document has no nodes.
|
|
@@ -271,14 +277,21 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
271
277
|
* units.
|
|
272
278
|
*/
|
|
273
279
|
resizedLabel: string;
|
|
280
|
+
/**
|
|
281
|
+
* What a screen reader calls the board in place of its role, after its
|
|
282
|
+
* name: "Release plan, canvas". Said only where the board has a name of its
|
|
283
|
+
* own, since the fallback name is the same word. Empty leaves the role.
|
|
284
|
+
*/
|
|
285
|
+
roleLabel: string;
|
|
274
286
|
/**
|
|
275
287
|
* The names of the selected cards and connections, comma separated. Empty
|
|
276
288
|
* when nothing is selected.
|
|
277
289
|
*
|
|
278
290
|
* **Comma separated, the way `Listbox` writes a multiple value**, so a page
|
|
279
291
|
* reads one selection the same way wherever it comes from. A click, or a key
|
|
280
|
-
* that lands on one, selects it alone;
|
|
281
|
-
*
|
|
292
|
+
* that lands on one, selects it alone; Command or Control with an arrow key
|
|
293
|
+
* moves without selecting; Shift, Command or Control with a click adds or
|
|
294
|
+
* takes one away, and so does Space; Command or Control with A
|
|
282
295
|
* selects everything; Shift and a drag across the empty board selects the
|
|
283
296
|
* cards the box touches. A click on the empty board, or Escape, selects
|
|
284
297
|
* nothing. Dragging, Shift with an arrow, and Delete act on the whole
|
|
@@ -882,6 +895,8 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
882
895
|
* asks for it and Escape stops.
|
|
883
896
|
*/
|
|
884
897
|
private readonly handleStopKeydown;
|
|
898
|
+
/** Whether the stop focus is moving to keeps the selection as it is. */
|
|
899
|
+
private walkingApart;
|
|
885
900
|
/**
|
|
886
901
|
* A stop a reader landed on becomes the tab stop, and a card comes into view.
|
|
887
902
|
*
|
|
@@ -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 { assignSlots, capturePointer, customElement, formatPercent, formatWords, HelixBeforeMoveEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixConnectEvent, HelixElement, HelixMoveEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixSelectEvent, hostAria, inlineStep, isRtl, MixinViewport, words, wordsConverter, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { assignSlots, auditName, capturePointer, customElement, formatPercent, formatWords, HelixBeforeMoveEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixConnectEvent, HelixElement, HelixMoveEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixSelectEvent, hostAria, inlineStep, isRtl, MixinViewport, words, wordsConverter, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { descriptionOf } from '../describe.js';
|
|
@@ -57,6 +57,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
57
57
|
this.readOnly = false;
|
|
58
58
|
this.reconnectingLabel = 'Moving the end of the connection from {from} to {to}. Move to a card and press Enter, or Escape to stop.';
|
|
59
59
|
this.resizedLabel = '{name} resized to {width} by {height}';
|
|
60
|
+
this.roleLabel = 'canvas';
|
|
60
61
|
this.selected = '';
|
|
61
62
|
this.selectedLabel = 'selected';
|
|
62
63
|
this.selectionLabel = '{count} selected';
|
|
@@ -592,8 +593,15 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
592
593
|
return;
|
|
593
594
|
}
|
|
594
595
|
event.preventDefault();
|
|
595
|
-
this.
|
|
596
|
+
this.walkingApart = event.ctrlKey || event.metaKey;
|
|
597
|
+
try {
|
|
598
|
+
this.focusStop(walk[to]?.key);
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
this.walkingApart = false;
|
|
602
|
+
}
|
|
596
603
|
};
|
|
604
|
+
this.walkingApart = false;
|
|
597
605
|
this.handleFocusIn = (event) => {
|
|
598
606
|
const edge = this.edgeOf(event);
|
|
599
607
|
const card = edge === undefined ? this.cardOf(event) : undefined;
|
|
@@ -603,7 +611,10 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
603
611
|
this.refocus = undefined;
|
|
604
612
|
this.focusedCard = card;
|
|
605
613
|
this.stop = edge ?? card?.name;
|
|
606
|
-
if (!this.pointing &&
|
|
614
|
+
if (!this.pointing &&
|
|
615
|
+
!this.walkingApart &&
|
|
616
|
+
this.stop !== undefined &&
|
|
617
|
+
!this.picked.includes(this.stop)) {
|
|
607
618
|
this.choose(this.stop);
|
|
608
619
|
}
|
|
609
620
|
if (card) {
|
|
@@ -1650,6 +1661,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
1650
1661
|
firstUpdated(changed) {
|
|
1651
1662
|
super.firstUpdated(changed);
|
|
1652
1663
|
this.finishHydration();
|
|
1664
|
+
auditName(this, () => null, 'Give it a `label` that says what the board holds, such as "Release plan".', () => this.label !== '');
|
|
1653
1665
|
if (this.waiting) {
|
|
1654
1666
|
this.measured = this.box;
|
|
1655
1667
|
this.view = this.waiting;
|
|
@@ -1941,7 +1953,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
1941
1953
|
d=${drawn.d}
|
|
1942
1954
|
data-edge=${key}
|
|
1943
1955
|
fill="none"
|
|
1944
|
-
aria-keyshortcuts="Enter F2 C Delete"
|
|
1956
|
+
aria-keyshortcuts="Enter F2 Space Control+ArrowDown Control+ArrowUp C Delete"
|
|
1945
1957
|
role="button"
|
|
1946
1958
|
tabindex=${key === stop ? '0' : '-1'}
|
|
1947
1959
|
/>
|
|
@@ -2050,7 +2062,8 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2050
2062
|
return html `<div
|
|
2051
2063
|
part="region"
|
|
2052
2064
|
role="group"
|
|
2053
|
-
aria-label=${this.label || this.fallbackLabel}
|
|
2065
|
+
aria-label=${this.label || this.getAttribute?.('aria-label') || this.fallbackLabel}
|
|
2066
|
+
aria-roledescription=${(this.label || this.getAttribute?.('aria-label')) && this.roleLabel ? this.roleLabel : nothing}
|
|
2054
2067
|
aria-keyshortcuts="ArrowUp ArrowDown ArrowLeft ArrowRight Plus Minus"
|
|
2055
2068
|
tabindex="0"
|
|
2056
2069
|
@focusin=${this.idle.stir}
|
|
@@ -2063,7 +2076,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2063
2076
|
</div>
|
|
2064
2077
|
<div
|
|
2065
2078
|
part="viewport"
|
|
2066
|
-
style=${`--_canvas-x: ${this.at.x}px; --_canvas-y: ${this.at.y}px; --_canvas-zoom: ${this.at.zoom}; --_canvas-rule-x: ${this.at.x - origin.x * this.at.zoom}px; --_canvas-rule-y: ${this.at.y - origin.y * this.at.zoom}px; --_canvas-rule-zoom: ${ruling(this.at.zoom)}`}
|
|
2079
|
+
style=${`--_canvas-x: ${this.at.x}px; --_canvas-y: ${this.at.y}px; --_canvas-zoom: ${this.at.zoom}; --canvas-zoom: ${this.at.zoom}; --_canvas-rule-x: ${this.at.x - origin.x * this.at.zoom}px; --_canvas-rule-y: ${this.at.y - origin.y * this.at.zoom}px; --_canvas-rule-zoom: ${ruling(this.at.zoom)}`}
|
|
2067
2080
|
@wheel=${this.handleWheel}
|
|
2068
2081
|
>
|
|
2069
2082
|
<!-- **One shape, whatever the board holds**, which is what hydration
|
|
@@ -2175,6 +2188,10 @@ __decorate([
|
|
|
2175
2188
|
words,
|
|
2176
2189
|
property({ attribute: 'resized-label' })
|
|
2177
2190
|
], HelixHeadlessCanvas.prototype, "resizedLabel", void 0);
|
|
2191
|
+
__decorate([
|
|
2192
|
+
words,
|
|
2193
|
+
property({ attribute: 'role-label' })
|
|
2194
|
+
], HelixHeadlessCanvas.prototype, "roleLabel", void 0);
|
|
2178
2195
|
__decorate([
|
|
2179
2196
|
property()
|
|
2180
2197
|
], HelixHeadlessCanvas.prototype, "selected", void 0);
|
|
@@ -105,7 +105,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
105
105
|
super.updated(changed);
|
|
106
106
|
this.markEmpty();
|
|
107
107
|
if (changed.has('value')) {
|
|
108
|
-
this.internals.setFormValue(this.value === '' ? null : this.value);
|
|
108
|
+
this.internals.setFormValue(this.value === '' ? null : this.value, this.value);
|
|
109
109
|
}
|
|
110
110
|
const field = this.partNamed('field');
|
|
111
111
|
if (field?.validity) {
|
|
@@ -40,7 +40,8 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
|
|
|
40
40
|
* The log is `aria-live="off"`. A separate status region announces each new
|
|
41
41
|
* turn once, when it has finished arriving. It skips the user's own turns
|
|
42
42
|
* unless they failed to send, turns loaded as history, and the turns already
|
|
43
|
-
* there when the transcript opened.
|
|
43
|
+
* there when the transcript opened. A turn is said with its author first, such
|
|
44
|
+
* as "Ada: It ships today" (`arrival-label`), where its author can be named.
|
|
44
45
|
*
|
|
45
46
|
* Pass the turns as children or as `turns` data. Do not use both. Children
|
|
46
47
|
* render from the server and suit most conversations. `turns` draws a long
|
|
@@ -145,6 +146,13 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
145
146
|
static shadowRootOptions: ShadowRootInit;
|
|
146
147
|
/** How tall a turn is assumed to be before it has been drawn, in pixels. */
|
|
147
148
|
static readonly ESTIMATE = 72;
|
|
149
|
+
/**
|
|
150
|
+
* What a screen reader hears when a turn arrives. `{author}` is who wrote
|
|
151
|
+
* it, as its name says it, and `{text}` its words. A turn with no author a
|
|
152
|
+
* reader can be told, a turn by `them` with no `author-label`, says its
|
|
153
|
+
* words alone.
|
|
154
|
+
*/
|
|
155
|
+
arrivalLabel: string;
|
|
148
156
|
/**
|
|
149
157
|
* Declarations for the turns from `turns` by each author, keyed by `author`,
|
|
150
158
|
* as JSON in the attribute: `{"ada": "--chat-message-background-color: …"}`.
|
|
@@ -430,6 +438,11 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
430
438
|
private announce;
|
|
431
439
|
/** The turns after the newest one already accounted for, by the rules above. */
|
|
432
440
|
private arrived;
|
|
441
|
+
/**
|
|
442
|
+
* What is said for a turn: a failed one with `failed-turn-label`, and
|
|
443
|
+
* another with `arrival-label` where its author can be named, so a reader
|
|
444
|
+
* in a conversation of several knows who spoke.
|
|
445
|
+
*/
|
|
433
446
|
private phrase;
|
|
434
447
|
/** Watches what is drawn for size, re-observing only when that changed. */
|
|
435
448
|
private watchSizes;
|
|
@@ -11,7 +11,7 @@ import { property, state } from 'lit/decorators.js';
|
|
|
11
11
|
import { keyed } from 'lit/directives/keyed.js';
|
|
12
12
|
import { repeat } from 'lit/directives/repeat.js';
|
|
13
13
|
import { html } from 'lit/static-html.js';
|
|
14
|
-
import { TURN_WORDS } from '../naming.js';
|
|
14
|
+
import { authorWords, TURN_WORDS } from '../naming.js';
|
|
15
15
|
const REGIONS = ['header', 'actions', 'resume', 'status', 'composer'];
|
|
16
16
|
const SECOND_NAMES = {
|
|
17
17
|
author: {
|
|
@@ -40,6 +40,7 @@ const drawnInFull = ({ element }) => element.firstElementChild?.hasUpdated !== f
|
|
|
40
40
|
let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixElement) {
|
|
41
41
|
constructor() {
|
|
42
42
|
super(...arguments);
|
|
43
|
+
this.arrivalLabel = '{author}: {text}';
|
|
43
44
|
this.authorStyles = {};
|
|
44
45
|
this.defaultPinned = true;
|
|
45
46
|
this.failedLabel = 'Not sent';
|
|
@@ -180,6 +181,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
180
181
|
if (this.fed) {
|
|
181
182
|
return this.turns.map((turn) => ({
|
|
182
183
|
author: turn.author,
|
|
184
|
+
by: authorWords(turn.author, turn.authorLabel ?? '', this.words),
|
|
183
185
|
failed: this.failedLabel,
|
|
184
186
|
id: this.identity(turn),
|
|
185
187
|
state: turn.state,
|
|
@@ -188,6 +190,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
188
190
|
}
|
|
189
191
|
return this.messages.map((message) => ({
|
|
190
192
|
author: message.author,
|
|
193
|
+
by: authorWords(message.author, message.authorLabel, this.words),
|
|
191
194
|
failed: message.failedLabel,
|
|
192
195
|
id: message,
|
|
193
196
|
state: message.state,
|
|
@@ -349,9 +352,12 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
349
352
|
return at < 0 ? [] : turns.slice(at + 1);
|
|
350
353
|
}
|
|
351
354
|
phrase(turn) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
+
if (turn.state === 'failed') {
|
|
356
|
+
return formatWords(this, 'failedTurnLabel', { failed: turn.failed, text: turn.text });
|
|
357
|
+
}
|
|
358
|
+
return turn.by === ''
|
|
359
|
+
? turn.text
|
|
360
|
+
: formatWords(this, 'arrivalLabel', { author: turn.by, text: turn.text });
|
|
355
361
|
}
|
|
356
362
|
watchSizes() {
|
|
357
363
|
if (typeof ResizeObserver === 'undefined') {
|
|
@@ -509,6 +515,10 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
509
515
|
</div>`;
|
|
510
516
|
}
|
|
511
517
|
};
|
|
518
|
+
__decorate([
|
|
519
|
+
words,
|
|
520
|
+
property({ attribute: 'arrival-label' })
|
|
521
|
+
], HelixHeadlessChat.prototype, "arrivalLabel", void 0);
|
|
512
522
|
__decorate([
|
|
513
523
|
property({ attribute: 'author-styles', converter: wordsConverter })
|
|
514
524
|
], HelixHeadlessChat.prototype, "authorStyles", void 0);
|
|
@@ -198,6 +198,16 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
198
198
|
* every keystroke. See Listbox's `filter-delay`.
|
|
199
199
|
*/
|
|
200
200
|
filterDelay: number;
|
|
201
|
+
/**
|
|
202
|
+
* What the form receives for each chosen option: `key`, its `value`, or
|
|
203
|
+
* `text`, its words, as React Aria's ComboBox `formValue` offers.
|
|
204
|
+
*
|
|
205
|
+
* `text` is for a server that wants what the reader saw, such as a city's
|
|
206
|
+
* name. A value no option carries, typed with `allows-custom-value` or made
|
|
207
|
+
* with `creatable`, is sent as it is either way. Absent is `key`, and the
|
|
208
|
+
* host carries no attribute for it.
|
|
209
|
+
*/
|
|
210
|
+
formValue?: 'key' | 'text';
|
|
201
211
|
/**
|
|
202
212
|
* The list's name, read by screen readers. The list has no visible name.
|
|
203
213
|
*/
|
|
@@ -453,11 +453,23 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
453
453
|
return this.partNamed('control') ?? super.focusTarget;
|
|
454
454
|
}
|
|
455
455
|
sync() {
|
|
456
|
-
|
|
457
|
-
|
|
456
|
+
const values = this.multiple ? this.values : this.value === '' ? [] : [this.value];
|
|
457
|
+
const words = this.formValue === 'text'
|
|
458
|
+
? new Map(optionsAmong(this.knownChildren).map((option) => [option.value, option.text]))
|
|
459
|
+
: undefined;
|
|
460
|
+
const answers = words ? values.map((value) => words.get(value) ?? value) : values;
|
|
461
|
+
if (!this.multiple) {
|
|
462
|
+
this.internals.setFormValue(answers[0] ?? null, this.value);
|
|
463
|
+
}
|
|
464
|
+
else if (words && this.name !== '' && answers.length > 0) {
|
|
465
|
+
const data = new FormData();
|
|
466
|
+
for (const answer of answers) {
|
|
467
|
+
data.append(this.name, answer);
|
|
468
|
+
}
|
|
469
|
+
this.internals.setFormValue(data, this.value);
|
|
458
470
|
}
|
|
459
471
|
else {
|
|
460
|
-
this.internals
|
|
472
|
+
reportValues(this.internals, this.name, answers);
|
|
461
473
|
}
|
|
462
474
|
this.reportRequired(this.value === '', this.valueMissingMessage, 'control');
|
|
463
475
|
}
|
|
@@ -677,6 +689,9 @@ __decorate([
|
|
|
677
689
|
__decorate([
|
|
678
690
|
property({ attribute: 'filter-delay', type: Number })
|
|
679
691
|
], HelixHeadlessCombobox.prototype, "filterDelay", void 0);
|
|
692
|
+
__decorate([
|
|
693
|
+
property({ attribute: 'form-value' })
|
|
694
|
+
], HelixHeadlessCombobox.prototype, "formValue", void 0);
|
|
680
695
|
__decorate([
|
|
681
696
|
words,
|
|
682
697
|
property({ attribute: 'list-label' })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { type DataOptionItem, HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../dialog/index.js';
|
|
4
4
|
import '../../listbox/index.js';
|
|
@@ -22,10 +22,51 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
22
22
|
* palette is open. `mod` means Command on a Mac and Control elsewhere, so you
|
|
23
23
|
* write one binding.
|
|
24
24
|
*
|
|
25
|
+
* With `inline`, the palette is drawn in the page with no dialog, as a search
|
|
26
|
+
* over a list that is always shown.
|
|
27
|
+
*
|
|
28
|
+
* **Pages.** A command can open a page of commands of its own, as a palette
|
|
29
|
+
* offers "Change theme" and then the themes. Give the option `opens` with a
|
|
30
|
+
* name, and slot the page's commands as `page:` and that name:
|
|
31
|
+
*
|
|
32
|
+
* ```html
|
|
33
|
+
* <helix-command>
|
|
34
|
+
* <helix-option value="theme" opens="theme">Change theme</helix-option>
|
|
35
|
+
* <helix-option value="dark" slot="page:theme">Dark</helix-option>
|
|
36
|
+
* <helix-option value="light" slot="page:theme">Light</helix-option>
|
|
37
|
+
* </helix-command>
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* Choosing the opener says `helix-select` with its value, and canceling it
|
|
41
|
+
* keeps the page that is shown. The list then shows the page, named by the
|
|
42
|
+
* opener's words, and Backspace in an empty filter goes back. `page` says
|
|
43
|
+
* which page is shown.
|
|
44
|
+
*
|
|
25
45
|
* The filter is cleared when the palette closes, so each opening starts from
|
|
26
46
|
* the full list. Combobox and Select do the same. As typing narrows the list, a
|
|
27
47
|
* screen reader hears how many commands are left, such as "3 commands".
|
|
28
48
|
*
|
|
49
|
+
* **Keyboard.** Focus goes to the filter field as the palette opens, and the
|
|
50
|
+
* highlight moves through the commands while you type.
|
|
51
|
+
*
|
|
52
|
+
* | Key | What it does |
|
|
53
|
+
* |---|---|
|
|
54
|
+
* | `hotkey`, Command or Control with K by default | Opens the palette from anywhere on the page, or closes it. With `inline`, puts focus in its filter field. |
|
|
55
|
+
* | Down, Up | Moves the highlight to the next or the previous command, round from the last to the first. |
|
|
56
|
+
* | Page Down, Page Up | Moves the highlight by as many commands as the list shows at once. |
|
|
57
|
+
* | Home, End, Left, Right | Move the caret in the filter field. |
|
|
58
|
+
* | Enter | Runs the highlighted command, or follows it where it is a link, and closes the palette. On a command with `opens`, shows its page. |
|
|
59
|
+
* | Command or Control with Enter | Takes the highlighted command's `alternate` destination, where it has one. |
|
|
60
|
+
* | Backspace | In an empty filter on a page, goes back to the page before. |
|
|
61
|
+
* | Escape | Clears the filter. With the filter empty, closes the palette and puts focus back where it was. |
|
|
62
|
+
* | Tab | Stays in the palette, which is modal. With `inline`, moves on through the page. |
|
|
63
|
+
*
|
|
64
|
+
* **ARIA.** The palette is a modal `dialog` named by `label`, or with
|
|
65
|
+
* `inline` no dialog at all. Inside it the filter field is a `combobox` with
|
|
66
|
+
* `aria-activedescendant` on the highlighted command, and the commands are a
|
|
67
|
+
* `listbox` of `option`s. How many commands typing left, the empty text and
|
|
68
|
+
* the loading text are said in a polite live region.
|
|
69
|
+
*
|
|
29
70
|
* @since 0.1.0
|
|
30
71
|
* @status stable
|
|
31
72
|
* @category navigation
|
|
@@ -35,7 +76,9 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
35
76
|
* @slot - The commands, and any groups of them.
|
|
36
77
|
* @slot empty - What the palette shows when typing matches no commands, for content richer than `empty-text`.
|
|
37
78
|
* @slot error - What the palette shows while `load-error` is set, for content richer than its text.
|
|
79
|
+
* @slot filter-icon - Replaces the magnifying glass in the filter field.
|
|
38
80
|
* @slot footer - Content under the list, such as the keys that work in the palette.
|
|
81
|
+
* @slot header - Content above the filter field, such as a title or the scope being searched. A heading here does not rename the palette, which `label` names.
|
|
39
82
|
* @slot loading - What the palette shows while the commands load, for content richer than `loading-text`.
|
|
40
83
|
* @slot no-results - What the palette shows when typing matches no commands, for content richer than `no-results-text`.
|
|
41
84
|
*
|
|
@@ -67,6 +110,10 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
67
110
|
* @csspart listbox-search - The region that holds the filter field.
|
|
68
111
|
* @csspart listbox-search-field - The filter field.
|
|
69
112
|
*
|
|
113
|
+
* @cssstate empty - The list shows no commands: there are none, or typing hid every one.
|
|
114
|
+
* @cssstate error - While `load-error` is set.
|
|
115
|
+
* @cssstate loading - While `loading` is set.
|
|
116
|
+
* @cssstate no-results - Typing hid every one of the commands, where there are some.
|
|
70
117
|
* @cssstate open - While the palette is open.
|
|
71
118
|
*/
|
|
72
119
|
export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
@@ -95,6 +142,26 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
95
142
|
* with: one key, said once.
|
|
96
143
|
*/
|
|
97
144
|
hotkey: string;
|
|
145
|
+
/**
|
|
146
|
+
* Draws the palette in the page, where it stands, with no dialog: a search
|
|
147
|
+
* over a list of commands, as React Aria's Autocomplete is drawn.
|
|
148
|
+
*
|
|
149
|
+
* The list is always shown, and `hotkey` puts focus in its filter field.
|
|
150
|
+
* Choosing a command says `helix-select` and leaves the list where it is,
|
|
151
|
+
* and `open`, `toggle` and the dialog's parts do nothing.
|
|
152
|
+
*/
|
|
153
|
+
inline: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* The commands as data, for a palette too long to draw whole: each a value,
|
|
156
|
+
* which is also its words, or a record with a `value`, a `label` and
|
|
157
|
+
* `disabled`. Handed to the list, which draws only the commands in sight and
|
|
158
|
+
* reads no children while it is set. See Listbox's `items`.
|
|
159
|
+
*
|
|
160
|
+
* Choosing one says `helix-select` with its value, as a child option does.
|
|
161
|
+
* A command that is a link, or that has an `alternate`, is an option you
|
|
162
|
+
* write as a child.
|
|
163
|
+
*/
|
|
164
|
+
items: readonly DataOptionItem[] | undefined;
|
|
98
165
|
/**
|
|
99
166
|
* Accessible name for the overlay, which has no visible heading.
|
|
100
167
|
*/
|
|
@@ -122,6 +189,19 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
122
189
|
* some. Absent, `empty-text` shows. See Listbox's `no-results-text`.
|
|
123
190
|
*/
|
|
124
191
|
noResultsText?: string;
|
|
192
|
+
/**
|
|
193
|
+
* The page of commands shown, by name: empty for the commands you write
|
|
194
|
+
* with no `slot`, or a name for the commands slotted as `page:` and the
|
|
195
|
+
* name, such as `slot="page:theme"`.
|
|
196
|
+
*
|
|
197
|
+
* An option with `opens` set to a name goes to that page when it is chosen,
|
|
198
|
+
* in place of running, and Backspace in an empty filter goes back to the
|
|
199
|
+
* page before. Closing the palette goes back to the first page. Set it to
|
|
200
|
+
* open the palette on a page of your choosing.
|
|
201
|
+
*/
|
|
202
|
+
page: string;
|
|
203
|
+
/** The pages gone through to reach this one, which Backspace walks back. */
|
|
204
|
+
private trail;
|
|
125
205
|
/**
|
|
126
206
|
* The text shown in the filter field before anything is typed.
|
|
127
207
|
*/
|
|
@@ -169,6 +249,8 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
169
249
|
* palette the reader never sees.
|
|
170
250
|
*/
|
|
171
251
|
private readonly handleHotkey;
|
|
252
|
+
/** The list's filter field, where a reader types. */
|
|
253
|
+
private get searchField();
|
|
172
254
|
/**
|
|
173
255
|
* Takes what the list announced and says it under this element's name.
|
|
174
256
|
*
|
|
@@ -183,6 +265,18 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
183
265
|
* could not be run again: Enter did nothing and the palette stayed open.
|
|
184
266
|
*/
|
|
185
267
|
private readonly handleChange;
|
|
268
|
+
/**
|
|
269
|
+
* Shows a page of commands, with the trail that leads back from it. The
|
|
270
|
+
* filter starts empty, as it does on opening, and the field keeps focus.
|
|
271
|
+
*/
|
|
272
|
+
private goTo;
|
|
273
|
+
/** Backspace in an empty filter, on a page, goes back to the page before. */
|
|
274
|
+
private readonly handleBack;
|
|
275
|
+
/**
|
|
276
|
+
* The list's name: the palette's, or on a page, the words of the command
|
|
277
|
+
* that opens it.
|
|
278
|
+
*/
|
|
279
|
+
private get listName();
|
|
186
280
|
/**
|
|
187
281
|
* The row under the keyboard, read from the `active` property the listbox
|
|
188
282
|
* writes on each option.
|
|
@@ -217,6 +311,20 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
217
311
|
disconnectedCallback(): void;
|
|
218
312
|
protected willUpdate(changed: PropertyValues): void;
|
|
219
313
|
protected firstUpdated(changed: PropertyValues): void;
|
|
314
|
+
/**
|
|
315
|
+
* The list's loading, error and empty states on the host too, so a page
|
|
316
|
+
* styles the whole palette from them, as Combobox does. Read once the list
|
|
317
|
+
* has drawn its filter, since `hidden` is what the filter writes onto a
|
|
318
|
+
* command.
|
|
319
|
+
*/
|
|
320
|
+
private markStatus;
|
|
321
|
+
/**
|
|
322
|
+
* After the list has filtered on what was typed. The event comes before the
|
|
323
|
+
* filter is applied, so the list's update is waited for a turn later.
|
|
324
|
+
*/
|
|
325
|
+
private readonly handleFilter;
|
|
326
|
+
/** Whether a child fills the named slot, so it is handed on to the dialog or the list. */
|
|
327
|
+
private fills;
|
|
220
328
|
protected updated(changed: PropertyValues): void;
|
|
221
329
|
/**
|
|
222
330
|
* The surface the palette opens in, behind a seam the styled tier can move.
|
|
@@ -229,6 +337,13 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
229
337
|
* from the same template.
|
|
230
338
|
*/
|
|
231
339
|
protected renderDialog(): TemplateResult;
|
|
340
|
+
/**
|
|
341
|
+
* The filterable list of commands, which the dialog holds, or the page with
|
|
342
|
+
* `inline`, behind a seam the styled tier can move.
|
|
343
|
+
*/
|
|
344
|
+
protected renderListbox(): TemplateResult;
|
|
345
|
+
/** The row under the list, in the dialog's footer or, `inline`, under the list itself. */
|
|
346
|
+
private renderFooter;
|
|
232
347
|
protected render(): TemplateResult;
|
|
233
348
|
}
|
|
234
349
|
declare global {
|