@livepeer/react
Version:
React primitives for video apps.
1,330 lines (1,302 loc) • 46.1 kB
JavaScript
"use client";
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/player.tsx
var player_exports = {};
__export(player_exports, {
ClipTrigger: () => ClipTrigger,
Container: () => Container,
Controls: () => Controls,
ErrorIndicator: () => ErrorIndicator,
FullscreenIndicator: () => FullscreenIndicator,
FullscreenTrigger: () => FullscreenTrigger,
LiveIndicator: () => LiveIndicator,
LoadingIndicator: () => LoadingIndicator,
MediaProvider: () => MediaProvider,
MuteTrigger: () => MuteTrigger,
PictureInPictureTrigger: () => PictureInPictureTrigger,
PlayPauseTrigger: () => PlayPauseTrigger,
PlayingIndicator: () => PlayingIndicator,
Portal: () => Portal,
Poster: () => Poster,
Range: () => Range2,
RateSelect: () => RateSelect,
RateSelectItem: () => RateSelectItem,
Root: () => Root,
Seek: () => Seek,
SeekBuffer: () => SeekBuffer,
SelectArrow: () => SelectArrow2,
SelectContent: () => SelectContent2,
SelectGroup: () => SelectGroup2,
SelectIcon: () => SelectIcon2,
SelectItemIndicator: () => SelectItemIndicator2,
SelectItemText: () => SelectItemText2,
SelectLabel: () => SelectLabel2,
SelectPortal: () => SelectPortal2,
SelectScrollDownButton: () => SelectScrollDownButton2,
SelectScrollUpButton: () => SelectScrollUpButton2,
SelectSeparator: () => SelectSeparator2,
SelectTrigger: () => SelectTrigger2,
SelectValue: () => SelectValue2,
SelectViewport: () => SelectViewport2,
Thumb: () => Thumb2,
Time: () => Time,
Track: () => Track2,
Video: () => Video,
VideoQualitySelect: () => VideoQualitySelect,
VideoQualitySelectItem: () => VideoQualitySelectItem,
Volume: () => Volume,
VolumeIndicator: () => VolumeIndicator,
createMediaScope: () => createMediaScope,
useMediaContext: () => useMediaContext,
useStore: () => useStore
});
module.exports = __toCommonJS(player_exports);
// src/player/ClipTrigger.tsx
var import_primitive = require("@radix-ui/primitive");
var import_react_presence = require("@radix-ui/react-presence");
var import_react = __toESM(require("react"), 1);
var import_zustand2 = require("zustand");
var import_shallow = require("zustand/react/shallow");
// src/shared/context.tsx
var import_react_context = require("@radix-ui/react-context");
var import_zustand = require("zustand");
var MEDIA_NAME = "Media";
var [createMediaContext, createMediaScope] = (0, import_react_context.createContextScope)(MEDIA_NAME);
var [MediaProvider, useMediaContext] = createMediaContext(MEDIA_NAME);
var useStore = import_zustand.useStore;
// src/shared/primitive.tsx
var import_react_slot = require("@radix-ui/react-slot");
var React = __toESM(require("react"), 1);
var ReactDOM = __toESM(require("react-dom"), 1);
var NODES = [
"a",
"audio",
"button",
"div",
"form",
"h2",
"h3",
"img",
"input",
"label",
"li",
"nav",
"ol",
"p",
"span",
"svg",
"ul",
"video"
];
var Primitive = NODES.reduce((primitive, node) => {
const Node = React.forwardRef(
// biome-ignore lint/suspicious/noExplicitAny: any
(props, forwardedRef) => {
const { asChild, ...primitiveProps } = props;
const Comp = asChild ? import_react_slot.Slot : node;
React.useEffect(() => {
window[Symbol.for("radix-ui")] = true;
}, []);
return /* @__PURE__ */ React.createElement(Comp, { ...primitiveProps, ref: forwardedRef });
}
);
Node.displayName = `Primitive.${node}`;
return { ...primitive, [node]: Node };
}, {});
// src/shared/utils.ts
var noPropagate = (cb) => (event) => {
event.stopPropagation();
return cb();
};
// src/player/ClipTrigger.tsx
var CLIP_TRIGGER_NAME = "ClipTrigger";
var ClipTrigger = import_react.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, forceMount, onClip, ...clipTriggerProps } = props;
const context = useMediaContext(CLIP_TRIGGER_NAME, __scopeMedia);
const { clipLength, requestClip, playbackId, title } = (0, import_zustand2.useStore)(
context.store,
(0, import_shallow.useShallow)(
({ __controls, __controlsFunctions, aria, __initialProps }) => ({
requestClip: __controlsFunctions.requestClip,
playbackId: __controls.playbackId,
clipLength: __initialProps.clipLength,
title: aria.clip
})
)
);
(0, import_react.useEffect)(() => {
if (playbackId) {
return context.store.subscribe(
(state) => state.__controls.requestedClipParams,
(params) => {
if (params) {
onClip({ playbackId, ...params });
}
}
);
}
}, [playbackId]);
return /* @__PURE__ */ import_react.default.createElement(import_react_presence.Presence, { present: forceMount || Boolean(clipLength) }, /* @__PURE__ */ import_react.default.createElement(
Primitive.button,
{
type: "button",
"aria-label": title ?? void 0,
title: title ?? void 0,
disabled: !playbackId || !requestClip,
...clipTriggerProps,
onClick: (0, import_primitive.composeEventHandlers)(
props.onClick,
noPropagate(requestClip)
),
ref: forwardedRef,
"data-livepeer-controls-clip-button": "",
"data-visible": String(Boolean(clipLength))
}
));
}
);
ClipTrigger.displayName = CLIP_TRIGGER_NAME;
// src/player/Controls.tsx
var import_primitive2 = require("@radix-ui/primitive");
var import_react_presence2 = require("@radix-ui/react-presence");
var import_react2 = __toESM(require("react"), 1);
var import_zustand3 = require("zustand");
var import_shallow2 = require("zustand/react/shallow");
var CONTROLS_NAME = "Controls";
var Controls = import_react2.default.forwardRef(
(props, forwardedRef) => {
const {
forceMount,
__scopeMedia,
onClick,
style,
autoHide,
...controlsProps
} = props;
const context = useMediaContext(CONTROLS_NAME, __scopeMedia);
const { hidden, loading, togglePlay, error } = (0, import_zustand3.useStore)(
context.store,
(0, import_shallow2.useShallow)(({ hidden: hidden2, loading: loading2, __controlsFunctions, error: error2 }) => ({
hidden: hidden2,
loading: loading2,
togglePlay: __controlsFunctions.togglePlay,
error: error2?.type ?? null
}))
);
const shown = (0, import_react2.useMemo)(
() => !hidden && !loading && !error,
[hidden, loading, error]
);
(0, import_react2.useEffect)(() => {
if (autoHide !== void 0) {
context.store.getState().__controlsFunctions.setAutohide(autoHide);
}
}, []);
return /* @__PURE__ */ import_react2.default.createElement(import_react_presence2.Presence, { present: forceMount || shown }, /* @__PURE__ */ import_react2.default.createElement(
Primitive.div,
{
...controlsProps,
ref: forwardedRef,
"data-livepeer-controls": "",
"data-visible": String(shown),
onClick: (0, import_primitive2.composeEventHandlers)(onClick, noPropagate(togglePlay)),
style: {
...style,
// ensures controls expands in ratio
position: "absolute",
inset: 0
}
}
));
}
);
Controls.displayName = CONTROLS_NAME;
// src/player/LiveIndicator.tsx
var import_react_presence3 = require("@radix-ui/react-presence");
var import_react3 = __toESM(require("react"), 1);
var import_zustand4 = require("zustand");
var LIVE_INDICATOR_NAME = "LiveIndicator";
var LiveIndicator = import_react3.default.forwardRef((props, forwardedRef) => {
const {
__scopeMedia,
forceMount,
matcher = true,
...liveIndicatorProps
} = props;
const context = useMediaContext(LIVE_INDICATOR_NAME, __scopeMedia);
const live = (0, import_zustand4.useStore)(context.store, ({ live: live2 }) => live2);
const isPresent = (0, import_react3.useMemo)(
() => typeof matcher === "function" ? matcher(live) : matcher === live,
[matcher, live]
);
return /* @__PURE__ */ import_react3.default.createElement(import_react_presence3.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react3.default.createElement(
Primitive.span,
{
"aria-label": "live",
...liveIndicatorProps,
ref: forwardedRef,
"data-livepeer-controls-live-indicator": "",
"data-live": String(Boolean(live)),
"data-visible": String(isPresent)
}
));
});
LiveIndicator.displayName = LIVE_INDICATOR_NAME;
// src/player/MuteTrigger.tsx
var import_primitive3 = require("@radix-ui/primitive");
var import_react4 = __toESM(require("react"), 1);
var import_zustand5 = require("zustand");
var import_shallow3 = require("zustand/react/shallow");
var MUTE_TRIGGER_NAME = "MuteTrigger";
var MuteTrigger = import_react4.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, ...playProps } = props;
const context = useMediaContext(MUTE_TRIGGER_NAME, __scopeMedia);
const { muted, toggleMute } = (0, import_zustand5.useStore)(
context.store,
(0, import_shallow3.useShallow)(({ __controls, __controlsFunctions }) => ({
muted: __controls.muted,
toggleMute: __controlsFunctions.requestToggleMute
}))
);
const title = import_react4.default.useMemo(
() => muted ? "Unmute (m)" : "Mute (m)",
[muted]
);
return /* @__PURE__ */ import_react4.default.createElement(
Primitive.button,
{
type: "button",
"aria-pressed": muted,
"aria-label": title,
title,
...playProps,
onClick: (0, import_primitive3.composeEventHandlers)(props.onClick, noPropagate(toggleMute)),
ref: forwardedRef,
"data-livepeer-controls-mute-trigger": "",
"data-muted": String(muted)
}
);
}
);
MuteTrigger.displayName = MUTE_TRIGGER_NAME;
// src/player/Play.tsx
var import_primitive4 = require("@radix-ui/primitive");
var import_react_presence4 = require("@radix-ui/react-presence");
var import_react5 = __toESM(require("react"), 1);
var import_zustand6 = require("zustand");
var import_shallow4 = require("zustand/react/shallow");
var PLAY_PAUSE_TRIGGER_NAME = "PlayPauseTrigger";
var PlayPauseTrigger = import_react5.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, ...playProps } = props;
const context = useMediaContext(PLAY_PAUSE_TRIGGER_NAME, __scopeMedia);
const { playing, togglePlay, title } = (0, import_zustand6.useStore)(
context.store,
(0, import_shallow4.useShallow)(({ playing: playing2, __controlsFunctions, aria }) => ({
playing: playing2,
togglePlay: __controlsFunctions.togglePlay,
title: aria.playPause
}))
);
return /* @__PURE__ */ import_react5.default.createElement(
Primitive.button,
{
type: "button",
"aria-pressed": playing,
"aria-label": title ?? void 0,
title: title ?? void 0,
...playProps,
onClick: (0, import_primitive4.composeEventHandlers)(props.onClick, noPropagate(togglePlay)),
ref: forwardedRef,
"data-livepeer-controls-play-pause-trigger": "",
"data-playing": String(playing)
}
);
});
PlayPauseTrigger.displayName = PLAY_PAUSE_TRIGGER_NAME;
var PLAYING_INDICATOR_NAME = "PlayingIndicator";
var PlayingIndicator = import_react5.default.forwardRef((props, forwardedRef) => {
const {
__scopeMedia,
forceMount,
matcher = true,
...playPauseIndicatorProps
} = props;
const context = useMediaContext(PLAYING_INDICATOR_NAME, __scopeMedia);
const playing = (0, import_zustand6.useStore)(
context.store,
(0, import_shallow4.useShallow)(({ playing: playing2 }) => playing2)
);
const isPresent = (0, import_react5.useMemo)(
() => typeof matcher === "boolean" ? matcher === playing : matcher(playing),
[playing, matcher]
);
return /* @__PURE__ */ import_react5.default.createElement(import_react_presence4.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react5.default.createElement(
Primitive.div,
{
...playPauseIndicatorProps,
ref: forwardedRef,
"data-livepeer-controls-play-pause-indicator": "",
"data-playing": String(playing),
"data-visible": String(isPresent)
}
));
});
PlayingIndicator.displayName = PLAYING_INDICATOR_NAME;
// src/player/Player.tsx
var import_media = require("@livepeer/core/media");
var import_storage = require("@livepeer/core/storage");
var import_version = require("@livepeer/core/version");
var import_browser = require("@livepeer/core-web/browser");
var import_react6 = __toESM(require("react"), 1);
var Player = import_react6.default.memo((props) => {
const {
aspectRatio = 16 / 9,
src,
children,
jwt,
accessKey,
storage,
onPlaybackEvents,
metricsInterval,
playbackId,
...rest
} = props;
const store = (0, import_react6.useRef)(
(0, import_media.createControllerStore)({
device: (0, import_browser.getDeviceInfo)(import_version.version.react),
storage: storage ?? (0, import_storage.createStorage)(
storage !== null && typeof window !== "undefined" ? {
storage: window.localStorage
} : {
storage: import_storage.noopStorage
}
),
src,
playbackId,
initialProps: {
aspectRatio,
jwt,
accessKey,
...rest
}
})
);
(0, import_react6.useEffect)(() => {
if (jwt) {
store?.current?.store.setState((prev) => ({
__initialProps: {
...prev.__initialProps,
jwt
}
}));
}
}, [jwt]);
(0, import_react6.useEffect)(() => {
if (accessKey) {
store?.current?.store.setState((prev) => ({
__initialProps: {
...prev.__initialProps,
accessKey
}
}));
}
}, [accessKey]);
(0, import_react6.useEffect)(() => {
return () => {
store?.current?.destroy?.();
};
}, []);
(0, import_react6.useEffect)(() => {
const metrics = (0, import_media.addLegacyMediaMetricsToStore)(store.current.store);
return () => {
metrics.destroy();
};
}, []);
(0, import_react6.useEffect)(() => {
const metrics = (0, import_media.addMetricsToStore)(store.current.store, {
onPlaybackEvents,
interval: metricsInterval
});
return () => {
metrics.destroy();
};
}, []);
return /* @__PURE__ */ import_react6.default.createElement(MediaProvider, { store: store.current.store, scope: props.__scopeMedia }, children);
});
Player.displayName = "Player";
var Root = Player;
// src/player/Poster.tsx
var import_react_presence5 = require("@radix-ui/react-presence");
var import_react7 = __toESM(require("react"), 1);
var import_zustand7 = require("zustand");
var POSTER_NAME = "Poster";
var Poster = import_react7.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, forceMount, src, ...posterProps } = props;
const context = useMediaContext(POSTER_NAME, __scopeMedia);
const poster = (0, import_zustand7.useStore)(context.store, ({ poster: poster2 }) => poster2);
return /* @__PURE__ */ import_react7.default.createElement(import_react_presence5.Presence, { present: forceMount || Boolean(src || poster) }, /* @__PURE__ */ import_react7.default.createElement(
Primitive.img,
{
alt: "Poster for video",
"aria-hidden": "true",
...posterProps,
src: src || poster || void 0,
ref: forwardedRef,
"data-livepeer-poster": "",
"data-visible": String(Boolean(src || poster))
}
));
}
);
Poster.displayName = POSTER_NAME;
// src/player/RateSelect.tsx
var import_react8 = __toESM(require("react"), 1);
var import_zustand8 = require("zustand");
var import_shallow5 = require("zustand/react/shallow");
// src/shared/Select.tsx
var SelectPrimitive = __toESM(require("@radix-ui/react-select"), 1);
var SelectRoot = SelectPrimitive.Root;
var SelectTrigger2 = SelectPrimitive.SelectTrigger;
var SelectValue2 = SelectPrimitive.SelectValue;
var SelectIcon2 = SelectPrimitive.SelectIcon;
var SelectPortal2 = SelectPrimitive.SelectPortal;
var SelectContent2 = SelectPrimitive.SelectContent;
var SelectViewport2 = SelectPrimitive.SelectViewport;
var SelectGroup2 = SelectPrimitive.SelectGroup;
var SelectLabel2 = SelectPrimitive.SelectLabel;
var SelectItem2 = SelectPrimitive.SelectItem;
var SelectItemText2 = SelectPrimitive.SelectItemText;
var SelectItemIndicator2 = SelectPrimitive.SelectItemIndicator;
var SelectScrollUpButton2 = SelectPrimitive.SelectScrollUpButton;
var SelectScrollDownButton2 = SelectPrimitive.SelectScrollDownButton;
var SelectSeparator2 = SelectPrimitive.SelectSeparator;
var SelectArrow2 = SelectPrimitive.SelectArrow;
// src/player/RateSelect.tsx
var RATE_SELECT_NAME = "RateSelect";
var RateSelect = (props) => {
const { __scopeMedia, defaultValue, ...rateSelectProps } = props;
const context = useMediaContext(RATE_SELECT_NAME, __scopeMedia);
const { playbackRate, setPlaybackRate } = (0, import_zustand8.useStore)(
context.store,
(0, import_shallow5.useShallow)(({ playbackRate: playbackRate2, __controlsFunctions }) => ({
playbackRate: playbackRate2,
setPlaybackRate: __controlsFunctions.setPlaybackRate
}))
);
const onValueChangeComposed = (0, import_react8.useCallback)(
(value) => {
setPlaybackRate(value);
props.onValueChange?.(value);
},
[props.onValueChange, setPlaybackRate]
);
return /* @__PURE__ */ import_react8.default.createElement(
SelectRoot,
{
...rateSelectProps,
value: playbackRate === "constant" ? "constant" : playbackRate.toFixed(2),
onValueChange: onValueChangeComposed,
"data-livepeer-rate-select": "",
"data-rate": String(playbackRate)
}
);
};
RateSelect.displayName = RATE_SELECT_NAME;
var RATE_SELECT_ITEM_NAME = "RateSelectItem";
var RateSelectItem = import_react8.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, value, ...rateSelectItemProps } = props;
return /* @__PURE__ */ import_react8.default.createElement(
SelectItem2,
{
...rateSelectItemProps,
ref: forwardedRef,
value: Number(value).toFixed(2),
"data-livepeer-rate-select-item": ""
}
);
});
RateSelectItem.displayName = RATE_SELECT_ITEM_NAME;
// src/player/Seek.tsx
var import_react_presence6 = require("@radix-ui/react-presence");
var import_react9 = __toESM(require("react"), 1);
var import_zustand9 = require("zustand");
var import_shallow6 = require("zustand/react/shallow");
// src/shared/Slider.tsx
var SliderPrimitive = __toESM(require("@radix-ui/react-slider"), 1);
var Root4 = SliderPrimitive.Root;
var Track2 = SliderPrimitive.Track;
var Range2 = SliderPrimitive.Range;
var Thumb2 = SliderPrimitive.Thumb;
// src/player/Seek.tsx
var SEEK_NAME = "Seek";
var Seek = import_react9.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, forceMount, style, ...seekProps } = props;
const context = useMediaContext(SEEK_NAME, __scopeMedia);
const {
ariaProgress,
duration,
buffered,
bufferedPercent,
progress,
live,
seek
} = (0, import_zustand9.useStore)(
context.store,
(0, import_shallow6.useShallow)(
({
aria,
duration: duration2,
buffered: buffered2,
bufferedPercent: bufferedPercent2,
progress: progress2,
live: live2,
__controlsFunctions
}) => ({
ariaProgress: aria.progress,
duration: duration2,
buffered: buffered2,
bufferedPercent: bufferedPercent2,
progress: progress2,
live: live2,
seek: __controlsFunctions.requestSeek
})
)
);
const onValueChange = import_react9.default.useCallback(
([value]) => seek(value),
[seek]
);
const onValueCommit = import_react9.default.useCallback(
([value]) => seek(value),
[seek]
);
const onValueChangeComposed = (0, import_react9.useCallback)(
(value) => {
if (props.onValueChange) {
props.onValueChange(value);
}
onValueChange(value);
},
[props.onValueChange, onValueChange]
);
const onValueCommitComposed = (0, import_react9.useCallback)(
(value) => {
if (props.onValueCommit) {
props.onValueCommit(value);
}
onValueCommit(value);
},
[props.onValueCommit, onValueCommit]
);
return /* @__PURE__ */ import_react9.default.createElement(import_react_presence6.Presence, { present: forceMount || !live }, /* @__PURE__ */ import_react9.default.createElement(
Root4,
{
"aria-label": live ? "Live Seek Slider" : "Video Seek Slider",
"aria-valuetext": ariaProgress ?? void 0,
step: 0.1,
max: duration,
value: [progress],
role: "slider",
...seekProps,
onValueChange: onValueChangeComposed,
onValueCommit: onValueCommitComposed,
onClick: noPropagate(() => {
}),
ref: forwardedRef,
"data-livepeer-controls-seek": "",
"data-duration": duration,
"data-progress": progress,
"data-live": String(live),
"data-buffered": buffered,
"data-visible": String(!live),
style: {
// biome-ignore lint/suspicious/noExplicitAny: player container css var
["--livepeer-player-buffering-width"]: `${bufferedPercent ?? 0}%`,
...style
}
}
));
}
);
Seek.displayName = SEEK_NAME;
var SEEK_BUFFER_NAME = "SeekBuffer";
var SeekBuffer = import_react9.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, style, ...bufferProps } = props;
const context = useMediaContext(SEEK_BUFFER_NAME, __scopeMedia);
const { bufferedPercent, buffered } = (0, import_zustand9.useStore)(
context.store,
(0, import_shallow6.useShallow)(({ bufferedPercent: bufferedPercent2, buffered: buffered2 }) => ({
buffered: buffered2,
bufferedPercent: bufferedPercent2
}))
);
return /* @__PURE__ */ import_react9.default.createElement(
Track2,
{
...bufferProps,
ref: forwardedRef,
style: {
left: 0,
right: `${100 - (bufferedPercent ?? 0)}%`,
...style
},
"data-livepeer-controls-seek-buffer": "",
"data-buffered": buffered
}
);
}
);
SeekBuffer.displayName = SEEK_BUFFER_NAME;
// src/player/Video.tsx
var import_browser2 = require("@livepeer/core-web/browser");
var import_react_compose_refs = require("@radix-ui/react-compose-refs");
var import_react10 = __toESM(require("react"), 1);
var import_zustand10 = require("zustand");
var import_shallow7 = require("zustand/react/shallow");
var VIDEO_NAME = "Video";
var Video = import_react10.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, style, poster, hlsConfig, title, ...videoProps } = props;
const context = useMediaContext(VIDEO_NAME, __scopeMedia);
const ref = import_react10.default.useRef(null);
const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref);
const {
currentSource,
setMounted,
autoPlay,
preload,
thumbnailPoster,
volume,
requestToggleMute
} = (0, import_zustand10.useStore)(
context.store,
(0, import_shallow7.useShallow)(
({
__controlsFunctions,
__initialProps,
currentSource: currentSource2,
live,
poster: poster2,
volume: volume2
}) => ({
autoPlay: __initialProps.autoPlay,
currentSource: currentSource2,
live,
preload: __initialProps.preload,
setMounted: __controlsFunctions.setMounted,
thumbnailPoster: poster2,
volume: volume2,
requestToggleMute: __controlsFunctions.requestToggleMute
})
)
);
(0, import_react10.useEffect)(() => {
if (ref.current) {
const { destroy } = (0, import_browser2.addEventListeners)(ref.current, context.store);
return destroy;
}
}, [context?.store]);
(0, import_react10.useEffect)(() => {
if (hlsConfig) {
context.store.getState().__controlsFunctions.setHlsConfig(hlsConfig);
}
}, []);
(0, import_react10.useEffect)(() => {
setMounted();
}, [setMounted]);
(0, import_react10.useEffect)(() => {
if (typeof videoProps.muted !== "undefined") {
requestToggleMute(videoProps.muted);
}
}, [videoProps.muted, requestToggleMute]);
return /* @__PURE__ */ import_react10.default.createElement(
Primitive.video,
{
playsInline: true,
poster: poster === null ? void 0 : poster ?? thumbnailPoster ?? void 0,
muted: volume === 0,
...videoProps,
"aria-label": title ?? videoProps["aria-label"],
autoPlay,
preload,
ref: composedRefs,
"data-livepeer-video": "",
"data-livepeer-source-type": currentSource?.type ?? "none",
style: {
...style,
// ensures video expands in ratio
position: "absolute",
inset: 0
}
}
);
}
);
Video.displayName = VIDEO_NAME;
// src/player/VideoQualitySelect.tsx
var import_react11 = __toESM(require("react"), 1);
var import_zustand11 = require("zustand");
var import_shallow8 = require("zustand/react/shallow");
var VIDEO_QUALITY_SELECT_NAME = "VideoQualitySelect";
var VideoQualitySelect = (props) => {
const { __scopeMedia, defaultValue, ...videoQualitySelectProps } = props;
const context = useMediaContext(VIDEO_QUALITY_SELECT_NAME, __scopeMedia);
const { videoQuality, setVideoQuality } = (0, import_zustand11.useStore)(
context.store,
(0, import_shallow8.useShallow)(({ videoQuality: videoQuality2, __controlsFunctions }) => ({
videoQuality: videoQuality2,
setVideoQuality: __controlsFunctions.setVideoQuality
}))
);
const onValueChangeComposed = (0, import_react11.useCallback)(
(value) => {
if (props.onValueChange) {
props.onValueChange(value);
}
setVideoQuality(value);
},
[props.onValueChange, setVideoQuality]
);
return /* @__PURE__ */ import_react11.default.createElement(
SelectRoot,
{
...videoQualitySelectProps,
value: videoQuality,
onValueChange: onValueChangeComposed,
"data-livepeer-quality-select": "",
"data-video-quality": String(videoQuality)
}
);
};
VideoQualitySelect.displayName = VIDEO_QUALITY_SELECT_NAME;
var VIDEO_QUALITY_SELECT_ITEM_NAME = "VideoQualitySelectItem";
var VideoQualitySelectItem = import_react11.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, ...videoQualitySelectItemProps } = props;
return /* @__PURE__ */ import_react11.default.createElement(
SelectItem2,
{
...videoQualitySelectItemProps,
ref: forwardedRef,
"data-livepeer-quality-select-item": ""
}
);
});
VideoQualitySelectItem.displayName = VIDEO_QUALITY_SELECT_ITEM_NAME;
// src/player/Volume.tsx
var import_utils6 = require("@livepeer/core/utils");
var import_react_presence7 = require("@radix-ui/react-presence");
var import_react12 = __toESM(require("react"), 1);
var import_zustand12 = require("zustand");
var import_shallow9 = require("zustand/react/shallow");
var VOLUME_NAME = "Volume";
var Volume = import_react12.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, forceMount, ...volumeProps } = props;
const context = useMediaContext(VOLUME_NAME, __scopeMedia);
const { volume, requestVolume, isVolumeChangeSupported } = (0, import_zustand12.useStore)(
context.store,
(0, import_shallow9.useShallow)(({ volume: volume2, __controlsFunctions, __device }) => ({
volume: volume2,
requestVolume: __controlsFunctions.requestVolume,
isVolumeChangeSupported: __device.isVolumeChangeSupported
}))
);
const onValueChange = import_react12.default.useCallback(
([value]) => requestVolume(value),
[requestVolume]
);
const onValueCommit = import_react12.default.useCallback(
([value]) => requestVolume(value),
[requestVolume]
);
const onValueChangeComposed = (0, import_react12.useCallback)(
(value) => {
if (props.onValueChange) {
props.onValueChange(value);
}
onValueChange(value);
},
[props.onValueChange, onValueChange]
);
const onValueCommitComposed = (0, import_react12.useCallback)(
(value) => {
if (props.onValueCommit) {
props.onValueCommit(value);
}
onValueCommit(value);
},
[props.onValueCommit, onValueCommit]
);
return /* @__PURE__ */ import_react12.default.createElement(import_react_presence7.Presence, { present: forceMount || isVolumeChangeSupported }, /* @__PURE__ */ import_react12.default.createElement(
Root4,
{
"aria-label": "Volume Slider",
step: 0.01,
max: 1,
value: [volume],
...volumeProps,
onClick: noPropagate(() => {
}),
onValueChange: onValueChangeComposed,
onValueCommit: onValueCommitComposed,
ref: forwardedRef,
"data-livepeer-controls-volume": "",
"data-livepeer-muted": String(volume === 0),
"data-livepeer-volume": String((100 * volume).toFixed(0)),
"data-visible": String(Boolean(isVolumeChangeSupported))
}
));
}
);
Volume.displayName = VOLUME_NAME;
var VOLUME_INDICATOR_NAME = "VolumeIndicator";
var VolumeIndicator = import_react12.default.forwardRef((props, forwardedRef) => {
const {
__scopeMedia,
forceMount,
matcher = false,
...volumeIndicatorProps
} = props;
const context = useMediaContext(VOLUME_INDICATOR_NAME, __scopeMedia);
const { volume, muted, isVolumeChangeSupported } = (0, import_zustand12.useStore)(
context.store,
(0, import_shallow9.useShallow)(({ volume: volume2, __device, __controls }) => ({
volume: volume2,
muted: __controls.muted,
isVolumeChangeSupported: __device.isVolumeChangeSupported
}))
);
const isPresent = (0, import_react12.useMemo)(
() => matcher !== void 0 ? typeof matcher === "boolean" ? matcher ? !muted : muted : matcher(volume) : muted,
[volume, matcher, muted]
);
(0, import_react12.useEffect)(() => {
if (isVolumeChangeSupported && typeof matcher !== "boolean") {
(0, import_utils6.warn)("Volume change is not supported on this device.");
}
}, [isVolumeChangeSupported, matcher]);
return /* @__PURE__ */ import_react12.default.createElement(import_react_presence7.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react12.default.createElement(
Primitive.div,
{
...volumeIndicatorProps,
ref: forwardedRef,
"data-livepeer-muted": String(muted),
"data-livepeer-volume": String((100 * volume).toFixed(0)),
"data-livepeer-controls-volume-indicator": "",
"data-visible": String(isPresent)
}
));
});
VolumeIndicator.displayName = VOLUME_INDICATOR_NAME;
// src/shared/Container.tsx
var RadixAspectRatio = __toESM(require("@radix-ui/react-aspect-ratio"), 1);
var import_react13 = __toESM(require("react"), 1);
var import_zustand13 = require("zustand");
var import_shallow10 = require("zustand/react/shallow");
var CONTAINER_NAME = "Container";
var Container = import_react13.default.memo(
import_react13.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, ...aspectRatioProps } = props;
const context = useMediaContext(CONTAINER_NAME, __scopeMedia);
const {
aspectRatio,
fullscreen,
playing,
canPlay,
rate,
error,
live,
hasPlayed,
hidden,
pictureInPicture,
loading,
videoQuality
} = (0, import_zustand13.useStore)(
context.store,
(0, import_shallow10.useShallow)(
({
__initialProps,
fullscreen: fullscreen2,
playing: playing2,
canPlay: canPlay2,
playbackRate,
error: error2,
live: live2,
hasPlayed: hasPlayed2,
hidden: hidden2,
pictureInPicture: pictureInPicture2,
loading: loading2,
videoQuality: videoQuality2
}) => ({
aspectRatio: __initialProps.aspectRatio,
fullscreen: fullscreen2,
playing: playing2,
canPlay: canPlay2,
error: Boolean(error2),
rate: playbackRate === "constant" ? "constant" : playbackRate > 1 ? "fast" : playbackRate < 1 ? "slow" : "normal",
live: live2,
hasPlayed: hasPlayed2,
hidden: hidden2,
pictureInPicture: pictureInPicture2,
loading: loading2,
videoQuality: videoQuality2
})
)
);
return aspectRatio ? /* @__PURE__ */ import_react13.default.createElement(
RadixAspectRatio.Root,
{
ratio: aspectRatio,
...aspectRatioProps,
ref: forwardedRef,
"data-livepeer-aspect-ratio": "",
"data-fullscreen": String(fullscreen),
"data-playing": String(playing),
"data-can-play": String(canPlay),
"data-playback-rate": rate,
"data-error": String(error),
"data-loading": String(loading),
"data-live": String(live),
"data-has-played": String(hasPlayed),
"data-controls-hidden": String(hidden),
"data-picture-in-picture": String(pictureInPicture),
"data-video-quality": String(videoQuality)
}
) : /* @__PURE__ */ import_react13.default.createElement(
Primitive.div,
{
...aspectRatioProps,
ref: forwardedRef,
"data-livepeer-wrapper": "",
"data-fullscreen": String(fullscreen),
"data-playing": String(playing),
"data-can-play": String(canPlay),
"data-playback-rate": rate,
"data-error": String(error),
"data-loading": String(loading),
"data-live": String(live),
"data-has-played": String(hasPlayed),
"data-controls-hidden": String(hidden),
"data-picture-in-picture": String(pictureInPicture),
"data-video-quality": String(videoQuality)
}
);
}
)
);
Container.displayName = CONTAINER_NAME;
// src/shared/ErrorIndicator.tsx
var import_react_presence8 = require("@radix-ui/react-presence");
var import_react14 = __toESM(require("react"), 1);
var import_zustand14 = require("zustand");
var ERROR_INDICATOR_NAME = "ErrorIndicator";
var ErrorIndicator = import_react14.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, forceMount, matcher, ...offlineErrorProps } = props;
const context = useMediaContext(ERROR_INDICATOR_NAME, __scopeMedia);
const error = (0, import_zustand14.useStore)(context.store, ({ error: error2 }) => error2);
const isPresent = (0, import_react14.useMemo)(
() => error ? typeof matcher === "string" ? matcher === "all" ? true : matcher === "not-permissions" ? error.type !== "permissions" : matcher === error.type : matcher(error.type) : false,
[error, matcher]
);
return /* @__PURE__ */ import_react14.default.createElement(import_react_presence8.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react14.default.createElement(
Primitive.div,
{
...offlineErrorProps,
ref: forwardedRef,
"data-livepeer-error-indicator": "",
"data-error-state": String(Boolean(error)),
"data-error-type": error?.type ?? "none",
"data-visible": String(isPresent)
}
));
});
ErrorIndicator.displayName = ERROR_INDICATOR_NAME;
// src/shared/Fullscreen.tsx
var import_primitive5 = require("@radix-ui/primitive");
var import_react_presence9 = require("@radix-ui/react-presence");
var import_react15 = __toESM(require("react"), 1);
var import_zustand15 = require("zustand");
var import_shallow11 = require("zustand/react/shallow");
var FULLSCREEN_INDICATOR_NAME = "FullscreenIndicator";
var FullscreenIndicator = import_react15.default.forwardRef((props, forwardedRef) => {
const {
__scopeMedia,
forceMount,
matcher = true,
...fullscreenIndicatorProps
} = props;
const context = useMediaContext(FULLSCREEN_INDICATOR_NAME, __scopeMedia);
const fullscreen = (0, import_zustand15.useStore)(
context.store,
(0, import_shallow11.useShallow)(({ fullscreen: fullscreen2 }) => fullscreen2)
);
const isPresent = (0, import_react15.useMemo)(
() => typeof matcher === "function" ? matcher(fullscreen) : matcher === fullscreen,
[matcher, fullscreen]
);
return /* @__PURE__ */ import_react15.default.createElement(import_react_presence9.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react15.default.createElement(
Primitive.div,
{
...fullscreenIndicatorProps,
ref: forwardedRef,
"data-livepeer-controls-fullscreen-indicator": "",
"data-fullscreen": String(Boolean(fullscreen)),
"data-visible": String(isPresent)
}
));
});
FullscreenIndicator.displayName = FULLSCREEN_INDICATOR_NAME;
var FULLSCREEN_TRIGGER_NAME = "FullscreenTrigger";
var FullscreenTrigger = import_react15.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, ...fullscreenProps } = props;
const context = useMediaContext(FULLSCREEN_TRIGGER_NAME, __scopeMedia);
const { title, fullscreen, requestToggleFullscreen } = (0, import_zustand15.useStore)(
context.store,
(0, import_shallow11.useShallow)(({ fullscreen: fullscreen2, __controlsFunctions, aria }) => ({
fullscreen: fullscreen2,
requestToggleFullscreen: __controlsFunctions.requestToggleFullscreen,
title: aria.fullscreen
}))
);
return /* @__PURE__ */ import_react15.default.createElement(
Primitive.button,
{
type: "button",
"aria-pressed": fullscreen,
"aria-label": title ?? void 0,
title: title ?? void 0,
...fullscreenProps,
onClick: (0, import_primitive5.composeEventHandlers)(
props.onClick,
noPropagate(requestToggleFullscreen)
),
ref: forwardedRef,
"data-livepeer-controls-fullscreen-trigger": "",
"data-fullscreen-state": String(Boolean(fullscreen))
}
);
});
FullscreenTrigger.displayName = FULLSCREEN_TRIGGER_NAME;
// src/shared/LoadingIndicator.tsx
var import_react_presence10 = require("@radix-ui/react-presence");
var import_react16 = __toESM(require("react"), 1);
var import_zustand16 = require("zustand");
var LOADING_INDICATOR_NAME = "LoadingIndicator";
var LoadingIndicator = import_react16.default.forwardRef((props, forwardedRef) => {
const {
__scopeMedia,
forceMount,
matcher = true,
...offlineErrorProps
} = props;
const context = useMediaContext(LOADING_INDICATOR_NAME, __scopeMedia);
const loading = (0, import_zustand16.useStore)(context.store, ({ loading: loading2 }) => loading2);
const isPresent = (0, import_react16.useMemo)(
() => typeof matcher === "function" ? matcher(loading) : matcher === loading,
[matcher, loading]
);
return /* @__PURE__ */ import_react16.default.createElement(import_react_presence10.Presence, { present: forceMount || isPresent }, /* @__PURE__ */ import_react16.default.createElement(
Primitive.div,
{
"aria-label": "Loading",
...offlineErrorProps,
ref: forwardedRef,
"data-livepeer-loading-indicator": "",
"data-loading": String(Boolean(loading)),
"data-visible": String(isPresent)
}
));
});
LoadingIndicator.displayName = LOADING_INDICATOR_NAME;
// src/shared/PictureInPictureTrigger.tsx
var import_primitive6 = require("@radix-ui/primitive");
var import_react_presence11 = require("@radix-ui/react-presence");
var import_react17 = __toESM(require("react"), 1);
var import_zustand17 = require("zustand");
var import_shallow12 = require("zustand/react/shallow");
var PICTURE_IN_PICTURE_TRIGGER_NAME = "PictureInPictureTrigger";
var PictureInPictureTrigger = import_react17.default.forwardRef((props, forwardedRef) => {
const { __scopeMedia, forceMount, ...pictureInPictureProps } = props;
const context = useMediaContext(
PICTURE_IN_PICTURE_TRIGGER_NAME,
__scopeMedia
);
const {
pictureInPicture,
requestTogglePictureInPicture,
isPictureInPictureSupported,
fullscreen,
title
} = (0, import_zustand17.useStore)(
context.store,
(0, import_shallow12.useShallow)(
({
pictureInPicture: pictureInPicture2,
__controlsFunctions,
__device,
fullscreen: fullscreen2,
aria
}) => ({
pictureInPicture: pictureInPicture2,
requestTogglePictureInPicture: __controlsFunctions.requestTogglePictureInPicture,
isPictureInPictureSupported: __device.isPictureInPictureSupported,
fullscreen: fullscreen2,
title: aria.pictureInPicture
})
)
);
return (
// do not show button if it is not supported or if currently fullscreen
/* @__PURE__ */ import_react17.default.createElement(
import_react_presence11.Presence,
{
present: forceMount || isPictureInPictureSupported && !fullscreen
},
/* @__PURE__ */ import_react17.default.createElement(
Primitive.button,
{
type: "button",
"aria-pressed": pictureInPicture,
"aria-label": title ?? void 0,
title: title ?? void 0,
...pictureInPictureProps,
onClick: (0, import_primitive6.composeEventHandlers)(
props.onClick,
noPropagate(requestTogglePictureInPicture)
),
ref: forwardedRef,
"data-livepeer-controls-picture-in-picture-trigger": "",
"data-picture-in-picture": String(Boolean(pictureInPicture)),
"data-visible": String(isPictureInPictureSupported && !fullscreen)
}
)
)
);
});
PictureInPictureTrigger.displayName = PICTURE_IN_PICTURE_TRIGGER_NAME;
// src/shared/Portal.tsx
var RadixPortal = __toESM(require("@radix-ui/react-portal"), 1);
var import_react18 = __toESM(require("react"), 1);
var PORTAL_NAME = "Portal";
var Portal = (props) => {
return /* @__PURE__ */ import_react18.default.createElement(RadixPortal.Root, { ...props });
};
Portal.displayName = PORTAL_NAME;
// src/shared/Time.tsx
var import_react19 = __toESM(require("react"), 1);
var import_zustand18 = require("zustand");
var import_shallow13 = require("zustand/react/shallow");
var TIME_NAME = "Time";
var Time = import_react19.default.forwardRef(
(props, forwardedRef) => {
const { __scopeMedia, ...timeProps } = props;
const context = useMediaContext(TIME_NAME, __scopeMedia);
const { progress, duration, live, formattedTime } = (0, import_zustand18.useStore)(
context.store,
(0, import_shallow13.useShallow)(({ progress: progress2, duration: duration2, live: live2, aria }) => ({
formattedTime: aria.time,
progress: progress2,
duration: duration2,
live: live2
}))
);
return /* @__PURE__ */ import_react19.default.createElement(
Primitive.span,
{
"aria-label": formattedTime ?? void 0,
title: formattedTime ?? void 0,
...timeProps,
ref: forwardedRef,
"data-livepeer-controls-time": "",
"data-duration": duration,
"data-progress": progress,
"data-live": String(live)
},
formattedTime
);
}
);
Time.displayName = TIME_NAME;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ClipTrigger,
Container,
Controls,
ErrorIndicator,
FullscreenIndicator,
FullscreenTrigger,
LiveIndicator,
LoadingIndicator,
MediaProvider,
MuteTrigger,
PictureInPictureTrigger,
PlayPauseTrigger,
PlayingIndicator,
Portal,
Poster,
Range,
RateSelect,
RateSelectItem,
Root,
Seek,
SeekBuffer,
SelectArrow,
SelectContent,
SelectGroup,
SelectIcon,
SelectItemIndicator,
SelectItemText,
SelectLabel,
SelectPortal,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
SelectViewport,
Thumb,
Time,
Track,
Video,
VideoQualitySelect,
VideoQualitySelectItem,
Volume,
VolumeIndicator,
createMediaScope,
useMediaContext,
useStore
});
//# sourceMappingURL=index.cjs.map