@fusion.dev/helix-headless 0.144.0 → 0.145.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 +8312 -881
- package/dist/accordion/item/accordion-item.d.ts +48 -12
- package/dist/accordion/item/accordion-item.js +26 -5
- package/dist/accordion/root/accordion-root.d.ts +6 -0
- package/dist/accordion/root/accordion-root.js +7 -1
- package/dist/announcement/root/announcement-root.d.ts +21 -1
- package/dist/announcement/root/announcement-root.js +36 -1
- package/dist/app-header/root/app-header-root.d.ts +7 -0
- package/dist/app-header/root/app-header-root.js +5 -1
- package/dist/app-header/sectioning.d.ts +7 -0
- package/dist/app-header/sectioning.js +26 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
- package/dist/canvas/root/canvas-root.d.ts +38 -3
- package/dist/canvas/root/canvas-root.js +121 -40
- package/dist/carousel/root/carousel-root.d.ts +90 -0
- package/dist/carousel/root/carousel-root.js +134 -12
- package/dist/chat/composer/chat-composer.d.ts +38 -6
- package/dist/chat/composer/chat-composer.js +42 -9
- package/dist/chat/message/chat-message.d.ts +32 -1
- package/dist/chat/message/chat-message.js +25 -9
- package/dist/chat/naming.d.ts +22 -0
- package/dist/chat/naming.js +12 -0
- package/dist/chat/relative-clock.d.ts +39 -0
- package/dist/chat/relative-clock.js +31 -0
- package/dist/chat/root/chat-root.d.ts +36 -0
- package/dist/chat/root/chat-root.js +24 -1
- package/dist/code-block/root/code-block-root.d.ts +8 -1
- package/dist/code-block/root/code-block-root.js +5 -5
- package/dist/copy-button/root/copy-button-root.d.ts +21 -1
- package/dist/copy-button/root/copy-button-root.js +2 -2
- package/dist/data-table/root/data-table-root.d.ts +150 -2
- package/dist/data-table/root/data-table-root.js +303 -29
- package/dist/data-table/tree.d.ts +40 -0
- package/dist/data-table/tree.js +55 -0
- package/dist/description-list/item/description-list-item.d.ts +17 -2
- package/dist/description-list/item/description-list-item.js +15 -2
- package/dist/description-list/root/description-list-root.d.ts +27 -1
- package/dist/description-list/root/description-list-root.js +26 -1
- package/dist/drilldown/item/drilldown-item.d.ts +11 -6
- package/dist/drilldown/item/drilldown-item.js +8 -5
- package/dist/drilldown/root/drilldown-root.d.ts +11 -0
- package/dist/drilldown/root/drilldown-root.js +8 -3
- package/dist/element-events.generated.js +53 -8
- package/dist/filter-input/menu/filter-menu.d.ts +3 -0
- package/dist/filter-input/menu/filter-menu.js +8 -4
- package/dist/image/root/image-root.js +9 -1
- package/dist/input/root/input-root.d.ts +1 -1
- package/dist/input/root/input-root.js +2 -2
- package/dist/list/item/list-item.d.ts +68 -1
- package/dist/list/item/list-item.js +66 -4
- package/dist/list/numbering.d.ts +14 -0
- package/dist/list/numbering.js +8 -0
- package/dist/list/root/list-root.d.ts +106 -9
- package/dist/list/root/list-root.js +189 -4
- package/dist/list/selection.d.ts +19 -0
- package/dist/list/selection.js +21 -0
- package/dist/listbox/root/listbox-root.js +1 -2
- package/dist/map/root/map-root.d.ts +6 -2
- package/dist/map/root/map-root.js +5 -2
- package/dist/megamenu/root/megamenu-root.d.ts +21 -0
- package/dist/megamenu/root/megamenu-root.js +16 -2
- package/dist/menu/index.d.ts +1 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/item/menu-item.d.ts +38 -11
- package/dist/menu/item/menu-item.js +33 -1
- package/dist/menu/root/menu-root.d.ts +25 -1
- package/dist/menu/root/menu-root.js +63 -6
- package/dist/menu/section/menu-section.d.ts +55 -0
- package/dist/menu/section/menu-section.js +60 -0
- package/dist/navigation/root/navigation-root.d.ts +16 -1
- package/dist/navigation/root/navigation-root.js +27 -1
- package/dist/notifications/root/notifications-root.d.ts +18 -0
- package/dist/notifications/root/notifications-root.js +17 -1
- package/dist/object-inspector/root/object-inspector-root.d.ts +97 -16
- package/dist/object-inspector/root/object-inspector-root.js +179 -79
- package/dist/object-inspector/rows.d.ts +57 -0
- package/dist/object-inspector/rows.js +86 -0
- package/dist/outline/levels.d.ts +31 -0
- package/dist/outline/levels.js +23 -0
- package/dist/outline/root/outline-root.d.ts +68 -3
- package/dist/outline/root/outline-root.js +73 -8
- package/dist/pagination/size/pagination-size.d.ts +11 -2
- package/dist/pagination/size/pagination-size.js +19 -4
- package/dist/radio/group/radio-group.d.ts +4 -10
- package/dist/radio/group/radio-group.js +5 -10
- package/dist/rating/root/rating-root.d.ts +1 -1
- package/dist/rating/root/rating-root.js +2 -2
- package/dist/scroll-area/root/scroll-area-root.d.ts +113 -0
- package/dist/scroll-area/root/scroll-area-root.js +218 -3
- package/dist/scroll-area/scrollbar.d.ts +65 -0
- package/dist/scroll-area/scrollbar.js +24 -0
- package/dist/select/root/select-root.js +1 -1
- package/dist/splitter/root/splitter-root.d.ts +13 -0
- package/dist/splitter/root/splitter-root.js +20 -2
- package/dist/splitter/snap.d.ts +9 -0
- package/dist/splitter/snap.js +12 -0
- package/dist/steps/reach.d.ts +15 -0
- package/dist/steps/reach.js +12 -0
- package/dist/steps/root/steps-root.d.ts +30 -0
- package/dist/steps/root/steps-root.js +53 -0
- package/dist/steps/step/step.d.ts +38 -2
- package/dist/steps/step/step.js +28 -6
- package/dist/table/column/table-column.d.ts +11 -0
- package/dist/table/column/table-column.js +6 -1
- package/dist/table/root/table-root.d.ts +101 -0
- package/dist/table/root/table-root.js +166 -5
- package/dist/table/row/table-row.d.ts +47 -1
- package/dist/table/row/table-row.js +39 -2
- package/dist/tabs/panel/tab-panel.d.ts +9 -5
- package/dist/tabs/panel/tab-panel.js +3 -4
- package/dist/tabs/root/tabs-root.d.ts +25 -1
- package/dist/tabs/root/tabs-root.js +21 -3
- package/dist/tabs/tab/tab.d.ts +36 -4
- package/dist/tabs/tab/tab.js +46 -3
- package/dist/timeline/group/timeline-group.d.ts +46 -0
- package/dist/timeline/group/timeline-group.js +37 -0
- package/dist/timeline/index.d.ts +1 -0
- package/dist/timeline/index.js +1 -0
- package/dist/timeline/root/timeline-root.d.ts +10 -3
- package/dist/toast/item/toast-item.d.ts +10 -17
- package/dist/toast/item/toast-item.js +15 -36
- package/dist/toolbar/root/toolbar-root.d.ts +13 -0
- package/dist/toolbar/root/toolbar-root.js +15 -5
- package/dist/tree/item/tree-item.d.ts +54 -1
- package/dist/tree/item/tree-item.js +89 -20
- package/dist/tree/root/tree-root.d.ts +37 -0
- package/dist/tree/root/tree-root.js +51 -6
- package/dist/tree/selection.d.ts +24 -0
- package/dist/tree/selection.js +26 -0
- package/dist/video/media-events.d.ts +49 -0
- package/dist/video/media-events.js +39 -0
- package/dist/video/root/video-root.d.ts +99 -13
- package/dist/video/root/video-root.js +84 -47
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
- package/dist/video-playlist/order.d.ts +41 -0
- package/dist/video-playlist/order.js +34 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
- package/dist/video-playlist/root/video-playlist-root.js +60 -16
- package/dist/virtual-list/controls.d.ts +4 -0
- package/dist/virtual-list/controls.js +30 -0
- package/dist/virtual-list/fill.d.ts +30 -0
- package/dist/virtual-list/fill.js +39 -0
- package/dist/virtual-list/keys.d.ts +15 -0
- package/dist/virtual-list/keys.js +24 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
- package/dist/virtual-list/root/virtual-list-root.js +175 -34
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +3778 -801
- package/types/svelte.d.ts +2 -0
- package/types/vue.d.ts +782 -215
- package/dist/listbox/range.d.ts +0 -17
- package/dist/listbox/range.js +0 -5
|
@@ -29,6 +29,11 @@ import '../../menu/index.js';
|
|
|
29
29
|
* `readyState` of zero, and it would not play. They are copied so your light
|
|
30
30
|
* DOM stays intact, and are copied again whenever the slot changes.
|
|
31
31
|
*
|
|
32
|
+
* The player sends every media event of its inner video under the same name,
|
|
33
|
+
* without bubbling, as a `<video>` sends it. Listen on the player as you would
|
|
34
|
+
* on a video: `timeupdate`, `volumechange`, `loadedmetadata`, `error`, and the
|
|
35
|
+
* rest.
|
|
36
|
+
*
|
|
32
37
|
* @since 0.1.0
|
|
33
38
|
* @status stable
|
|
34
39
|
* @category media
|
|
@@ -36,15 +41,52 @@ import '../../menu/index.js';
|
|
|
36
41
|
* @documentation https://fusion.dev/helix/components/video
|
|
37
42
|
*
|
|
38
43
|
* @slot - `<source>` and `<track>` elements, which are copied into the video.
|
|
44
|
+
* @slot captions-icon - Replaces the glyph on the captions control.
|
|
39
45
|
* @slot controls - Extra controls, added to the control bar before the fullscreen button.
|
|
46
|
+
* @slot download-icon - Replaces the glyph on the download control, on the bar or in the `overflow` menu.
|
|
47
|
+
* @slot exit-fullscreen-icon - Replaces the glyph on the fullscreen control while the player fills the screen.
|
|
48
|
+
* @slot forward-icon - Replaces the glyph on the control that skips forward, on the bar or in the `overflow` menu.
|
|
49
|
+
* @slot fullscreen-icon - Replaces the glyph on the fullscreen control.
|
|
40
50
|
* @slot header - Content over the top left of the picture, such as a heading and a back button.
|
|
51
|
+
* @slot more-icon - Replaces the glyph on the button that opens the `overflow` menu.
|
|
52
|
+
* @slot mute-icon - Replaces the glyph on the mute control while there is no sound.
|
|
53
|
+
* @slot next-icon - Replaces the glyph on the control that asks for the next video, on the bar or in the `overflow` menu.
|
|
54
|
+
* @slot pause-icon - Replaces the glyph on the play control while the video plays.
|
|
55
|
+
* @slot pip-icon - Replaces the glyph on the picture-in-picture control, on the bar or in the `overflow` menu.
|
|
56
|
+
* @slot play-icon - Replaces the glyph on the play control while the video is paused.
|
|
41
57
|
* @slot poster - Shown over the video until it starts.
|
|
58
|
+
* @slot previous-icon - Replaces the glyph on the control that asks for the previous video, on the bar or in the `overflow` menu.
|
|
59
|
+
* @slot remote-icon - Replaces the glyph on the control that plays on another device, on the bar or in the `overflow` menu.
|
|
60
|
+
* @slot rewind-icon - Replaces the glyph on the control that skips back, on the bar or in the `overflow` menu.
|
|
61
|
+
* @slot speed-icon - Replaces the glyph beside the playback speed in the `overflow` menu. On the bar, the speed is shown as a number.
|
|
62
|
+
* @slot start-icon - Replaces the glyph on the large play button over the picture.
|
|
63
|
+
* @slot volume-icon - Replaces the glyph on the mute control while there is sound.
|
|
42
64
|
*
|
|
43
|
-
* @event {Event}
|
|
65
|
+
* @event {Event} abort - The media stopped loading before it finished, without an error.
|
|
66
|
+
* @event {Event} canplay - Enough has loaded to start playing.
|
|
67
|
+
* @event {Event} canplaythrough - Enough has loaded to play to the end without stopping, at the current rate.
|
|
68
|
+
* @event {Event} durationchange - The duration changed, which it does once the metadata is in.
|
|
69
|
+
* @event {Event} emptied - The media was reset, as when `src` changes.
|
|
70
|
+
* @event {Event} ended - The video reached its end.
|
|
71
|
+
* @event {Event} error - The source could not be loaded or played. The player shows `error-label` as well.
|
|
44
72
|
* @event {HelixNextEvent} helix-next - The next video was requested. Load it in response.
|
|
45
73
|
* @event {HelixPreviousEvent} helix-previous - The previous video was requested. Load it in response.
|
|
46
|
-
* @event {Event}
|
|
47
|
-
* @event {Event}
|
|
74
|
+
* @event {Event} loadeddata - The frame at the current position has loaded.
|
|
75
|
+
* @event {Event} loadedmetadata - The duration and the size of the video are known.
|
|
76
|
+
* @event {Event} loadstart - The video started looking for its media.
|
|
77
|
+
* @event {Event} pause - Playback paused.
|
|
78
|
+
* @event {Event} play - Playback was asked to start.
|
|
79
|
+
* @event {Event} playing - Playback started, or started again after waiting for data.
|
|
80
|
+
* @event {Event} progress - More of the media has downloaded.
|
|
81
|
+
* @event {Event} ratechange - The playback rate changed. Read `playbackRate`.
|
|
82
|
+
* @event {Event} resize - The video's own width or height changed. Read `videoWidth` and `videoHeight`.
|
|
83
|
+
* @event {Event} seeked - A move to a new position finished.
|
|
84
|
+
* @event {Event} seeking - A move to a new position started.
|
|
85
|
+
* @event {Event} stalled - The browser is trying to load more data and none is arriving.
|
|
86
|
+
* @event {Event} suspend - Loading paused on purpose, as when `preload` asks for no more.
|
|
87
|
+
* @event {Event} timeupdate - The position changed. Read `currentTime`.
|
|
88
|
+
* @event {Event} volumechange - The volume or the mute changed. Read `volume` and `muted`.
|
|
89
|
+
* @event {Event} waiting - Playback stopped to wait for more data.
|
|
48
90
|
*
|
|
49
91
|
* @csspart captions - The button that turns captions on and off.
|
|
50
92
|
* @csspart captions-menu - The menu of caption languages, when the video has more than one track.
|
|
@@ -60,8 +102,11 @@ import '../../menu/index.js';
|
|
|
60
102
|
* @csspart cue - The caption currently on screen, drawn by this element.
|
|
61
103
|
* @csspart download - The button that downloads the file, when `extra-controls` includes it.
|
|
62
104
|
* @csspart entry - One row in a menu that the player opens.
|
|
105
|
+
* @csspart entry-end - The trailing column of a row, which holds its shortcut. It is always rendered.
|
|
63
106
|
* @csspart entry-item - The row, which is an `<a>` where `href` is set.
|
|
64
107
|
* @csspart entry-mark - The trailing mark: a check mark when the row is checked, or a caret when it opens a submenu.
|
|
108
|
+
* @csspart entry-shortcut - The key drawn for a row's shortcut. The player's rows set none.
|
|
109
|
+
* @csspart entry-shortcut-kbd - The key inside `entry-shortcut`.
|
|
65
110
|
* @csspart entry-start - The leading column that holds the `start` slot. It is always rendered.
|
|
66
111
|
* @csspart failure - The message shown when the source cannot be played.
|
|
67
112
|
* @csspart forward - The button that skips forward ten seconds.
|
|
@@ -109,10 +154,10 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
109
154
|
* Whether the video starts playing on its own. Browsers allow this only while
|
|
110
155
|
* it is muted.
|
|
111
156
|
*
|
|
112
|
-
* Forwarded
|
|
113
|
-
* the browser's policy and
|
|
114
|
-
*
|
|
115
|
-
*
|
|
157
|
+
* Forwarded to the video, which decides. Whether an unprompted play is
|
|
158
|
+
* permitted is the browser's policy, and what follows a refused autoplay is a
|
|
159
|
+
* paused player showing its start control, which is the correct thing to be
|
|
160
|
+
* looking at.
|
|
116
161
|
*/
|
|
117
162
|
autoplay: boolean;
|
|
118
163
|
/**
|
|
@@ -282,6 +327,11 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
282
327
|
* What the control that floats the video over other windows is called.
|
|
283
328
|
*/
|
|
284
329
|
pipLabel: string;
|
|
330
|
+
/**
|
|
331
|
+
* The name of the player when `label` is not set. The player is a group, and
|
|
332
|
+
* this is what a screen reader calls it on arriving.
|
|
333
|
+
*/
|
|
334
|
+
playerLabel: string;
|
|
285
335
|
/**
|
|
286
336
|
* The name of the play control while pressing it would start the video.
|
|
287
337
|
*/
|
|
@@ -340,6 +390,14 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
340
390
|
* behind a hero, a grid of eight thumbnails, anything where the video is
|
|
341
391
|
* decoration rather than something to start.
|
|
342
392
|
*
|
|
393
|
+
* **It is for the pointer, and the bar's Play is for everyone else.** Both
|
|
394
|
+
* play the video, so a keyboard met two stops called "Play" one after the
|
|
395
|
+
* other and a screen reader read the same button twice. The large one is out
|
|
396
|
+
* of the tab order and hidden from assistive technology while the bar is
|
|
397
|
+
* drawn, which is whenever it is: it shows only while the video is not
|
|
398
|
+
* playing, and the bar does not fade then. A press on it leaves focus on the
|
|
399
|
+
* player, so the keys go on working once it has gone.
|
|
400
|
+
*
|
|
343
401
|
* A value rather than a boolean, so the resting state is the attribute's
|
|
344
402
|
* absence: a boolean defaulting to on would have to be spelled as its own
|
|
345
403
|
* negation.
|
|
@@ -510,14 +568,19 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
510
568
|
set volume(level: number);
|
|
511
569
|
private get video();
|
|
512
570
|
/**
|
|
513
|
-
* Starts playback,
|
|
571
|
+
* Starts playback, and returns the video's own promise: it settles when
|
|
572
|
+
* playback starts, and rejects when the browser refuses.
|
|
573
|
+
*
|
|
574
|
+
* **A refusal is not reported as an unhandled rejection.** A browser refuses
|
|
575
|
+
* `play()` when nothing the reader did asked for it, and that is a policy
|
|
576
|
+
* answer: what follows is a paused player showing its start control. So the
|
|
577
|
+
* promise is marked handled here, and a caller who awaits it still hears the
|
|
578
|
+
* refusal.
|
|
514
579
|
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
* a fault. What follows a refused play is a paused player showing its start
|
|
518
|
-
* control, which is the correct thing to be looking at.
|
|
580
|
+
* Before the first render there is no video yet, and the promise waits for
|
|
581
|
+
* one.
|
|
519
582
|
*/
|
|
520
|
-
play(): void
|
|
583
|
+
play(): Promise<void>;
|
|
521
584
|
/** Pauses playback and leaves the current frame showing. */
|
|
522
585
|
pause(): void;
|
|
523
586
|
/**
|
|
@@ -645,6 +708,16 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
645
708
|
* mouse left resting until the bar faded.
|
|
646
709
|
*/
|
|
647
710
|
private readonly handlePicture;
|
|
711
|
+
/**
|
|
712
|
+
* A press on the large play button plays, and leaves focus on the player.
|
|
713
|
+
*
|
|
714
|
+
* The button takes focus from the press and is gone once the video plays,
|
|
715
|
+
* which drops focus to the page, and the keys bound to the player stop
|
|
716
|
+
* answering. Moved to the stage first, they keep working. The press came from
|
|
717
|
+
* a pointer, the button being out of the tab order, so the stage draws no
|
|
718
|
+
* ring for it.
|
|
719
|
+
*/
|
|
720
|
+
private readonly handleStart;
|
|
648
721
|
/**
|
|
649
722
|
* The keys every player uses, bound to this element rather than the document.
|
|
650
723
|
*
|
|
@@ -716,6 +789,19 @@ export declare class HelixHeadlessVideo extends HelixElement {
|
|
|
716
789
|
private heardMenu;
|
|
717
790
|
protected renderOverflowItem(one: string): TemplateResult;
|
|
718
791
|
protected renderBar(): TemplateResult;
|
|
792
|
+
/**
|
|
793
|
+
* The large play button over the picture, or nothing.
|
|
794
|
+
*
|
|
795
|
+
* Drawn whenever the video is not playing, which is what the mark means:
|
|
796
|
+
* press this to play. Not only before the first play, which would make the
|
|
797
|
+
* mark mean two different things at two moments that are the same state.
|
|
798
|
+
* Every player a reader has used shows it on pause, and the one who paused to
|
|
799
|
+
* study a frame clears it by pressing the thing in the way.
|
|
800
|
+
*
|
|
801
|
+
* Not drawn at all where the browser draws its own controls, since that bar
|
|
802
|
+
* has a play button and two would be two.
|
|
803
|
+
*/
|
|
804
|
+
protected renderStart(): TemplateResult | typeof nothing;
|
|
719
805
|
protected render(): TemplateResult;
|
|
720
806
|
}
|
|
721
807
|
declare global {
|
|
@@ -11,6 +11,7 @@ import { property, state } from 'lit/decorators.js';
|
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
12
12
|
import '../../menu/index.js';
|
|
13
13
|
import { HelixHeadlessMenu } from '../../menu/root/menu-root.js';
|
|
14
|
+
import { relayMedia } from '../media-events.js';
|
|
14
15
|
import { barControls, menuControls, ownFile } from './extra-controls.js';
|
|
15
16
|
import { takeKey } from './keys.js';
|
|
16
17
|
const clock = (seconds) => {
|
|
@@ -53,6 +54,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
53
54
|
this.pauseLabel = 'Pause';
|
|
54
55
|
this.percentLabel = '{percent} percent';
|
|
55
56
|
this.pipLabel = 'Picture in picture';
|
|
57
|
+
this.playerLabel = 'Video';
|
|
56
58
|
this.playLabel = 'Play';
|
|
57
59
|
this.playsInline = true;
|
|
58
60
|
this.poster = '';
|
|
@@ -149,7 +151,6 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
149
151
|
this.claimSession();
|
|
150
152
|
}
|
|
151
153
|
this.session.report(this.playing);
|
|
152
|
-
this.dispatchEvent(new Event(event.type, { bubbles: false, composed: false }));
|
|
153
154
|
};
|
|
154
155
|
this.handleVolume = () => {
|
|
155
156
|
const video = this.video;
|
|
@@ -179,6 +180,10 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
179
180
|
}
|
|
180
181
|
this.toggle();
|
|
181
182
|
};
|
|
183
|
+
this.handleStart = () => {
|
|
184
|
+
this.partNamed('stage')?.focus({ preventScroll: true });
|
|
185
|
+
this.toggle();
|
|
186
|
+
};
|
|
182
187
|
this.handleKeydown = (event) => {
|
|
183
188
|
if (this.controls) {
|
|
184
189
|
return;
|
|
@@ -296,7 +301,14 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
296
301
|
return this.partNamed('video');
|
|
297
302
|
}
|
|
298
303
|
play() {
|
|
299
|
-
|
|
304
|
+
const video = this.video;
|
|
305
|
+
const started = video
|
|
306
|
+
? video.play()
|
|
307
|
+
: this.updateComplete.then(async () => {
|
|
308
|
+
await this.video?.play();
|
|
309
|
+
});
|
|
310
|
+
started.catch(() => { });
|
|
311
|
+
return started;
|
|
300
312
|
}
|
|
301
313
|
pause() {
|
|
302
314
|
this.video?.pause();
|
|
@@ -307,7 +319,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
307
319
|
return;
|
|
308
320
|
}
|
|
309
321
|
if (video.paused) {
|
|
310
|
-
this.play();
|
|
322
|
+
void this.play();
|
|
311
323
|
}
|
|
312
324
|
else {
|
|
313
325
|
this.pause();
|
|
@@ -419,6 +431,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
419
431
|
this.silent = video.muted;
|
|
420
432
|
this.loudness = video.volume;
|
|
421
433
|
void this.remote.attach(video);
|
|
434
|
+
relayMedia(video, this);
|
|
422
435
|
}
|
|
423
436
|
}
|
|
424
437
|
updated(changed) {
|
|
@@ -438,7 +451,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
438
451
|
aria-pressed=${this.remoteConnected ? 'true' : 'false'}
|
|
439
452
|
@click=${() => void this.remote.prompt()}
|
|
440
453
|
>
|
|
441
|
-
|
|
454
|
+
<slot name="remote-icon">${drawIcon(airplay)}</slot>
|
|
442
455
|
</button>`;
|
|
443
456
|
}
|
|
444
457
|
if (one === 'rewind') {
|
|
@@ -448,7 +461,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
448
461
|
aria-label=${fillTemplate(this.rewindLabel, { seconds: SKIP_SECONDS })}
|
|
449
462
|
@click=${() => this.skipBy(-SKIP_SECONDS)}
|
|
450
463
|
>
|
|
451
|
-
|
|
464
|
+
<slot name="rewind-icon">${drawIcon(arrowCounterClockwise)}</slot>
|
|
452
465
|
</button>`;
|
|
453
466
|
}
|
|
454
467
|
if (one === 'forward') {
|
|
@@ -458,7 +471,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
458
471
|
aria-label=${fillTemplate(this.forwardLabel, { seconds: SKIP_SECONDS })}
|
|
459
472
|
@click=${() => this.skipBy(SKIP_SECONDS)}
|
|
460
473
|
>
|
|
461
|
-
|
|
474
|
+
<slot name="forward-icon">${drawIcon(arrowClockwise)}</slot>
|
|
462
475
|
</button>`;
|
|
463
476
|
}
|
|
464
477
|
if (one === 'speed') {
|
|
@@ -478,7 +491,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
478
491
|
aria-label=${this.previousLabel}
|
|
479
492
|
@click=${() => this.skip(true)}
|
|
480
493
|
>
|
|
481
|
-
|
|
494
|
+
<slot name="previous-icon">${drawIcon(skipBack)}</slot>
|
|
482
495
|
</button>`;
|
|
483
496
|
}
|
|
484
497
|
if (one === 'next') {
|
|
@@ -488,7 +501,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
488
501
|
aria-label=${this.nextLabel}
|
|
489
502
|
@click=${() => this.skip(false)}
|
|
490
503
|
>
|
|
491
|
-
|
|
504
|
+
<slot name="next-icon">${drawIcon(skipForward)}</slot>
|
|
492
505
|
</button>`;
|
|
493
506
|
}
|
|
494
507
|
if (one === 'download') {
|
|
@@ -501,7 +514,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
501
514
|
rel=${local ? nothing : 'noreferrer'}
|
|
502
515
|
aria-label=${this.downloadLabel}
|
|
503
516
|
>
|
|
504
|
-
|
|
517
|
+
<slot name="download-icon">${drawIcon(download)}</slot>
|
|
505
518
|
</a>`;
|
|
506
519
|
}
|
|
507
520
|
return html `<button
|
|
@@ -510,7 +523,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
510
523
|
aria-label=${this.pipLabel}
|
|
511
524
|
@click=${() => void this.togglePip()}
|
|
512
525
|
>
|
|
513
|
-
|
|
526
|
+
<slot name="pip-icon">${drawIcon(pictureInPicture)}</slot>
|
|
514
527
|
</button>`;
|
|
515
528
|
}
|
|
516
529
|
renderCaptions() {
|
|
@@ -526,7 +539,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
526
539
|
aria-pressed=${this.captioned ? 'true' : 'false'}
|
|
527
540
|
@click=${() => this.toggleCaptions()}
|
|
528
541
|
>
|
|
529
|
-
|
|
542
|
+
<slot name="captions-icon">${drawIcon(closedCaptioning)}</slot>
|
|
530
543
|
</button>`;
|
|
531
544
|
}
|
|
532
545
|
return html `<${this.tag('menu')} part="captions-menu"
|
|
@@ -542,11 +555,12 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
542
555
|
aria-haspopup="menu"
|
|
543
556
|
aria-expanded=${this.menuOpen === 'captions' ? 'true' : 'false'}
|
|
544
557
|
>
|
|
545
|
-
|
|
558
|
+
<slot name="captions-icon">${drawIcon(closedCaptioning)}</slot>
|
|
546
559
|
</button>
|
|
547
560
|
<${this.tag('menu-item')}
|
|
548
561
|
part="entry"
|
|
549
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
562
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
563
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
550
564
|
value="off"
|
|
551
565
|
checkable
|
|
552
566
|
exclusive
|
|
@@ -557,7 +571,8 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
557
571
|
${this.captionsOffLabel}
|
|
558
572
|
</${this.tag('menu-item')}>
|
|
559
573
|
${tracks.map((track, at) => html `<${this.tag('menu-item')} part="entry"
|
|
560
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
574
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
575
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
561
576
|
value=${track.language || String(at)}
|
|
562
577
|
checkable
|
|
563
578
|
exclusive
|
|
@@ -584,63 +599,72 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
584
599
|
renderOverflowItem(one) {
|
|
585
600
|
if (one === 'remote') {
|
|
586
601
|
return html `<${this.tag('menu-item')} part="entry"
|
|
587
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
602
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
603
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
588
604
|
value="remote"
|
|
589
605
|
@helix-select=${() => void this.remote.prompt()}
|
|
590
606
|
>
|
|
591
|
-
|
|
607
|
+
<slot name="remote-icon" slot="start">${drawIcon(airplay)}</slot>
|
|
592
608
|
${this.remoteLabel}
|
|
593
609
|
</${this.tag('menu-item')}>`;
|
|
594
610
|
}
|
|
595
611
|
if (one === 'speed') {
|
|
596
612
|
return html `<${this.tag('menu-item')} part="entry"
|
|
597
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
598
|
-
|
|
613
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
614
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start" value="speed" @helix-select=${() => this.cycleSpeed()}>
|
|
615
|
+
<slot name="speed-icon" slot="start">${drawIcon(gauge)}</slot>
|
|
599
616
|
${this.speedLabel}: ${this.rate}x
|
|
600
617
|
</${this.tag('menu-item')}>`;
|
|
601
618
|
}
|
|
602
619
|
if (one === 'rewind' || one === 'forward') {
|
|
603
620
|
const back = one === 'rewind';
|
|
604
621
|
return html `<${this.tag('menu-item')} part="entry"
|
|
605
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
622
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
623
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
606
624
|
value=${one}
|
|
607
625
|
@helix-select=${() => this.skipBy(back ? -SKIP_SECONDS : SKIP_SECONDS)}
|
|
608
626
|
>
|
|
609
|
-
${
|
|
627
|
+
${back
|
|
628
|
+
? html `<slot name="rewind-icon" slot="start">${drawIcon(arrowCounterClockwise)}</slot>`
|
|
629
|
+
: html `<slot name="forward-icon" slot="start">${drawIcon(arrowClockwise)}</slot>`}
|
|
610
630
|
${fillTemplate(back ? this.rewindLabel : this.forwardLabel, { seconds: SKIP_SECONDS })}
|
|
611
631
|
</${this.tag('menu-item')}>`;
|
|
612
632
|
}
|
|
613
633
|
if (one === 'previous') {
|
|
614
634
|
return html `<${this.tag('menu-item')} part="entry"
|
|
615
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
616
|
-
|
|
635
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
636
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start" value="previous" @helix-select=${() => this.skip(true)}>
|
|
637
|
+
<slot name="previous-icon" slot="start">${drawIcon(skipBack)}</slot>
|
|
617
638
|
${this.previousLabel}
|
|
618
639
|
</${this.tag('menu-item')}>`;
|
|
619
640
|
}
|
|
620
641
|
if (one === 'next') {
|
|
621
642
|
return html `<${this.tag('menu-item')} part="entry"
|
|
622
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
623
|
-
|
|
643
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
644
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start" value="next" @helix-select=${() => this.skip(false)}>
|
|
645
|
+
<slot name="next-icon" slot="start">${drawIcon(skipForward)}</slot>
|
|
624
646
|
${this.nextLabel}
|
|
625
647
|
</${this.tag('menu-item')}>`;
|
|
626
648
|
}
|
|
627
649
|
if (one === 'download') {
|
|
628
650
|
const local = ownFile(this.src);
|
|
629
651
|
return html `<${this.tag('menu-item')} part="entry"
|
|
630
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
652
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
653
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
631
654
|
?download=${local}
|
|
632
655
|
href=${this.src}
|
|
633
656
|
rel=${local ? nothing : 'noreferrer'}
|
|
634
657
|
target=${local ? nothing : '_blank'}
|
|
635
658
|
value="download"
|
|
636
659
|
>
|
|
637
|
-
|
|
660
|
+
<slot name="download-icon" slot="start">${drawIcon(download)}</slot>
|
|
638
661
|
${this.downloadLabel}
|
|
639
662
|
</${this.tag('menu-item')}>`;
|
|
640
663
|
}
|
|
641
664
|
return html `<${this.tag('menu-item')} part="entry"
|
|
642
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
643
|
-
|
|
665
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
666
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start" value="pip" @helix-select=${() => void this.togglePip()}>
|
|
667
|
+
<slot name="pip-icon" slot="start">${drawIcon(pictureInPicture)}</slot>
|
|
644
668
|
${this.pipLabel}
|
|
645
669
|
</${this.tag('menu-item')}>`;
|
|
646
670
|
}
|
|
@@ -653,7 +677,9 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
653
677
|
aria-label=${this.playing ? this.pauseLabel : this.playLabel}
|
|
654
678
|
@click=${() => this.toggle()}
|
|
655
679
|
>
|
|
656
|
-
${
|
|
680
|
+
${this.playing
|
|
681
|
+
? html `<slot name="pause-icon">${drawIcon(pause)}</slot>`
|
|
682
|
+
: html `<slot name="play-icon">${drawIcon(play)}</slot>`}
|
|
657
683
|
</button>
|
|
658
684
|
<span part="time">${clock(this.position)} / ${clock(this.knownDuration)}</span>
|
|
659
685
|
<!-- Left to right on every page. A player's timeline is a tape running
|
|
@@ -691,7 +717,9 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
691
717
|
}
|
|
692
718
|
}}
|
|
693
719
|
>
|
|
694
|
-
${
|
|
720
|
+
${this.silent || this.loudness === 0
|
|
721
|
+
? html `<slot name="mute-icon">${drawIcon(speakerX)}</slot>`
|
|
722
|
+
: html `<slot name="volume-icon">${drawIcon(speakerHigh)}</slot>`}
|
|
695
723
|
</button>
|
|
696
724
|
<input
|
|
697
725
|
part="volume"
|
|
@@ -725,7 +753,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
725
753
|
aria-haspopup="menu"
|
|
726
754
|
aria-expanded=${this.menuOpen === 'overflow' ? 'true' : 'false'}
|
|
727
755
|
>
|
|
728
|
-
|
|
756
|
+
<slot name="more-icon">${drawIcon(dotsThreeVertical)}</slot>
|
|
729
757
|
</button>
|
|
730
758
|
${this.overflow.map((one) => this.renderOverflowItem(one))}
|
|
731
759
|
</${this.tag('menu')}>`}
|
|
@@ -738,16 +766,35 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
738
766
|
aria-pressed=${this.full ? 'true' : 'false'}
|
|
739
767
|
@click=${() => void this.fullscreen.toggle()}
|
|
740
768
|
>
|
|
741
|
-
${
|
|
769
|
+
${this.full
|
|
770
|
+
? html `<slot name="exit-fullscreen-icon">${drawIcon(cornersIn)}</slot>`
|
|
771
|
+
: html `<slot name="fullscreen-icon">${drawIcon(cornersOut)}</slot>`}
|
|
742
772
|
</button>
|
|
743
773
|
</div>`;
|
|
744
774
|
}
|
|
775
|
+
renderStart() {
|
|
776
|
+
if (this.controls ||
|
|
777
|
+
this.playing ||
|
|
778
|
+
this.buffering ||
|
|
779
|
+
this.failed ||
|
|
780
|
+
this.startControl === 'none') {
|
|
781
|
+
return nothing;
|
|
782
|
+
}
|
|
783
|
+
return html `<button
|
|
784
|
+
part="start"
|
|
785
|
+
type="button"
|
|
786
|
+
tabindex="-1"
|
|
787
|
+
aria-hidden="true"
|
|
788
|
+
aria-label=${this.playLabel}
|
|
789
|
+
@click=${this.handleStart}
|
|
790
|
+
><slot name="start-icon">${drawIcon(play)}</slot></button>`;
|
|
791
|
+
}
|
|
745
792
|
render() {
|
|
746
793
|
return html `<div
|
|
747
794
|
part="stage"
|
|
748
795
|
tabindex=${this.controls ? nothing : '0'}
|
|
749
796
|
role=${this.controls ? nothing : 'group'}
|
|
750
|
-
aria-label=${this.controls ? nothing : this.label ||
|
|
797
|
+
aria-label=${this.controls ? nothing : this.label || this.playerLabel}
|
|
751
798
|
@keydown=${this.handleKeydown}
|
|
752
799
|
@pointermove=${this.idler.stir}
|
|
753
800
|
@pointerleave=${this.idler.leave}
|
|
@@ -820,20 +867,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
820
867
|
${this.errorLabel}
|
|
821
868
|
</div>`
|
|
822
869
|
: nothing}
|
|
823
|
-
${this.
|
|
824
|
-
this.playing ||
|
|
825
|
-
this.buffering ||
|
|
826
|
-
this.failed ||
|
|
827
|
-
this.startControl === 'none'
|
|
828
|
-
? nothing
|
|
829
|
-
: html `<button
|
|
830
|
-
part="start"
|
|
831
|
-
type="button"
|
|
832
|
-
aria-label=${this.playLabel}
|
|
833
|
-
@click=${() => this.toggle()}
|
|
834
|
-
>
|
|
835
|
-
${drawIcon(play)}
|
|
836
|
-
</button>`}
|
|
870
|
+
${this.renderStart()}
|
|
837
871
|
${this.controls ? nothing : this.renderBar()}
|
|
838
872
|
</div>`;
|
|
839
873
|
}
|
|
@@ -910,6 +944,9 @@ __decorate([
|
|
|
910
944
|
__decorate([
|
|
911
945
|
property({ attribute: 'pip-label' })
|
|
912
946
|
], HelixHeadlessVideo.prototype, "pipLabel", void 0);
|
|
947
|
+
__decorate([
|
|
948
|
+
property({ attribute: 'player-label' })
|
|
949
|
+
], HelixHeadlessVideo.prototype, "playerLabel", void 0);
|
|
913
950
|
__decorate([
|
|
914
951
|
property({ attribute: 'play-label' })
|
|
915
952
|
], HelixHeadlessVideo.prototype, "playLabel", void 0);
|
|
@@ -22,8 +22,11 @@ import '../../menu/index.js';
|
|
|
22
22
|
* @event {HelixSelectEvent} helix-select - An entry was chosen. The event carries its value, and the playlist listens for it. Cancelable: cancel it to keep the current entry.
|
|
23
23
|
*
|
|
24
24
|
* @csspart entry - One entry in the menu.
|
|
25
|
+
* @csspart entry-end - The column at the end of an entry, which holds its shortcut. It is always present.
|
|
25
26
|
* @csspart entry-item - The entry's row. It is an `<a>` when `href` is set.
|
|
26
27
|
* @csspart entry-mark - The mark at the end of an entry: a check mark when it is checked, or a caret when it opens a submenu.
|
|
28
|
+
* @csspart entry-shortcut - The key drawn for an entry's shortcut. The playlist's entries set none.
|
|
29
|
+
* @csspart entry-shortcut-kbd - The key inside `entry-shortcut`.
|
|
27
30
|
* @csspart entry-start - The column at the start of an entry. It is a slot and is always present.
|
|
28
31
|
* @csspart menu - The menu that holds the entries.
|
|
29
32
|
* @csspart menu-back - The button that goes back from a submenu.
|
|
@@ -85,7 +85,8 @@ let HelixHeadlessVideoPlaylistMenu = class HelixHeadlessVideoPlaylistMenu extend
|
|
|
85
85
|
</slot>
|
|
86
86
|
${this.entries.map((entry) => html `<${this.tag('menu-item')}
|
|
87
87
|
part="entry"
|
|
88
|
-
exportparts="item: entry-item, mark: entry-mark,
|
|
88
|
+
exportparts="end: entry-end, item: entry-item, mark: entry-mark, shortcut: entry-shortcut,
|
|
89
|
+
shortcut-kbd: entry-shortcut-kbd, start: entry-start"
|
|
89
90
|
value=${entry.value}
|
|
90
91
|
checkable
|
|
91
92
|
?checked=${entry.current}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order a playlist plays its entries in, and where a step from one lands.
|
|
3
|
+
*
|
|
4
|
+
* Separable because every answer is taken from a list of values and two
|
|
5
|
+
* settings, before anything moves: nothing here reaches a player or an entry.
|
|
6
|
+
* The playlist asks it which value comes next and applies the answer.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* How a playlist goes on at its end. `one` plays the same entry again when it
|
|
10
|
+
* ends, and `all` goes from the last entry back to the first.
|
|
11
|
+
*/
|
|
12
|
+
export type Repeat = 'all' | 'one';
|
|
13
|
+
/**
|
|
14
|
+
* A shuffled copy of a list, with `first` leading where it is in the list.
|
|
15
|
+
*
|
|
16
|
+
* `first` leads because a reader turning shuffle on is watching something, and
|
|
17
|
+
* a shuffle that could put that entry anywhere would bring it round again
|
|
18
|
+
* before the rest have played.
|
|
19
|
+
*
|
|
20
|
+
* Fisher and Yates' shuffle, which gives every order of the rest the same
|
|
21
|
+
* chance. `random` is the source of chance, `Math.random` unless a caller
|
|
22
|
+
* passes one.
|
|
23
|
+
*/
|
|
24
|
+
export declare const shuffled: <T>(items: readonly T[], first?: T, random?: () => number) => T[];
|
|
25
|
+
/**
|
|
26
|
+
* The order to play in.
|
|
27
|
+
*
|
|
28
|
+
* As written without shuffle. With it, the shuffled order already `held` for
|
|
29
|
+
* as long as it covers the same entries, so stepping back retraces what was
|
|
30
|
+
* played; a new one, led by `current`, once an entry arrives or leaves.
|
|
31
|
+
*/
|
|
32
|
+
export declare const playOrder: (values: readonly string[], shuffle: boolean, held: readonly string[] | undefined, current: string | undefined, random?: () => number) => string[];
|
|
33
|
+
/**
|
|
34
|
+
* Where a step of `by` from `current` lands in `order`, or `undefined` past an
|
|
35
|
+
* end.
|
|
36
|
+
*
|
|
37
|
+
* `wrap` goes round instead of stopping, which is what `repeat="all"` asks
|
|
38
|
+
* for. With no current entry, a step forward lands on the first one and a step
|
|
39
|
+
* back on nothing, as a list that has not started has nothing behind it.
|
|
40
|
+
*/
|
|
41
|
+
export declare const stepTo: (order: readonly string[], current: string | undefined, by: number, wrap: boolean) => string | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const shuffled = (items, first, random = Math.random) => {
|
|
2
|
+
const rest = items.filter((one) => one !== first);
|
|
3
|
+
for (let at = rest.length - 1; at > 0; at -= 1) {
|
|
4
|
+
const swap = Math.floor(random() * (at + 1));
|
|
5
|
+
const held = rest[at];
|
|
6
|
+
rest[at] = rest[swap];
|
|
7
|
+
rest[swap] = held;
|
|
8
|
+
}
|
|
9
|
+
return first !== undefined && items.includes(first) ? [first, ...rest] : rest;
|
|
10
|
+
};
|
|
11
|
+
const sameValues = (one, other) => one.length === other.length && one.every((value) => other.includes(value));
|
|
12
|
+
export const playOrder = (values, shuffle, held, current, random = Math.random) => {
|
|
13
|
+
if (!shuffle) {
|
|
14
|
+
return [...values];
|
|
15
|
+
}
|
|
16
|
+
if (held !== undefined && sameValues(held, values)) {
|
|
17
|
+
return [...held];
|
|
18
|
+
}
|
|
19
|
+
return shuffled(values, current, random);
|
|
20
|
+
};
|
|
21
|
+
export const stepTo = (order, current, by, wrap) => {
|
|
22
|
+
const at = current === undefined ? -1 : order.indexOf(current);
|
|
23
|
+
if (at < 0) {
|
|
24
|
+
return by > 0 ? order[0] : undefined;
|
|
25
|
+
}
|
|
26
|
+
const to = at + by;
|
|
27
|
+
if (to >= 0 && to < order.length) {
|
|
28
|
+
return order[to];
|
|
29
|
+
}
|
|
30
|
+
if (!wrap || order.length === 0) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return order[((to % order.length) + order.length) % order.length];
|
|
34
|
+
};
|