@fusion.dev/helix-headless 0.154.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 +3213 -179
- package/dist/accordion/item/accordion-item.d.ts +15 -0
- package/dist/accordion/item/accordion-item.js +12 -1
- package/dist/accordion/root/accordion-root.d.ts +2 -1
- package/dist/accordion/root/accordion-root.js +1 -0
- package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
- package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
- package/dist/button-group/root/button-group-root.d.ts +2 -0
- package/dist/button-group/root/button-group-root.js +1 -0
- 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/color-input/root/color-input-root.d.ts +9 -3
- package/dist/color-input/root/color-input-root.js +3 -1
- package/dist/color-picker/channels.d.ts +5 -3
- package/dist/color-picker/channels.js +79 -21
- package/dist/color-picker/root/color-picker-root.d.ts +58 -5
- package/dist/color-picker/root/color-picker-root.js +75 -13
- 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 +33 -1
- package/dist/date-input/root/date-input-root.js +9 -2
- package/dist/date-picker/root/date-picker-root.d.ts +43 -13
- package/dist/date-picker/root/date-picker-root.js +54 -31
- package/dist/element-events.generated.js +3 -1
- 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/navigation/root/navigation-root.d.ts +26 -0
- package/dist/navigation/root/navigation-root.js +58 -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/select/root/select-root.d.ts +29 -3
- package/dist/select/root/select-root.js +34 -9
- package/dist/slider/root/slider-root.d.ts +71 -9
- package/dist/slider/root/slider-root.js +129 -42
- package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
- package/dist/speed-dial/item/speed-dial-item.js +48 -7
- package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
- package/dist/speed-dial/root/speed-dial-root.js +50 -5
- package/dist/splitter/root/splitter-root.d.ts +22 -0
- package/dist/splitter/root/splitter-root.js +36 -7
- package/dist/steps/reach.d.ts +5 -1
- package/dist/steps/reach.js +2 -2
- package/dist/steps/root/steps-root.d.ts +37 -2
- package/dist/steps/root/steps-root.js +49 -7
- package/dist/steps/step/step.d.ts +6 -0
- package/dist/steps/step/step.js +4 -0
- package/dist/table/root/table-root.d.ts +20 -0
- package/dist/table/root/table-root.js +13 -3
- package/dist/tabs/root/tabs-root.d.ts +37 -0
- package/dist/tabs/root/tabs-root.js +92 -2
- package/dist/tabs/tab/tab.d.ts +38 -2
- package/dist/tabs/tab/tab.js +45 -4
- package/dist/tag/group/tag-group.d.ts +29 -3
- package/dist/tag/group/tag-group.js +42 -11
- 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/toggle/root/toggle-root.d.ts +2 -0
- package/dist/toggle/root/toggle-root.js +1 -1
- package/dist/tree/item/tree-item.d.ts +10 -0
- package/dist/tree/item/tree-item.js +12 -0
- package/dist/tree/root/tree-root.d.ts +40 -1
- package/dist/tree/root/tree-root.js +120 -4
- package/dist/tree/selection.d.ts +6 -0
- package/dist/tree/selection.js +11 -0
- 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 +3885 -383
- package/types/vue.d.ts +1045 -107
- package/types/words.d.ts +146 -1
|
@@ -35,6 +35,13 @@ declare const HelixHeadlessTree_base: (new (...args: any[]) => import("@fusion.d
|
|
|
35
35
|
* or open, and the tree's one tab stop stays on the item last reached. In a
|
|
36
36
|
* tree of check marks, Control+A (Command+A on an Apple keyboard) checks every
|
|
37
37
|
* item or clears them all, and Shift with Up or Down Arrow moves and checks.
|
|
38
|
+
* Shift+Space checks every item from the one last checked to the focused one,
|
|
39
|
+
* and Control+Shift+Home or End checks every item from the focused one to the
|
|
40
|
+
* first or the last, then moves there, as the tree pattern's range keys do.
|
|
41
|
+
*
|
|
42
|
+
* A tree with no name of its own is announced as "Tree", which says nothing
|
|
43
|
+
* about what it holds, so a development build warns once with the kind
|
|
44
|
+
* `missing-name` where neither `label` nor an `aria-label` names it.
|
|
38
45
|
*
|
|
39
46
|
* Removing the item that has focus moves focus to the item after it among its
|
|
40
47
|
* siblings, or the one before, or its parent, so focus stays in the tree.
|
|
@@ -50,7 +57,15 @@ declare const HelixHeadlessTree_base: (new (...args: any[]) => import("@fusion.d
|
|
|
50
57
|
* `helix-before-reorder`, which you can refuse, and then `helix-reorder`, whose
|
|
51
58
|
* `keys` hold the siblings in the new order. **You move the items**: the tree
|
|
52
59
|
* moves no element of yours, and focus follows the moved item once you have.
|
|
53
|
-
*
|
|
60
|
+
*
|
|
61
|
+
* **Alt with the arrow along the line moves an item into another branch.**
|
|
62
|
+
* Alt with the arrow that opens a branch (Right on a left-to-right page) puts
|
|
63
|
+
* the focused item last inside the item before it, and opens that item; Alt
|
|
64
|
+
* with the other arrow puts it after its parent, a level out. The same
|
|
65
|
+
* `helix-before-reorder` and `helix-reorder` are sent, with `parent` naming
|
|
66
|
+
* the branch it moves into by its key, `''` for the top level, and `keys`
|
|
67
|
+
* holding that branch's items in their new order. The tree says the move with
|
|
68
|
+
* `moved-into-label`.
|
|
54
69
|
*
|
|
55
70
|
* @since 0.1.0
|
|
56
71
|
* @status stable
|
|
@@ -92,6 +107,14 @@ export declare class HelixHeadlessTree extends HelixHeadlessTree_base {
|
|
|
92
107
|
* wins over this.
|
|
93
108
|
*/
|
|
94
109
|
label: string;
|
|
110
|
+
/**
|
|
111
|
+
* What screen readers hear when an item moves into another branch, in a
|
|
112
|
+
* `reorderable` tree. `{name}` is the item's words, `{parent}` the words of
|
|
113
|
+
* the branch it moved into, or the tree's `label` for the top level,
|
|
114
|
+
* `{position}` its place there, counting from 1, and `{count}` the number of
|
|
115
|
+
* items there.
|
|
116
|
+
*/
|
|
117
|
+
movedIntoLabel: string;
|
|
95
118
|
/**
|
|
96
119
|
* What screen readers hear each time an item moves, in a `reorderable`
|
|
97
120
|
* tree. `{name}` is the item's words, `{position}` its place among its
|
|
@@ -188,6 +211,8 @@ export declare class HelixHeadlessTree extends HelixHeadlessTree_base {
|
|
|
188
211
|
private watcher;
|
|
189
212
|
/** The item that last had focus, so its removal can hand focus on. */
|
|
190
213
|
private focused;
|
|
214
|
+
/** The item last checked or unchecked, where a range Shift+Space checks starts. */
|
|
215
|
+
private anchor;
|
|
191
216
|
private readonly handleFocusIn;
|
|
192
217
|
/**
|
|
193
218
|
* Puts focus back in the tree where the item that had it was removed and
|
|
@@ -253,6 +278,20 @@ export declare class HelixHeadlessTree extends HelixHeadlessTree_base {
|
|
|
253
278
|
* Returns whether the press was one of those.
|
|
254
279
|
*/
|
|
255
280
|
private handleMultiple;
|
|
281
|
+
/** Checks every item of a range, and keeps the ones already checked. */
|
|
282
|
+
private checkRange;
|
|
283
|
+
/**
|
|
284
|
+
* Moves the focused item into another branch, in a `reorderable` tree: last
|
|
285
|
+
* inside the item before it, `toward` the line's end, or after its parent,
|
|
286
|
+
* toward its start. Asks the page, which moves the item, and says where it
|
|
287
|
+
* went or that the move was refused. Returns whether the key moved anything.
|
|
288
|
+
*/
|
|
289
|
+
private nest;
|
|
290
|
+
/**
|
|
291
|
+
* Gives focus back to a moved item once the page has moved it, where moving
|
|
292
|
+
* it sent focus to the body, as a move among siblings does.
|
|
293
|
+
*/
|
|
294
|
+
private follow;
|
|
256
295
|
private move;
|
|
257
296
|
private focusOn;
|
|
258
297
|
private readonly handleKeydown;
|
|
@@ -4,12 +4,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, closestInstance, commaList, customElement, deepActiveElement, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, ReorderController, scrollItemIntoView, selectsAll, slottedWords, Typeahead, treeArrow, typesCharacter, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, auditName, closestInstance, commaList, customElement, deepActiveElement, formatWords, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, ReorderController, requestReorder, scrollItemIntoView, selectsAll, slottedWords, Typeahead, treeArrow, typesCharacter, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { fromGrip } from '../grip.js';
|
|
11
11
|
import { HelixHeadlessTreeItem } from '../item/tree-item.js';
|
|
12
|
-
import { checkedState, pressRow } from '../selection.js';
|
|
12
|
+
import { checkedState, checkRows, pressRow } from '../selection.js';
|
|
13
13
|
const KEYS = new Set([
|
|
14
14
|
'ArrowDown',
|
|
15
15
|
'ArrowUp',
|
|
@@ -26,6 +26,7 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
26
26
|
this.canceledLabel = 'Canceled';
|
|
27
27
|
this.droppedLabel = 'Dropped';
|
|
28
28
|
this.label = 'Tree';
|
|
29
|
+
this.movedIntoLabel = '{name} moved into {parent}, position {position} of {count}';
|
|
29
30
|
this.movedLabel = '{name} moved to position {position} of {count}';
|
|
30
31
|
this.aria = new AriaForwardController(this, {
|
|
31
32
|
label: true,
|
|
@@ -66,6 +67,7 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
66
67
|
this.requestActive(this.multiple
|
|
67
68
|
? pressRow(this.rowOf(origin), this.values, this.order).join(',')
|
|
68
69
|
: event.detail.value);
|
|
70
|
+
this.anchor = origin;
|
|
69
71
|
}
|
|
70
72
|
this.syncItems();
|
|
71
73
|
};
|
|
@@ -87,8 +89,15 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
87
89
|
}
|
|
88
90
|
this.typeahead.reset();
|
|
89
91
|
const along = inlineStep(this, event.key);
|
|
92
|
+
if (event.altKey && along !== 0 && this.reorderable) {
|
|
93
|
+
if (!current.disabled && this.nest(current, along)) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
90
98
|
if (this.multiple && event.key === ' ') {
|
|
91
99
|
current.select();
|
|
100
|
+
this.anchor = current;
|
|
92
101
|
event.preventDefault();
|
|
93
102
|
return;
|
|
94
103
|
}
|
|
@@ -240,8 +249,18 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
240
249
|
const heads = this.nesting === 'heading' || this.selectionMode === 'leaf';
|
|
241
250
|
const multiple = this.multiple;
|
|
242
251
|
const checked = new Set(this.values);
|
|
252
|
+
const places = new Map();
|
|
253
|
+
for (const item of this.items) {
|
|
254
|
+
if (!places.has(item)) {
|
|
255
|
+
const siblings = this.siblingsOf(item);
|
|
256
|
+
siblings.forEach((one, at) => {
|
|
257
|
+
places.set(one, { position: at + 1, size: siblings.length });
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
243
261
|
for (const item of this.items) {
|
|
244
262
|
const key = this.keyOf(item);
|
|
263
|
+
const place = places.get(item);
|
|
245
264
|
const selected = !multiple && item.value !== '' && item.value === this.active;
|
|
246
265
|
const state = multiple ? checkedState(this.rowOf(item), checked) : false;
|
|
247
266
|
const focusable = item === stop;
|
|
@@ -259,6 +278,8 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
259
278
|
item.checked = state;
|
|
260
279
|
item.focusable = focusable;
|
|
261
280
|
item.level = level;
|
|
281
|
+
item.position = place?.position;
|
|
282
|
+
item.setSize = place?.size;
|
|
262
283
|
item.heads = heads;
|
|
263
284
|
item.grip = this.reorderable;
|
|
264
285
|
item.move = this.reorder.moveOf(key);
|
|
@@ -308,9 +329,24 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
308
329
|
this.syncItems();
|
|
309
330
|
return true;
|
|
310
331
|
}
|
|
332
|
+
const visible = this.visibleItems;
|
|
333
|
+
const at = visible.indexOf(current);
|
|
334
|
+
if (event.shiftKey && event.key === ' ') {
|
|
335
|
+
const from = this.anchor ? visible.indexOf(this.anchor) : -1;
|
|
336
|
+
const range = from < 0 ? [current] : visible.slice(Math.min(from, at), Math.max(from, at) + 1);
|
|
337
|
+
this.checkRange(range);
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
if (event.shiftKey &&
|
|
341
|
+
(event.ctrlKey || event.metaKey) &&
|
|
342
|
+
(event.key === 'Home' || event.key === 'End')) {
|
|
343
|
+
const range = event.key === 'Home' ? visible.slice(0, at + 1) : visible.slice(at);
|
|
344
|
+
this.checkRange(range);
|
|
345
|
+
this.focusOn(event.key === 'Home' ? range[0] : range.at(-1));
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
311
348
|
if (event.shiftKey && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
|
|
312
|
-
const
|
|
313
|
-
const next = visible[visible.indexOf(current) + (event.key === 'ArrowDown' ? 1 : -1)];
|
|
349
|
+
const next = visible[at + (event.key === 'ArrowDown' ? 1 : -1)];
|
|
314
350
|
if (next) {
|
|
315
351
|
this.focusOn(next);
|
|
316
352
|
next.select();
|
|
@@ -319,6 +355,81 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
319
355
|
}
|
|
320
356
|
return false;
|
|
321
357
|
}
|
|
358
|
+
checkRange(range) {
|
|
359
|
+
this.requestActive(checkRows(range.map((item) => this.rowOf(item)), this.values, this.order).join(','));
|
|
360
|
+
this.syncItems();
|
|
361
|
+
}
|
|
362
|
+
nest(current, toward) {
|
|
363
|
+
const siblings = this.siblingsOf(current);
|
|
364
|
+
const from = siblings.indexOf(current);
|
|
365
|
+
const key = this.keyOf(current);
|
|
366
|
+
let into;
|
|
367
|
+
let keys;
|
|
368
|
+
if (toward > 0) {
|
|
369
|
+
const before = siblings[from - 1];
|
|
370
|
+
if (!before || before.disabled) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
into = before;
|
|
374
|
+
const inside = [...before.children].filter((node) => node instanceof HelixHeadlessTreeItem);
|
|
375
|
+
keys = [...inside.map((one) => this.keyOf(one)), key];
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const parent = closestInstance(current.parentElement, HelixHeadlessTreeItem);
|
|
379
|
+
if (!(parent instanceof HelixHeadlessTreeItem) || !this.items.includes(parent)) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
const above = closestInstance(parent.parentElement, HelixHeadlessTreeItem);
|
|
383
|
+
into =
|
|
384
|
+
above instanceof HelixHeadlessTreeItem && this.items.includes(above) ? above : undefined;
|
|
385
|
+
keys = this.siblingsOf(parent).map((one) => this.keyOf(one));
|
|
386
|
+
keys.splice(keys.indexOf(this.keyOf(parent)) + 1, 0, key);
|
|
387
|
+
}
|
|
388
|
+
const to = keys.indexOf(key);
|
|
389
|
+
const stood = requestReorder(this, {
|
|
390
|
+
from,
|
|
391
|
+
key,
|
|
392
|
+
keys,
|
|
393
|
+
kind: 'item',
|
|
394
|
+
parent: into ? this.keyOf(into) : '',
|
|
395
|
+
to,
|
|
396
|
+
value: keys.join(','),
|
|
397
|
+
});
|
|
398
|
+
const name = slottedWords(current, ['']) || key;
|
|
399
|
+
this.announced = stood
|
|
400
|
+
? formatWords(this, 'movedIntoLabel', {
|
|
401
|
+
count: keys.length,
|
|
402
|
+
name,
|
|
403
|
+
parent: into ? slottedWords(into, ['']) || this.keyOf(into) : this.label,
|
|
404
|
+
position: to + 1,
|
|
405
|
+
})
|
|
406
|
+
: this.canceledLabel;
|
|
407
|
+
this.requestUpdate();
|
|
408
|
+
if (stood) {
|
|
409
|
+
if (into && !into.open) {
|
|
410
|
+
into.requestOpen(true);
|
|
411
|
+
}
|
|
412
|
+
this.follow(key);
|
|
413
|
+
}
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
follow(key) {
|
|
417
|
+
const lost = () => {
|
|
418
|
+
const active = deepActiveElement();
|
|
419
|
+
return !active || active === document.body;
|
|
420
|
+
};
|
|
421
|
+
if (lost()) {
|
|
422
|
+
this.focusOn(this.itemFor(key));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const watch = new MutationObserver(() => {
|
|
426
|
+
watch.disconnect();
|
|
427
|
+
if (lost()) {
|
|
428
|
+
this.focusOn(this.itemFor(key));
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
watch.observe(this, { childList: true, subtree: true });
|
|
432
|
+
}
|
|
322
433
|
move(from, delta) {
|
|
323
434
|
const visible = this.visibleItems;
|
|
324
435
|
const index = visible.indexOf(from);
|
|
@@ -359,6 +470,7 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
359
470
|
firstUpdated(changed) {
|
|
360
471
|
super.firstUpdated(changed);
|
|
361
472
|
this.syncItems();
|
|
473
|
+
auditName(this, () => null, 'Give it a `label` that says what it holds, such as "Files".', () => this.hasAttribute('label') || this.label !== 'Tree');
|
|
362
474
|
}
|
|
363
475
|
updated(changed) {
|
|
364
476
|
super.updated(changed);
|
|
@@ -400,6 +512,10 @@ __decorate([
|
|
|
400
512
|
words,
|
|
401
513
|
property()
|
|
402
514
|
], HelixHeadlessTree.prototype, "label", void 0);
|
|
515
|
+
__decorate([
|
|
516
|
+
words,
|
|
517
|
+
property({ attribute: 'moved-into-label' })
|
|
518
|
+
], HelixHeadlessTree.prototype, "movedIntoLabel", void 0);
|
|
403
519
|
__decorate([
|
|
404
520
|
words,
|
|
405
521
|
property({ attribute: 'moved-label' })
|
package/dist/tree/selection.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ export interface CheckableRow {
|
|
|
14
14
|
* a branch by its leaves, `mixed` where some of them are.
|
|
15
15
|
*/
|
|
16
16
|
export declare const checkedState: (row: CheckableRow, chosen: ReadonlySet<string>) => boolean | 'mixed';
|
|
17
|
+
/**
|
|
18
|
+
* The checked values after checking every row in `rows`, in `order`: a range
|
|
19
|
+
* the reader extends with Shift and Space, or with Control, Shift and Home or
|
|
20
|
+
* End. Rows already checked stay checked, and disabled rows are passed over.
|
|
21
|
+
*/
|
|
22
|
+
export declare const checkRows: (rows: readonly CheckableRow[], chosen: readonly string[], order: readonly string[]) => string[];
|
|
17
23
|
/**
|
|
18
24
|
* The checked values after a press on `row`, in `order`.
|
|
19
25
|
*
|
package/dist/tree/selection.js
CHANGED
|
@@ -8,6 +8,17 @@ export const checkedState = (row, chosen) => {
|
|
|
8
8
|
}
|
|
9
9
|
return count === values.length ? true : 'mixed';
|
|
10
10
|
};
|
|
11
|
+
export const checkRows = (rows, chosen, order) => {
|
|
12
|
+
const added = new Set(chosen);
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
if (!row.disabled) {
|
|
15
|
+
for (const value of valuesOf(row)) {
|
|
16
|
+
added.add(value);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return order.filter((value) => added.has(value));
|
|
21
|
+
};
|
|
11
22
|
export const pressRow = (row, chosen, order) => {
|
|
12
23
|
return row.disabled ? [...chosen] : toggleEvery(chosen, valuesOf(row), order);
|
|
13
24
|
};
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Separable because the table is a convention rather than a fact about this
|
|
5
5
|
* element: Space and `k` play, the arrows scrub and change the volume, `m`
|
|
6
|
-
* mutes, `f` fills the screen,
|
|
7
|
-
*
|
|
6
|
+
* mutes, `f` fills the screen, `c` turns captions on and off, Home, End and
|
|
7
|
+
* the digits go to the start, the end and each tenth, and `j` and `l` jump the
|
|
8
|
+
* distance the bar's own buttons jump. What it needs is the media element and four actions, which is
|
|
8
9
|
* the same handful the media session hands to a headset. Whether the keys are
|
|
9
10
|
* bound at all, and what a taken press does to the event, stay with the element.
|
|
10
11
|
*/
|
|
11
12
|
/** What a press asks the player for, which is all a key can do. */
|
|
12
13
|
export interface KeyActions {
|
|
14
|
+
captions: () => void;
|
|
13
15
|
fullscreen: () => void;
|
|
14
16
|
seek: (seconds: number) => void;
|
|
15
17
|
skipBy: (seconds: number) => void;
|
|
16
18
|
toggle: () => void;
|
|
17
19
|
}
|
|
20
|
+
/** The keys `takeKey` answers, as `aria-keyshortcuts` names them. */
|
|
21
|
+
export declare const PLAYER_KEYS = "Space K ArrowLeft ArrowRight Shift+ArrowLeft Shift+ArrowRight J L ArrowUp ArrowDown M F C Home End 0 1 2 3 4 5 6 7 8 9";
|
|
18
22
|
/**
|
|
19
23
|
* Answers one press, and says whether it was one of these.
|
|
20
24
|
*
|
package/dist/video/root/keys.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { SKIP_SECONDS } from '@fusion.dev/helix-core';
|
|
2
2
|
const PRESSED_BY_SPACE = new Set(['a', 'button', 'select', 'summary', 'textarea']);
|
|
3
3
|
const PRESSED_ROLES = /^(button|checkbox|link|menuitem\w*|option|radio|switch|tab)$/;
|
|
4
|
+
export const PLAYER_KEYS = 'Space K ArrowLeft ArrowRight Shift+ArrowLeft Shift+ArrowRight J L ArrowUp ArrowDown M F C Home End 0 1 2 3 4 5 6 7 8 9';
|
|
4
5
|
export const takeKey = (event, video, actions) => {
|
|
5
|
-
if (event.defaultPrevented) {
|
|
6
|
+
if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey) {
|
|
6
7
|
return false;
|
|
7
8
|
}
|
|
8
9
|
const origin = (event.composedPath?.()[0] ?? event.target);
|
|
@@ -54,7 +55,23 @@ export const takeKey = (event, video, actions) => {
|
|
|
54
55
|
actions.fullscreen();
|
|
55
56
|
break;
|
|
56
57
|
}
|
|
58
|
+
case 'c': {
|
|
59
|
+
actions.captions();
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'Home': {
|
|
63
|
+
actions.seek(0);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case 'End': {
|
|
67
|
+
actions.seek(video.duration);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
57
70
|
default: {
|
|
71
|
+
if (/^[0-9]$/.test(event.key) && Number.isFinite(video.duration)) {
|
|
72
|
+
actions.seek((video.duration * Number(event.key)) / 10);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
58
75
|
return false;
|
|
59
76
|
}
|
|
60
77
|
}
|
|
@@ -29,8 +29,16 @@ import '../../menu/index.js';
|
|
|
29
29
|
* | Up, Down | Raises or lowers the volume by a tenth. |
|
|
30
30
|
* | `m` | Mutes or unmutes. |
|
|
31
31
|
* | `f` | Enters or leaves fullscreen. |
|
|
32
|
+
* | `c` | Turns captions on or off. |
|
|
33
|
+
* | Home, End | Goes to the start or the end. |
|
|
34
|
+
* | `0` to `9` | Goes to that tenth of the video: `5` is the middle. |
|
|
32
35
|
* | Tab | Moves through the controls on the bar. |
|
|
33
36
|
*
|
|
37
|
+
* A key with Control, Command, or Alt held is left to the browser. The
|
|
38
|
+
* player lists its keys in `aria-keyshortcuts`, and a live region says what a
|
|
39
|
+
* key changed that is not on the focused control: playing or paused, muted,
|
|
40
|
+
* the volume, captions on or off, and where the video is after a seek.
|
|
41
|
+
*
|
|
34
42
|
* On the scrubber and the volume slider, the arrow keys, Page Up, Page Down,
|
|
35
43
|
* Home, and End move the slider, as on any range input.
|
|
36
44
|
*
|
|
@@ -125,7 +133,7 @@ import '../../menu/index.js';
|
|
|
125
133
|
* @csspart forward - The button that skips forward ten seconds.
|
|
126
134
|
* @csspart fullscreen - The button that enters and leaves fullscreen.
|
|
127
135
|
* @csspart header - The region at the top left that holds the `header` slot.
|
|
128
|
-
* @csspart live - A live region that
|
|
136
|
+
* @csspart live - A live region that says the loading text, and what a key changed, such as the volume. Visually hidden in the styled tier.
|
|
129
137
|
* @csspart loading - The text shown while the video waits for more data. Draw the spinner
|
|
130
138
|
* with `::part(loading)::before` in your stylesheet. This tier ships no CSS and renders
|
|
131
139
|
* no extra box for it.
|
|
@@ -187,6 +195,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
187
195
|
* looking at.
|
|
188
196
|
*/
|
|
189
197
|
autoplay: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* What the live region says when `c` turns captions off.
|
|
200
|
+
*/
|
|
201
|
+
captionsHiddenLabel: string;
|
|
190
202
|
/**
|
|
191
203
|
* The name of the captions control when the video has one track. The control
|
|
192
204
|
* turns that track on and off.
|
|
@@ -201,6 +213,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
201
213
|
* tracks.
|
|
202
214
|
*/
|
|
203
215
|
captionsOffLabel: string;
|
|
216
|
+
/**
|
|
217
|
+
* What the live region says when `c` turns captions on.
|
|
218
|
+
*/
|
|
219
|
+
captionsShownLabel: string;
|
|
204
220
|
/**
|
|
205
221
|
* Whether the browser draws its native controls. When set, this element draws
|
|
206
222
|
* no controls of its own.
|
|
@@ -326,6 +342,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
326
342
|
* What the button that opens `overflow-controls` is called.
|
|
327
343
|
*/
|
|
328
344
|
moreLabel: string;
|
|
345
|
+
/**
|
|
346
|
+
* What the live region says when a key mutes the video.
|
|
347
|
+
*/
|
|
348
|
+
mutedLabel: string;
|
|
329
349
|
/**
|
|
330
350
|
* The name of the mute control. The control shows as pressed while the video
|
|
331
351
|
* is muted.
|
|
@@ -353,6 +373,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
353
373
|
* bar: two of the same control is worse than either placement.
|
|
354
374
|
*/
|
|
355
375
|
overflowControls: string;
|
|
376
|
+
/**
|
|
377
|
+
* What the live region says when a key pauses the video.
|
|
378
|
+
*/
|
|
379
|
+
pausedLabel: string;
|
|
356
380
|
/**
|
|
357
381
|
* The name of the play control while pressing it would pause the video.
|
|
358
382
|
*/
|
|
@@ -372,6 +396,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
372
396
|
* this is what a screen reader calls it on arriving.
|
|
373
397
|
*/
|
|
374
398
|
playerLabel: string;
|
|
399
|
+
/**
|
|
400
|
+
* What the live region says when a key plays the video.
|
|
401
|
+
*/
|
|
402
|
+
playingLabel: string;
|
|
375
403
|
/**
|
|
376
404
|
* The name of the play control while pressing it would start the video.
|
|
377
405
|
*/
|
|
@@ -453,6 +481,15 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
453
481
|
* `{duration}` filled in.
|
|
454
482
|
*/
|
|
455
483
|
timeLabel: string;
|
|
484
|
+
/**
|
|
485
|
+
* What the live region says when a key unmutes the video.
|
|
486
|
+
*/
|
|
487
|
+
unmutedLabel: string;
|
|
488
|
+
/**
|
|
489
|
+
* What the live region says when a key changes the volume. `{percent}` is the
|
|
490
|
+
* volume from 0 to 100, written in the locale.
|
|
491
|
+
*/
|
|
492
|
+
volumeChangedLabel: string;
|
|
456
493
|
/**
|
|
457
494
|
* What the volume slider is called.
|
|
458
495
|
*/
|
|
@@ -773,6 +810,28 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
773
810
|
* key means and this says that nothing else gets it.
|
|
774
811
|
*/
|
|
775
812
|
private readonly handleKeydown;
|
|
813
|
+
/**
|
|
814
|
+
* What a reader hears after a key, which changed something they may not be
|
|
815
|
+
* looking at: the focus is on the player or a control, and the bar may be
|
|
816
|
+
* hidden.
|
|
817
|
+
*
|
|
818
|
+
* **Said from the video's state after the press**, except for playing,
|
|
819
|
+
* which starts on a promise: whether the video was paused before the press
|
|
820
|
+
* says which way it went. Nothing for fullscreen, which a reader sees and a
|
|
821
|
+
* screen reader reports as the window changing, nor for `c` where there are
|
|
822
|
+
* no captions to turn on.
|
|
823
|
+
*/
|
|
824
|
+
private sayKey;
|
|
825
|
+
/**
|
|
826
|
+
* What the live region says after a key, until the video waits for data,
|
|
827
|
+
* which it says over this.
|
|
828
|
+
*/
|
|
829
|
+
private said;
|
|
830
|
+
/**
|
|
831
|
+
* Puts words in the live region. The same words twice differ by a space no
|
|
832
|
+
* reader hears, since a region whose text does not change is not read again.
|
|
833
|
+
*/
|
|
834
|
+
private say;
|
|
776
835
|
/** The caption tracks among the copies, named as the menu names them. */
|
|
777
836
|
private captions;
|
|
778
837
|
/**
|
|
@@ -13,7 +13,7 @@ import '../../menu/index.js';
|
|
|
13
13
|
import { HelixHeadlessMenu } from '../../menu/root/menu-root.js';
|
|
14
14
|
import { relayMedia } from '../media-events.js';
|
|
15
15
|
import { barControls, menuControls, ownFile } from './extra-controls.js';
|
|
16
|
-
import { takeKey } from './keys.js';
|
|
16
|
+
import { PLAYER_KEYS, takeKey } from './keys.js';
|
|
17
17
|
const clock = (seconds) => {
|
|
18
18
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
19
19
|
return '0:00';
|
|
@@ -31,8 +31,10 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
31
31
|
constructor() {
|
|
32
32
|
super(...arguments);
|
|
33
33
|
this.autoplay = false;
|
|
34
|
+
this.captionsHiddenLabel = 'Captions off';
|
|
34
35
|
this.captionsLabel = 'Captions';
|
|
35
36
|
this.captionsOffLabel = 'Off';
|
|
37
|
+
this.captionsShownLabel = 'Captions on';
|
|
36
38
|
this.controls = false;
|
|
37
39
|
this.crossOrigin = null;
|
|
38
40
|
this.defaultMuted = false;
|
|
@@ -49,13 +51,16 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
49
51
|
this.loop = false;
|
|
50
52
|
this.mediaTitle = '';
|
|
51
53
|
this.moreLabel = 'More controls';
|
|
54
|
+
this.mutedLabel = 'Muted';
|
|
52
55
|
this.muteLabel = 'Mute';
|
|
53
56
|
this.nextLabel = 'Next';
|
|
54
57
|
this.overflowControls = '';
|
|
58
|
+
this.pausedLabel = 'Paused';
|
|
55
59
|
this.pauseLabel = 'Pause';
|
|
56
60
|
this.percentLabel = '{percent} percent';
|
|
57
61
|
this.pipLabel = 'Picture in picture';
|
|
58
62
|
this.playerLabel = 'Video';
|
|
63
|
+
this.playingLabel = 'Playing';
|
|
59
64
|
this.playLabel = 'Play';
|
|
60
65
|
this.playsInline = true;
|
|
61
66
|
this.poster = '';
|
|
@@ -69,6 +74,8 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
69
74
|
this.src = '';
|
|
70
75
|
this.subtitlesLabel = 'Subtitles';
|
|
71
76
|
this.timeLabel = '{position} of {duration}';
|
|
77
|
+
this.unmutedLabel = 'Unmuted';
|
|
78
|
+
this.volumeChangedLabel = 'Volume {percent} percent';
|
|
72
79
|
this.volumeLabel = 'Volume';
|
|
73
80
|
this.buffering = false;
|
|
74
81
|
this.failed = false;
|
|
@@ -199,7 +206,9 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
199
206
|
if (event.composedPath().some((node) => node instanceof HelixHeadlessMenu)) {
|
|
200
207
|
return;
|
|
201
208
|
}
|
|
209
|
+
const paused = video.paused;
|
|
202
210
|
const taken = takeKey(event, video, {
|
|
211
|
+
captions: () => this.toggleCaptions(),
|
|
203
212
|
fullscreen: () => void this.fullscreen.toggle(),
|
|
204
213
|
seek: (seconds) => this.seek(seconds),
|
|
205
214
|
skipBy: (seconds) => this.skipBy(seconds),
|
|
@@ -207,8 +216,10 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
207
216
|
});
|
|
208
217
|
if (taken) {
|
|
209
218
|
event.preventDefault();
|
|
219
|
+
this.sayKey(event.key, paused, video);
|
|
210
220
|
}
|
|
211
221
|
};
|
|
222
|
+
this.said = '';
|
|
212
223
|
this.captions = [];
|
|
213
224
|
}
|
|
214
225
|
static { this.words = 'helix-video'; }
|
|
@@ -414,6 +425,31 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
414
425
|
});
|
|
415
426
|
}
|
|
416
427
|
}
|
|
428
|
+
sayKey(key, paused, video) {
|
|
429
|
+
if (key === ' ' || key === 'k') {
|
|
430
|
+
this.say(paused ? this.playingLabel : this.pausedLabel);
|
|
431
|
+
}
|
|
432
|
+
else if (key === 'm') {
|
|
433
|
+
this.say(video.muted ? this.mutedLabel : this.unmutedLabel);
|
|
434
|
+
}
|
|
435
|
+
else if (key === 'ArrowUp' || key === 'ArrowDown') {
|
|
436
|
+
this.say(formatWords(this, 'volumeChangedLabel', { percent: Math.round(video.volume * 100) }));
|
|
437
|
+
}
|
|
438
|
+
else if (key === 'c') {
|
|
439
|
+
if (this.captions.length > 0) {
|
|
440
|
+
this.say(this.captioned ? this.captionsShownLabel : this.captionsHiddenLabel);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else if (key !== 'f') {
|
|
444
|
+
this.say(formatWords(this, 'timeLabel', {
|
|
445
|
+
duration: clock(video.duration),
|
|
446
|
+
position: clock(video.currentTime),
|
|
447
|
+
}));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
say(words) {
|
|
451
|
+
this.said = words !== '' && words === this.said ? `${words}\u00a0` : words;
|
|
452
|
+
}
|
|
417
453
|
willUpdate(changed) {
|
|
418
454
|
super.willUpdate(changed);
|
|
419
455
|
if (!this.mediaRead && this.childrenKnown) {
|
|
@@ -820,6 +856,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
820
856
|
tabindex=${this.controls ? nothing : '0'}
|
|
821
857
|
role=${this.controls ? nothing : 'group'}
|
|
822
858
|
aria-label=${this.controls ? nothing : this.label || this.playerLabel}
|
|
859
|
+
aria-keyshortcuts=${this.controls ? nothing : PLAYER_KEYS}
|
|
823
860
|
@keydown=${this.handleKeydown}
|
|
824
861
|
@pointermove=${this.idler.stir}
|
|
825
862
|
@pointerleave=${this.idler.leave}
|
|
@@ -883,7 +920,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
883
920
|
? html `<div part="loading">${this.loadingLabel}</div>`
|
|
884
921
|
: nothing}
|
|
885
922
|
${''}
|
|
886
|
-
<span part="live" role="status">${this.buffering && !this.failed ? this.loadingLabel :
|
|
923
|
+
<span part="live" role="status">${this.buffering && !this.failed ? this.loadingLabel : this.said}</span>
|
|
887
924
|
${this.failed
|
|
888
925
|
? html `<div
|
|
889
926
|
part="failure"
|
|
@@ -900,6 +937,10 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
900
937
|
__decorate([
|
|
901
938
|
property({ reflect: true, type: Boolean })
|
|
902
939
|
], HelixHeadlessVideo.prototype, "autoplay", void 0);
|
|
940
|
+
__decorate([
|
|
941
|
+
words,
|
|
942
|
+
property({ attribute: 'captions-hidden-label' })
|
|
943
|
+
], HelixHeadlessVideo.prototype, "captionsHiddenLabel", void 0);
|
|
903
944
|
__decorate([
|
|
904
945
|
words,
|
|
905
946
|
property({ attribute: 'captions-label' })
|
|
@@ -908,6 +949,10 @@ __decorate([
|
|
|
908
949
|
words,
|
|
909
950
|
property({ attribute: 'captions-off-label' })
|
|
910
951
|
], HelixHeadlessVideo.prototype, "captionsOffLabel", void 0);
|
|
952
|
+
__decorate([
|
|
953
|
+
words,
|
|
954
|
+
property({ attribute: 'captions-shown-label' })
|
|
955
|
+
], HelixHeadlessVideo.prototype, "captionsShownLabel", void 0);
|
|
911
956
|
__decorate([
|
|
912
957
|
property({ reflect: true, type: Boolean })
|
|
913
958
|
], HelixHeadlessVideo.prototype, "controls", void 0);
|
|
@@ -962,6 +1007,10 @@ __decorate([
|
|
|
962
1007
|
words,
|
|
963
1008
|
property({ attribute: 'more-label' })
|
|
964
1009
|
], HelixHeadlessVideo.prototype, "moreLabel", void 0);
|
|
1010
|
+
__decorate([
|
|
1011
|
+
words,
|
|
1012
|
+
property({ attribute: 'muted-label' })
|
|
1013
|
+
], HelixHeadlessVideo.prototype, "mutedLabel", void 0);
|
|
965
1014
|
__decorate([
|
|
966
1015
|
words,
|
|
967
1016
|
property({ attribute: 'mute-label' })
|
|
@@ -973,6 +1022,10 @@ __decorate([
|
|
|
973
1022
|
__decorate([
|
|
974
1023
|
property({ attribute: 'overflow-controls' })
|
|
975
1024
|
], HelixHeadlessVideo.prototype, "overflowControls", void 0);
|
|
1025
|
+
__decorate([
|
|
1026
|
+
words,
|
|
1027
|
+
property({ attribute: 'paused-label' })
|
|
1028
|
+
], HelixHeadlessVideo.prototype, "pausedLabel", void 0);
|
|
976
1029
|
__decorate([
|
|
977
1030
|
words,
|
|
978
1031
|
property({ attribute: 'pause-label' })
|
|
@@ -989,6 +1042,10 @@ __decorate([
|
|
|
989
1042
|
words,
|
|
990
1043
|
property({ attribute: 'player-label' })
|
|
991
1044
|
], HelixHeadlessVideo.prototype, "playerLabel", void 0);
|
|
1045
|
+
__decorate([
|
|
1046
|
+
words,
|
|
1047
|
+
property({ attribute: 'playing-label' })
|
|
1048
|
+
], HelixHeadlessVideo.prototype, "playingLabel", void 0);
|
|
992
1049
|
__decorate([
|
|
993
1050
|
words,
|
|
994
1051
|
property({ attribute: 'play-label' })
|
|
@@ -1039,6 +1096,14 @@ __decorate([
|
|
|
1039
1096
|
words,
|
|
1040
1097
|
property({ attribute: 'time-label' })
|
|
1041
1098
|
], HelixHeadlessVideo.prototype, "timeLabel", void 0);
|
|
1099
|
+
__decorate([
|
|
1100
|
+
words,
|
|
1101
|
+
property({ attribute: 'unmuted-label' })
|
|
1102
|
+
], HelixHeadlessVideo.prototype, "unmutedLabel", void 0);
|
|
1103
|
+
__decorate([
|
|
1104
|
+
words,
|
|
1105
|
+
property({ attribute: 'volume-changed-label' })
|
|
1106
|
+
], HelixHeadlessVideo.prototype, "volumeChangedLabel", void 0);
|
|
1042
1107
|
__decorate([
|
|
1043
1108
|
words,
|
|
1044
1109
|
property({ attribute: 'volume-label' })
|
|
@@ -1100,6 +1165,9 @@ __decorate([
|
|
|
1100
1165
|
__decorate([
|
|
1101
1166
|
state()
|
|
1102
1167
|
], HelixHeadlessVideo.prototype, "media", void 0);
|
|
1168
|
+
__decorate([
|
|
1169
|
+
state()
|
|
1170
|
+
], HelixHeadlessVideo.prototype, "said", void 0);
|
|
1103
1171
|
HelixHeadlessVideo = __decorate([
|
|
1104
1172
|
customElement('helix-headless-video')
|
|
1105
1173
|
], HelixHeadlessVideo);
|