@fusion.dev/helix-headless 0.170.0 → 0.172.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 +1045 -154
- package/dist/chat/composer/chat-composer.d.ts +30 -2
- package/dist/chat/composer/chat-composer.js +29 -4
- package/dist/chat/root/chat-root.d.ts +73 -7
- package/dist/chat/root/chat-root.js +90 -5
- package/dist/code-block/root/code-block-root.d.ts +20 -2
- package/dist/code-block/root/code-block-root.js +29 -8
- package/dist/color-input/root/color-input-root.d.ts +15 -1
- package/dist/color-input/root/color-input-root.js +19 -1
- package/dist/color-picker/root/color-picker-root.d.ts +54 -7
- package/dist/color-picker/root/color-picker-root.js +62 -1
- package/dist/combobox/root/combobox-root.d.ts +16 -6
- package/dist/combobox/root/combobox-root.js +9 -2
- package/dist/command/root/command-root.d.ts +16 -1
- package/dist/command/root/command-root.js +8 -0
- package/dist/data-table/root/data-table-root.d.ts +5 -1
- package/dist/data-table/root/data-table-root.js +1 -0
- package/dist/dialog/root/dialog-root.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +4 -0
- package/dist/drawer/root/drawer-root.js +1 -0
- package/dist/element-events.generated.js +1 -0
- package/dist/filter-input/chip/filter-chip.d.ts +13 -3
- package/dist/filter-input/chip/filter-chip.js +5 -0
- package/dist/filter-input/menu/filter-menu.js +0 -1
- package/dist/filter-input/root/filter-input-root.d.ts +71 -4
- package/dist/filter-input/root/filter-input-root.js +87 -2
- package/dist/format-date/root/format-date-root.d.ts +3 -1
- package/dist/input/root/input-root.d.ts +6 -0
- package/dist/input/root/input-root.js +13 -0
- package/dist/lightbox/root/lightbox-root.js +1 -0
- package/dist/listbox/root/listbox-root.d.ts +8 -1
- package/dist/listbox/root/listbox-root.js +9 -3
- package/dist/menu/item/menu-item.d.ts +2 -0
- package/dist/menu/item/menu-item.js +2 -0
- package/dist/menu/root/menu-root.d.ts +73 -12
- package/dist/menu/root/menu-root.js +103 -7
- package/dist/number-input/root/number-input-root.d.ts +16 -6
- package/dist/number-input/root/number-input-root.js +15 -4
- package/dist/popover/root/popover-root.d.ts +59 -6
- package/dist/popover/root/popover-root.js +51 -3
- package/dist/select/root/select-root.d.ts +2 -2
- package/dist/select/root/select-root.js +4 -2
- package/dist/steps/root/steps-root.d.ts +32 -8
- package/dist/steps/root/steps-root.js +70 -9
- package/dist/steps/step/step.d.ts +9 -1
- package/dist/steps/step/step.js +5 -0
- package/dist/textarea/root/textarea-root.d.ts +8 -0
- package/dist/textarea/root/textarea-root.js +16 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +19 -1
- package/dist/video-playlist/root/video-playlist-root.js +14 -3
- package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
- package/dist/virtual-list/root/virtual-list-root.js +7 -2
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +800 -144
- package/types/vue.d.ts +165 -31
- package/types/words.d.ts +20 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { CommandController, HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import type { HelixHeadlessStep } from '../step/step.js';
|
|
4
4
|
declare const HelixHeadlessSteps_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & typeof HelixElement;
|
|
@@ -14,12 +14,21 @@ declare const HelixHeadlessSteps_base: (new (...args: any[]) => import("@fusion.
|
|
|
14
14
|
* sequence is correct in the server's markup, where the root cannot count its
|
|
15
15
|
* slotted children.
|
|
16
16
|
*
|
|
17
|
+
* `active` names a step by its `value`. A step with no `value` is named by its
|
|
18
|
+
* place, counted from 1, so a sequence written without values moves as one
|
|
19
|
+
* written with them, and `helix-activate` says `1`, `2` and so on. Give every
|
|
20
|
+
* step a value or none, because a value of `2` and the second place are one
|
|
21
|
+
* name.
|
|
22
|
+
*
|
|
17
23
|
* `linear` keeps a reader from passing a stage they have not finished, as Ark
|
|
18
24
|
* UI's linear steps do: a stage ahead is out of reach, and its button disabled,
|
|
19
25
|
* until every stage before it is `complete` or `optional`. Going back is always
|
|
20
26
|
* allowed. `next()` and `previous()` move one stage either way, under the
|
|
21
27
|
* same rules, for your own buttons. `reset()` goes back to the first stage and
|
|
22
|
-
* clears every stage's `complete`, as Ark UI's `resetStep` does.
|
|
28
|
+
* clears every stage's `complete`, as Ark UI's `resetStep` does. A button
|
|
29
|
+
* anywhere on the page calls them with no script, as Ark UI's previous and
|
|
30
|
+
* next triggers do: give it `commandfor` with the sequence's `id`, and
|
|
31
|
+
* `command="--previous"`, `"--next"` or `"--reset"`.
|
|
23
32
|
*
|
|
24
33
|
* `completion="passed"` marks a stage `complete` once a reader moves past it,
|
|
25
34
|
* as Ark UI counts a stage before the current one as completed. Going back
|
|
@@ -46,11 +55,13 @@ declare const HelixHeadlessSteps_base: (new (...args: any[]) => import("@fusion.
|
|
|
46
55
|
* button is described by its `complete-label`.
|
|
47
56
|
*
|
|
48
57
|
* **Keyboard.** Each stage's name is a button and its own tab stop, and Enter
|
|
49
|
-
* or Space goes to the stage. Set `arrows` and the
|
|
50
|
-
*
|
|
51
|
-
* Up and Down a column, Home and End to the first and the last.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
58
|
+
* or Space goes to the stage. Set `arrows` and the names are one tab stop, the
|
|
59
|
+
* current stage's, and the arrow keys move focus between them: Left and Right
|
|
60
|
+
* along a row, Up and Down a column, Home and End to the first and the last.
|
|
61
|
+
* The stop follows focus, so Tab leaves the sequence and Shift+Tab comes back
|
|
62
|
+
* to the name the reader left. The arrows pass over a disabled stage and one a
|
|
63
|
+
* linear sequence holds out of reach, and they move focus without going to the
|
|
64
|
+
* stage.
|
|
54
65
|
*
|
|
55
66
|
* @since 0.1.0
|
|
56
67
|
* @status stable
|
|
@@ -78,7 +89,8 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
|
78
89
|
static words: string;
|
|
79
90
|
/**
|
|
80
91
|
* Whether the arrow keys move focus between the stages' names, along the
|
|
81
|
-
* way the steps run. Off by default, so the
|
|
92
|
+
* way the steps run, and the names are one tab stop. Off by default, so the
|
|
93
|
+
* arrows keep scrolling the page.
|
|
82
94
|
*/
|
|
83
95
|
arrows: boolean;
|
|
84
96
|
/**
|
|
@@ -148,6 +160,7 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
|
148
160
|
*/
|
|
149
161
|
variant?: 'numeric';
|
|
150
162
|
protected readonly keys: KeyboardNavigationController;
|
|
163
|
+
protected readonly commands: CommandController;
|
|
151
164
|
/** The steps, in the order the consumer wrote them. */
|
|
152
165
|
private get steps();
|
|
153
166
|
/**
|
|
@@ -158,6 +171,8 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
|
158
171
|
* calls to `next()` in one task then move two stages.
|
|
159
172
|
*/
|
|
160
173
|
private get at();
|
|
174
|
+
/** The step `active` names, by `value` or by place, or the first enabled one. */
|
|
175
|
+
private named;
|
|
161
176
|
/**
|
|
162
177
|
* Requests a new current stage, and returns whether it moved.
|
|
163
178
|
*
|
|
@@ -207,6 +222,15 @@ export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
|
207
222
|
*/
|
|
208
223
|
private sync;
|
|
209
224
|
private readonly handleSelection;
|
|
225
|
+
/** The step whose name holds the sequence's one tab stop under `arrows`, once focus has chosen it. */
|
|
226
|
+
private holder;
|
|
227
|
+
/**
|
|
228
|
+
* Gives the one tab stop to a name, under `arrows`: the one focus last left,
|
|
229
|
+
* else the current stage's, else the first a reader can go to. Without
|
|
230
|
+
* `arrows` every name is its own stop. Client only.
|
|
231
|
+
*/
|
|
232
|
+
private rove;
|
|
233
|
+
private readonly handleFocus;
|
|
210
234
|
private readonly handleSlotChange;
|
|
211
235
|
connectedCallback(): void;
|
|
212
236
|
disconnectedCallback(): void;
|
|
@@ -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, focusItemOf, formatWords, HelixElement, hostAria, KeyboardNavigationController, MixinControllerActive, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { CommandController, customElement, focusItemOf, formatWords, HelixElement, hostAria, KeyboardNavigationController, MixinControllerActive, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { canReach, progressOf } from '../reach.js';
|
|
@@ -19,6 +19,7 @@ const navigable = (step) => {
|
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
|
+
const keyOf = (step, index) => step.value || String(index + 1);
|
|
22
23
|
let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(HelixElement) {
|
|
23
24
|
constructor() {
|
|
24
25
|
super(...arguments);
|
|
@@ -33,6 +34,13 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
33
34
|
selects: () => false,
|
|
34
35
|
wrap: false,
|
|
35
36
|
});
|
|
37
|
+
this.commands = new CommandController(this, {
|
|
38
|
+
commands: {
|
|
39
|
+
'--next': () => this.next(),
|
|
40
|
+
'--previous': () => this.previous(),
|
|
41
|
+
'--reset': () => this.reset(),
|
|
42
|
+
},
|
|
43
|
+
});
|
|
36
44
|
this.handleSelection = (event) => {
|
|
37
45
|
if (!this.steps.includes(event.target)) {
|
|
38
46
|
return;
|
|
@@ -42,12 +50,27 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
42
50
|
if (event.defaultPrevented || !this.reachable(index) || !this.valid(index)) {
|
|
43
51
|
return;
|
|
44
52
|
}
|
|
45
|
-
this.requestActive(
|
|
53
|
+
this.requestActive(keyOf(this.steps[index], index));
|
|
46
54
|
});
|
|
47
55
|
};
|
|
56
|
+
this.handleFocus = (event) => {
|
|
57
|
+
if (!this.arrows) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const path = event.composedPath();
|
|
61
|
+
const step = this.steps.find((one) => {
|
|
62
|
+
const name = one.shadowRoot?.querySelector('[part="label"]');
|
|
63
|
+
return name ? path.includes(name) : false;
|
|
64
|
+
});
|
|
65
|
+
if (step && step !== this.holder) {
|
|
66
|
+
this.holder = step;
|
|
67
|
+
this.rove();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
48
70
|
this.handleSlotChange = () => {
|
|
49
71
|
this.sync();
|
|
50
72
|
this.lock();
|
|
73
|
+
this.rove();
|
|
51
74
|
};
|
|
52
75
|
}
|
|
53
76
|
static { this.words = 'helix-steps'; }
|
|
@@ -62,11 +85,20 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
62
85
|
if (this.active === '') {
|
|
63
86
|
return steps.findIndex((step) => step.current);
|
|
64
87
|
}
|
|
65
|
-
const item = this.
|
|
88
|
+
const item = this.named(steps);
|
|
66
89
|
return item ? steps.indexOf(item) : -1;
|
|
67
90
|
}
|
|
91
|
+
named(steps) {
|
|
92
|
+
const keyed = steps.map((step, index) => ({
|
|
93
|
+
disabled: step.disabled,
|
|
94
|
+
step,
|
|
95
|
+
value: keyOf(step, index),
|
|
96
|
+
}));
|
|
97
|
+
return this.resolveActive(keyed)?.step;
|
|
98
|
+
}
|
|
68
99
|
requestActive(next, cause) {
|
|
69
|
-
const
|
|
100
|
+
const at = this.active === '' ? this.at : -1;
|
|
101
|
+
const here = at === -1 ? undefined : keyOf(this.steps[at], at);
|
|
70
102
|
if (here) {
|
|
71
103
|
this.active = here;
|
|
72
104
|
}
|
|
@@ -80,8 +112,8 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
80
112
|
}
|
|
81
113
|
reset() {
|
|
82
114
|
const steps = this.steps;
|
|
83
|
-
const first = steps.
|
|
84
|
-
if (
|
|
115
|
+
const first = steps.findIndex((one) => !one.disabled);
|
|
116
|
+
if (first === -1 || !this.requestActive(keyOf(steps[first], first))) {
|
|
85
117
|
return false;
|
|
86
118
|
}
|
|
87
119
|
for (const one of steps) {
|
|
@@ -100,7 +132,7 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
100
132
|
if (!target || !this.reachable(index) || !this.valid(index)) {
|
|
101
133
|
return false;
|
|
102
134
|
}
|
|
103
|
-
return this.requestActive(target
|
|
135
|
+
return this.requestActive(keyOf(target, index));
|
|
104
136
|
}
|
|
105
137
|
valid(index) {
|
|
106
138
|
const from = this.steps[this.at];
|
|
@@ -128,8 +160,8 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
128
160
|
return;
|
|
129
161
|
}
|
|
130
162
|
const steps = this.steps;
|
|
131
|
-
const
|
|
132
|
-
const reached = steps.
|
|
163
|
+
const named = this.named(steps);
|
|
164
|
+
const reached = named ? steps.indexOf(named) : -1;
|
|
133
165
|
steps.forEach((step, index) => {
|
|
134
166
|
const current = index === reached;
|
|
135
167
|
const passed = this.completion === 'passed' && index < reached;
|
|
@@ -141,11 +173,30 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
141
173
|
});
|
|
142
174
|
});
|
|
143
175
|
}
|
|
176
|
+
rove() {
|
|
177
|
+
const steps = this.steps;
|
|
178
|
+
const open = steps.filter((step, index) => !step.disabled && this.reachable(index));
|
|
179
|
+
const current = steps[this.at];
|
|
180
|
+
const holder = !this.arrows
|
|
181
|
+
? undefined
|
|
182
|
+
: this.holder && open.includes(this.holder)
|
|
183
|
+
? this.holder
|
|
184
|
+
: current && open.includes(current)
|
|
185
|
+
? current
|
|
186
|
+
: open[0];
|
|
187
|
+
for (const step of steps) {
|
|
188
|
+
whenHydrated(step, () => {
|
|
189
|
+
step.stop = !this.arrows || step === holder;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
144
193
|
connectedCallback() {
|
|
145
194
|
super.connectedCallback();
|
|
146
195
|
this.addEventListener('helix-select', this.handleSelection);
|
|
196
|
+
this.addEventListener('focusin', this.handleFocus);
|
|
147
197
|
this.watcher = new MutationObserver(() => {
|
|
148
198
|
this.lock();
|
|
199
|
+
this.rove();
|
|
149
200
|
this.requestUpdate();
|
|
150
201
|
});
|
|
151
202
|
this.watcher.observe(this, {
|
|
@@ -157,6 +208,7 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
157
208
|
disconnectedCallback() {
|
|
158
209
|
super.disconnectedCallback();
|
|
159
210
|
this.removeEventListener('helix-select', this.handleSelection);
|
|
211
|
+
this.removeEventListener('focusin', this.handleFocus);
|
|
160
212
|
this.watcher?.disconnect();
|
|
161
213
|
this.watcher = undefined;
|
|
162
214
|
}
|
|
@@ -181,6 +233,15 @@ let HelixHeadlessSteps = class HelixHeadlessSteps extends MixinControllerActive(
|
|
|
181
233
|
if (changed.has('linear') || changed.has('active') || changed.has('completion')) {
|
|
182
234
|
this.lock();
|
|
183
235
|
}
|
|
236
|
+
if (changed.has('active')) {
|
|
237
|
+
this.holder = undefined;
|
|
238
|
+
}
|
|
239
|
+
if (changed.has('arrows') ||
|
|
240
|
+
changed.has('linear') ||
|
|
241
|
+
changed.has('active') ||
|
|
242
|
+
changed.has('completion')) {
|
|
243
|
+
this.rove();
|
|
244
|
+
}
|
|
184
245
|
}
|
|
185
246
|
render() {
|
|
186
247
|
const progress = progressOf(this.known);
|
|
@@ -106,7 +106,15 @@ export declare class HelixHeadlessStep extends HelixElement {
|
|
|
106
106
|
*/
|
|
107
107
|
optional: boolean;
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
109
|
+
* @internal Set by a sequence with `arrows`: whether the name holds the
|
|
110
|
+
* sequence's one tab stop. A property, because the sequence writes it after
|
|
111
|
+
* the step upgrades.
|
|
112
|
+
*/
|
|
113
|
+
stop: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* The value the step sends in `helix-select`, and what names the step in its
|
|
116
|
+
* sequence's `active`. Without one, the sequence names the step by its place,
|
|
117
|
+
* counted from 1.
|
|
110
118
|
*/
|
|
111
119
|
value: string;
|
|
112
120
|
/** Whether anything is slotted into `content`, which makes it a named group. */
|
package/dist/steps/step/step.js
CHANGED
|
@@ -22,6 +22,7 @@ let HelixHeadlessStep = class HelixHeadlessStep extends HelixElement {
|
|
|
22
22
|
this.invalid = false;
|
|
23
23
|
this.locked = false;
|
|
24
24
|
this.optional = false;
|
|
25
|
+
this.stop = true;
|
|
25
26
|
this.value = '';
|
|
26
27
|
this.holding = false;
|
|
27
28
|
this.readContent = () => {
|
|
@@ -73,6 +74,7 @@ let HelixHeadlessStep = class HelixHeadlessStep extends HelixElement {
|
|
|
73
74
|
part="label"
|
|
74
75
|
exportparts="button: label-button, live: label-live, spinner: label-spinner, text: label-text"
|
|
75
76
|
emphasis="ghost"
|
|
77
|
+
.focusable=${this.stop}
|
|
76
78
|
aria-current=${this.current ? 'step' : nothing}
|
|
77
79
|
aria-describedby=${this.complete ? 'state' : nothing}
|
|
78
80
|
?disabled=${this.disabled || this.locked}
|
|
@@ -122,6 +124,9 @@ __decorate([
|
|
|
122
124
|
__decorate([
|
|
123
125
|
property({ reflect: true, type: Boolean })
|
|
124
126
|
], HelixHeadlessStep.prototype, "optional", void 0);
|
|
127
|
+
__decorate([
|
|
128
|
+
property({ attribute: false })
|
|
129
|
+
], HelixHeadlessStep.prototype, "stop", void 0);
|
|
125
130
|
__decorate([
|
|
126
131
|
property()
|
|
127
132
|
], HelixHeadlessStep.prototype, "value", void 0);
|
|
@@ -217,6 +217,14 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
|
|
|
217
217
|
* and so does this.
|
|
218
218
|
*/
|
|
219
219
|
setSelectionRange(start: number, end: number, direction?: SelectionDirection): void;
|
|
220
|
+
/**
|
|
221
|
+
* Replaces a range of the text, as the native method does: the selection
|
|
222
|
+
* where `start` and `end` are left out, and `selectMode` says what is
|
|
223
|
+
* selected after. It sends no event, as the native method sends none.
|
|
224
|
+
*/
|
|
225
|
+
setRangeText(replacement: string, start?: number, end?: number, selectMode?: SelectionMode): void;
|
|
226
|
+
/** Selects everything in the field. */
|
|
227
|
+
select(): void;
|
|
220
228
|
/** How many characters the value holds. */
|
|
221
229
|
get textLength(): number;
|
|
222
230
|
/** The control's type, which is always `textarea`. */
|
|
@@ -111,6 +111,22 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
|
|
|
111
111
|
setSelectionRange(start, end, direction) {
|
|
112
112
|
this.control?.setSelectionRange(start, end, direction);
|
|
113
113
|
}
|
|
114
|
+
setRangeText(replacement, start, end, selectMode) {
|
|
115
|
+
const control = this.control;
|
|
116
|
+
if (!control) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (start === undefined || end === undefined) {
|
|
120
|
+
control.setRangeText(replacement);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
control.setRangeText(replacement, start, end, selectMode);
|
|
124
|
+
}
|
|
125
|
+
this.value = control.value;
|
|
126
|
+
}
|
|
127
|
+
select() {
|
|
128
|
+
this.control?.select();
|
|
129
|
+
}
|
|
114
130
|
get textLength() {
|
|
115
131
|
return this.value.length;
|
|
116
132
|
}
|
|
@@ -11,6 +11,12 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
|
|
|
11
11
|
* captions, and poster treatment. `Sandbox` takes its preview and `Menu` its
|
|
12
12
|
* trigger the same way.
|
|
13
13
|
*
|
|
14
|
+
* **The player is a `Video` or a plain `<video>`.** Either takes `src` and
|
|
15
|
+
* `poster` from the entry, is named by it, plays on, and answers Shift with N
|
|
16
|
+
* or P while focused. A plain `<video>` has no previous and next controls
|
|
17
|
+
* that send `helix-previous` or `helix-next`, so its moves are the entries,
|
|
18
|
+
* the menu, and your own buttons calling `previous()` and `next()`.
|
|
19
|
+
*
|
|
14
20
|
* **Entries are markup.** React skips properties it would set imperatively
|
|
15
21
|
* during hydration, so an array property on a custom element is missing on the
|
|
16
22
|
* first paint of a server-rendered page. `using-the-elements.mdx` has the
|
|
@@ -43,7 +49,8 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
|
|
|
43
49
|
*
|
|
44
50
|
* **The player is named by the current entry**, and the device's own media
|
|
45
51
|
* controls show the entry as the title. A player you gave a `label` keeps
|
|
46
|
-
* your name, and only the title follows the entry.
|
|
52
|
+
* your name, and only the title follows the entry. A plain `<video>` is
|
|
53
|
+
* named through `aria-label`, unless you wrote one.
|
|
47
54
|
*
|
|
48
55
|
* **Keyboard.** The entries take one stop in the tab order: the entry you
|
|
49
56
|
* last focused, or the current entry before you have focused one. Enter or
|
|
@@ -235,6 +242,17 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
|
|
|
235
242
|
* Before they focus one, the current entry holds it.
|
|
236
243
|
*/
|
|
237
244
|
private stop;
|
|
245
|
+
/** The name this playlist last wrote on a plain media player, to tell it from yours. */
|
|
246
|
+
private wroteName;
|
|
247
|
+
/**
|
|
248
|
+
* Names the player by the entry.
|
|
249
|
+
*
|
|
250
|
+
* The consumer's own name for the player stands. Only a player named by
|
|
251
|
+
* nobody takes the entry's words as its name: `label` on a player that has
|
|
252
|
+
* one, and `aria-label` on a plain `<video>` or `<audio>`, which has none and
|
|
253
|
+
* was heard as an unnamed video.
|
|
254
|
+
*/
|
|
255
|
+
private name;
|
|
238
256
|
/** The entry the player was last pointed at. */
|
|
239
257
|
private pointed;
|
|
240
258
|
/** Hears entries arrive and leave, which change no property here. */
|
|
@@ -161,6 +161,19 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
161
161
|
get items() {
|
|
162
162
|
return [...this.querySelectorAll('*')].filter((one) => one instanceof HelixHeadlessVideoPlaylistItem && one.value !== '');
|
|
163
163
|
}
|
|
164
|
+
name(player, text) {
|
|
165
|
+
if (player instanceof HTMLMediaElement) {
|
|
166
|
+
const own = player.getAttribute('aria-label');
|
|
167
|
+
if (own === null || own === this.wroteName) {
|
|
168
|
+
player.setAttribute('aria-label', text);
|
|
169
|
+
this.wroteName = text;
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!player.hasAttribute('label')) {
|
|
174
|
+
player.label = text;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
164
177
|
get player() {
|
|
165
178
|
const slot = this.renderRoot?.querySelector('slot[name="player"]');
|
|
166
179
|
return slot?.assignedElements()[0] ?? null;
|
|
@@ -299,9 +312,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
299
312
|
}
|
|
300
313
|
player.poster = current.poster;
|
|
301
314
|
player.mediaTitle = current.text;
|
|
302
|
-
|
|
303
|
-
player.label = current.text;
|
|
304
|
-
}
|
|
315
|
+
this.name(player, current.text);
|
|
305
316
|
if (this.chased) {
|
|
306
317
|
this.status.say(formatWords(this, 'playingLabel', { title: current.text }));
|
|
307
318
|
void player.updateComplete?.then(() => player.play()).catch(() => { });
|
|
@@ -119,6 +119,11 @@ export interface VirtualListRange {
|
|
|
119
119
|
* commas. Their rows carry `aria-disabled`, the keys and typing pass over
|
|
120
120
|
* them, and a press, Space, or Control with A does not select them.
|
|
121
121
|
*
|
|
122
|
+
* **Rows a stylesheet can tell apart.** A chosen row carries `data-selected`,
|
|
123
|
+
* a disabled one `data-disabled`, as React Aria's rows do, and the row the
|
|
124
|
+
* keys are on, which holds the tab stop, `data-active`, whether or not the
|
|
125
|
+
* list has focus.
|
|
126
|
+
*
|
|
122
127
|
* **Loading more.** `helix-range-change` fires as the items on screen change,
|
|
123
128
|
* and `range` holds them. `helix-load-more` fires once a reader is within one
|
|
124
129
|
* screen of the end, once for each length of the set: add to `items` and it can
|
|
@@ -560,6 +565,9 @@ export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
|
560
565
|
* `option` with `aria-selected` there, and `listitem` otherwise, and
|
|
561
566
|
* `aria-disabled` on an item `disabled-keys` names. Every pass,
|
|
562
567
|
* since a choice changes no row's content.
|
|
568
|
+
*
|
|
569
|
+
* `data-selected` and `data-disabled` say the same to a stylesheet, as
|
|
570
|
+
* React Aria's rows do, so one rule reads the same in either kind of list.
|
|
563
571
|
*/
|
|
564
572
|
private mark;
|
|
565
573
|
/** Writes a row's place in the set. */
|
|
@@ -532,18 +532,22 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
532
532
|
}
|
|
533
533
|
mark(element, at, chosen, off) {
|
|
534
534
|
element.setAttribute('role', this.selectionMode ? 'option' : 'listitem');
|
|
535
|
+
const selected = Boolean(this.selectionMode) && chosen.has(this.keyAt(at));
|
|
535
536
|
if (this.selectionMode) {
|
|
536
|
-
element.setAttribute('aria-selected', String(
|
|
537
|
+
element.setAttribute('aria-selected', String(selected));
|
|
537
538
|
}
|
|
538
539
|
else {
|
|
539
540
|
element.removeAttribute('aria-selected');
|
|
540
541
|
}
|
|
541
|
-
|
|
542
|
+
element.toggleAttribute('data-selected', selected);
|
|
543
|
+
const disabled = off.size > 0 && off.has(this.keyAt(at));
|
|
544
|
+
if (disabled) {
|
|
542
545
|
element.setAttribute('aria-disabled', 'true');
|
|
543
546
|
}
|
|
544
547
|
else {
|
|
545
548
|
element.removeAttribute('aria-disabled');
|
|
546
549
|
}
|
|
550
|
+
element.toggleAttribute('data-disabled', disabled);
|
|
547
551
|
}
|
|
548
552
|
stamp(element, at) {
|
|
549
553
|
element.setAttribute('aria-posinset', String(at + 1));
|
|
@@ -660,6 +664,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
660
664
|
}
|
|
661
665
|
const stop = at === this.active;
|
|
662
666
|
group[0]?.setAttribute('tabindex', stop ? '0' : '-1');
|
|
667
|
+
group[0]?.toggleAttribute('data-active', stop);
|
|
663
668
|
if (stop && (fresh.has(at) || this.released !== at)) {
|
|
664
669
|
releaseControls(group);
|
|
665
670
|
}
|