UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

917 lines (899 loc) 36.2 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _chunk7CYRHMOFjs = require('./chunk-7CYRHMOF.js'); var _chunkZHWRUIVEjs = require('./chunk-ZHWRUIVE.js'); var _chunkBVUPM6EYjs = require('./chunk-BVUPM6EY.js'); var _chunkANT66KVKjs = require('./chunk-ANT66KVK.js'); var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js'); // src/components/VideoPlayer/VideoPlayer.tsx var _react = require('react'); var _reactdom = require('react-dom'); var _Play = require('@phosphor-icons/react/dist/csr/Play'); var _Pause = require('@phosphor-icons/react/dist/csr/Pause'); var _SpeakerHigh = require('@phosphor-icons/react/dist/csr/SpeakerHigh'); var _SpeakerSlash = require('@phosphor-icons/react/dist/csr/SpeakerSlash'); var _ArrowsOutSimple = require('@phosphor-icons/react/dist/csr/ArrowsOutSimple'); var _ArrowsInSimple = require('@phosphor-icons/react/dist/csr/ArrowsInSimple'); var _ClosedCaptioning = require('@phosphor-icons/react/dist/csr/ClosedCaptioning'); var _DotsThreeVertical = require('@phosphor-icons/react/dist/csr/DotsThreeVertical'); var _jsxruntime = require('react/jsx-runtime'); var CONTROLS_HIDE_TIMEOUT = 3e3; var LEAVE_HIDE_TIMEOUT = 1e3; var INIT_DELAY = 100; var formatTime = (seconds) => { if (!seconds || Number.isNaN(seconds)) return "0:00"; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, "0")}`; }; var ProgressBar = ({ currentTime, duration, progressPercentage, onSeek, className = "px-4 pb-2" }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "range", min: 0, max: duration || 100, value: currentTime, onChange: (e) => onSeek(Number.parseFloat(e.target.value)), className: "w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500", "aria-label": "Video progress", style: { background: `linear-gradient(to right, var(--color-primary-700) ${progressPercentage}%, var(--color-secondary-300) ${progressPercentage}%)` } } ) }); var VolumeControls = ({ volume, isMuted, onVolumeChange, onToggleMute, iconSize = 24, showSlider = true }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { icon: isMuted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _SpeakerSlash.SpeakerSlashIcon, { size: iconSize }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _SpeakerHigh.SpeakerHighIcon, { size: iconSize }), onClick: onToggleMute, "aria-label": isMuted ? "Unmute" : "Mute", className: "!bg-transparent !text-white hover:!bg-white/20" } ), showSlider && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "range", min: 0, max: 100, value: Math.round(volume * 100), onChange: (e) => onVolumeChange(Number.parseInt(e.target.value)), className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500", "aria-label": "Volume control", style: { background: `linear-gradient(to right, var(--color-primary-700) ${volume * 100}%, var(--color-secondary-300) ${volume * 100}%)` } } ) ] }); var SpeedMenu = ({ showSpeedMenu, playbackRate, onToggleMenu, onSpeedChange, isFullscreen, iconSize = 24, isTinyMobile = false }) => { const buttonRef = _react.useRef.call(void 0, null); const speedMenuContainerRef = _react.useRef.call(void 0, null); const speedMenuRef = _react.useRef.call(void 0, null); const getMenuPosition = () => { if (!buttonRef.current) return { top: 0, left: 0 }; const rect = buttonRef.current.getBoundingClientRect(); const menuHeight = isTinyMobile ? 150 : 180; const menuWidth = isTinyMobile ? 60 : 80; const padding = isTinyMobile ? 4 : 8; return { // Fixed coords are viewport-based — no scroll offsets. top: Math.max(padding, rect.top - menuHeight), left: Math.max(padding, rect.right - menuWidth) }; }; const position = getMenuPosition(); _react.useEffect.call(void 0, () => { const handleClickOutside = (event) => { const target = event.target; const isOutsideContainer = speedMenuContainerRef.current && !speedMenuContainerRef.current.contains(target); const isOutsideMenu = speedMenuRef.current && !speedMenuRef.current.contains(target); if (isOutsideContainer && isOutsideMenu) { onToggleMenu(); } }; if (showSpeedMenu) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [showSpeedMenu, onToggleMenu]); const menuContent = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: speedMenuRef, role: "menu", "aria-label": "Playback speed", className: isFullscreen ? "absolute bottom-12 right-0 bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]" : "fixed bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]", style: isFullscreen ? void 0 : { top: `${position.top}px`, left: `${position.left}px` }, children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { role: "menuitemradio", "aria-checked": playbackRate === speed, onClick: () => onSpeedChange(speed), className: `block w-full text-left px-3 py-1 text-sm rounded hover:bg-border-50 transition-colors ${playbackRate === speed ? "bg-primary-950 text-secondary-100 font-medium" : "text-text-950"}`, children: [ speed, "x" ] }, speed )) } ); const portalContent = showSpeedMenu && globalThis.window !== void 0 && globalThis.document !== void 0 && !!_optionalChain([globalThis, 'access', _ => _.document, 'optionalAccess', _2 => _2.body]) ? _reactdom.createPortal.call(void 0, menuContent, globalThis.document.body) : null; return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "relative", ref: speedMenuContainerRef, children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { ref: buttonRef, icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _DotsThreeVertical.DotsThreeVerticalIcon, { size: iconSize }), onClick: onToggleMenu, "aria-label": "Playback speed", "aria-haspopup": "menu", "aria-expanded": showSpeedMenu, className: "!bg-transparent !text-white hover:!bg-white/20" } ), showSpeedMenu && (isFullscreen ? menuContent : portalContent) ] }); }; var VideoPlayer = ({ src, poster, subtitles, title, subtitle: subtitleText, initialTime = 0, onTimeUpdate, onProgress, onVideoComplete, className, autoSave = true, storageKey = "video-progress", userId, downloadContent, showDownloadButton = false, onDownloadStart, onDownloadComplete, onDownloadError }) => { const videoRef = _react.useRef.call(void 0, null); const { isUltraSmallMobile, isTinyMobile } = _chunkZHWRUIVEjs.useMobile.call(void 0, ); const [isPlaying, setIsPlaying] = _react.useState.call(void 0, false); const [hasStarted, setHasStarted] = _react.useState.call(void 0, false); const [currentTime, setCurrentTime] = _react.useState.call(void 0, 0); const [duration, setDuration] = _react.useState.call(void 0, 0); const [isMuted, setIsMuted] = _react.useState.call(void 0, false); const [volume, setVolume] = _react.useState.call(void 0, 1); const [isFullscreen, setIsFullscreen] = _react.useState.call(void 0, false); const [showControls, setShowControls] = _react.useState.call(void 0, true); const [hasCompleted, setHasCompleted] = _react.useState.call(void 0, false); const [showCaptions, setShowCaptions] = _react.useState.call(void 0, false); const [subtitlesValidation, setSubtitlesValidation] = _react.useState.call(void 0, "idle"); _react.useEffect.call(void 0, () => { setHasCompleted(false); setHasStarted(false); }, [src]); const [playbackRate, setPlaybackRate] = _react.useState.call(void 0, 1); const [showSpeedMenu, setShowSpeedMenu] = _react.useState.call(void 0, false); const lastSaveTimeRef = _react.useRef.call(void 0, 0); const trackRef = _react.useRef.call(void 0, null); const controlsTimeoutRef = _react.useRef.call(void 0, null); const lastMousePositionRef = _react.useRef.call(void 0, { x: 0, y: 0 }); const isUserInteracting = _react.useCallback.call(void 0, () => { if (showSpeedMenu) { return true; } const activeElement = document.activeElement; const videoContainer = _optionalChain([videoRef, 'access', _3 => _3.current, 'optionalAccess', _4 => _4.parentElement]); if (activeElement && _optionalChain([videoContainer, 'optionalAccess', _5 => _5.contains, 'call', _6 => _6(activeElement)])) { if (activeElement === videoRef.current) { return false; } const isControl = activeElement.matches("button, input, [tabindex]"); if (isControl) { return true; } } return false; }, [showSpeedMenu]); const clearControlsTimeout = _react.useCallback.call(void 0, () => { if (controlsTimeoutRef.current) { clearTimeout(controlsTimeoutRef.current); controlsTimeoutRef.current = null; } }, []); const showControlsWithTimer = _react.useCallback.call(void 0, () => { setShowControls(true); clearControlsTimeout(); if (isFullscreen) { if (isPlaying) { controlsTimeoutRef.current = globalThis.setTimeout(() => { setShowControls(false); }, CONTROLS_HIDE_TIMEOUT); } } else { controlsTimeoutRef.current = globalThis.setTimeout(() => { setShowControls(false); }, CONTROLS_HIDE_TIMEOUT); } }, [isFullscreen, isPlaying, clearControlsTimeout]); const handleMouseMove = _react.useCallback.call(void 0, (event) => { const currentX = event.clientX; const currentY = event.clientY; const lastPos = lastMousePositionRef.current; const hasMoved = Math.abs(currentX - lastPos.x) > 5 || Math.abs(currentY - lastPos.y) > 5; if (hasMoved) { lastMousePositionRef.current = { x: currentX, y: currentY }; showControlsWithTimer(); } }, [showControlsWithTimer] ); const handleMouseEnter = _react.useCallback.call(void 0, () => { showControlsWithTimer(); }, [showControlsWithTimer]); const handleMouseLeave = _react.useCallback.call(void 0, () => { const userInteracting = isUserInteracting(); clearControlsTimeout(); if (!isFullscreen && !userInteracting) { controlsTimeoutRef.current = globalThis.setTimeout(() => { setShowControls(false); }, LEAVE_HIDE_TIMEOUT); } }, [isFullscreen, clearControlsTimeout, isUserInteracting]); _react.useEffect.call(void 0, () => { if (videoRef.current) { videoRef.current.volume = volume; videoRef.current.muted = isMuted; } }, [volume, isMuted]); _react.useEffect.call(void 0, () => { const video = videoRef.current; if (!video) return; const onPlay = () => { setIsPlaying(true); setHasStarted(true); }; const onPause = () => setIsPlaying(false); const onEnded = () => setIsPlaying(false); video.addEventListener("play", onPlay); video.addEventListener("pause", onPause); video.addEventListener("ended", onEnded); return () => { video.removeEventListener("play", onPlay); video.removeEventListener("pause", onPause); video.removeEventListener("ended", onEnded); }; }, []); _react.useEffect.call(void 0, () => { const video = videoRef.current; if (!video) return; video.setAttribute("playsinline", ""); video.setAttribute("webkit-playsinline", ""); }, []); _react.useEffect.call(void 0, () => { if (isPlaying) { showControlsWithTimer(); } else { clearControlsTimeout(); if (isFullscreen) { setShowControls(true); } else { showControlsWithTimer(); } } }, [isPlaying, isFullscreen, showControlsWithTimer, clearControlsTimeout]); _react.useEffect.call(void 0, () => { const video = videoRef.current; if (!video) return; const handleFullscreenChange = () => { const isCurrentlyFullscreen = !!document.fullscreenElement; setIsFullscreen(isCurrentlyFullscreen); if (isCurrentlyFullscreen) { showControlsWithTimer(); } }; const handleWebkitBeginFullscreen = () => { setIsFullscreen(true); showControlsWithTimer(); }; const handleWebkitEndFullscreen = () => { setIsFullscreen(false); }; document.addEventListener("fullscreenchange", handleFullscreenChange); video.addEventListener( "webkitbeginfullscreen", handleWebkitBeginFullscreen ); video.addEventListener("webkitendfullscreen", handleWebkitEndFullscreen); return () => { document.removeEventListener("fullscreenchange", handleFullscreenChange); video.removeEventListener( "webkitbeginfullscreen", handleWebkitBeginFullscreen ); video.removeEventListener( "webkitendfullscreen", handleWebkitEndFullscreen ); }; }, [showControlsWithTimer]); _react.useEffect.call(void 0, () => { const init = () => { if (!isFullscreen) { showControlsWithTimer(); } }; let raf1 = 0, raf2 = 0, tid; if (globalThis.requestAnimationFrame === void 0) { tid = globalThis.setTimeout(init, INIT_DELAY); return () => { if (tid) clearTimeout(tid); }; } else { raf1 = requestAnimationFrame(() => { raf2 = requestAnimationFrame(init); }); return () => { cancelAnimationFrame(raf1); cancelAnimationFrame(raf2); }; } }, []); const getInitialTime = _react.useCallback.call(void 0, () => { if (!autoSave || !storageKey) { return Number.isFinite(initialTime) && initialTime >= 0 ? initialTime : void 0; } const scopedKey = userId ? `${storageKey}-${userId}` : storageKey; const saved = Number( localStorage.getItem(`${scopedKey}-${src}`) || Number.NaN ); const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0; const hasValidSaved = Number.isFinite(saved) && saved >= 0; if (hasValidInitial) return initialTime; if (hasValidSaved) return saved; return void 0; }, [autoSave, storageKey, userId, src, initialTime]); _react.useEffect.call(void 0, () => { const start = getInitialTime(); if (start !== void 0 && Number.isFinite(start) && videoRef.current) { videoRef.current.currentTime = start; setCurrentTime(start); } }, [getInitialTime]); const saveProgress = _react.useCallback.call(void 0, (time) => { if (!autoSave || !storageKey) return; const now = Date.now(); if (now - lastSaveTimeRef.current > 5e3) { const scopedKey = userId ? `${storageKey}-${userId}` : storageKey; localStorage.setItem(`${scopedKey}-${src}`, time.toString()); lastSaveTimeRef.current = now; } }, [autoSave, storageKey, userId, src] ); const togglePlayPause = _react.useCallback.call(void 0, async () => { const video = videoRef.current; if (!video) return; if (!video.paused) { video.pause(); return; } try { await video.play(); } catch (e2) { } }, []); const handleVolumeChange = _react.useCallback.call(void 0, (newVolume) => { const video = videoRef.current; if (!video) return; const volumeValue = newVolume / 100; video.volume = volumeValue; setVolume(volumeValue); const shouldMute = volumeValue === 0; const shouldUnmute = volumeValue > 0 && isMuted; if (shouldMute) { video.muted = true; setIsMuted(true); } else if (shouldUnmute) { video.muted = false; setIsMuted(false); } }, [isMuted] ); const toggleMute = _react.useCallback.call(void 0, () => { const video = videoRef.current; if (!video) return; if (isMuted) { const restoreVolume = volume > 0 ? volume : 0.5; video.volume = restoreVolume; video.muted = false; setVolume(restoreVolume); setIsMuted(false); } else { video.muted = true; setIsMuted(true); } }, [isMuted, volume]); const handleSeek = _react.useCallback.call(void 0, (newTime) => { const video = videoRef.current; if (video && Number.isFinite(newTime)) { video.currentTime = newTime; } }, []); const isSafariIOS = _react.useCallback.call(void 0, () => { const ua = navigator.userAgent; const isIOS = /iPad|iPhone|iPod/.test(ua); const isWebKit = /WebKit/.test(ua); const isNotChrome = !/CriOS|Chrome/.test(ua); return isIOS && isWebKit && isNotChrome; }, []); const toggleFullscreen = _react.useCallback.call(void 0, () => { const video = videoRef.current; const container = _optionalChain([video, 'optionalAccess', _7 => _7.parentElement]); if (!video || !container) return; if (isSafariIOS()) { const videoElement = video; if (!isFullscreen && videoElement.webkitEnterFullscreen) { videoElement.webkitEnterFullscreen(); } else if (isFullscreen && videoElement.webkitExitFullscreen) { videoElement.webkitExitFullscreen(); } } else if (!isFullscreen && container.requestFullscreen) { container.requestFullscreen().catch(() => { }); } else if (isFullscreen && document.exitFullscreen) { document.exitFullscreen().catch(() => { }); } }, [isFullscreen, isSafariIOS]); const handleSpeedChange = _react.useCallback.call(void 0, (speed) => { if (videoRef.current) { videoRef.current.playbackRate = speed; setPlaybackRate(speed); setShowSpeedMenu(false); } }, []); const toggleSpeedMenu = _react.useCallback.call(void 0, () => { setShowSpeedMenu(!showSpeedMenu); }, [showSpeedMenu]); const toggleCaptions = _react.useCallback.call(void 0, () => { if (!_optionalChain([trackRef, 'access', _8 => _8.current, 'optionalAccess', _9 => _9.track]) || !subtitles || subtitlesValidation !== "valid") return; const newShowCaptions = !showCaptions; setShowCaptions(newShowCaptions); trackRef.current.track.mode = newShowCaptions ? "showing" : "hidden"; }, [showCaptions, subtitles, subtitlesValidation]); const checkVideoCompletion = _react.useCallback.call(void 0, (progressPercent) => { if (progressPercent >= 95 && !hasCompleted) { setHasCompleted(true); _optionalChain([onVideoComplete, 'optionalCall', _10 => _10()]); } }, [hasCompleted, onVideoComplete] ); const handleTimeUpdate = _react.useCallback.call(void 0, () => { const video = videoRef.current; if (!video) return; const current = video.currentTime; setCurrentTime(current); saveProgress(current); _optionalChain([onTimeUpdate, 'optionalCall', _11 => _11(current)]); if (duration > 0) { const progressPercent = current / duration * 100; _optionalChain([onProgress, 'optionalCall', _12 => _12(progressPercent)]); checkVideoCompletion(progressPercent); } }, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]); const handleLoadedMetadata = _react.useCallback.call(void 0, () => { if (videoRef.current) { setDuration(videoRef.current.duration); } }, []); _react.useEffect.call(void 0, () => { const controller = new AbortController(); const validateSubtitles = async () => { if (!subtitles) { setSubtitlesValidation("idle"); return; } setSubtitlesValidation("validating"); try { if (subtitles.startsWith("data:")) { setSubtitlesValidation("valid"); return; } const response = await fetch(subtitles, { method: "HEAD", signal: controller.signal }); if (response.ok) { const contentType = response.headers.get("content-type"); const isValidType = !contentType || contentType.includes("text/vtt") || contentType.includes("text/plain") || contentType.includes("application/octet-stream"); if (isValidType) { setSubtitlesValidation("valid"); } else { setSubtitlesValidation("invalid"); console.warn( `Subtitles URL has invalid content type: ${contentType}` ); } } else { setSubtitlesValidation("invalid"); console.warn( `Subtitles URL returned status: ${response.status} ${response.statusText}` ); } } catch (error) { if (error instanceof Error && error.name === "AbortError") { return; } console.warn("Subtitles URL validation failed:", error); setSubtitlesValidation("invalid"); } }; validateSubtitles(); return () => { controller.abort(); }; }, [subtitles]); _react.useEffect.call(void 0, () => { if (_optionalChain([trackRef, 'access', _13 => _13.current, 'optionalAccess', _14 => _14.track])) { trackRef.current.track.mode = showCaptions && subtitles && subtitlesValidation === "valid" ? "showing" : "hidden"; } }, [subtitles, showCaptions, subtitlesValidation]); _react.useEffect.call(void 0, () => { const handleVisibilityChange = () => { if (document.hidden && isPlaying && videoRef.current) { videoRef.current.pause(); setIsPlaying(false); } }; const handleBlur = () => { if (isPlaying && videoRef.current) { videoRef.current.pause(); setIsPlaying(false); } }; document.addEventListener("visibilitychange", handleVisibilityChange); globalThis.addEventListener("blur", handleBlur); return () => { document.removeEventListener("visibilitychange", handleVisibilityChange); globalThis.removeEventListener("blur", handleBlur); clearControlsTimeout(); }; }, [isPlaying, clearControlsTimeout]); const progressPercentage = duration > 0 ? currentTime / duration * 100 : 0; const getIconSize = _react.useCallback.call(void 0, () => { if (isTinyMobile) return 18; if (isUltraSmallMobile) return 20; return 24; }, [isTinyMobile, isUltraSmallMobile]); const getControlsPadding = _react.useCallback.call(void 0, () => { if (isTinyMobile) return "px-2 pb-2 pt-1"; if (isUltraSmallMobile) return "px-3 pb-3 pt-1"; return "px-4 pb-4"; }, [isTinyMobile, isUltraSmallMobile]); const getControlsGap = _react.useCallback.call(void 0, () => { if (isTinyMobile) return "gap-1"; if (isUltraSmallMobile) return "gap-2"; return "gap-4"; }, [isTinyMobile, isUltraSmallMobile]); const getProgressBarPadding = _react.useCallback.call(void 0, () => { if (isTinyMobile) return "px-2 pb-1"; if (isUltraSmallMobile) return "px-3 pb-1"; return "px-4 pb-2"; }, [isTinyMobile, isUltraSmallMobile]); const getCenterPlayButtonPosition = _react.useCallback.call(void 0, () => { if (isTinyMobile) return "items-center justify-center -translate-y-12"; if (isUltraSmallMobile) return "items-center justify-center -translate-y-8"; return "items-center justify-center"; }, [isTinyMobile, isUltraSmallMobile]); const getTopControlsOpacity = _react.useCallback.call(void 0, () => { return showControls ? "opacity-100" : "opacity-0"; }, [showControls]); const getBottomControlsOpacity = _react.useCallback.call(void 0, () => { return showControls ? "opacity-100" : "opacity-0"; }, [showControls]); const seekBackward = _react.useCallback.call(void 0, () => { if (videoRef.current) { videoRef.current.currentTime -= 10; } }, []); const seekForward = _react.useCallback.call(void 0, () => { if (videoRef.current) { videoRef.current.currentTime += 10; } }, []); const increaseVolume = _react.useCallback.call(void 0, () => { handleVolumeChange(Math.min(100, volume * 100 + 10)); }, [handleVolumeChange, volume]); const decreaseVolume = _react.useCallback.call(void 0, () => { handleVolumeChange(Math.max(0, volume * 100 - 10)); }, [handleVolumeChange, volume]); const handleVideoKeyDown = _react.useCallback.call(void 0, (e) => { if (!e.key) return; e.stopPropagation(); showControlsWithTimer(); const keyHandlers = { " ": togglePlayPause, Enter: togglePlayPause, ArrowLeft: seekBackward, ArrowRight: seekForward, ArrowUp: increaseVolume, ArrowDown: decreaseVolume, m: toggleMute, M: toggleMute, f: toggleFullscreen, F: toggleFullscreen }; const handler = keyHandlers[e.key]; if (handler) { e.preventDefault(); handler(); } }, [ showControlsWithTimer, togglePlayPause, seekBackward, seekForward, increaseVolume, decreaseVolume, toggleMute, toggleFullscreen ] ); const groupedSubTitleValid = subtitles && subtitlesValidation === "valid"; const showPoster = Boolean(poster) && !hasStarted; return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "flex flex-col", className), children: [ (title || subtitleText) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "bg-subject-1 px-8 py-4 flex items-end justify-between min-h-20", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-1", children: [ title && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { as: "h2", size: "lg", weight: "bold", color: "text-text-900", className: "leading-5 tracking-wide", children: title } ), subtitleText && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { as: "p", size: "sm", weight: "normal", color: "text-text-600", className: "leading-5", children: subtitleText } ) ] }), showDownloadButton && downloadContent && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7CYRHMOFjs.DownloadButton_default, { content: downloadContent, lessonTitle: title, onDownloadStart, onDownloadComplete, onDownloadError, className: "flex-shrink-0" } ) ] }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { className: _chunkTN3AYOMVjs.cn.call(void 0, "relative w-full bg-background overflow-hidden group", "rounded-b-xl", // Hide cursor when controls are hidden and video is playing isPlaying && !showControls ? "cursor-none group-hover:cursor-default" : "cursor-default" ), "aria-label": title ? `Video player: ${title}` : "Video player", onMouseMove: handleMouseMove, onMouseEnter: handleMouseEnter, onTouchStart: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "video", { ref: videoRef, src, crossOrigin: "anonymous", className: "w-full h-full object-contain analytica-video", controlsList: "nodownload", playsInline: true, onTimeUpdate: handleTimeUpdate, onLoadedMetadata: handleLoadedMetadata, onClick: togglePlayPause, onKeyDown: handleVideoKeyDown, tabIndex: 0, "aria-label": title ? `Video: ${title}` : "Video player", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "track", { ref: trackRef, kind: "captions", src: groupedSubTitleValid ? subtitles : "data:text/vtt;charset=utf-8,WEBVTT", srcLang: "pt-br", label: groupedSubTitleValid ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis", default: false } ) } ), showPoster && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: poster, alt: title ? `Capa: ${title}` : "Capa do v\xEDdeo", className: "absolute inset-0 w-full h-full object-cover", "aria-hidden": "true" } ), !isPlaying && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "absolute inset-0 flex bg-black/30 transition-opacity", getCenterPlayButtonPosition() ), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _Play.PlayIcon, { size: 32, weight: "regular", className: "ml-1" }), onClick: togglePlayPause, "aria-label": "Play video", className: "!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200" } ) } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity", getTopControlsOpacity() ), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex justify-start", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { icon: isFullscreen ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ArrowsInSimple.ArrowsInSimpleIcon, { size: 24 }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ArrowsOutSimple.ArrowsOutSimpleIcon, { size: 24 }), onClick: toggleFullscreen, "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen", className: "!bg-transparent !text-white hover:!bg-white/20" } ) }) } ), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity", getBottomControlsOpacity() ), children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ProgressBar, { currentTime, duration, progressPercentage, onSeek: handleSeek, className: getProgressBarPadding() } ), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "flex items-center justify-between", getControlsPadding() ), children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "flex items-center", getControlsGap()), children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { icon: isPlaying ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _Pause.PauseIcon, { size: getIconSize() }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _Play.PlayIcon, { size: getIconSize() }), onClick: togglePlayPause, "aria-label": isPlaying ? "Pause" : "Play", className: "!bg-transparent !text-white hover:!bg-white/20" } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, VolumeControls, { volume, isMuted, onVolumeChange: handleVolumeChange, onToggleMute: toggleMute, iconSize: getIconSize(), showSlider: !isUltraSmallMobile } ), groupedSubTitleValid && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBVUPM6EYjs.IconButton_default, { icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ClosedCaptioning.ClosedCaptioningIcon, { size: getIconSize() }), onClick: toggleCaptions, "aria-label": showCaptions ? "Hide captions" : "Show captions", className: _chunkTN3AYOMVjs.cn.call(void 0, "!bg-transparent hover:!bg-white/20", showCaptions ? "!text-primary-400" : "!text-white" ) } ), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", weight: "medium", color: "text-white", children: [ formatTime(currentTime), " / ", formatTime(duration) ] }) ] }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center gap-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SpeedMenu, { showSpeedMenu, playbackRate, onToggleMenu: toggleSpeedMenu, onSpeedChange: handleSpeedChange, iconSize: getIconSize(), isTinyMobile, isFullscreen } ) }) ] } ) ] } ) ] } ) ] }); }; var VideoPlayer_default = VideoPlayer; exports.VideoPlayer_default = VideoPlayer_default; //# sourceMappingURL=chunk-52VZ6TOF.js.map