@fusion.dev/helix-headless 0.155.0 → 0.157.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 +2695 -137
- 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/drilldown/item/drilldown-item.d.ts +49 -1
- package/dist/drilldown/item/drilldown-item.js +77 -17
- package/dist/drilldown/root/drilldown-root.d.ts +45 -5
- package/dist/drilldown/root/drilldown-root.js +51 -5
- package/dist/element-events.generated.js +2 -1
- package/dist/filter-input/menu/filter-menu.d.ts +1 -0
- package/dist/filter-input/menu/filter-menu.js +4 -4
- 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/megamenu/root/megamenu-root.d.ts +50 -13
- package/dist/megamenu/root/megamenu-root.js +44 -6
- package/dist/menu/item/menu-item.d.ts +24 -1
- package/dist/menu/item/menu-item.js +42 -2
- package/dist/menu/root/menu-root.d.ts +120 -9
- package/dist/menu/root/menu-root.js +329 -26
- package/dist/notifications/root/notifications-root.d.ts +20 -0
- package/dist/notifications/root/notifications-root.js +32 -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/toast/item/toast-item.d.ts +26 -0
- package/dist/toast/item/toast-item.js +55 -3
- package/dist/toast/root/toast-root.d.ts +32 -0
- package/dist/toast/root/toast-root.js +47 -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 +61 -1
- package/dist/video/root/video-root.js +79 -11
- 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/menu/video-playlist-menu.d.ts +1 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -1
- 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 +3346 -266
- package/types/vue.d.ts +910 -66
- package/types/words.d.ts +124 -0
|
@@ -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) {
|
|
@@ -574,7 +610,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
574
610
|
part="entry"
|
|
575
611
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
576
612
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
577
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
613
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
578
614
|
value="off"
|
|
579
615
|
checkable
|
|
580
616
|
exclusive
|
|
@@ -587,7 +623,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
587
623
|
${tracks.map((track, at) => html `<${this.tag('menu-item')} part="entry"
|
|
588
624
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
589
625
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
590
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
626
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
591
627
|
value=${track.language || String(at)}
|
|
592
628
|
checkable
|
|
593
629
|
exclusive
|
|
@@ -616,7 +652,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
616
652
|
return html `<${this.tag('menu-item')} part="entry"
|
|
617
653
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
618
654
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
619
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
655
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
620
656
|
value="remote"
|
|
621
657
|
@helix-select=${() => void this.remote.prompt()}
|
|
622
658
|
>
|
|
@@ -628,7 +664,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
628
664
|
return html `<${this.tag('menu-item')} part="entry"
|
|
629
665
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
630
666
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
631
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start" value="speed" @helix-select=${() => this.cycleSpeed()}>
|
|
667
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface" value="speed" @helix-select=${() => this.cycleSpeed()}>
|
|
632
668
|
<slot name="speed-icon" slot="start">${drawIcon(gauge)}</slot>
|
|
633
669
|
${this.speedLabel}: ${this.rate}x
|
|
634
670
|
</${this.tag('menu-item')}>`;
|
|
@@ -638,7 +674,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
638
674
|
return html `<${this.tag('menu-item')} part="entry"
|
|
639
675
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
640
676
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
641
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
677
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
642
678
|
value=${one}
|
|
643
679
|
@helix-select=${() => this.skipBy(back ? -SKIP_SECONDS : SKIP_SECONDS)}
|
|
644
680
|
>
|
|
@@ -652,7 +688,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
652
688
|
return html `<${this.tag('menu-item')} part="entry"
|
|
653
689
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
654
690
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
655
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start" value="previous" @helix-select=${() => this.skip(true)}>
|
|
691
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface" value="previous" @helix-select=${() => this.skip(true)}>
|
|
656
692
|
<slot name="previous-icon" slot="start">${drawIcon(skipBack)}</slot>
|
|
657
693
|
${this.previousLabel}
|
|
658
694
|
</${this.tag('menu-item')}>`;
|
|
@@ -661,7 +697,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
661
697
|
return html `<${this.tag('menu-item')} part="entry"
|
|
662
698
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
663
699
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
664
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start" value="next" @helix-select=${() => this.skip(false)}>
|
|
700
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface" value="next" @helix-select=${() => this.skip(false)}>
|
|
665
701
|
<slot name="next-icon" slot="start">${drawIcon(skipForward)}</slot>
|
|
666
702
|
${this.nextLabel}
|
|
667
703
|
</${this.tag('menu-item')}>`;
|
|
@@ -671,7 +707,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
671
707
|
return html `<${this.tag('menu-item')} part="entry"
|
|
672
708
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
673
709
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
674
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
710
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
675
711
|
?download=${local}
|
|
676
712
|
href=${this.src}
|
|
677
713
|
rel=${local ? nothing : 'noreferrer'}
|
|
@@ -685,7 +721,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
685
721
|
return html `<${this.tag('menu-item')} part="entry"
|
|
686
722
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
687
723
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
688
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start" value="pip" @helix-select=${() => void this.togglePip()}>
|
|
724
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface" value="pip" @helix-select=${() => void this.togglePip()}>
|
|
689
725
|
<slot name="pip-icon" slot="start">${drawIcon(pictureInPicture)}</slot>
|
|
690
726
|
${this.pipLabel}
|
|
691
727
|
</${this.tag('menu-item')}>`;
|
|
@@ -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);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary One entry in a video playlist.
|
|
5
5
|
*
|
|
@@ -34,6 +34,8 @@ import { type TemplateResult } from 'lit';
|
|
|
34
34
|
* @csspart label - The name and the content after it.
|
|
35
35
|
* @csspart media - The column at the start of the row.
|
|
36
36
|
* @csspart thumbnail - The image of `poster`, shown when the `media` slot is empty.
|
|
37
|
+
*
|
|
38
|
+
* @cssstate current - The entry that is playing.
|
|
37
39
|
*/
|
|
38
40
|
export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
|
|
39
41
|
/**
|
|
@@ -78,6 +80,11 @@ export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
|
|
|
78
80
|
* where there is no slot to ask. `Option.text` reads its own the same way.
|
|
79
81
|
*/
|
|
80
82
|
get text(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Focuses the entry's button, which is where the playlist's arrow keys send
|
|
85
|
+
* focus. The host itself takes no focus.
|
|
86
|
+
*/
|
|
87
|
+
focus(options?: FocusOptions): void;
|
|
81
88
|
private readonly choose;
|
|
82
89
|
/**
|
|
83
90
|
* What stands for the row: a thumbnail where there is one, and nothing where
|
|
@@ -88,6 +95,13 @@ export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
|
|
|
88
95
|
* slots `media` for the same reasons and neither has to know about the other.
|
|
89
96
|
*/
|
|
90
97
|
protected renderMedia(): TemplateResult;
|
|
98
|
+
/**
|
|
99
|
+
* Mirrors the playing entry where a stylesheet can select on the host,
|
|
100
|
+
* `helix-video-playlist-item:state(current)`. A custom state, because the
|
|
101
|
+
* playlist writes `current`, and an attribute it wrote would be markup a
|
|
102
|
+
* server never sent.
|
|
103
|
+
*/
|
|
104
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
91
105
|
protected render(): TemplateResult;
|
|
92
106
|
}
|
|
93
107
|
declare global {
|
|
@@ -29,11 +29,18 @@ let HelixHeadlessVideoPlaylistItem = class HelixHeadlessVideoPlaylistItem extend
|
|
|
29
29
|
.join('')
|
|
30
30
|
.trim();
|
|
31
31
|
}
|
|
32
|
+
focus(options) {
|
|
33
|
+
this.renderRoot?.querySelector('button')?.focus(options);
|
|
34
|
+
}
|
|
32
35
|
renderMedia() {
|
|
33
36
|
return html `<slot name="media">
|
|
34
37
|
${this.poster === '' ? nothing : html `<img part="thumbnail" alt="" src=${this.poster} />`}
|
|
35
38
|
</slot>`;
|
|
36
39
|
}
|
|
40
|
+
willUpdate(changed) {
|
|
41
|
+
super.willUpdate(changed);
|
|
42
|
+
this.toggleState('current', this.current);
|
|
43
|
+
}
|
|
37
44
|
render() {
|
|
38
45
|
return html `<div part="container" role="listitem">
|
|
39
46
|
<button
|
|
@@ -30,6 +30,7 @@ import '../../menu/index.js';
|
|
|
30
30
|
* @csspart entry-shortcut-key - One key of an entry's shortcut chord, inside `entry-shortcut`. The playlist's entries set none.
|
|
31
31
|
* @csspart entry-shortcut-separator - The `+` between two keys of an entry's shortcut, where the platform writes one.
|
|
32
32
|
* @csspart entry-start - The column at the start of an entry. It is a slot and is always present.
|
|
33
|
+
* @csspart entry-surface - The panel an entry's submenu opens in beside it. The playlist's entries have none.
|
|
33
34
|
* @csspart menu - The menu that holds the entries.
|
|
34
35
|
* @csspart menu-back - The button that goes back from a submenu.
|
|
35
36
|
* @csspart menu-back-mark - The icon on a submenu's back button.
|
|
@@ -88,7 +88,7 @@ let HelixHeadlessVideoPlaylistMenu = class HelixHeadlessVideoPlaylistMenu extend
|
|
|
88
88
|
part="entry"
|
|
89
89
|
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
90
90
|
shortcut-kbd: entry-shortcut-kbd, shortcut-key: entry-shortcut-key,
|
|
91
|
-
shortcut-separator: entry-shortcut-separator, start: entry-start"
|
|
91
|
+
shortcut-separator: entry-shortcut-separator, start: entry-start, surface: entry-surface"
|
|
92
92
|
value=${entry.value}
|
|
93
93
|
checkable
|
|
94
94
|
?checked=${entry.current}
|
|
@@ -35,10 +35,17 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
|
|
|
35
35
|
* your name, and only the title follows the entry.
|
|
36
36
|
*
|
|
37
37
|
* **Keyboard.** Each entry is a button in the tab order, and Enter or Space
|
|
38
|
-
* plays it. The
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* plays it. The arrow keys move focus to the entry before or after, and Home
|
|
39
|
+
* and End to the first and the last, passing over disabled entries. The
|
|
40
|
+
* current entry carries `aria-current`. While focus is on the player, the
|
|
41
|
+
* player's own keys work, and `Video` lists them, and Shift with N or P plays
|
|
42
|
+
* the next or the previous entry. The player's previous and next controls
|
|
43
|
+
* move through the entries in the same order the playlist does.
|
|
44
|
+
*
|
|
45
|
+
* **What a screen reader hears.** A list named by `label`, with a button for
|
|
46
|
+
* each entry. When the playlist moves to another entry, by a press, by the
|
|
47
|
+
* player's controls, or at the end of a clip, a live region says
|
|
48
|
+
* `playing-label`, "Now playing" and the entry's name.
|
|
42
49
|
*
|
|
43
50
|
* @since 0.1.0
|
|
44
51
|
* @status stable
|
|
@@ -54,7 +61,10 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
|
|
|
54
61
|
* @event {HelixEndEvent} helix-end - The last entry finished playing, and the playlist stopped there. Not sent while `repeat` is set.
|
|
55
62
|
*
|
|
56
63
|
* @csspart items - The list of entries.
|
|
64
|
+
* @csspart live - A live region that says which entry plays after the playlist moves.
|
|
57
65
|
* @csspart player - The region that holds the player.
|
|
66
|
+
*
|
|
67
|
+
* @cssstate ended - The last entry finished and the playlist stopped there, until an entry plays again.
|
|
58
68
|
*/
|
|
59
69
|
export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlaylist_base {
|
|
60
70
|
/**
|
|
@@ -66,6 +76,17 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
|
|
|
66
76
|
* The list's name, read by screen readers.
|
|
67
77
|
*/
|
|
68
78
|
label: string;
|
|
79
|
+
/**
|
|
80
|
+
* What the live region says when the playlist moves to another entry.
|
|
81
|
+
* `{title}` is the entry's name.
|
|
82
|
+
*
|
|
83
|
+
* **A move a reader did not make is said.** At the end of a clip the
|
|
84
|
+
* playlist starts the next one, and the only other trace of that is the
|
|
85
|
+
* `aria-current` on a button the reader is not on.
|
|
86
|
+
*/
|
|
87
|
+
playingLabel: string;
|
|
88
|
+
/** The live region, rendered from the first move. */
|
|
89
|
+
private readonly status;
|
|
69
90
|
/**
|
|
70
91
|
* What happens when an entry ends with no next one to go to. `one` plays the
|
|
71
92
|
* same entry again, and `all` goes from the last entry back to the first.
|
|
@@ -168,6 +189,24 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
|
|
|
168
189
|
*/
|
|
169
190
|
previous(): void;
|
|
170
191
|
private readonly handleSelect;
|
|
192
|
+
/**
|
|
193
|
+
* The arrow keys, Home and End on an entry move focus to another one.
|
|
194
|
+
*
|
|
195
|
+
* **The entries the keys reach are the enabled ones**, because a disabled
|
|
196
|
+
* button cannot take focus and a key that landed on one would leave focus
|
|
197
|
+
* where it was. Up and Left go back, Down and Right go on, and Left and
|
|
198
|
+
* Right swap on a right-to-left page, so a list laid out in a row moves the
|
|
199
|
+
* way it reads.
|
|
200
|
+
*/
|
|
201
|
+
private readonly handleKeydown;
|
|
202
|
+
/** The player playing again, after the end or anywhere, means the list has not ended. */
|
|
203
|
+
private readonly handlePlay;
|
|
204
|
+
/**
|
|
205
|
+
* Shift with N plays the next entry and Shift with P the previous one, while
|
|
206
|
+
* focus is on the player, the keys a reader knows from the common players.
|
|
207
|
+
* Not inside a field the player holds, where they are letters.
|
|
208
|
+
*/
|
|
209
|
+
private takePlayerKey;
|
|
171
210
|
private readonly handleNext;
|
|
172
211
|
private readonly handlePrevious;
|
|
173
212
|
/**
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, HelixElement, HelixEndEvent, MixinControllerActive, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, drawStatus, formatWords, HelixElement, HelixEndEvent, inlineStep, MixinControllerActive, StatusController, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessVideoPlaylistItem } from '../item/video-playlist-item.js';
|
|
@@ -13,6 +13,8 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
15
15
|
this.label = 'Playlist';
|
|
16
|
+
this.playingLabel = 'Now playing {title}';
|
|
17
|
+
this.status = new StatusController(this);
|
|
16
18
|
this.shuffle = false;
|
|
17
19
|
this.chased = false;
|
|
18
20
|
this.handleSelect = (event) => {
|
|
@@ -23,6 +25,39 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
23
25
|
this.chased = true;
|
|
24
26
|
this.requestActive(event.detail.value);
|
|
25
27
|
};
|
|
28
|
+
this.handleKeydown = (event) => {
|
|
29
|
+
const from = event.target;
|
|
30
|
+
if (this.takePlayerKey(event)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!(from instanceof HelixHeadlessVideoPlaylistItem) ||
|
|
34
|
+
event.altKey ||
|
|
35
|
+
event.ctrlKey ||
|
|
36
|
+
event.metaKey ||
|
|
37
|
+
event.shiftKey) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const reachable = this.items.filter((one) => !one.disabled);
|
|
41
|
+
const at = reachable.indexOf(from);
|
|
42
|
+
const step = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : inlineStep(this, event.key);
|
|
43
|
+
const to = event.key === 'Home'
|
|
44
|
+
? 0
|
|
45
|
+
: event.key === 'End'
|
|
46
|
+
? reachable.length - 1
|
|
47
|
+
: step !== 0 && at !== -1
|
|
48
|
+
? Math.min(Math.max(at + step, 0), reachable.length - 1)
|
|
49
|
+
: undefined;
|
|
50
|
+
if (to === undefined) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
reachable[to]?.focus();
|
|
55
|
+
};
|
|
56
|
+
this.handlePlay = (event) => {
|
|
57
|
+
if (event.target === this.player) {
|
|
58
|
+
this.toggleState('ended', false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
26
61
|
this.handleNext = () => {
|
|
27
62
|
this.next();
|
|
28
63
|
};
|
|
@@ -38,6 +73,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
38
73
|
return;
|
|
39
74
|
}
|
|
40
75
|
if (this.following(1).next === undefined) {
|
|
76
|
+
this.toggleState('ended', true);
|
|
41
77
|
this.dispatchEvent(new HelixEndEvent());
|
|
42
78
|
return;
|
|
43
79
|
}
|
|
@@ -91,19 +127,43 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
91
127
|
previous() {
|
|
92
128
|
this.step(-1);
|
|
93
129
|
}
|
|
130
|
+
takePlayerKey(event) {
|
|
131
|
+
const player = this.player;
|
|
132
|
+
if (!player ||
|
|
133
|
+
event.target !== player ||
|
|
134
|
+
event.defaultPrevented ||
|
|
135
|
+
!event.shiftKey ||
|
|
136
|
+
event.altKey ||
|
|
137
|
+
event.ctrlKey ||
|
|
138
|
+
event.metaKey ||
|
|
139
|
+
(event.key !== 'N' && event.key !== 'P')) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const origin = event.composedPath()[0];
|
|
143
|
+
if (origin?.tagName === 'INPUT' || origin?.isContentEditable) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
this.step(event.key === 'N' ? 1 : -1);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
94
150
|
connectedCallback() {
|
|
95
151
|
super.connectedCallback();
|
|
96
152
|
this.addEventListener('helix-select', this.handleSelect);
|
|
153
|
+
this.addEventListener('keydown', this.handleKeydown);
|
|
97
154
|
this.addEventListener('helix-next', this.handleNext);
|
|
98
155
|
this.addEventListener('helix-previous', this.handlePrevious);
|
|
99
156
|
this.addEventListener('ended', this.handleEnded, true);
|
|
157
|
+
this.addEventListener('play', this.handlePlay, true);
|
|
100
158
|
}
|
|
101
159
|
disconnectedCallback() {
|
|
102
160
|
super.disconnectedCallback();
|
|
103
161
|
this.removeEventListener('helix-select', this.handleSelect);
|
|
162
|
+
this.removeEventListener('keydown', this.handleKeydown);
|
|
104
163
|
this.removeEventListener('helix-next', this.handleNext);
|
|
105
164
|
this.removeEventListener('helix-previous', this.handlePrevious);
|
|
106
165
|
this.removeEventListener('ended', this.handleEnded, true);
|
|
166
|
+
this.removeEventListener('play', this.handlePlay, true);
|
|
107
167
|
}
|
|
108
168
|
willUpdate(changed) {
|
|
109
169
|
super.willUpdate(changed);
|
|
@@ -123,6 +183,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
123
183
|
return;
|
|
124
184
|
}
|
|
125
185
|
if (player.src !== current.src) {
|
|
186
|
+
this.toggleState('ended', false);
|
|
126
187
|
player.src = current.src;
|
|
127
188
|
player.poster = current.poster;
|
|
128
189
|
player.mediaTitle = current.text;
|
|
@@ -130,6 +191,7 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
130
191
|
player.label = current.text;
|
|
131
192
|
}
|
|
132
193
|
if (this.chased) {
|
|
194
|
+
this.status.say(formatWords(this, 'playingLabel', { title: current.text }));
|
|
133
195
|
void player.updateComplete?.then(() => player.play()).catch(() => { });
|
|
134
196
|
}
|
|
135
197
|
}
|
|
@@ -139,13 +201,18 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
|
|
|
139
201
|
return html `<div part="player"><slot name="player"></slot></div>
|
|
140
202
|
<div part="items" role="list" aria-label=${this.label}>
|
|
141
203
|
<slot></slot>
|
|
142
|
-
</div
|
|
204
|
+
</div>
|
|
205
|
+
${drawStatus(this.status)}`;
|
|
143
206
|
}
|
|
144
207
|
};
|
|
145
208
|
__decorate([
|
|
146
209
|
words,
|
|
147
210
|
property()
|
|
148
211
|
], HelixHeadlessVideoPlaylist.prototype, "label", void 0);
|
|
212
|
+
__decorate([
|
|
213
|
+
words,
|
|
214
|
+
property({ attribute: 'playing-label' })
|
|
215
|
+
], HelixHeadlessVideoPlaylist.prototype, "playingLabel", void 0);
|
|
149
216
|
__decorate([
|
|
150
217
|
property()
|
|
151
218
|
], HelixHeadlessVideoPlaylist.prototype, "repeat", void 0);
|
|
@@ -97,6 +97,11 @@ export interface VirtualListRange {
|
|
|
97
97
|
* shows nothing, since a list whose items have not arrived yet has not said
|
|
98
98
|
* it has none.
|
|
99
99
|
*
|
|
100
|
+
* The list takes the `empty` state while it has read its items and holds none,
|
|
101
|
+
* and the `loading` state while `loading` is set, so your stylesheet can
|
|
102
|
+
* change the box around it for either. A list with no name is reported once
|
|
103
|
+
* in a development build. Name it with `label` or `aria-label`.
|
|
104
|
+
*
|
|
100
105
|
* **Row height.** Rows are placed by `estimate` until they are drawn. Then the
|
|
101
106
|
* list reads the drawn rows' height once and places every row by it, so a
|
|
102
107
|
* wrong estimate still reaches the last item. Set `measured` for rows of
|
|
@@ -131,6 +136,9 @@ export interface VirtualListRange {
|
|
|
131
136
|
* @csspart empty - The row shown when there are no items and none are loading, where `empty-label` or the `empty` slot gives it content.
|
|
132
137
|
* @csspart loading - The row after the last item while `loading` is set.
|
|
133
138
|
* @csspart viewport - The scrolling box. On this tier, it needs only a block size.
|
|
139
|
+
*
|
|
140
|
+
* @cssstate empty - While the list has read its items and holds none.
|
|
141
|
+
* @cssstate loading - While `loading` is set.
|
|
134
142
|
*/
|
|
135
143
|
export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
136
144
|
/**
|
|
@@ -33,6 +33,7 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
33
33
|
this.aria = new AriaForwardController(this, {
|
|
34
34
|
label: true,
|
|
35
35
|
target: () => this.partNamed('viewport'),
|
|
36
|
+
unnamed: 'Give it a `label`, or an `aria-label` that says what the items are.',
|
|
36
37
|
});
|
|
37
38
|
this.virtualize = new VirtualizeController(this, {
|
|
38
39
|
container: () => this.partNamed('viewport'),
|
|
@@ -210,6 +211,8 @@ let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixEleme
|
|
|
210
211
|
}
|
|
211
212
|
updated(changed) {
|
|
212
213
|
super.updated?.(changed);
|
|
214
|
+
this.toggleState('empty', this.readFor !== undefined && this.data.length === 0);
|
|
215
|
+
this.toggleState('loading', this.loading);
|
|
213
216
|
this.virtualize.measure();
|
|
214
217
|
this.place();
|
|
215
218
|
this.virtualize.settle();
|