@bysages/solid 0.3.1 → 0.4.1
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/dist/{ai-response-slzE03wh.mjs → ai-response-CJ-uEAgj.mjs} +14 -6
- package/dist/chevron-BHQrYKPM.mjs +8 -0
- package/dist/components/accordion/index.d.mts +22 -2
- package/dist/components/accordion/index.mjs +69 -3
- package/dist/components/affix/index.d.mts +3 -1
- package/dist/components/affix/index.mjs +6 -5
- package/dist/components/ai/index.d.mts +1 -1
- package/dist/components/ai/index.mjs +7 -6
- package/dist/components/ai-action/index.d.mts +3 -1
- package/dist/components/ai-action/index.mjs +4 -3
- package/dist/components/ai-attachments/index.d.mts +8 -4
- package/dist/components/ai-attachments/index.mjs +39 -37
- package/dist/components/ai-message/index.d.mts +3 -1
- package/dist/components/ai-message/index.mjs +4 -3
- package/dist/components/ai-prompt-input/index.d.mts +3 -1
- package/dist/components/ai-prompt-input/index.mjs +48 -44
- package/dist/components/ai-reasoning/index.d.mts +3 -1
- package/dist/components/ai-reasoning/index.mjs +5 -4
- package/dist/components/ai-response/index.d.mts +3 -1
- package/dist/components/ai-response/index.mjs +1 -1
- package/dist/components/ai-source/index.d.mts +6 -2
- package/dist/components/ai-source/index.mjs +6 -5
- package/dist/components/ai-suggestion/index.d.mts +3 -1
- package/dist/components/ai-suggestion/index.mjs +4 -3
- package/dist/components/ai-tool/index.d.mts +3 -1
- package/dist/components/ai-tool/index.mjs +5 -4
- package/dist/components/alert/index.mjs +32 -35
- package/dist/components/angle-slider/index.d.mts +1 -1
- package/dist/components/angle-slider/index.mjs +14 -6
- package/dist/components/aspect-ratio/index.d.mts +3 -1
- package/dist/components/aspect-ratio/index.mjs +4 -3
- package/dist/components/autocomplete/index.d.mts +4 -1
- package/dist/components/autocomplete/index.mjs +19 -4
- package/dist/components/avatar/index.d.mts +1 -1
- package/dist/components/avatar/index.mjs +11 -2
- package/dist/components/avatar-group/index.d.mts +3 -1
- package/dist/components/avatar-group/index.mjs +4 -3
- package/dist/components/back-top/index.d.mts +3 -1
- package/dist/components/back-top/index.mjs +10 -6
- package/dist/components/badge/index.d.mts +3 -1
- package/dist/components/badge/index.mjs +4 -3
- package/dist/components/banner/index.mjs +7 -3
- package/dist/components/bento/index.mjs +1 -1
- package/dist/components/block-ui/index.d.mts +3 -1
- package/dist/components/block-ui/index.mjs +4 -3
- package/dist/components/breadcrumb/index.mjs +1 -1
- package/dist/components/browser/index.mjs +1 -1
- package/dist/components/button/index.d.mts +3 -1
- package/dist/components/button/index.mjs +4 -3
- package/dist/components/button-group/index.mjs +1 -1
- package/dist/components/calendar/index.d.mts +3 -1
- package/dist/components/calendar/index.mjs +30 -22
- package/dist/components/card/index.mjs +1 -1
- package/dist/components/carousel/index.d.mts +5 -1
- package/dist/components/carousel/index.mjs +14 -2
- package/dist/components/cascade-select/index.d.mts +3 -1
- package/dist/components/cascade-select/index.mjs +74 -73
- package/dist/components/checkbox/index.d.mts +1 -1
- package/dist/components/checkbox/index.mjs +14 -6
- package/dist/components/checkbox-group/index.d.mts +4 -9
- package/dist/components/checkbox-group/index.mjs +66 -69
- package/dist/components/chip/index.d.mts +3 -1
- package/dist/components/chip/index.mjs +4 -3
- package/dist/components/client-only/index.d.mts +7 -2
- package/dist/components/client-only/index.mjs +8 -1
- package/dist/components/clipboard/index.d.mts +1 -1
- package/dist/components/clipboard/index.mjs +14 -6
- package/dist/components/collapsible/index.d.mts +15 -1
- package/dist/components/collapsible/index.mjs +40 -3
- package/dist/components/color-picker/index.d.mts +13 -2
- package/dist/components/color-picker/index.mjs +96 -9
- package/dist/components/combobox/index.d.mts +24 -1
- package/dist/components/combobox/index.mjs +130 -9
- package/dist/components/command/index.d.mts +4 -1
- package/dist/components/command/index.mjs +14 -2
- package/dist/components/comment/index.d.mts +3 -1
- package/dist/components/comment/index.mjs +4 -3
- package/dist/components/config-provider/index.d.mts +3 -1
- package/dist/components/config-provider/index.mjs +3 -2
- package/dist/components/container/index.d.mts +3 -1
- package/dist/components/container/index.mjs +4 -3
- package/dist/components/data-view/index.d.mts +3 -1
- package/dist/components/data-view/index.mjs +6 -7
- package/dist/components/date-input/index.d.mts +1 -1
- package/dist/components/date-input/index.mjs +14 -6
- package/dist/components/date-picker/index.d.mts +1 -1
- package/dist/components/date-picker/index.mjs +14 -6
- package/dist/components/deferred-content/index.d.mts +3 -1
- package/dist/components/deferred-content/index.mjs +4 -3
- package/dist/components/descriptions/index.d.mts +7 -0
- package/dist/components/descriptions/index.mjs +22 -3
- package/dist/components/dialog/index.d.mts +17 -1
- package/dist/components/dialog/index.mjs +85 -3
- package/dist/components/dock/index.mjs +1 -1
- package/dist/components/download-trigger/index.d.mts +7 -2
- package/dist/components/download-trigger/index.mjs +8 -1
- package/dist/components/drawer/index.d.mts +17 -1
- package/dist/components/drawer/index.mjs +84 -3
- package/dist/components/dynamic-input/index.d.mts +3 -1
- package/dist/components/dynamic-input/index.mjs +11 -7
- package/dist/components/editable/index.d.mts +1 -1
- package/dist/components/editable/index.mjs +14 -6
- package/dist/components/ellipsis/index.d.mts +3 -1
- package/dist/components/ellipsis/index.mjs +4 -3
- package/dist/components/empty/index.mjs +1 -1
- package/dist/components/field/index.d.mts +5 -1
- package/dist/components/field/index.mjs +14 -2
- package/dist/components/fieldset/index.d.mts +5 -1
- package/dist/components/fieldset/index.mjs +14 -2
- package/dist/components/file-upload/index.d.mts +1 -7
- package/dist/components/file-upload/index.mjs +14 -6
- package/dist/components/float-button/index.mjs +1 -1
- package/dist/components/floating-panel/index.d.mts +5 -1
- package/dist/components/floating-panel/index.mjs +14 -2
- package/dist/components/focus-trap/index.d.mts +7 -2
- package/dist/components/focus-trap/index.mjs +8 -1
- package/dist/components/form/index.d.mts +1 -1
- package/dist/components/form/index.mjs +11 -5
- package/dist/components/frame/index.d.mts +6 -1
- package/dist/components/frame/index.mjs +10 -1
- package/dist/components/grid/index.d.mts +3 -1
- package/dist/components/grid/index.mjs +4 -3
- package/dist/components/highlight/index.d.mts +3 -1
- package/dist/components/highlight/index.mjs +3 -2
- package/dist/components/hover-card/index.d.mts +19 -1
- package/dist/components/hover-card/index.mjs +92 -3
- package/dist/components/icon/index.d.mts +8 -7
- package/dist/components/icon/index.mjs +5 -4
- package/dist/components/image/index.d.mts +4 -2
- package/dist/components/image/index.mjs +35 -34
- package/dist/components/image-cropper/index.d.mts +5 -1
- package/dist/components/image-cropper/index.mjs +14 -2
- package/dist/components/image-viewer/index.d.mts +2 -23
- package/dist/components/image-viewer/index.mjs +3 -159
- package/dist/components/input/index.d.mts +3 -1
- package/dist/components/input/index.mjs +1 -1
- package/dist/components/input-group/index.mjs +1 -1
- package/dist/components/json-tree-view/index.d.mts +5 -4
- package/dist/components/json-tree-view/index.mjs +16 -19
- package/dist/components/kbd/index.d.mts +3 -1
- package/dist/components/kbd/index.mjs +4 -3
- package/dist/components/layout/index.mjs +1 -1
- package/dist/components/link/index.d.mts +3 -1
- package/dist/components/link/index.mjs +4 -3
- package/dist/components/list/index.mjs +1 -1
- package/dist/components/listbox/index.d.mts +1 -1
- package/dist/components/listbox/index.mjs +14 -6
- package/dist/components/marquee/index.d.mts +5 -1
- package/dist/components/marquee/index.mjs +14 -2
- package/dist/components/masonry/index.d.mts +3 -1
- package/dist/components/masonry/index.mjs +4 -3
- package/dist/components/media-player/index.d.mts +2 -0
- package/dist/components/media-player/index.mjs +2 -0
- package/dist/components/mentions/index.d.mts +1 -1
- package/dist/components/mentions/index.mjs +2 -2
- package/dist/components/menu/index.d.mts +21 -1
- package/dist/components/menu/index.mjs +63 -6
- package/dist/components/menubar/index.d.mts +3 -1
- package/dist/components/menubar/index.mjs +31 -10
- package/dist/components/meter/index.mjs +1 -1
- package/dist/components/navigation-menu/index.d.mts +5 -1
- package/dist/components/navigation-menu/index.mjs +14 -2
- package/dist/components/number-input/index.d.mts +1 -1
- package/dist/components/number-input/index.mjs +14 -6
- package/dist/components/order-list/index.d.mts +3 -1
- package/dist/components/order-list/index.mjs +68 -64
- package/dist/components/page-header/index.mjs +1 -1
- package/dist/components/pagination/index.d.mts +1 -1
- package/dist/components/pagination/index.mjs +14 -6
- package/dist/components/password-input/index.d.mts +1 -1
- package/dist/components/password-input/index.mjs +14 -6
- package/dist/components/pin-input/index.d.mts +1 -1
- package/dist/components/pin-input/index.mjs +14 -6
- package/dist/components/popconfirm/index.d.mts +3 -1
- package/dist/components/popconfirm/index.mjs +17 -9
- package/dist/components/popover/index.d.mts +19 -1
- package/dist/components/popover/index.mjs +92 -3
- package/dist/components/presence/index.d.mts +10 -2
- package/dist/components/presence/index.mjs +9 -1
- package/dist/components/progress/index.d.mts +1 -1
- package/dist/components/progress/index.mjs +14 -6
- package/dist/components/progress-group/index.d.mts +3 -1
- package/dist/components/progress-group/index.mjs +5 -4
- package/dist/components/qr-code/index.d.mts +5 -1
- package/dist/components/qr-code/index.mjs +14 -2
- package/dist/components/radio-group/index.d.mts +1 -1
- package/dist/components/radio-group/index.mjs +14 -6
- package/dist/components/rating-group/index.d.mts +2 -2
- package/dist/components/rating-group/index.mjs +14 -6
- package/dist/components/result/index.d.mts +1 -1
- package/dist/components/result/index.mjs +6 -4
- package/dist/components/scroll-area/index.d.mts +5 -1
- package/dist/components/scroll-area/index.mjs +14 -2
- package/dist/components/segment-group/index.d.mts +1 -1
- package/dist/components/segment-group/index.mjs +12 -7
- package/dist/components/select/index.d.mts +2 -2
- package/dist/components/select/index.mjs +1 -1
- package/dist/components/separator/index.d.mts +3 -1
- package/dist/components/separator/index.mjs +4 -3
- package/dist/components/signature-pad/index.d.mts +5 -1
- package/dist/components/signature-pad/index.mjs +14 -2
- package/dist/components/skeleton/index.d.mts +3 -1
- package/dist/components/skeleton/index.mjs +4 -3
- package/dist/components/slider/index.d.mts +1 -1
- package/dist/components/slider/index.mjs +14 -6
- package/dist/components/spinner/index.d.mts +3 -1
- package/dist/components/spinner/index.mjs +4 -3
- package/dist/components/split-button/index.d.mts +3 -1
- package/dist/components/split-button/index.mjs +15 -4
- package/dist/components/splitter/index.d.mts +5 -1
- package/dist/components/splitter/index.mjs +14 -2
- package/dist/components/spotlight/index.d.mts +3 -1
- package/dist/components/spotlight/index.mjs +4 -3
- package/dist/components/stack/index.d.mts +3 -1
- package/dist/components/stack/index.mjs +7 -6
- package/dist/components/stat/index.mjs +1 -1
- package/dist/components/steps/index.d.mts +20 -3
- package/dist/components/steps/index.mjs +67 -8
- package/dist/components/swap/index.d.mts +5 -1
- package/dist/components/swap/index.mjs +14 -2
- package/dist/components/switch/index.d.mts +1 -1
- package/dist/components/switch/index.mjs +14 -6
- package/dist/components/table/index.d.mts +3 -1
- package/dist/components/table/index.mjs +191 -153
- package/dist/components/tabs/index.d.mts +20 -2
- package/dist/components/tabs/index.mjs +60 -4
- package/dist/components/tags-input/index.d.mts +1 -1
- package/dist/components/tags-input/index.mjs +14 -6
- package/dist/components/terminal/index.d.mts +3 -1
- package/dist/components/terminal/index.mjs +4 -3
- package/dist/components/textarea/index.d.mts +3 -1
- package/dist/components/textarea/index.mjs +4 -3
- package/dist/components/timeline/index.mjs +1 -1
- package/dist/components/timer/index.d.mts +5 -1
- package/dist/components/timer/index.mjs +14 -2
- package/dist/components/toast/index.d.mts +8 -2
- package/dist/components/toast/index.mjs +15 -3
- package/dist/components/toc/index.d.mts +11 -2
- package/dist/components/toc/index.mjs +1 -1
- package/dist/components/toggle/index.d.mts +5 -1
- package/dist/components/toggle/index.mjs +14 -2
- package/dist/components/toggle-group/index.d.mts +1 -1
- package/dist/components/toggle-group/index.mjs +14 -6
- package/dist/components/toolbar/index.d.mts +3 -1
- package/dist/components/toolbar/index.mjs +4 -3
- package/dist/components/tooltip/index.d.mts +16 -1
- package/dist/components/tooltip/index.mjs +56 -3
- package/dist/components/tour/index.d.mts +16 -3
- package/dist/components/tour/index.mjs +75 -10
- package/dist/components/transfer/index.d.mts +3 -1
- package/dist/components/transfer/index.mjs +55 -51
- package/dist/components/tree-select/index.d.mts +3 -1
- package/dist/components/tree-select/index.mjs +38 -31
- package/dist/components/tree-view/index.d.mts +1 -8
- package/dist/components/tree-view/index.mjs +17 -22
- package/dist/components/typography/index.mjs +1 -1
- package/dist/components/user/index.d.mts +3 -1
- package/dist/components/user/index.mjs +9 -3
- package/dist/components/virtual-list/index.d.mts +3 -1
- package/dist/components/virtual-list/index.mjs +14 -10
- package/dist/components/watermark/index.d.mts +3 -1
- package/dist/components/watermark/index.mjs +5 -4
- package/dist/element-dGIhOTJ6.mjs +16 -0
- package/dist/family-Cmd8HEyK.mjs +11 -0
- package/dist/icon-DTqmJCd5.mjs +48 -0
- package/dist/id-2ygNzPHF.mjs +12 -0
- package/dist/image-viewer-19s75Hz_.mjs +228 -0
- package/dist/{index-B1xiJ5ki.d.mts → index-B1-quIrl.d.mts} +6 -2
- package/dist/{index-DDX7x8aT.d.mts → index-DEArHiFI.d.mts} +16 -7
- package/dist/{index-IfofF-SW.d.mts → index-DVc1L_zt.d.mts} +191 -112
- package/dist/index-DhripUtf.d.mts +39 -0
- package/dist/{index-YPUiqD3n.d.mts → index-ID48LCMU.d.mts} +28 -2
- package/dist/index-wW28BYf1.d.mts +124 -0
- package/dist/index.d.mts +23 -22
- package/dist/index.mjs +13 -12
- package/dist/{input-DoFQ7RXh.mjs → input-BL3N3L8g.mjs} +4 -3
- package/dist/media-player-yF7KVUxM.mjs +796 -0
- package/dist/{mentions-f5Js_2W7.mjs → mentions-DKUG7eHt.mjs} +68 -52
- package/dist/select-DI7pFspF.mjs +292 -0
- package/dist/{toc-CZmYEaQR.mjs → toc-CqvGOp34.mjs} +13 -3
- package/package.json +10 -82
- package/dist/chevron-D5RzcayK.mjs +0 -9
- package/dist/index-DdsnfV4f.d.mts +0 -16
- package/dist/select-CX6OuQlU.mjs +0 -123
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
import { r as iconNode } from "./icon-DTqmJCd5.mjs";
|
|
2
|
+
import { t as Select } from "./select-DI7pFspF.mjs";
|
|
3
|
+
import { createComponent, effect, insert, memo, mergeProps, ref, setAttribute, spread, template } from "@solidjs/web";
|
|
4
|
+
import { injectComponentStyle } from "@bysages/core/styling";
|
|
5
|
+
import { For, Show, createContext, createEffect, createSignal, onCleanup, splitProps, useContext } from "solid-js";
|
|
6
|
+
import { Dynamic } from "solid-js/web";
|
|
7
|
+
//#region src/components/media-player/use-media-controls.ts
|
|
8
|
+
/** The shared HTMLMediaElement state: one source of truth for both the
|
|
9
|
+
* audio and video faces, so their controls never drift apart. */
|
|
10
|
+
function useMediaControls(media) {
|
|
11
|
+
const [currentTime, setCurrentTime] = createSignal(0);
|
|
12
|
+
const [duration, setDuration] = createSignal(0);
|
|
13
|
+
const [playing, setPlaying] = createSignal(false);
|
|
14
|
+
const [muted, setMuted] = createSignal(false);
|
|
15
|
+
const [volume, setVolumeState] = createSignal(1);
|
|
16
|
+
const [playbackRate, setPlaybackRateState] = createSignal(1);
|
|
17
|
+
const [selectedTextTrack, setSelectedTextTrack] = createSignal(-1);
|
|
18
|
+
const [error, setError] = createSignal(false);
|
|
19
|
+
const readMedia = (element) => {
|
|
20
|
+
setCurrentTime(element.currentTime || 0);
|
|
21
|
+
setDuration(Number.isFinite(element.duration) ? element.duration : 0);
|
|
22
|
+
setPlaying(!element.paused && !element.ended);
|
|
23
|
+
setMuted(element.muted);
|
|
24
|
+
setVolumeState(element.volume);
|
|
25
|
+
setPlaybackRateState(element.playbackRate);
|
|
26
|
+
setError(false);
|
|
27
|
+
};
|
|
28
|
+
const readTextTracks = (element) => {
|
|
29
|
+
setSelectedTextTrack(Array.from(element.textTracks).findIndex((track) => track.mode === "showing"));
|
|
30
|
+
};
|
|
31
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
32
|
+
const seek = (time) => {
|
|
33
|
+
const element = media.current;
|
|
34
|
+
if (!element) return;
|
|
35
|
+
const limit = Number.isFinite(element.duration) ? element.duration : Number.MAX_SAFE_INTEGER;
|
|
36
|
+
element.currentTime = clamp(time, 0, limit);
|
|
37
|
+
};
|
|
38
|
+
const setVolume = (value) => {
|
|
39
|
+
const element = media.current;
|
|
40
|
+
if (!element) return;
|
|
41
|
+
element.volume = clamp(value, 0, 1);
|
|
42
|
+
};
|
|
43
|
+
const setPlaybackRate = (value) => {
|
|
44
|
+
const element = media.current;
|
|
45
|
+
if (!element) return;
|
|
46
|
+
element.playbackRate = value;
|
|
47
|
+
};
|
|
48
|
+
const toggleMuted = () => {
|
|
49
|
+
const element = media.current;
|
|
50
|
+
if (!element) return;
|
|
51
|
+
element.muted = !element.muted;
|
|
52
|
+
};
|
|
53
|
+
const toggle = async () => {
|
|
54
|
+
const element = media.current;
|
|
55
|
+
if (!element) return;
|
|
56
|
+
if (element.paused) await element.play();
|
|
57
|
+
else element.pause();
|
|
58
|
+
};
|
|
59
|
+
const selectTextTrack = (index) => {
|
|
60
|
+
const element = media.current;
|
|
61
|
+
if (!element) return;
|
|
62
|
+
Array.from(element.textTracks).forEach((track, trackIndex) => {
|
|
63
|
+
track.mode = trackIndex === index ? "showing" : "disabled";
|
|
64
|
+
});
|
|
65
|
+
setSelectedTextTrack(index);
|
|
66
|
+
};
|
|
67
|
+
const togglePictureInPicture = async () => {
|
|
68
|
+
const pipElement = media.current;
|
|
69
|
+
if (!pipElement?.requestPictureInPicture) return;
|
|
70
|
+
if (document.pictureInPictureElement) await document.exitPictureInPicture();
|
|
71
|
+
else await pipElement.requestPictureInPicture();
|
|
72
|
+
};
|
|
73
|
+
const toggleFullscreen = async (root) => {
|
|
74
|
+
if (document.fullscreenElement) await document.exitFullscreen();
|
|
75
|
+
else await root.requestFullscreen();
|
|
76
|
+
};
|
|
77
|
+
createEffectOnMedia(media, (element) => {
|
|
78
|
+
const updateFromEvent = (event) => {
|
|
79
|
+
readMedia(element);
|
|
80
|
+
setError(event.type === "error");
|
|
81
|
+
};
|
|
82
|
+
const syncTracks = () => {
|
|
83
|
+
readTextTracks(element);
|
|
84
|
+
window.setTimeout(() => readTextTracks(element), 0);
|
|
85
|
+
};
|
|
86
|
+
const events = [
|
|
87
|
+
"loadedmetadata",
|
|
88
|
+
"durationchange",
|
|
89
|
+
"timeupdate",
|
|
90
|
+
"progress",
|
|
91
|
+
"play",
|
|
92
|
+
"pause",
|
|
93
|
+
"ended",
|
|
94
|
+
"waiting",
|
|
95
|
+
"playing",
|
|
96
|
+
"seeking",
|
|
97
|
+
"seeked",
|
|
98
|
+
"volumechange",
|
|
99
|
+
"ratechange",
|
|
100
|
+
"error"
|
|
101
|
+
];
|
|
102
|
+
for (const event of events) element.addEventListener(event, updateFromEvent);
|
|
103
|
+
element.addEventListener("loadedmetadata", syncTracks);
|
|
104
|
+
element.addEventListener("loadstart", syncTracks);
|
|
105
|
+
readMedia(element);
|
|
106
|
+
readTextTracks(element);
|
|
107
|
+
onCleanup(() => {
|
|
108
|
+
for (const event of events) element.removeEventListener(event, updateFromEvent);
|
|
109
|
+
element.removeEventListener("loadedmetadata", syncTracks);
|
|
110
|
+
element.removeEventListener("loadstart", syncTracks);
|
|
111
|
+
element.pause();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
currentTime,
|
|
116
|
+
duration,
|
|
117
|
+
error,
|
|
118
|
+
muted,
|
|
119
|
+
playing,
|
|
120
|
+
playbackRate,
|
|
121
|
+
selectedTextTrack,
|
|
122
|
+
seek,
|
|
123
|
+
selectTextTrack,
|
|
124
|
+
setPlaybackRate,
|
|
125
|
+
setVolume,
|
|
126
|
+
toggle,
|
|
127
|
+
toggleFullscreen,
|
|
128
|
+
toggleMuted,
|
|
129
|
+
togglePictureInPicture,
|
|
130
|
+
volume
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** The media element mounts inside a child part, so its reference is
|
|
134
|
+
* observed as a mutable box. */
|
|
135
|
+
function createEffectOnMedia(media, effect) {
|
|
136
|
+
createEffect(() => {
|
|
137
|
+
if (media.current) effect(media.current);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/** A compact clock for player chrome: hours only when the media asks
|
|
141
|
+
* for them, stable digits so the time never jitters the controls. */
|
|
142
|
+
function formatMediaTime(seconds) {
|
|
143
|
+
if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
|
|
144
|
+
const whole = Math.floor(seconds);
|
|
145
|
+
const hours = Math.floor(whole / 3600);
|
|
146
|
+
const minutes = Math.floor(whole % 3600 / 60);
|
|
147
|
+
const secs = String(whole % 3600 % 60).padStart(2, "0");
|
|
148
|
+
return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${secs}` : `${minutes}:${secs}`;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/components/media-player/shell.tsx
|
|
152
|
+
var _tmpl$$1 = /* @__PURE__ */ template(`<div>`);
|
|
153
|
+
const MediaPlayerProvider = createContext();
|
|
154
|
+
function useMediaPlayer() {
|
|
155
|
+
const context = useContext(MediaPlayerProvider);
|
|
156
|
+
if (!context) throw new Error("Media player parts must be used inside Audio.Root or Video.Root");
|
|
157
|
+
return context;
|
|
158
|
+
}
|
|
159
|
+
/** The headless shell: it owns the media reference and the shared state
|
|
160
|
+
* machine, while the public parts remain free to compose in any order. */
|
|
161
|
+
function MediaPlayerShell(props) {
|
|
162
|
+
injectComponentStyle("media-player");
|
|
163
|
+
const [local, rest] = splitShellProps(props);
|
|
164
|
+
const [mediaElement, setMediaElement] = createSignal(null);
|
|
165
|
+
const media = {
|
|
166
|
+
get current() {
|
|
167
|
+
return mediaElement();
|
|
168
|
+
},
|
|
169
|
+
set current(value) {
|
|
170
|
+
setMediaElement(value);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const root = { current: null };
|
|
174
|
+
const controls = useMediaControls(media);
|
|
175
|
+
createEffect(() => {
|
|
176
|
+
const element = media.current;
|
|
177
|
+
if (!element) return;
|
|
178
|
+
element.volume = local.volume ?? 1;
|
|
179
|
+
element.playbackRate = local.playbackRate ?? 1;
|
|
180
|
+
});
|
|
181
|
+
const context = {
|
|
182
|
+
get autoplay() {
|
|
183
|
+
return local.autoplay ?? false;
|
|
184
|
+
},
|
|
185
|
+
controls,
|
|
186
|
+
get disabled() {
|
|
187
|
+
return local.disabled ?? false;
|
|
188
|
+
},
|
|
189
|
+
emit: (event, source) => {
|
|
190
|
+
if (event === "play") local.onPlay?.(source);
|
|
191
|
+
if (event === "pause") local.onPause?.(source);
|
|
192
|
+
if (event === "ended") local.onEnded?.(source);
|
|
193
|
+
if (event === "timeupdate") local.onTimeupdate?.(source);
|
|
194
|
+
if (event === "volumechange") local.onVolumechange?.(source);
|
|
195
|
+
if (event === "ratechange") local.onRatechange?.(source);
|
|
196
|
+
if (event === "error") local.onError?.(source);
|
|
197
|
+
},
|
|
198
|
+
kind: local.kind,
|
|
199
|
+
get label() {
|
|
200
|
+
return local.label ?? "Media player";
|
|
201
|
+
},
|
|
202
|
+
get loop() {
|
|
203
|
+
return local.loop ?? false;
|
|
204
|
+
},
|
|
205
|
+
media,
|
|
206
|
+
get muted() {
|
|
207
|
+
return local.muted ?? false;
|
|
208
|
+
},
|
|
209
|
+
get playbackRates() {
|
|
210
|
+
return local.playbackRates ?? [
|
|
211
|
+
.5,
|
|
212
|
+
.75,
|
|
213
|
+
1,
|
|
214
|
+
1.25,
|
|
215
|
+
1.5,
|
|
216
|
+
2
|
|
217
|
+
];
|
|
218
|
+
},
|
|
219
|
+
get playsInline() {
|
|
220
|
+
return local.playsInline ?? true;
|
|
221
|
+
},
|
|
222
|
+
get poster() {
|
|
223
|
+
return local.poster;
|
|
224
|
+
},
|
|
225
|
+
get preload() {
|
|
226
|
+
return local.preload ?? "metadata";
|
|
227
|
+
},
|
|
228
|
+
root,
|
|
229
|
+
get sources() {
|
|
230
|
+
return local.sources ?? [];
|
|
231
|
+
},
|
|
232
|
+
get src() {
|
|
233
|
+
return local.src;
|
|
234
|
+
},
|
|
235
|
+
get tracks() {
|
|
236
|
+
return local.tracks ?? [];
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
return createComponent(MediaPlayerProvider.Provider, {
|
|
240
|
+
value: context,
|
|
241
|
+
get children() {
|
|
242
|
+
var _el$ = _tmpl$$1();
|
|
243
|
+
ref(() => {
|
|
244
|
+
return (element) => {
|
|
245
|
+
root.current = element;
|
|
246
|
+
};
|
|
247
|
+
}, _el$);
|
|
248
|
+
spread(_el$, [rest, {
|
|
249
|
+
"data-scope": "media-player",
|
|
250
|
+
"data-part": "root",
|
|
251
|
+
get "data-kind"() {
|
|
252
|
+
return local.kind;
|
|
253
|
+
},
|
|
254
|
+
get "data-playing"() {
|
|
255
|
+
return controls.playing();
|
|
256
|
+
},
|
|
257
|
+
get "data-muted"() {
|
|
258
|
+
return controls.muted();
|
|
259
|
+
},
|
|
260
|
+
get "data-error"() {
|
|
261
|
+
return controls.error() ? "" : void 0;
|
|
262
|
+
}
|
|
263
|
+
}], true);
|
|
264
|
+
insert(_el$, () => {
|
|
265
|
+
return local.children;
|
|
266
|
+
});
|
|
267
|
+
return _el$;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function splitShellProps(props) {
|
|
272
|
+
return splitProps(props, [
|
|
273
|
+
"kind",
|
|
274
|
+
"children",
|
|
275
|
+
"label",
|
|
276
|
+
"src",
|
|
277
|
+
"sources",
|
|
278
|
+
"tracks",
|
|
279
|
+
"autoplay",
|
|
280
|
+
"loop",
|
|
281
|
+
"muted",
|
|
282
|
+
"preload",
|
|
283
|
+
"volume",
|
|
284
|
+
"playbackRate",
|
|
285
|
+
"playbackRates",
|
|
286
|
+
"disabled",
|
|
287
|
+
"poster",
|
|
288
|
+
"playsInline",
|
|
289
|
+
"onPlay",
|
|
290
|
+
"onPause",
|
|
291
|
+
"onEnded",
|
|
292
|
+
"onTimeupdate",
|
|
293
|
+
"onVolumechange",
|
|
294
|
+
"onRatechange",
|
|
295
|
+
"onError"
|
|
296
|
+
]);
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/components/media-player/parts.tsx
|
|
300
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div>`);
|
|
301
|
+
var _tmpl$2 = /* @__PURE__ */ template(`<source>`);
|
|
302
|
+
var _tmpl$3 = /* @__PURE__ */ template(`<track>`);
|
|
303
|
+
var _tmpl$4 = /* @__PURE__ */ template(`<button>`);
|
|
304
|
+
var _tmpl$5 = /* @__PURE__ */ template(`<span> / <!>`);
|
|
305
|
+
var _tmpl$6 = /* @__PURE__ */ template(`<input>`);
|
|
306
|
+
function part(name) {
|
|
307
|
+
function MediaPart(props) {
|
|
308
|
+
var _el$ = _tmpl$();
|
|
309
|
+
spread(_el$, [props, {
|
|
310
|
+
"data-scope": "media-player",
|
|
311
|
+
get "data-part"() {
|
|
312
|
+
return name.toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
}], true);
|
|
315
|
+
insert(_el$, () => {
|
|
316
|
+
return props.children;
|
|
317
|
+
});
|
|
318
|
+
return _el$;
|
|
319
|
+
}
|
|
320
|
+
return MediaPart;
|
|
321
|
+
}
|
|
322
|
+
/** The native media element, framed by the shared shell's state machine. */
|
|
323
|
+
function Media(props) {
|
|
324
|
+
const context = useMediaPlayer();
|
|
325
|
+
const element = () => context.kind === "video" ? "video" : "audio";
|
|
326
|
+
return createComponent(Dynamic, mergeProps({ get component() {
|
|
327
|
+
return element();
|
|
328
|
+
} }, props, {
|
|
329
|
+
get src() {
|
|
330
|
+
return memo(() => {
|
|
331
|
+
return context.sources.length > 0;
|
|
332
|
+
})() ? void 0 : context.src;
|
|
333
|
+
},
|
|
334
|
+
"data-scope": "media-player",
|
|
335
|
+
"data-part": "media",
|
|
336
|
+
get autoplay() {
|
|
337
|
+
return context.autoplay;
|
|
338
|
+
},
|
|
339
|
+
get loop() {
|
|
340
|
+
return context.loop;
|
|
341
|
+
},
|
|
342
|
+
get muted() {
|
|
343
|
+
return context.muted;
|
|
344
|
+
},
|
|
345
|
+
get preload() {
|
|
346
|
+
return context.preload;
|
|
347
|
+
},
|
|
348
|
+
get poster() {
|
|
349
|
+
return context.poster;
|
|
350
|
+
},
|
|
351
|
+
get playsinline() {
|
|
352
|
+
return context.playsInline;
|
|
353
|
+
},
|
|
354
|
+
ref: (value) => {
|
|
355
|
+
context.media.current = value;
|
|
356
|
+
},
|
|
357
|
+
get "aria-label"() {
|
|
358
|
+
return context.label;
|
|
359
|
+
},
|
|
360
|
+
onPlay: (event) => context.emit("play", event),
|
|
361
|
+
onPause: (event) => context.emit("pause", event),
|
|
362
|
+
onEnded: (event) => context.emit("ended", event),
|
|
363
|
+
onTimeUpdate: (event) => context.emit("timeupdate", event),
|
|
364
|
+
onVolumeChange: (event) => context.emit("volumechange", event),
|
|
365
|
+
onRateChange: (event) => context.emit("ratechange", event),
|
|
366
|
+
onError: (event) => context.emit("error", event),
|
|
367
|
+
get children() {
|
|
368
|
+
return [createComponent(For, {
|
|
369
|
+
get each() {
|
|
370
|
+
return context.sources;
|
|
371
|
+
},
|
|
372
|
+
children: (source) => (() => {
|
|
373
|
+
var _el$2 = _tmpl$2();
|
|
374
|
+
effect(() => {
|
|
375
|
+
return {
|
|
376
|
+
e: source.src,
|
|
377
|
+
t: source.type
|
|
378
|
+
};
|
|
379
|
+
}, ({ e, t }, _p$) => {
|
|
380
|
+
e !== _p$?.e && setAttribute(_el$2, "src", e);
|
|
381
|
+
t !== _p$?.t && setAttribute(_el$2, "type", t);
|
|
382
|
+
});
|
|
383
|
+
return _el$2;
|
|
384
|
+
})()
|
|
385
|
+
}), createComponent(For, {
|
|
386
|
+
get each() {
|
|
387
|
+
return context.tracks;
|
|
388
|
+
},
|
|
389
|
+
children: (track) => (() => {
|
|
390
|
+
var _el$3 = _tmpl$3();
|
|
391
|
+
effect(() => {
|
|
392
|
+
return {
|
|
393
|
+
e: track.src,
|
|
394
|
+
t: track.kind ?? "subtitles",
|
|
395
|
+
a: track.label,
|
|
396
|
+
o: track.srcLang,
|
|
397
|
+
i: track.default
|
|
398
|
+
};
|
|
399
|
+
}, ({ e, t, a, o, i }, _p$) => {
|
|
400
|
+
e !== _p$?.e && setAttribute(_el$3, "src", e);
|
|
401
|
+
t !== _p$?.t && setAttribute(_el$3, "kind", t);
|
|
402
|
+
a !== _p$?.a && setAttribute(_el$3, "label", a);
|
|
403
|
+
o !== _p$?.o && setAttribute(_el$3, "srclang", o);
|
|
404
|
+
i !== _p$?.i && setAttribute(_el$3, "default", i);
|
|
405
|
+
});
|
|
406
|
+
return _el$3;
|
|
407
|
+
})()
|
|
408
|
+
})];
|
|
409
|
+
}
|
|
410
|
+
}));
|
|
411
|
+
}
|
|
412
|
+
/** Play and pause share one seal-cut control: the state changes the mark,
|
|
413
|
+
* never the control's place in the hand. */
|
|
414
|
+
function PlayButton(props) {
|
|
415
|
+
const context = useMediaPlayer();
|
|
416
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
417
|
+
const label = () => local.label ?? (context.controls.playing() ? "Pause" : "Play");
|
|
418
|
+
var _el$4 = _tmpl$4();
|
|
419
|
+
spread(_el$4, [rest, {
|
|
420
|
+
type: "button",
|
|
421
|
+
"data-scope": "media-player",
|
|
422
|
+
"data-part": "play-button",
|
|
423
|
+
get "aria-label"() {
|
|
424
|
+
return label();
|
|
425
|
+
},
|
|
426
|
+
get "aria-pressed"() {
|
|
427
|
+
return context.controls.playing();
|
|
428
|
+
},
|
|
429
|
+
get title() {
|
|
430
|
+
return label();
|
|
431
|
+
},
|
|
432
|
+
get disabled() {
|
|
433
|
+
return context.disabled;
|
|
434
|
+
},
|
|
435
|
+
onClick: () => void context.controls.toggle()
|
|
436
|
+
}], true);
|
|
437
|
+
insert(_el$4, () => {
|
|
438
|
+
return iconNode(context.controls.playing() ? "pause" : "play", {
|
|
439
|
+
width: "16",
|
|
440
|
+
height: "16"
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
return _el$4;
|
|
444
|
+
}
|
|
445
|
+
/** The elapsed and total time bracket the timeline. */
|
|
446
|
+
function Time(props) {
|
|
447
|
+
const context = useMediaPlayer();
|
|
448
|
+
var _el$5 = _tmpl$5();
|
|
449
|
+
var _el$6 = _el$5.firstChild;
|
|
450
|
+
var _el$7 = _el$6.nextSibling;
|
|
451
|
+
spread(_el$5, [props, {
|
|
452
|
+
"data-scope": "media-player",
|
|
453
|
+
"data-part": "time"
|
|
454
|
+
}], true);
|
|
455
|
+
insert(_el$5, () => {
|
|
456
|
+
return formatMediaTime(context.controls.currentTime());
|
|
457
|
+
}, _el$6);
|
|
458
|
+
insert(_el$5, () => {
|
|
459
|
+
return formatMediaTime(context.controls.duration());
|
|
460
|
+
}, _el$7);
|
|
461
|
+
return _el$5;
|
|
462
|
+
}
|
|
463
|
+
/** The timeline is the sheet on which the listener writes their place. */
|
|
464
|
+
function Timeline(props) {
|
|
465
|
+
const context = useMediaPlayer();
|
|
466
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
467
|
+
const progress = () => context.controls.duration() > 0 ? context.controls.currentTime() / context.controls.duration() * 100 : 0;
|
|
468
|
+
var _el$8 = _tmpl$6();
|
|
469
|
+
spread(_el$8, [rest, {
|
|
470
|
+
type: "range",
|
|
471
|
+
"data-scope": "media-player",
|
|
472
|
+
"data-part": "timeline",
|
|
473
|
+
min: 0,
|
|
474
|
+
get max() {
|
|
475
|
+
return context.controls.duration();
|
|
476
|
+
},
|
|
477
|
+
step: .1,
|
|
478
|
+
get value() {
|
|
479
|
+
return context.controls.currentTime();
|
|
480
|
+
},
|
|
481
|
+
get disabled() {
|
|
482
|
+
return context.disabled || context.controls.duration() <= 0;
|
|
483
|
+
},
|
|
484
|
+
get "aria-label"() {
|
|
485
|
+
return local.label ?? "Seek";
|
|
486
|
+
},
|
|
487
|
+
get "aria-valuetext"() {
|
|
488
|
+
return formatMediaTime(context.controls.currentTime());
|
|
489
|
+
},
|
|
490
|
+
get style() {
|
|
491
|
+
return {
|
|
492
|
+
...props.style,
|
|
493
|
+
"--bs-media-progress": `${progress()}%`
|
|
494
|
+
};
|
|
495
|
+
},
|
|
496
|
+
onInput: (event) => context.controls.seek(Number(event.currentTarget.value))
|
|
497
|
+
}], false);
|
|
498
|
+
return _el$8;
|
|
499
|
+
}
|
|
500
|
+
/** Muting stays at the left of the volume well, the way a hand expects. */
|
|
501
|
+
function MuteButton(props) {
|
|
502
|
+
const context = useMediaPlayer();
|
|
503
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
504
|
+
const muted = () => context.controls.muted() || context.controls.volume() === 0;
|
|
505
|
+
const label = () => local.label ?? (muted() ? "Unmute" : "Mute");
|
|
506
|
+
var _el$9 = _tmpl$4();
|
|
507
|
+
spread(_el$9, [rest, {
|
|
508
|
+
type: "button",
|
|
509
|
+
"data-scope": "media-player",
|
|
510
|
+
"data-part": "mute-button",
|
|
511
|
+
get "aria-label"() {
|
|
512
|
+
return label();
|
|
513
|
+
},
|
|
514
|
+
get "aria-pressed"() {
|
|
515
|
+
return muted();
|
|
516
|
+
},
|
|
517
|
+
get title() {
|
|
518
|
+
return label();
|
|
519
|
+
},
|
|
520
|
+
get disabled() {
|
|
521
|
+
return context.disabled;
|
|
522
|
+
},
|
|
523
|
+
onClick: () => context.controls.toggleMuted()
|
|
524
|
+
}], true);
|
|
525
|
+
insert(_el$9, () => {
|
|
526
|
+
return iconNode(muted() ? "volume-x" : "volume-2", {
|
|
527
|
+
width: "16",
|
|
528
|
+
height: "16"
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
return _el$9;
|
|
532
|
+
}
|
|
533
|
+
function Volume(props) {
|
|
534
|
+
const context = useMediaPlayer();
|
|
535
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
536
|
+
const volume = () => context.controls.muted() ? 0 : context.controls.volume();
|
|
537
|
+
var _el$10 = _tmpl$6();
|
|
538
|
+
spread(_el$10, [rest, {
|
|
539
|
+
type: "range",
|
|
540
|
+
"data-scope": "media-player",
|
|
541
|
+
"data-part": "volume",
|
|
542
|
+
min: 0,
|
|
543
|
+
max: 1,
|
|
544
|
+
step: .01,
|
|
545
|
+
get value() {
|
|
546
|
+
return volume();
|
|
547
|
+
},
|
|
548
|
+
get disabled() {
|
|
549
|
+
return context.disabled;
|
|
550
|
+
},
|
|
551
|
+
get "aria-label"() {
|
|
552
|
+
return local.label ?? "Volume";
|
|
553
|
+
},
|
|
554
|
+
get "aria-valuetext"() {
|
|
555
|
+
return `${Math.round(volume() * 100)}%`;
|
|
556
|
+
},
|
|
557
|
+
get style() {
|
|
558
|
+
return {
|
|
559
|
+
...props.style,
|
|
560
|
+
"--bs-media-progress": `${volume() * 100}%`
|
|
561
|
+
};
|
|
562
|
+
},
|
|
563
|
+
onInput: (event) => context.controls.setVolume(Number(event.currentTarget.value))
|
|
564
|
+
}], false);
|
|
565
|
+
return _el$10;
|
|
566
|
+
}
|
|
567
|
+
/** Playback rate is a real choice, not hidden behind native menus. */
|
|
568
|
+
function Rate(props) {
|
|
569
|
+
const context = useMediaPlayer();
|
|
570
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
571
|
+
var _el$11 = _tmpl$();
|
|
572
|
+
spread(_el$11, [rest, {
|
|
573
|
+
"data-scope": "media-player",
|
|
574
|
+
"data-part": "rate"
|
|
575
|
+
}], true);
|
|
576
|
+
insert(_el$11, createComponent(Select, {
|
|
577
|
+
clearable: false,
|
|
578
|
+
get disabled() {
|
|
579
|
+
return context.disabled;
|
|
580
|
+
},
|
|
581
|
+
get options() {
|
|
582
|
+
return context.playbackRates.map((rate) => ({
|
|
583
|
+
label: `${rate}×`,
|
|
584
|
+
value: String(rate)
|
|
585
|
+
}));
|
|
586
|
+
},
|
|
587
|
+
get placeholder() {
|
|
588
|
+
return local.label ?? "Playback speed";
|
|
589
|
+
},
|
|
590
|
+
size: "sm",
|
|
591
|
+
get value() {
|
|
592
|
+
return String(context.controls.playbackRate());
|
|
593
|
+
},
|
|
594
|
+
onValueChange: (value) => context.controls.setPlaybackRate(Number(value))
|
|
595
|
+
}));
|
|
596
|
+
return _el$11;
|
|
597
|
+
}
|
|
598
|
+
/** Text tracks use the browser's native model; the control chooses a
|
|
599
|
+
* track index, while rendering stays with the media element. */
|
|
600
|
+
function Captions(props) {
|
|
601
|
+
const context = useMediaPlayer();
|
|
602
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
603
|
+
return createComponent(Show, {
|
|
604
|
+
get when() {
|
|
605
|
+
return context.tracks.length > 0;
|
|
606
|
+
},
|
|
607
|
+
get children() {
|
|
608
|
+
var _el$12 = _tmpl$();
|
|
609
|
+
spread(_el$12, [rest, {
|
|
610
|
+
"data-scope": "media-player",
|
|
611
|
+
"data-part": "captions"
|
|
612
|
+
}], true);
|
|
613
|
+
insert(_el$12, createComponent(Select, {
|
|
614
|
+
clearable: false,
|
|
615
|
+
get disabled() {
|
|
616
|
+
return context.disabled;
|
|
617
|
+
},
|
|
618
|
+
get options() {
|
|
619
|
+
return [{
|
|
620
|
+
label: "Off",
|
|
621
|
+
value: "-1"
|
|
622
|
+
}, ...context.tracks.map((track, index) => ({
|
|
623
|
+
label: track.label ?? `Track ${index + 1}`,
|
|
624
|
+
value: String(index)
|
|
625
|
+
}))];
|
|
626
|
+
},
|
|
627
|
+
get placeholder() {
|
|
628
|
+
return local.label ?? "Captions";
|
|
629
|
+
},
|
|
630
|
+
size: "sm",
|
|
631
|
+
get value() {
|
|
632
|
+
return String(context.controls.selectedTextTrack());
|
|
633
|
+
},
|
|
634
|
+
onValueChange: (value) => context.controls.selectTextTrack(Number(value))
|
|
635
|
+
}));
|
|
636
|
+
return _el$12;
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/** Picture-in-picture is video-only and capability-detected. */
|
|
641
|
+
function PipButton(props) {
|
|
642
|
+
const context = useMediaPlayer();
|
|
643
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
644
|
+
return createComponent(Show, {
|
|
645
|
+
get when() {
|
|
646
|
+
return context.kind === "video";
|
|
647
|
+
},
|
|
648
|
+
get children() {
|
|
649
|
+
var _el$13 = _tmpl$4();
|
|
650
|
+
spread(_el$13, [rest, {
|
|
651
|
+
type: "button",
|
|
652
|
+
"data-scope": "media-player",
|
|
653
|
+
"data-part": "pip-button",
|
|
654
|
+
get "aria-label"() {
|
|
655
|
+
return local.label ?? "Toggle picture in picture";
|
|
656
|
+
},
|
|
657
|
+
get title() {
|
|
658
|
+
return local.label ?? "Toggle picture in picture";
|
|
659
|
+
},
|
|
660
|
+
get disabled() {
|
|
661
|
+
return context.disabled;
|
|
662
|
+
},
|
|
663
|
+
onClick: () => void context.controls.togglePictureInPicture()
|
|
664
|
+
}], true);
|
|
665
|
+
insert(_el$13, () => {
|
|
666
|
+
return iconNode("picture-in-picture", {
|
|
667
|
+
width: "16",
|
|
668
|
+
height: "16"
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
return _el$13;
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
/** Fullscreen takes the whole vessel, not just the moving picture. */
|
|
676
|
+
function FullscreenButton(props) {
|
|
677
|
+
const context = useMediaPlayer();
|
|
678
|
+
const [local, rest] = splitProps(props, ["label"]);
|
|
679
|
+
var _el$14 = _tmpl$4();
|
|
680
|
+
spread(_el$14, [rest, {
|
|
681
|
+
type: "button",
|
|
682
|
+
"data-scope": "media-player",
|
|
683
|
+
"data-part": "fullscreen-button",
|
|
684
|
+
get "aria-label"() {
|
|
685
|
+
return local.label ?? "Toggle fullscreen";
|
|
686
|
+
},
|
|
687
|
+
get title() {
|
|
688
|
+
return local.label ?? "Toggle fullscreen";
|
|
689
|
+
},
|
|
690
|
+
get disabled() {
|
|
691
|
+
return context.disabled;
|
|
692
|
+
},
|
|
693
|
+
onClick: () => {
|
|
694
|
+
const root = context.root.current;
|
|
695
|
+
if (root) context.controls.toggleFullscreen(root);
|
|
696
|
+
}
|
|
697
|
+
}], true);
|
|
698
|
+
insert(_el$14, () => {
|
|
699
|
+
return iconNode("maximize-2", {
|
|
700
|
+
width: "16",
|
|
701
|
+
height: "16"
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
return _el$14;
|
|
705
|
+
}
|
|
706
|
+
const Stage = part("Stage");
|
|
707
|
+
const Controls = part("Controls");
|
|
708
|
+
//#endregion
|
|
709
|
+
//#region src/components/media-player/index.tsx
|
|
710
|
+
function createRoot(kind) {
|
|
711
|
+
function MediaRoot(props) {
|
|
712
|
+
return createComponent(MediaPlayerShell, mergeProps({ kind }, props));
|
|
713
|
+
}
|
|
714
|
+
return MediaRoot;
|
|
715
|
+
}
|
|
716
|
+
const AudioRoot = createRoot("audio");
|
|
717
|
+
const VideoRoot = createRoot("video");
|
|
718
|
+
function facade(props) {
|
|
719
|
+
function MediaPlayerFacade(current) {
|
|
720
|
+
return createComponent(props.Root, mergeProps(current, { get children() {
|
|
721
|
+
return props.children;
|
|
722
|
+
} }));
|
|
723
|
+
}
|
|
724
|
+
return MediaPlayerFacade;
|
|
725
|
+
}
|
|
726
|
+
/** The complete audio player: source controls compose into one compact
|
|
727
|
+
* rail, while custom arrangement stays on Audio.Root. */
|
|
728
|
+
const AudioFacade = facade({
|
|
729
|
+
Root: AudioRoot,
|
|
730
|
+
children: [createComponent(Stage, { get children() {
|
|
731
|
+
return createComponent(Media, {});
|
|
732
|
+
} }), createComponent(Controls, { get children() {
|
|
733
|
+
return [
|
|
734
|
+
createComponent(PlayButton, {}),
|
|
735
|
+
createComponent(Timeline, {}),
|
|
736
|
+
createComponent(Time, {}),
|
|
737
|
+
createComponent(MuteButton, {}),
|
|
738
|
+
createComponent(Volume, {}),
|
|
739
|
+
createComponent(Rate, {})
|
|
740
|
+
];
|
|
741
|
+
} })]
|
|
742
|
+
});
|
|
743
|
+
/** The complete video player: source, caption, PiP and fullscreen
|
|
744
|
+
* controls compose under the stage, while Video.Root stays open. */
|
|
745
|
+
const VideoFacade = facade({
|
|
746
|
+
Root: VideoRoot,
|
|
747
|
+
children: [createComponent(Stage, { get children() {
|
|
748
|
+
return createComponent(Media, {});
|
|
749
|
+
} }), createComponent(Controls, { get children() {
|
|
750
|
+
return [
|
|
751
|
+
createComponent(PlayButton, {}),
|
|
752
|
+
createComponent(Timeline, {}),
|
|
753
|
+
createComponent(Time, {}),
|
|
754
|
+
createComponent(MuteButton, {}),
|
|
755
|
+
createComponent(Volume, {}),
|
|
756
|
+
createComponent(Rate, {}),
|
|
757
|
+
createComponent(Captions, {}),
|
|
758
|
+
createComponent(PipButton, {}),
|
|
759
|
+
createComponent(FullscreenButton, {})
|
|
760
|
+
];
|
|
761
|
+
} })]
|
|
762
|
+
});
|
|
763
|
+
/** Audio and video share one instrument: the same state, the same keys,
|
|
764
|
+
* and the same control rhythm, while each face keeps its native element's
|
|
765
|
+
* own semantics. The parts — Root, Stage, Media, Controls, PlayButton,
|
|
766
|
+
* Timeline, Time, MuteButton, Volume, Rate, Captions, PipButton,
|
|
767
|
+
* FullscreenButton. */
|
|
768
|
+
const Audio = Object.assign(AudioFacade, {
|
|
769
|
+
Root: AudioRoot,
|
|
770
|
+
Stage,
|
|
771
|
+
Media,
|
|
772
|
+
Controls,
|
|
773
|
+
PlayButton,
|
|
774
|
+
Timeline,
|
|
775
|
+
Time,
|
|
776
|
+
MuteButton,
|
|
777
|
+
Volume,
|
|
778
|
+
Rate
|
|
779
|
+
});
|
|
780
|
+
const Video = Object.assign(VideoFacade, {
|
|
781
|
+
Root: VideoRoot,
|
|
782
|
+
Stage,
|
|
783
|
+
Media,
|
|
784
|
+
Controls,
|
|
785
|
+
PlayButton,
|
|
786
|
+
Timeline,
|
|
787
|
+
Time,
|
|
788
|
+
MuteButton,
|
|
789
|
+
Volume,
|
|
790
|
+
Rate,
|
|
791
|
+
Captions,
|
|
792
|
+
PipButton,
|
|
793
|
+
FullscreenButton
|
|
794
|
+
});
|
|
795
|
+
//#endregion
|
|
796
|
+
export { Video as n, formatMediaTime as r, Audio as t };
|