@hexdn/react 0.1.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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/player/bounded-surface-position.d.ts +8 -0
- package/dist/player/bounded-surface-position.js +8 -0
- package/dist/player/hexdn-player-control-popover.module.css +113 -0
- package/dist/player/index.d.ts +25 -0
- package/dist/player/index.js +32 -0
- package/dist/player/media-video-player-control-hold.d.ts +2 -0
- package/dist/player/media-video-player-control-hold.js +9 -0
- package/dist/player/media-video-player-debug-hud-loader-contract.d.ts +5 -0
- package/dist/player/media-video-player-debug-hud-loader-contract.js +1 -0
- package/dist/player/media-video-player-keyboard.d.ts +5 -0
- package/dist/player/media-video-player-keyboard.js +52 -0
- package/dist/player/media-video-player-layout.d.ts +64 -0
- package/dist/player/media-video-player-layout.js +110 -0
- package/dist/player/media-video-player-scrubber.d.ts +16 -0
- package/dist/player/media-video-player-scrubber.js +333 -0
- package/dist/player/media-video-player-surface-click.d.ts +8 -0
- package/dist/player/media-video-player-surface-click.js +14 -0
- package/dist/player/media-video-player-view-state.d.ts +21 -0
- package/dist/player/media-video-player-view-state.js +31 -0
- package/dist/player/media-video-player.module.css +1254 -0
- package/dist/player/playback-landscape-presentation.d.ts +16 -0
- package/dist/player/playback-landscape-presentation.js +39 -0
- package/dist/player/playback-loading-indicator.d.ts +7 -0
- package/dist/player/playback-loading-indicator.js +35 -0
- package/dist/player/player-control-popover.d.ts +16 -0
- package/dist/player/player-control-popover.js +130 -0
- package/dist/player/player-host.d.ts +31 -0
- package/dist/player/player-host.js +29 -0
- package/dist/player/player-icon.d.ts +19 -0
- package/dist/player/player-icon.js +59 -0
- package/dist/player/player-labels.d.ts +27 -0
- package/dist/player/player-labels.js +35 -0
- package/dist/player/player-slots.d.ts +18 -0
- package/dist/player/player-slots.js +1 -0
- package/dist/player/ready-player.d.ts +34 -0
- package/dist/player/ready-player.js +293 -0
- package/dist/player/tooltip-surface.d.ts +2 -0
- package/dist/player/tooltip-surface.js +8 -0
- package/dist/player/ui-primitives.d.ts +17 -0
- package/dist/player/ui-primitives.js +15 -0
- package/dist/player/ui.module.css +113 -0
- package/dist/player/use-media-video-player-controls.d.ts +17 -0
- package/dist/player/use-media-video-player-controls.js +87 -0
- package/dist/player/use-media-video-player-fullscreen.d.ts +24 -0
- package/dist/player/use-media-video-player-fullscreen.js +38 -0
- package/dist/player/use-media-video-player-interactions.d.ts +25 -0
- package/dist/player/use-media-video-player-interactions.js +163 -0
- package/dist/player/use-media-video-player-presentation.d.ts +50 -0
- package/dist/player/use-media-video-player-presentation.js +125 -0
- package/dist/player/use-media-video-player.d.ts +81 -0
- package/dist/player/use-media-video-player.js +263 -0
- package/dist/player/use-player-analytics.d.ts +13 -0
- package/dist/player/use-player-analytics.js +79 -0
- package/dist/player/use-slider-pointer.d.ts +20 -0
- package/dist/player/use-slider-pointer.js +85 -0
- package/dist/player/utils.d.ts +1 -0
- package/dist/player/utils.js +3 -0
- package/dist/upload/index.d.ts +37 -0
- package/dist/upload/index.js +60 -0
- package/dist/upload/upload.module.css +41 -0
- package/dist/upload/use-uploader.d.ts +42 -0
- package/dist/upload/use-uploader.js +179 -0
- package/package.json +51 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare const landscapeOrientation: "landscape";
|
|
2
|
+
export type PlaybackScreenOrientation = {
|
|
3
|
+
readonly lock?: ((orientation: typeof landscapeOrientation) => PromiseLike<void> | void) | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare function isCoarsePlaybackPointer(): boolean;
|
|
6
|
+
export declare function readPlaybackScreenOrientation(): PlaybackScreenOrientation | undefined;
|
|
7
|
+
export declare function enterPlaybackFullscreen(input: {
|
|
8
|
+
readonly lockLandscape: boolean;
|
|
9
|
+
readonly orientation?: PlaybackScreenOrientation | undefined;
|
|
10
|
+
readonly requestFullscreen: () => Promise<void>;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function isPlayerFullscreen(input: {
|
|
13
|
+
readonly fullscreenElement: Element | null;
|
|
14
|
+
readonly player: Element | null;
|
|
15
|
+
}): boolean;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const coarsePlaybackPointerQuery = "(pointer: coarse)";
|
|
2
|
+
const landscapeOrientation = "landscape";
|
|
3
|
+
export function isCoarsePlaybackPointer() {
|
|
4
|
+
return (typeof window !== "undefined" &&
|
|
5
|
+
typeof window.matchMedia === "function" &&
|
|
6
|
+
window.matchMedia(coarsePlaybackPointerQuery).matches);
|
|
7
|
+
}
|
|
8
|
+
export function readPlaybackScreenOrientation() {
|
|
9
|
+
if (typeof screen === "undefined" || !screen.orientation) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return screen.orientation;
|
|
13
|
+
}
|
|
14
|
+
async function lockPlaybackLandscape(orientation) {
|
|
15
|
+
if (!orientation?.lock) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await orientation.lock(landscapeOrientation);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function enterPlaybackFullscreen(input) {
|
|
27
|
+
await input.requestFullscreen();
|
|
28
|
+
if (input.lockLandscape) {
|
|
29
|
+
void lockPlaybackLandscape(input.orientation);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function isPlayerFullscreen(input) {
|
|
33
|
+
const { fullscreenElement, player } = input;
|
|
34
|
+
return Boolean(fullscreenElement &&
|
|
35
|
+
player &&
|
|
36
|
+
(fullscreenElement === player ||
|
|
37
|
+
fullscreenElement.contains(player) ||
|
|
38
|
+
player.contains(fullscreenElement)));
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export declare function PlaybackLoadingIndicator({ active, deferUntilTransitionIdle, delayMs, content, }: {
|
|
3
|
+
readonly active?: boolean | undefined;
|
|
4
|
+
readonly deferUntilTransitionIdle?: boolean | undefined;
|
|
5
|
+
readonly delayMs?: number | undefined;
|
|
6
|
+
readonly content?: ReactNode;
|
|
7
|
+
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePlayerHost } from "./player-host.js";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { LoadingIndicator } from "./ui-primitives.js";
|
|
6
|
+
import { mediaLoadingIndicatorDelayMs, schedulePlaybackLoadingIndicator, } from "@hexdn/playback/internal";
|
|
7
|
+
export function PlaybackLoadingIndicator({ active = true, deferUntilTransitionIdle = false, delayMs = mediaLoadingIndicatorDelayMs, content, }) {
|
|
8
|
+
const { transition } = usePlayerHost();
|
|
9
|
+
const { waitForIdle: waitForPlaybackPowerTransitionIdle } = transition;
|
|
10
|
+
const [visible, setVisible] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!active) {
|
|
13
|
+
// Cancel the previous browser-owned delay before a new playback wait begins.
|
|
14
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
15
|
+
setVisible(false);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setVisible(false);
|
|
19
|
+
return schedulePlaybackLoadingIndicator({
|
|
20
|
+
delayMs,
|
|
21
|
+
onVisible: () => {
|
|
22
|
+
setVisible(true);
|
|
23
|
+
},
|
|
24
|
+
waitForTransitionIdle: deferUntilTransitionIdle
|
|
25
|
+
? waitForPlaybackPowerTransitionIdle
|
|
26
|
+
: () => Promise.resolve(),
|
|
27
|
+
});
|
|
28
|
+
}, [
|
|
29
|
+
active,
|
|
30
|
+
deferUntilTransitionIdle,
|
|
31
|
+
delayMs,
|
|
32
|
+
waitForPlaybackPowerTransitionIdle,
|
|
33
|
+
]);
|
|
34
|
+
return active && visible ? (content ?? _jsx(LoadingIndicator, {})) : null;
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
type PlayerControlPopoverAlign = "center" | "end" | "start";
|
|
3
|
+
type PlayerControlPopoverSide = "top";
|
|
4
|
+
export declare function PlayerControlPopover({ align, children, className, content, contentClassName, disabled, onInteraction, onInteractionEnd, openDelayMs, side, }: {
|
|
5
|
+
readonly align?: PlayerControlPopoverAlign | undefined;
|
|
6
|
+
readonly children: ReactNode;
|
|
7
|
+
readonly className?: string | undefined;
|
|
8
|
+
readonly content: ReactNode;
|
|
9
|
+
readonly contentClassName?: string | undefined;
|
|
10
|
+
readonly disabled?: boolean | undefined;
|
|
11
|
+
readonly onInteraction?: (() => void) | undefined;
|
|
12
|
+
readonly onInteractionEnd?: (() => void) | undefined;
|
|
13
|
+
readonly openDelayMs?: number | undefined;
|
|
14
|
+
readonly side?: PlayerControlPopoverSide | undefined;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState, } from "react";
|
|
4
|
+
import { resolveBoundedHorizontalSurfaceLeft } from "./bounded-surface-position.js";
|
|
5
|
+
import { cn } from "./utils.js";
|
|
6
|
+
import styles from "./hexdn-player-control-popover.module.css";
|
|
7
|
+
const DEFAULT_OPEN_DELAY_MS = 250;
|
|
8
|
+
function readCssPixelValue(element, propertyName, fallback) {
|
|
9
|
+
const parsed = Number.parseFloat(window.getComputedStyle(element).getPropertyValue(propertyName));
|
|
10
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
11
|
+
}
|
|
12
|
+
export function PlayerControlPopover({ align = "center", children, className, content, contentClassName, disabled = false, onInteraction, onInteractionEnd, openDelayMs = DEFAULT_OPEN_DELAY_MS, side = "top", }) {
|
|
13
|
+
const hostRef = useRef(null);
|
|
14
|
+
const openTimerRef = useRef(null);
|
|
15
|
+
const surfaceRef = useRef(null);
|
|
16
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
17
|
+
const [surfaceStyle, setSurfaceStyle] = useState();
|
|
18
|
+
const clearOpenTimer = useCallback(() => {
|
|
19
|
+
if (openTimerRef.current === null) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
window.clearTimeout(openTimerRef.current);
|
|
23
|
+
openTimerRef.current = null;
|
|
24
|
+
}, []);
|
|
25
|
+
const updateLayout = useCallback(() => {
|
|
26
|
+
if (align !== "center" || disabled) {
|
|
27
|
+
setSurfaceStyle(undefined);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const host = hostRef.current;
|
|
31
|
+
const surface = surfaceRef.current;
|
|
32
|
+
if (!host || !surface) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const boundary = host.closest("[data-hexdn-player-control-popover-boundary]") ?? host.offsetParent;
|
|
36
|
+
if (!(boundary instanceof HTMLElement)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const boundaryRect = boundary.getBoundingClientRect();
|
|
40
|
+
const hostRect = host.getBoundingClientRect();
|
|
41
|
+
const surfaceRect = surface.getBoundingClientRect();
|
|
42
|
+
const gutter = readCssPixelValue(boundary, "--hexdn-s-4", 16);
|
|
43
|
+
const hostCenter = hostRect.left + hostRect.width / 2;
|
|
44
|
+
const left = resolveBoundedHorizontalSurfaceLeft({
|
|
45
|
+
anchorCenter: hostCenter,
|
|
46
|
+
boundaryEnd: boundaryRect.right,
|
|
47
|
+
boundaryStart: boundaryRect.left,
|
|
48
|
+
gutter,
|
|
49
|
+
surfaceWidth: surfaceRect.width,
|
|
50
|
+
});
|
|
51
|
+
setSurfaceStyle({
|
|
52
|
+
"--hexdn-player-control-popover-left": `${left - hostRect.left}px`,
|
|
53
|
+
"--hexdn-player-control-popover-transform-x": "0px",
|
|
54
|
+
});
|
|
55
|
+
}, [align, disabled]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
// Measure the mounted anchor and boundary before accepting pointer interaction.
|
|
58
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
59
|
+
updateLayout();
|
|
60
|
+
if (align !== "center" || disabled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
window.addEventListener("resize", updateLayout);
|
|
64
|
+
window.addEventListener("scroll", updateLayout, true);
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener("resize", updateLayout);
|
|
67
|
+
window.removeEventListener("scroll", updateLayout, true);
|
|
68
|
+
};
|
|
69
|
+
}, [align, disabled, updateLayout]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (disabled) {
|
|
72
|
+
clearOpenTimer();
|
|
73
|
+
// A disabled host capability must close an already-open interaction surface.
|
|
74
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
75
|
+
setIsOpen(false);
|
|
76
|
+
}
|
|
77
|
+
}, [clearOpenTimer, disabled]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
return () => {
|
|
80
|
+
clearOpenTimer();
|
|
81
|
+
};
|
|
82
|
+
}, [clearOpenTimer]);
|
|
83
|
+
const keepAlive = useCallback(() => {
|
|
84
|
+
onInteraction?.();
|
|
85
|
+
updateLayout();
|
|
86
|
+
}, [onInteraction, updateLayout]);
|
|
87
|
+
const openImmediately = useCallback(() => {
|
|
88
|
+
if (disabled) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
clearOpenTimer();
|
|
92
|
+
keepAlive();
|
|
93
|
+
setIsOpen(true);
|
|
94
|
+
}, [clearOpenTimer, disabled, keepAlive]);
|
|
95
|
+
const scheduleOpen = useCallback(() => {
|
|
96
|
+
if (disabled) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
keepAlive();
|
|
100
|
+
if (isOpen || openTimerRef.current !== null) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (openDelayMs <= 0) {
|
|
104
|
+
setIsOpen(true);
|
|
105
|
+
updateLayout();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
openTimerRef.current = window.setTimeout(() => {
|
|
109
|
+
openTimerRef.current = null;
|
|
110
|
+
setIsOpen(true);
|
|
111
|
+
updateLayout();
|
|
112
|
+
}, openDelayMs);
|
|
113
|
+
}, [disabled, isOpen, keepAlive, openDelayMs, updateLayout]);
|
|
114
|
+
const close = useCallback(() => {
|
|
115
|
+
clearOpenTimer();
|
|
116
|
+
setIsOpen(false);
|
|
117
|
+
onInteractionEnd?.();
|
|
118
|
+
}, [clearOpenTimer, onInteractionEnd]);
|
|
119
|
+
const handleBlur = useCallback((event) => {
|
|
120
|
+
const nextTarget = event.relatedTarget;
|
|
121
|
+
if (nextTarget instanceof Node &&
|
|
122
|
+
event.currentTarget.contains(nextTarget)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
close();
|
|
126
|
+
}, [close]);
|
|
127
|
+
return (
|
|
128
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: The wrapper delegates hover/focus state for the child trigger and floating surface.
|
|
129
|
+
_jsxs("div", { className: cn(styles.host, className), "data-hexdn-align": align, "data-hexdn-open": isOpen ? "true" : "false", "data-hexdn-side": side, onBlur: handleBlur, onFocus: openImmediately, onPointerEnter: scheduleOpen, onPointerLeave: close, onPointerMove: keepAlive, ref: hostRef, children: [children, disabled ? null : (_jsx("div", { className: cn(styles.surface, contentClassName), ref: surfaceRef, style: surfaceStyle, children: content }))] }));
|
|
130
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { fetchPlaybackResource, loadMediaVideoPlayerHlsEngine, readMediaVideoPlayerCaptionsPreference, readMediaVideoPlayerVolumePreference, writeMediaVideoPlayerCaptionsPreference, writeMediaVideoPlayerVolumePreference, type PlaybackTransition } from "@hexdn/playback/internal";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
declare const defaultPreferences: {
|
|
4
|
+
readCaptions: typeof readMediaVideoPlayerCaptionsPreference;
|
|
5
|
+
readVolume: typeof readMediaVideoPlayerVolumePreference;
|
|
6
|
+
writeCaptions: typeof writeMediaVideoPlayerCaptionsPreference;
|
|
7
|
+
writeVolume: typeof writeMediaVideoPlayerVolumePreference;
|
|
8
|
+
};
|
|
9
|
+
/** Optional integration with an application's navigation and presentation owner. */
|
|
10
|
+
export type PlayerIntegration = {
|
|
11
|
+
readonly transition?: PlaybackTransition | undefined;
|
|
12
|
+
readonly loadHlsEngine?: typeof loadMediaVideoPlayerHlsEngine | undefined;
|
|
13
|
+
readonly fetchResource?: typeof fetchPlaybackResource | undefined;
|
|
14
|
+
readonly preferences?: typeof defaultPreferences | undefined;
|
|
15
|
+
};
|
|
16
|
+
export declare function PlayerHost({ children, integration, }: {
|
|
17
|
+
readonly children: ReactNode;
|
|
18
|
+
readonly integration?: PlayerIntegration | undefined;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
export declare function usePlayerHost(): {
|
|
21
|
+
transition: PlaybackTransition;
|
|
22
|
+
loadHlsEngine: typeof loadMediaVideoPlayerHlsEngine;
|
|
23
|
+
fetchResource: typeof fetchPlaybackResource;
|
|
24
|
+
preferences: {
|
|
25
|
+
readCaptions: typeof readMediaVideoPlayerCaptionsPreference;
|
|
26
|
+
readVolume: typeof readMediaVideoPlayerVolumePreference;
|
|
27
|
+
writeCaptions: typeof writeMediaVideoPlayerCaptionsPreference;
|
|
28
|
+
writeVolume: typeof writeMediaVideoPlayerVolumePreference;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { fetchPlaybackResource, immediatePlaybackTransition, loadMediaVideoPlayerHlsEngine, readMediaVideoPlayerCaptionsPreference, readMediaVideoPlayerVolumePreference, writeMediaVideoPlayerCaptionsPreference, writeMediaVideoPlayerVolumePreference, } from "@hexdn/playback/internal";
|
|
4
|
+
import { createContext, useContext, useMemo } from "react";
|
|
5
|
+
const defaultPreferences = {
|
|
6
|
+
readCaptions: readMediaVideoPlayerCaptionsPreference,
|
|
7
|
+
readVolume: readMediaVideoPlayerVolumePreference,
|
|
8
|
+
writeCaptions: writeMediaVideoPlayerCaptionsPreference,
|
|
9
|
+
writeVolume: writeMediaVideoPlayerVolumePreference,
|
|
10
|
+
};
|
|
11
|
+
const defaultHost = {
|
|
12
|
+
transition: immediatePlaybackTransition,
|
|
13
|
+
loadHlsEngine: loadMediaVideoPlayerHlsEngine,
|
|
14
|
+
fetchResource: fetchPlaybackResource,
|
|
15
|
+
preferences: defaultPreferences,
|
|
16
|
+
};
|
|
17
|
+
const PlayerHostContext = createContext(defaultHost);
|
|
18
|
+
export function PlayerHost({ children, integration, }) {
|
|
19
|
+
const host = useMemo(() => ({
|
|
20
|
+
transition: integration?.transition ?? defaultHost.transition,
|
|
21
|
+
loadHlsEngine: integration?.loadHlsEngine ?? defaultHost.loadHlsEngine,
|
|
22
|
+
fetchResource: integration?.fetchResource ?? defaultHost.fetchResource,
|
|
23
|
+
preferences: integration?.preferences ?? defaultHost.preferences,
|
|
24
|
+
}), [integration]);
|
|
25
|
+
return _jsx(PlayerHostContext, { value: host, children: children });
|
|
26
|
+
}
|
|
27
|
+
export function usePlayerHost() {
|
|
28
|
+
return useContext(PlayerHostContext);
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare const shapes: {
|
|
2
|
+
play: import("react").JSX.Element;
|
|
3
|
+
pause: import("react").JSX.Element;
|
|
4
|
+
back10: import("react").JSX.Element;
|
|
5
|
+
fwd10: import("react").JSX.Element;
|
|
6
|
+
volume: import("react").JSX.Element;
|
|
7
|
+
captions: import("react").JSX.Element;
|
|
8
|
+
fullscreen: import("react").JSX.Element;
|
|
9
|
+
chevron: import("react").JSX.Element;
|
|
10
|
+
info: import("react").JSX.Element;
|
|
11
|
+
error: import("react").JSX.Element;
|
|
12
|
+
};
|
|
13
|
+
export type DsIconName = keyof typeof shapes | "chevron-l";
|
|
14
|
+
export declare function resolveDsVolumeLevel(volume: number): "muted" | "low" | "high";
|
|
15
|
+
export declare function DsIcon({ name, volumeLevel, }: {
|
|
16
|
+
readonly name: DsIconName;
|
|
17
|
+
readonly volumeLevel?: "muted" | "low" | "high" | undefined;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styles from "./ui.module.css";
|
|
3
|
+
import { cn } from "./utils.js";
|
|
4
|
+
function path(d, props = {}) {
|
|
5
|
+
return _jsx("path", { d: d, ...props });
|
|
6
|
+
}
|
|
7
|
+
function circle(props) {
|
|
8
|
+
return _jsx("circle", { ...props });
|
|
9
|
+
}
|
|
10
|
+
function rect(props) {
|
|
11
|
+
return _jsx("rect", { ...props });
|
|
12
|
+
}
|
|
13
|
+
const seekText = (_jsx("text", { fill: "currentColor", fontFamily: "var(--hexdn-player-font-family, var(--hexdn-type-timecode-family, ui-sans-serif, system-ui, sans-serif))", fontSize: "7", fontWeight: "600", letterSpacing: "-.02em", stroke: "none", textAnchor: "middle", x: "12", y: "15.1", children: "10" }));
|
|
14
|
+
const volumeSpeakerPath = "M4 9.6h3.3L12 5.9v12.2L7.3 14.4H4z";
|
|
15
|
+
const volumeLowWavePath = "M15.4 9.9a3.4 3.4 0 0 1 0 4.2";
|
|
16
|
+
const volumeHighWavePath = "M18.1 7.6a7 7 0 0 1 0 8.8";
|
|
17
|
+
const volumeMutePath = "M15.9 10.1l4.1 3.8M20 10.1l-4.1 3.8";
|
|
18
|
+
const statefulVolumeIconShape = (_jsxs(_Fragment, { children: [path(volumeSpeakerPath, {
|
|
19
|
+
className: "hexdn-volume-glyph-speaker",
|
|
20
|
+
fill: "currentColor",
|
|
21
|
+
}), path(volumeLowWavePath, {
|
|
22
|
+
className: "hexdn-volume-glyph-wave hexdn-volume-glyph-wave--low",
|
|
23
|
+
}), path(volumeHighWavePath, {
|
|
24
|
+
className: "hexdn-volume-glyph-wave",
|
|
25
|
+
}), path(volumeMutePath, { className: "hexdn-volume-glyph-mute" })] }));
|
|
26
|
+
const shapes = {
|
|
27
|
+
play: path("M8 5v14l11-7z", { fill: "currentColor" }),
|
|
28
|
+
pause: path("M9.5 7v10M14.5 7v10"),
|
|
29
|
+
back10: (_jsxs(_Fragment, { children: [path("M5.42 8.6A7.6 7.6 0 1 0 12 4.8"), path("M8.4 4.8L12 2.5v4.6z", {
|
|
30
|
+
fill: "currentColor",
|
|
31
|
+
}), seekText] })),
|
|
32
|
+
fwd10: (_jsxs(_Fragment, { children: [path("M18.58 8.6A7.6 7.6 0 1 1 12 4.8"), path("M15.6 4.8L12 2.5v4.6z", {
|
|
33
|
+
fill: "currentColor",
|
|
34
|
+
}), seekText] })),
|
|
35
|
+
volume: (_jsxs(_Fragment, { children: [path(volumeSpeakerPath, { fill: "currentColor" }), path(`${volumeLowWavePath}${volumeHighWavePath}`)] })),
|
|
36
|
+
captions: (_jsxs(_Fragment, { children: [rect({ height: 13, rx: 3, width: 18, x: 3, y: 5.5 }), path("M6.8 11.2h10.4M6.8 14.6h6")] })),
|
|
37
|
+
fullscreen: path("M4.5 9V5.5H8M16 5.5h3.5V9M19.5 15v3.5H16M8 18.5H4.5V15"),
|
|
38
|
+
chevron: path("M10 6.5l5.5 5.5-5.5 5.5"),
|
|
39
|
+
info: (_jsxs(_Fragment, { children: [circle({ cx: 12, cy: 12, r: 8 }), path("M12 11.2v5"), circle({
|
|
40
|
+
cx: 12,
|
|
41
|
+
cy: 7.8,
|
|
42
|
+
fill: "currentColor",
|
|
43
|
+
r: 1.05,
|
|
44
|
+
stroke: "none",
|
|
45
|
+
})] })),
|
|
46
|
+
error: (_jsxs(_Fragment, { children: [circle({ cx: 12, cy: 12, r: 8 }), path("M9.3 9.3l5.4 5.4M14.7 9.3l-5.4 5.4")] })),
|
|
47
|
+
};
|
|
48
|
+
export function resolveDsVolumeLevel(volume) {
|
|
49
|
+
return !Number.isFinite(volume) || volume <= 0
|
|
50
|
+
? "muted"
|
|
51
|
+
: volume <= 0.5
|
|
52
|
+
? "low"
|
|
53
|
+
: "high";
|
|
54
|
+
}
|
|
55
|
+
export function DsIcon({ name, volumeLevel, }) {
|
|
56
|
+
return (_jsx("svg", { "aria-hidden": "true", className: cn("hexdn-icon", styles.icon, name === "volume" && "hexdn-volume-glyph"), "data-hexdn-volume-level": volumeLevel, fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", style: name === "chevron-l" ? { transform: "rotate(90deg)" } : undefined, children: name === "volume" && volumeLevel
|
|
57
|
+
? statefulVolumeIconShape
|
|
58
|
+
: shapes[name === "chevron-l" ? "chevron" : name] }));
|
|
59
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const defaultPlayerLabels: {
|
|
2
|
+
backAction: string;
|
|
3
|
+
hideStatsAction: string;
|
|
4
|
+
showStatsAction: string;
|
|
5
|
+
seekLabel: string;
|
|
6
|
+
seekValueText: string;
|
|
7
|
+
rewind10Action: string;
|
|
8
|
+
forward10Action: string;
|
|
9
|
+
pauseVideoAction: string;
|
|
10
|
+
playVideoAction: string;
|
|
11
|
+
volumeLabel: string;
|
|
12
|
+
unmuteVideoAction: string;
|
|
13
|
+
muteVideoAction: string;
|
|
14
|
+
disableCaptionsAction: string;
|
|
15
|
+
enableCaptionsAction: string;
|
|
16
|
+
exitFullscreenAction: string;
|
|
17
|
+
enterFullscreenAction: string;
|
|
18
|
+
retryAction: string;
|
|
19
|
+
sessionUnavailableBody: string;
|
|
20
|
+
sessionUnavailableTitle: string;
|
|
21
|
+
fullscreenUnavailableError: string;
|
|
22
|
+
playbackFailedError: string;
|
|
23
|
+
playbackStartFailedError: string;
|
|
24
|
+
protectedPlaybackUnsupportedError: string;
|
|
25
|
+
};
|
|
26
|
+
export type PlayerLabels = typeof defaultPlayerLabels;
|
|
27
|
+
export declare function createPlayerTranslator(labels?: Partial<PlayerLabels>): (key: `playback.${keyof PlayerLabels}`, values?: Readonly<Record<string, string>>) => string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const defaultPlayerLabels = {
|
|
2
|
+
backAction: "Back",
|
|
3
|
+
hideStatsAction: "Hide playback statistics",
|
|
4
|
+
showStatsAction: "Show playback statistics",
|
|
5
|
+
seekLabel: "Seek",
|
|
6
|
+
seekValueText: "{current} of {duration}",
|
|
7
|
+
rewind10Action: "Back 10 seconds",
|
|
8
|
+
forward10Action: "Forward 10 seconds",
|
|
9
|
+
pauseVideoAction: "Pause",
|
|
10
|
+
playVideoAction: "Play",
|
|
11
|
+
volumeLabel: "Volume",
|
|
12
|
+
unmuteVideoAction: "Unmute",
|
|
13
|
+
muteVideoAction: "Mute",
|
|
14
|
+
disableCaptionsAction: "Turn captions off",
|
|
15
|
+
enableCaptionsAction: "Turn captions on",
|
|
16
|
+
exitFullscreenAction: "Exit fullscreen",
|
|
17
|
+
enterFullscreenAction: "Enter fullscreen",
|
|
18
|
+
retryAction: "Try again",
|
|
19
|
+
sessionUnavailableBody: "The video could not be loaded. Please try again.",
|
|
20
|
+
sessionUnavailableTitle: "Playback unavailable",
|
|
21
|
+
fullscreenUnavailableError: "Fullscreen is unavailable in this browser.",
|
|
22
|
+
playbackFailedError: "The video could not be played.",
|
|
23
|
+
playbackStartFailedError: "The video could not start. Please try again.",
|
|
24
|
+
protectedPlaybackUnsupportedError: "This browser cannot play this protected video.",
|
|
25
|
+
};
|
|
26
|
+
export function createPlayerTranslator(labels) {
|
|
27
|
+
const copy = { ...defaultPlayerLabels, ...labels };
|
|
28
|
+
return (key, values) => {
|
|
29
|
+
let text = copy[key.slice("playback.".length)];
|
|
30
|
+
for (const [name, value] of Object.entries(values ?? {})) {
|
|
31
|
+
text = text.replaceAll(`{${name}}`, value);
|
|
32
|
+
}
|
|
33
|
+
return text;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MediaVideoPlayerError } from "@hexdn/playback/internal";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export type PlayerSlotControls = {
|
|
4
|
+
readonly showControls: () => void;
|
|
5
|
+
readonly holdControls: () => void;
|
|
6
|
+
readonly releaseControls: () => void;
|
|
7
|
+
};
|
|
8
|
+
export type PlayerSlots = {
|
|
9
|
+
readonly headerTrailing?: ((controls: PlayerSlotControls) => ReactNode) | undefined;
|
|
10
|
+
readonly leadingControls?: ((controls: PlayerSlotControls) => ReactNode) | undefined;
|
|
11
|
+
readonly loading?: ReactNode;
|
|
12
|
+
readonly controlError?: ((message: string) => ReactNode) | undefined;
|
|
13
|
+
readonly terminalError?: ((input: {
|
|
14
|
+
readonly error: MediaVideoPlayerError;
|
|
15
|
+
readonly retry: () => Promise<void>;
|
|
16
|
+
readonly back?: (() => Promise<void>) | undefined;
|
|
17
|
+
}) => ReactNode) | undefined;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import { type PlayerAnalyticsOptions } from "./use-player-analytics.js";
|
|
3
|
+
import { type PlayerLabels } from "./player-labels.js";
|
|
4
|
+
import type { PlayerSlots } from "./player-slots.js";
|
|
5
|
+
import type { PlaybackSource, VideoPlaybackRequestAuthorization, VideoPreviewThumbnails } from "@hexdn/playback";
|
|
6
|
+
import type { MediaVideoPlayerDebugHudComponent } from "./media-video-player-debug-hud-loader-contract.js";
|
|
7
|
+
import { type MediaVideoPlayerProgressSnapshot, type MediaVideoPlayerQualityEvent } from "./use-media-video-player.js";
|
|
8
|
+
export type ReadyPlayerProps = {
|
|
9
|
+
readonly getSource?: import("@hexdn/playback").PlaybackSourceResolver | undefined;
|
|
10
|
+
readonly autoPlay?: boolean | undefined;
|
|
11
|
+
readonly analytics?: PlayerAnalyticsOptions | undefined;
|
|
12
|
+
readonly style?: CSSProperties | undefined;
|
|
13
|
+
readonly sourceError?: string | null | undefined;
|
|
14
|
+
readonly onRetrySource?: (() => Promise<void>) | undefined;
|
|
15
|
+
readonly className?: string | undefined;
|
|
16
|
+
readonly initialPositionMs?: number | undefined;
|
|
17
|
+
readonly onBack?: (() => void) | undefined;
|
|
18
|
+
readonly onProgress?: ((snapshot: MediaVideoPlayerProgressSnapshot) => void) | undefined;
|
|
19
|
+
readonly onQualityEvent?: ((event: MediaVideoPlayerQualityEvent) => void) | undefined;
|
|
20
|
+
readonly onProtectedPlaybackRefresh?: (() => Promise<PlaybackSource | null | undefined>) | undefined;
|
|
21
|
+
readonly playbackIdentity?: string | undefined;
|
|
22
|
+
readonly posterUrl?: string | undefined;
|
|
23
|
+
readonly previewThumbnails?: VideoPreviewThumbnails | undefined;
|
|
24
|
+
readonly requestAuthorization?: VideoPlaybackRequestAuthorization | undefined;
|
|
25
|
+
readonly sourceUrl: string;
|
|
26
|
+
readonly expiresAt?: string | undefined;
|
|
27
|
+
readonly subtitle?: string | undefined;
|
|
28
|
+
readonly title?: string | undefined;
|
|
29
|
+
readonly labels?: Partial<PlayerLabels> | undefined;
|
|
30
|
+
readonly slots?: PlayerSlots | undefined;
|
|
31
|
+
readonly debugControls?: boolean | undefined;
|
|
32
|
+
readonly loadDebugHud?: (() => Promise<MediaVideoPlayerDebugHudComponent | null>) | undefined;
|
|
33
|
+
};
|
|
34
|
+
export declare function ReadyPlayer({ autoPlay, getSource, analytics, style, sourceError, onRetrySource, className, initialPositionMs, onBack, onProgress, onQualityEvent, onProtectedPlaybackRefresh, playbackIdentity, posterUrl, previewThumbnails, requestAuthorization, sourceUrl, expiresAt, subtitle, title, labels, slots, debugControls, loadDebugHud, }: ReadyPlayerProps): import("react").JSX.Element;
|