UNPKG

@livepeer/react

Version:

React primitives for video apps.

1,330 lines (1,302 loc) 46.1 kB
"use client"; "use strict"; 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