@aidenlx/vidstack-react
Version:
UI component library for building high-quality, accessible video and audio experiences on the web.
642 lines (633 loc) • 30.8 kB
JavaScript
"use client"
import { useMediaState, Primitive, IS_SERVER, isRemotionSrc, getDownloadFile } from '../chunks/vidstack-B1ySk2FQ.js';
import * as React from 'react';
import { createContext, effect, signal } from 'maverick.js';
import { composeRefs, useSignal } from 'maverick.js/react';
import { createDisposalBin, uppercaseFirstChar, isUndefined, isNumber, isString, listenEvent, isKeyboardEvent, isKeyboardClick } from 'maverick.js/std';
import { useMediaContext, PlayButton, Root, Img, AirPlayButton, CaptionButton, FullscreenButton, PIPButton, SeekButton, Root$1, Value, Preview, Thumbnail, appendParamsToURL, Gesture, Root$2, Button, Items, useAudioOptions, Root$3, Item, useCaptionOptions, Time, LiveButton, MuteButton, Root$4 } from '../chunks/vidstack-DZHxgbQz.js';
import { useMediaRemote, usePlaybackRateOptions, useVideoQualityOptions } from '../chunks/vidstack-BFI47c9T.js';
import { useLayoutName, useClassName } from '../chunks/vidstack-EWSz8NeB.js';
import { RemotionThumbnail, RemotionPoster, RemotionSliderThumbnail } from '../chunks/vidstack-DcGAdum5.js';
export { plyrLayoutIcons } from './vidstack-plyr-icons.js';
import '../chunks/vidstack-CPShcCv0.js';
import '@floating-ui/dom';
import 'react-dom';
import '../chunks/vidstack-CBF7iUqu.js';
createContext();
function usePlyrLayoutClasses(el, media) {
const {
canAirPlay,
canFullscreen,
canPictureInPicture,
controlsHidden,
currentTime,
fullscreen,
hasCaptions,
isAirPlayConnected,
paused,
pictureInPicture,
playing,
pointer,
poster,
textTrack,
viewType,
waiting
} = media.$state;
el.classList.add("plyr");
el.classList.add("plyr--full-ui");
const classes = {
"plyr--airplay-active": isAirPlayConnected,
"plyr--airplay-supported": canAirPlay,
"plyr--fullscreen-active": fullscreen,
"plyr--fullscreen-enabled": canFullscreen,
"plyr--hide-controls": controlsHidden,
"plyr--is-touch": () => pointer() === "coarse",
"plyr--loading": waiting,
"plyr--paused": paused,
"plyr--pip-active": pictureInPicture,
"plyr--pip-enabled": canPictureInPicture,
"plyr--playing": playing,
"plyr__poster-enabled": poster,
"plyr--stopped": () => paused() && currentTime() === 0,
"plyr--captions-active": textTrack,
"plyr--captions-enabled": hasCaptions
};
const disposal = createDisposalBin();
for (const token of Object.keys(classes)) {
disposal.add(effect(() => void el.classList.toggle(token, !!classes[token]())));
}
disposal.add(
effect(() => {
const token = `plyr--${viewType()}`;
el.classList.add(token);
return () => el.classList.remove(token);
}),
effect(() => {
const { $provider } = media, type = $provider()?.type, token = `plyr--${isHTMLProvider(type) ? "html5" : type}`;
el.classList.toggle(token, !!type);
return () => el.classList.remove(token);
})
);
return () => disposal.empty();
}
function isHTMLProvider(type) {
return type === "audio" || type === "video";
}
const PlyrLayoutContext = React.createContext({});
PlyrLayoutContext.displayName = "PlyrLayoutContext";
function usePlyrLayoutContext() {
return React.useContext(PlyrLayoutContext);
}
function usePlyrLayoutWord(word) {
const { translations } = usePlyrLayoutContext();
return i18n(translations, word);
}
function i18n(translations, word) {
return translations?.[word] ?? word;
}
const defaultPlyrLayoutProps = {
clickToPlay: true,
clickToFullscreen: true,
controls: [
"play-large",
"play",
"progress",
"current-time",
"mute+volume",
"captions",
"settings",
"pip",
"airplay",
"fullscreen"
],
displayDuration: false,
download: null,
markers: null,
invertTime: true,
thumbnails: null,
toggleTime: true,
translations: null,
seekTime: 10,
speed: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4]
};
function slot(name, defaultValue) {
const { slots } = usePlyrLayoutContext(), slot2 = slots?.[name], capitalizedName = uppercaseFirstChar(name);
return /* @__PURE__ */ React.createElement(React.Fragment, null, slots?.[`before${capitalizedName}`], isUndefined(slot2) ? defaultValue : slot2, slots?.[`after${capitalizedName}`]);
}
const PlyrLayout = React.forwardRef(
(userProps, forwardRef) => {
const {
clickToPlay,
clickToFullscreen,
controls,
displayDuration,
download,
markers,
invertTime,
thumbnails,
toggleTime,
translations,
seekTime,
speed,
icons,
slots,
posterFrame,
className,
...elProps
} = { ...defaultPlyrLayoutProps, ...userProps }, [el, setEl] = React.useState(null), media = useMediaContext(), previewTime = React.useMemo(() => signal(0), []), $viewType = useMediaState("viewType");
useLayoutName("plyr");
useClassName(el, className);
React.useEffect(() => {
if (!el || !media) return;
return usePlyrLayoutClasses(el, media);
}, [el, media]);
return /* @__PURE__ */ React.createElement(
PlyrLayoutContext.Provider,
{
value: {
clickToPlay,
clickToFullscreen,
controls,
displayDuration,
download,
markers,
invertTime,
thumbnails,
toggleTime,
translations,
seekTime,
speed,
previewTime,
icons,
slots,
posterFrame
}
},
/* @__PURE__ */ React.createElement(
Primitive.div,
{
...elProps,
className: IS_SERVER ? `plyr plyr--full-ui plyr--${$viewType} ${className || ""}` : void 0,
ref: composeRefs(setEl, forwardRef)
},
$viewType === "audio" ? /* @__PURE__ */ React.createElement(PlyrAudioLayout, null) : $viewType === "video" ? /* @__PURE__ */ React.createElement(PlyrVideoLayout, null) : null
)
);
}
);
PlyrLayout.displayName = "PlyrLayout";
function PlyrAudioLayout() {
return PlyrAudioControls();
}
PlyrAudioLayout.displayName = "PlyrAudioLayout";
function PlyrVideoLayout() {
const media = useMediaContext(), { controls } = usePlyrLayoutContext(), { load } = media.$props, { canLoad } = media.$state, $load = useSignal(load), $canLoad = useSignal(canLoad);
if ($load === "play" && !$canLoad) {
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(PlyrPlayLargeButton, null), /* @__PURE__ */ React.createElement(PlyrPoster, null));
}
return /* @__PURE__ */ React.createElement(React.Fragment, null, controls.includes("play-large") ? /* @__PURE__ */ React.createElement(PlyrPlayLargeButton, null) : null, /* @__PURE__ */ React.createElement(PlyrPreviewScrubbing, null), /* @__PURE__ */ React.createElement(PlyrPoster, null), /* @__PURE__ */ React.createElement(PlyrVideoControls, null), /* @__PURE__ */ React.createElement(PlyrGestures, null), /* @__PURE__ */ React.createElement(PlyrCaptions, null));
}
PlyrVideoLayout.displayName = "PlyrVideoLayout";
function PlyrPlayLargeButton() {
const { translations, icons: Icons } = usePlyrLayoutContext(), $title = useMediaState("title"), label = `${i18n(translations, "Play")} ${$title}`;
return slot(
"playLargeButton",
/* @__PURE__ */ React.createElement(
PlayButton,
{
className: "plyr__control plyr__control--overlaid",
"aria-label": label,
"data-plyr": "play"
},
/* @__PURE__ */ React.createElement(Icons.Play, null)
)
);
}
PlyrPlayLargeButton.displayName = "PlyrPlayLargeButton";
function PlyrPreviewScrubbing() {
const $src = useMediaState("source"), { thumbnails, previewTime } = usePlyrLayoutContext(), $previewTime = useSignal(previewTime), $RemotionThumbnail = useSignal(RemotionThumbnail), $hasRemotionThumbnail = $RemotionThumbnail && isRemotionSrc($src);
return $hasRemotionThumbnail ? /* @__PURE__ */ React.createElement($RemotionThumbnail, { className: "plyr__preview-scrubbing", frame: $previewTime * $src.fps }) : /* @__PURE__ */ React.createElement(Root, { src: thumbnails, className: "plyr__preview-scrubbing", time: $previewTime }, /* @__PURE__ */ React.createElement(Img, null));
}
PlyrPreviewScrubbing.displayName = "PlyrPreviewScrubbing";
function PlyrPoster() {
const $src = useMediaState("source"), $poster = useMediaState("poster"), { posterFrame } = usePlyrLayoutContext(), $RemotionPoster = useSignal(RemotionPoster), $hasRemotionPoster = $RemotionPoster && isRemotionSrc($src) && isNumber(posterFrame);
return slot(
"poster",
$hasRemotionPoster ? /* @__PURE__ */ React.createElement($RemotionPoster, { frame: posterFrame, className: "plyr__poster" }) : /* @__PURE__ */ React.createElement("div", { className: "plyr__poster", style: { backgroundImage: `url("${$poster}")` } })
);
}
PlyrPoster.displayName = "PlyrPoster";
const noAudioControl = /* @__PURE__ */ new Set(["captions", "pip", "airplay", "fullscreen"]);
function PlyrAudioControls() {
const { controls } = usePlyrLayoutContext();
return /* @__PURE__ */ React.createElement("div", { className: "plyr__controls" }, controls.filter((type) => !noAudioControl.has(type)).map((type, i) => {
const Control = getPlyrControl(type);
return Control ? React.createElement(Control, { key: i }) : null;
}));
}
PlyrAudioControls.displayName = "PlyrAudioControls";
function PlyrVideoControls() {
const { controls } = usePlyrLayoutContext();
return /* @__PURE__ */ React.createElement("div", { className: "plyr__controls" }, controls.map((type, i) => {
const Control = getPlyrControl(type);
return Control ? React.createElement(Control, { key: i }) : null;
}));
}
PlyrVideoControls.displayName = "PlyrVideoControls";
function getPlyrControl(type) {
switch (type) {
case "airplay":
return PlyrAirPlayButton;
case "captions":
return PlyrCaptionsButton;
case "current-time":
return PlyrCurrentTime;
case "download":
return PlyrDownloadButton;
case "duration":
return PlyrDuration;
case "fast-forward":
return PlyrFastForwardButton;
case "fullscreen":
return PlyrFullscreenButton;
case "mute":
case "volume":
case "mute+volume":
return () => PlyrVolume({ type });
case "pip":
return PlyrPIPButton;
case "play":
return PlyrPlayButton;
case "progress":
return PlyrTimeSlider;
case "restart":
return PlyrRestartButton;
case "rewind":
return PlyrRewindButton;
case "settings":
return PlyrSettings;
default:
return null;
}
}
function PlyrAirPlayButton() {
const { icons: Icons } = usePlyrLayoutContext(), airPlayText = usePlyrLayoutWord("AirPlay");
return slot(
"airPlayButton",
/* @__PURE__ */ React.createElement(AirPlayButton, { className: "plyr__controls__item plyr__control", "data-plyr": "airplay" }, /* @__PURE__ */ React.createElement(Icons.AirPlay, null), /* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, airPlayText))
);
}
PlyrAirPlayButton.displayName = "PlyrAirPlayButton";
function PlyrCaptionsButton() {
const { icons: Icons } = usePlyrLayoutContext(), enableText = usePlyrLayoutWord("Enable captions"), disableText = usePlyrLayoutWord("Disable captions");
return slot(
"captionsButton",
/* @__PURE__ */ React.createElement(
CaptionButton,
{
className: "plyr__controls__item plyr__control",
"data-no-label": true,
"data-plyr": "captions"
},
/* @__PURE__ */ React.createElement(Icons.CaptionsOn, { className: "icon--pressed" }),
/* @__PURE__ */ React.createElement(Icons.CaptionsOff, { className: "icon--not-pressed" }),
/* @__PURE__ */ React.createElement("span", { className: "label--pressed plyr__tooltip" }, disableText),
/* @__PURE__ */ React.createElement("span", { className: "label--not-pressed plyr__tooltip" }, enableText)
)
);
}
PlyrCaptionsButton.displayName = "PlyrCaptionsButton";
function PlyrFullscreenButton() {
const { icons: Icons } = usePlyrLayoutContext(), enterText = usePlyrLayoutWord("Enter Fullscreen"), exitText = usePlyrLayoutWord("Exit Fullscreen");
return slot(
"fullscreenButton",
/* @__PURE__ */ React.createElement(
FullscreenButton,
{
className: "plyr__controls__item plyr__control",
"data-no-label": true,
"data-plyr": "fullscreen"
},
/* @__PURE__ */ React.createElement(Icons.EnterFullscreen, { className: "icon--pressed" }),
/* @__PURE__ */ React.createElement(Icons.ExitFullscreen, { className: "icon--not-pressed" }),
/* @__PURE__ */ React.createElement("span", { className: "label--pressed plyr__tooltip" }, exitText),
/* @__PURE__ */ React.createElement("span", { className: "label--not-pressed plyr__tooltip" }, enterText)
)
);
}
PlyrFullscreenButton.displayName = "PlyrFullscreenButton";
function PlyrPIPButton() {
const { icons: Icons } = usePlyrLayoutContext(), enterText = usePlyrLayoutWord("Enter PiP"), exitText = usePlyrLayoutWord("Exit PiP");
return slot(
"pipButton",
/* @__PURE__ */ React.createElement(PIPButton, { className: "plyr__controls__item plyr__control", "data-no-label": true, "data-plyr": "pip" }, /* @__PURE__ */ React.createElement(Icons.EnterPiP, { className: "icon--pressed" }), /* @__PURE__ */ React.createElement(Icons.ExitPiP, { className: "icon--not-pressed" }), /* @__PURE__ */ React.createElement("span", { className: "label--pressed plyr__tooltip" }, exitText), /* @__PURE__ */ React.createElement("span", { className: "label--not-pressed plyr__tooltip" }, enterText))
);
}
PlyrPIPButton.displayName = "PlyrPIPButton";
function PlyrMuteButton() {
const { icons: Icons } = usePlyrLayoutContext(), muteText = usePlyrLayoutWord("Mute"), unmuteText = usePlyrLayoutWord("Unmute");
return slot(
"muteButton",
/* @__PURE__ */ React.createElement(MuteButton, { className: "plyr__control", "data-no-label": true, "data-plyr": "mute" }, /* @__PURE__ */ React.createElement(Icons.Muted, { className: "icon--pressed" }), /* @__PURE__ */ React.createElement(Icons.Volume, { className: "icon--not-pressed" }), /* @__PURE__ */ React.createElement("span", { className: "label--pressed plyr__tooltip" }, unmuteText), /* @__PURE__ */ React.createElement("span", { className: "label--not-pressed plyr__tooltip" }, muteText))
);
}
PlyrMuteButton.displayName = "PlyrMuteButton";
function PlyrPlayButton() {
const { icons: Icons } = usePlyrLayoutContext(), playText = usePlyrLayoutWord("Play"), pauseText = usePlyrLayoutWord("Pause");
return slot(
"playButton",
/* @__PURE__ */ React.createElement(PlayButton, { className: "plyr__controls__item plyr__control", "data-no-label": true, "data-plyr": "play" }, /* @__PURE__ */ React.createElement(Icons.Pause, { className: "icon--pressed" }), /* @__PURE__ */ React.createElement(Icons.Play, { className: "icon--not-pressed" }), /* @__PURE__ */ React.createElement("span", { className: "label--pressed plyr__tooltip" }, pauseText), /* @__PURE__ */ React.createElement("span", { className: "label--not-pressed plyr__tooltip" }, playText))
);
}
PlyrPlayButton.displayName = "PlyrPlayButton";
function PlyrRestartButton() {
const { icons: Icons } = usePlyrLayoutContext(), restartText = usePlyrLayoutWord("Restart"), remote = useMediaRemote();
function onPress({ nativeEvent: event }) {
if (isKeyboardEvent(event) && !isKeyboardClick(event)) return;
remote.seek(0, event);
}
return slot(
"restartButton",
/* @__PURE__ */ React.createElement(
"button",
{
type: "button",
className: "plyr__control",
"data-plyr": "restart",
onPointerUp: onPress,
onKeyDown: onPress
},
/* @__PURE__ */ React.createElement("slot", { name: "restart-icon", "data-class": "" }),
/* @__PURE__ */ React.createElement(Icons.Restart, null),
/* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, restartText)
)
);
}
PlyrRestartButton.displayName = "PlyrRestartButton";
function PlyrFastForwardButton() {
const { icons: Icons, seekTime } = usePlyrLayoutContext(), forwardText = usePlyrLayoutWord("Forward"), label = `${forwardText} ${seekTime}s`;
return slot(
"fastForwardButton",
/* @__PURE__ */ React.createElement(
SeekButton,
{
className: "plyr__controls__item plyr__control",
seconds: seekTime,
"data-no-label": true,
"data-plyr": "fast-forward"
},
/* @__PURE__ */ React.createElement(Icons.FastForward, null),
/* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, label)
)
);
}
PlyrFastForwardButton.displayName = "PlyrFastForwardButton";
function PlyrRewindButton() {
const { icons: Icons, seekTime } = usePlyrLayoutContext(), rewindText = usePlyrLayoutWord("Rewind"), label = `${rewindText} ${seekTime}s`;
return slot(
"rewindButton",
/* @__PURE__ */ React.createElement(
SeekButton,
{
className: "plyr__controls__item plyr__control",
seconds: -1 * seekTime,
"data-no-label": true,
"data-plyr": "rewind"
},
/* @__PURE__ */ React.createElement(Icons.Rewind, null),
/* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, label)
)
);
}
PlyrRewindButton.displayName = "PlyrRewindButton";
function PlyrTimeSlider() {
const { markers, thumbnails, seekTime, previewTime } = usePlyrLayoutContext(), $src = useMediaState("source"), $duration = useMediaState("duration"), seekText = usePlyrLayoutWord("Seek"), [activeMarker, setActiveMarker] = React.useState(null), $RemotionSliderThumbnail = useSignal(RemotionSliderThumbnail), $hasRemotionSliderThumbnail = $RemotionSliderThumbnail && isRemotionSrc($src);
function onSeekingRequest(time) {
previewTime.set(time);
}
function onMarkerEnter() {
setActiveMarker(this);
}
function onMarkerLeave() {
setActiveMarker(null);
}
const markerLabel = activeMarker ? /* @__PURE__ */ React.createElement(
"span",
{
className: "plyr__progress__marker-label",
dangerouslySetInnerHTML: { __html: activeMarker.label + "<br />" }
}
) : null;
return slot(
"timeSlider",
/* @__PURE__ */ React.createElement("div", { className: "plyr__controls__item plyr__progress__container" }, /* @__PURE__ */ React.createElement("div", { className: "plyr__progress" }, /* @__PURE__ */ React.createElement(
Root$1,
{
className: "plyr__slider",
pauseWhileDragging: true,
keyStep: seekTime,
"aria-label": seekText,
"data-plyr": "seek",
onMediaSeekingRequest: onSeekingRequest
},
/* @__PURE__ */ React.createElement("div", { className: "plyr__slider__track" }),
/* @__PURE__ */ React.createElement("div", { className: "plyr__slider__thumb" }),
/* @__PURE__ */ React.createElement("div", { className: "plyr__slider__buffer" }),
!thumbnails && !$hasRemotionSliderThumbnail ? /* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, markerLabel, /* @__PURE__ */ React.createElement(Value, null)) : $hasRemotionSliderThumbnail ? /* @__PURE__ */ React.createElement(Preview, { className: "plyr__slider__preview" }, /* @__PURE__ */ React.createElement("div", { className: "plyr__slider__preview__thumbnail" }, /* @__PURE__ */ React.createElement("span", { className: "plyr__slider__preview__time-container" }, markerLabel, /* @__PURE__ */ React.createElement(Value, { className: "plyr__slider__preview__time" })), /* @__PURE__ */ React.createElement($RemotionSliderThumbnail, { className: "plyr__slider__preview__thumbnail" }))) : /* @__PURE__ */ React.createElement(Preview, { className: "plyr__slider__preview" }, /* @__PURE__ */ React.createElement(
Thumbnail.Root,
{
src: thumbnails,
className: "plyr__slider__preview__thumbnail"
},
/* @__PURE__ */ React.createElement("span", { className: "plyr__slider__preview__time-container" }, markerLabel, /* @__PURE__ */ React.createElement(Value, { className: "plyr__slider__preview__time" })),
/* @__PURE__ */ React.createElement(Thumbnail.Img, null)
)),
markers && Number.isFinite($duration) ? markers.map((marker, i) => /* @__PURE__ */ React.createElement(
"span",
{
className: "plyr__progress__marker",
key: i,
onMouseEnter: onMarkerEnter.bind(marker),
onMouseLeave: onMarkerLeave,
style: { left: `${marker.time / $duration * 100}%` }
}
)) : null
)))
);
}
PlyrTimeSlider.displayName = "PlyrTimeSlider";
function PlyrVolumeSlider() {
const volumeText = usePlyrLayoutWord("Volume");
return slot(
"volumeSlider",
/* @__PURE__ */ React.createElement(Root$4, { className: "plyr__slider", "data-plyr": "volume", "aria-label": volumeText }, /* @__PURE__ */ React.createElement("div", { className: "plyr__slider__track" }), /* @__PURE__ */ React.createElement("div", { className: "plyr__slider__thumb" }))
);
}
PlyrVolumeSlider.displayName = "PlyrVolumeSlider";
function PlyrVolume({ type }) {
const hasMuteButton = type === "mute" || type === "mute+volume", hasVolumeSlider = type === "volume" || type === "mute+volume";
return /* @__PURE__ */ React.createElement("div", { className: "plyr__controls__item plyr__volume" }, hasMuteButton ? /* @__PURE__ */ React.createElement(PlyrMuteButton, null) : null, hasVolumeSlider ? /* @__PURE__ */ React.createElement(PlyrVolumeSlider, null) : null);
}
PlyrVolume.displayName = "PlyrVolume";
function PlyrCurrentTime() {
const { invertTime, toggleTime, displayDuration } = usePlyrLayoutContext(), $streamType = useMediaState("streamType"), currentTimeText = usePlyrLayoutWord("Current time"), liveText = usePlyrLayoutWord("LIVE"), [invert, setInvert] = React.useState(invertTime), remainder = !displayDuration && invert;
function onPress({ nativeEvent: event }) {
if (!toggleTime || displayDuration || isKeyboardEvent(event) && !isKeyboardClick(event)) {
return;
}
setInvert((n) => !n);
}
return slot(
"currentTime",
$streamType === "live" || $streamType === "ll-live" ? /* @__PURE__ */ React.createElement(LiveButton, { className: "plyr__controls__item plyr__control plyr__live-button", "data-plyr": "live" }, /* @__PURE__ */ React.createElement("span", { className: "plyr__live-button__text" }, liveText)) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
Time,
{
type: "current",
className: "plyr__controls__item plyr__time plyr__time--current",
role: "timer",
"aria-label": currentTimeText,
tabIndex: 0,
remainder,
onPointerUp: onPress,
onKeyDown: onPress
}
), displayDuration ? /* @__PURE__ */ React.createElement(PlyrDuration, null) : null)
);
}
PlyrCurrentTime.displayName = "PlyrCurrentTime";
function PlyrDuration() {
const durationText = usePlyrLayoutWord("Duration");
return slot(
"duration",
/* @__PURE__ */ React.createElement(
Time,
{
className: "plyr__controls__item plyr__time plyr__time--duration",
type: "duration",
role: "timer",
tabIndex: 0,
"aria-label": durationText
}
)
);
}
PlyrDuration.displayName = "PlyrDuration";
function PlyrDownloadButton() {
const { download } = usePlyrLayoutContext(), $src = useMediaState("source"), $title = useMediaState("title"), file = getDownloadFile({
title: $title,
src: $src,
download
}), downloadText = usePlyrLayoutWord("Download");
return slot(
"download",
isString(file?.url) ? /* @__PURE__ */ React.createElement(
"a",
{
className: "plyr__controls__item plyr__control",
href: appendParamsToURL(file.url, { download: file.name }),
download: file.name,
target: "_blank"
},
/* @__PURE__ */ React.createElement("slot", { name: "download-icon" }),
/* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, downloadText)
) : null
);
}
PlyrDownloadButton.displayName = "PlyrDownloadButton";
function PlyrGestures() {
const { clickToPlay, clickToFullscreen } = usePlyrLayoutContext();
return /* @__PURE__ */ React.createElement(React.Fragment, null, clickToPlay ? /* @__PURE__ */ React.createElement(Gesture, { className: "plyr__gesture", event: "pointerup", action: "toggle:paused" }) : null, clickToFullscreen ? /* @__PURE__ */ React.createElement(Gesture, { className: "plyr__gesture", event: "dblpointerup", action: "toggle:fullscreen" }) : null);
}
PlyrGestures.displayName = "PlyrGestures";
function PlyrCaptions() {
const $track = useMediaState("textTrack"), [activeCue, setActiveCue] = React.useState(null);
React.useEffect(() => {
if (!$track) return;
function onCueChange() {
setActiveCue($track ? $track.activeCues[0] : null);
}
onCueChange();
return listenEvent($track, "cue-change", onCueChange);
}, [$track]);
return /* @__PURE__ */ React.createElement("div", { className: "plyr__captions", dir: "auto" }, /* @__PURE__ */ React.createElement(
"span",
{
className: "plyr__caption",
dangerouslySetInnerHTML: {
__html: activeCue?.text || ""
}
}
));
}
PlyrCaptions.displayName = "PlyrCaptions";
function PlyrSettings() {
const { icons: Icons } = usePlyrLayoutContext(), settingsText = usePlyrLayoutWord("Settings");
return slot(
"settings",
/* @__PURE__ */ React.createElement("div", { className: "plyr__controls__item plyr__menu" }, /* @__PURE__ */ React.createElement(Root$2, null, /* @__PURE__ */ React.createElement(Button, { className: "plyr__control", "data-plyr": "settings" }, slot(
"settingsButton",
/* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icons.Settings, null), /* @__PURE__ */ React.createElement("span", { className: "plyr__tooltip" }, settingsText))
)), /* @__PURE__ */ React.createElement(Items, { className: "plyr__menu__container", placement: "top end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, slot(
"settingsMenu",
/* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(PlyrAudioMenu, null), /* @__PURE__ */ React.createElement(PlyrCaptionsMenu, null), /* @__PURE__ */ React.createElement(PlyrQualityMenu, null), /* @__PURE__ */ React.createElement(PlyrSpeedMenu, null))
))))))
);
}
PlyrSettings.displayName = "PlyrSettings";
function PlyrMenuButton({
label,
hint,
open,
disabled
}) {
const buttonText = usePlyrLayoutWord(label), goBackText = usePlyrLayoutWord("Go back to previous menu");
return /* @__PURE__ */ React.createElement(
Button,
{
className: `plyr__control plyr__control--${open ? "back" : "forward"}`,
"data-plyr": "settings",
disabled
},
/* @__PURE__ */ React.createElement("span", { className: "plyr__menu__label", "aria-hidden": open ? "true" : void 0 }, buttonText),
hint ? /* @__PURE__ */ React.createElement("span", { className: "plyr__menu__value" }, hint) : null,
open ? /* @__PURE__ */ React.createElement("span", { className: "plyr__sr-only" }, goBackText) : null
);
}
PlyrMenuButton.displayName = "PlyrMenuButton";
function PlyrMenu({
label,
hint,
children,
disabled
}) {
const [open, setOpen] = React.useState(false);
function onOpen() {
setOpen(true);
}
function onClose() {
setOpen(false);
}
return /* @__PURE__ */ React.createElement(Root$2, { onOpen, onClose }, /* @__PURE__ */ React.createElement(PlyrMenuButton, { label, open, hint, disabled }), /* @__PURE__ */ React.createElement(Items, null, children));
}
PlyrMenu.displayName = "PlyrMenu";
function PlyrAudioMenu() {
const defaultText = usePlyrLayoutWord("Default"), $track = useMediaState("audioTrack"), options = useAudioOptions();
return /* @__PURE__ */ React.createElement(PlyrMenu, { label: "Audio", hint: $track?.label ?? defaultText, disabled: options.disabled }, /* @__PURE__ */ React.createElement(Root$3, { value: options.selectedValue }, options.map(({ label, value, select }) => /* @__PURE__ */ React.createElement(Item, { className: "plyr__control", value, onSelect: select, key: value }, /* @__PURE__ */ React.createElement("span", null, label)))));
}
PlyrAudioMenu.displayName = "PlyrAudioMenu";
function PlyrSpeedMenu() {
const normalLabel = usePlyrLayoutWord("Normal"), options = usePlaybackRateOptions({ normalLabel }), hint = options.selectedValue === "1" ? normalLabel : options.selectedValue + "x";
return /* @__PURE__ */ React.createElement(PlyrMenu, { label: "Speed", hint, disabled: options.disabled }, /* @__PURE__ */ React.createElement(Root$3, { value: options.selectedValue }, options.map(({ label, value, select }) => /* @__PURE__ */ React.createElement(Item, { className: "plyr__control", value, onSelect: select, key: value }, /* @__PURE__ */ React.createElement("span", null, label)))));
}
PlyrSpeedMenu.displayName = "PlyrSpeedMenu";
function PlyrCaptionsMenu() {
const offText = usePlyrLayoutWord("Disabled"), options = useCaptionOptions({ off: offText }), hint = options.selectedTrack?.label ?? offText;
return /* @__PURE__ */ React.createElement(PlyrMenu, { label: "Captions", hint, disabled: options.disabled }, /* @__PURE__ */ React.createElement(Root$3, { value: options.selectedValue }, options.map(({ label, value, select }) => /* @__PURE__ */ React.createElement(Item, { className: "plyr__control", value, onSelect: select, key: value }, /* @__PURE__ */ React.createElement("span", null, label)))));
}
PlyrCaptionsMenu.displayName = "PlyrCaptionsMenu";
function PlyrQualityMenu() {
const autoText = usePlyrLayoutWord("Auto"), options = useVideoQualityOptions({ auto: autoText, sort: "descending" }), currentQuality = options.selectedQuality?.height, hint = options.selectedValue !== "auto" && currentQuality ? `${currentQuality}p` : `${autoText}${currentQuality ? ` (${currentQuality}p)` : ""}`;
return /* @__PURE__ */ React.createElement(PlyrMenu, { label: "Quality", hint, disabled: options.disabled }, /* @__PURE__ */ React.createElement(Root$3, { value: options.selectedValue }, options.map(({ label, value, select }) => /* @__PURE__ */ React.createElement(Item, { className: "plyr__control", value, onSelect: select, key: value }, /* @__PURE__ */ React.createElement("span", null, label)))));
}
PlyrQualityMenu.displayName = "PlyrQualityMenu";
export { PlyrAudioLayout, PlyrLayout, PlyrLayoutContext, PlyrVideoLayout, i18n, usePlyrLayoutContext, usePlyrLayoutWord };