analytica-frontend-lib 1.2.44 → 1.2.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Accordation/index.js.map +1 -1
- package/dist/Accordation/index.mjs.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.js.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.mjs.map +1 -1
- package/dist/ActivityCardQuestionPreview/index.js.map +1 -1
- package/dist/ActivityCardQuestionPreview/index.mjs.map +1 -1
- package/dist/ActivityDetails/index.d.ts.map +1 -1
- package/dist/ActivityDetails/index.js +59 -50
- package/dist/ActivityDetails/index.js.map +1 -1
- package/dist/ActivityDetails/index.mjs +59 -50
- package/dist/ActivityDetails/index.mjs.map +1 -1
- package/dist/ActivityFilters/index.js.map +1 -1
- package/dist/ActivityFilters/index.mjs.map +1 -1
- package/dist/ActivityPreview/index.js.map +1 -1
- package/dist/ActivityPreview/index.mjs.map +1 -1
- package/dist/Alert/index.js.map +1 -1
- package/dist/Alert/index.mjs.map +1 -1
- package/dist/AlertDialog/index.js.map +1 -1
- package/dist/AlertDialog/index.mjs.map +1 -1
- package/dist/AlertManager/index.js.map +1 -1
- package/dist/AlertManager/index.mjs.map +1 -1
- package/dist/AlertManagerView/index.js.map +1 -1
- package/dist/AlertManagerView/index.mjs.map +1 -1
- package/dist/Alternative/index.js.map +1 -1
- package/dist/Alternative/index.mjs.map +1 -1
- package/dist/Badge/index.js.map +1 -1
- package/dist/Badge/index.mjs.map +1 -1
- package/dist/BreadcrumbMenu/index.js.map +1 -1
- package/dist/BreadcrumbMenu/index.mjs.map +1 -1
- package/dist/Button/index.js.map +1 -1
- package/dist/Button/index.mjs.map +1 -1
- package/dist/Calendar/index.js.map +1 -1
- package/dist/Calendar/index.mjs.map +1 -1
- package/dist/Card/index.js.map +1 -1
- package/dist/Card/index.mjs.map +1 -1
- package/dist/CheckBox/index.js.map +1 -1
- package/dist/CheckBox/index.mjs.map +1 -1
- package/dist/Chips/index.js.map +1 -1
- package/dist/Chips/index.mjs.map +1 -1
- package/dist/CorrectActivityModal/index.js.map +1 -1
- package/dist/CorrectActivityModal/index.mjs.map +1 -1
- package/dist/Divider/index.js.map +1 -1
- package/dist/Divider/index.mjs.map +1 -1
- package/dist/DownloadButton/index.js.map +1 -1
- package/dist/DownloadButton/index.mjs.map +1 -1
- package/dist/DropdownMenu/index.js.map +1 -1
- package/dist/DropdownMenu/index.mjs.map +1 -1
- package/dist/EmptyState/index.js.map +1 -1
- package/dist/EmptyState/index.mjs.map +1 -1
- package/dist/FileAttachment/index.js.map +1 -1
- package/dist/FileAttachment/index.mjs.map +1 -1
- package/dist/IconButton/index.js.map +1 -1
- package/dist/IconButton/index.mjs.map +1 -1
- package/dist/IconRoundedButton/index.js.map +1 -1
- package/dist/IconRoundedButton/index.mjs.map +1 -1
- package/dist/LatexRenderer/index.js.map +1 -1
- package/dist/LatexRenderer/index.mjs.map +1 -1
- package/dist/Menu/index.js.map +1 -1
- package/dist/Menu/index.mjs.map +1 -1
- package/dist/Modal/index.js.map +1 -1
- package/dist/Modal/index.mjs.map +1 -1
- package/dist/MultipleChoice/index.js.map +1 -1
- package/dist/MultipleChoice/index.mjs.map +1 -1
- package/dist/NavButton/index.js.map +1 -1
- package/dist/NavButton/index.mjs.map +1 -1
- package/dist/NoSearchResult/index.js.map +1 -1
- package/dist/NoSearchResult/index.mjs.map +1 -1
- package/dist/NotFound/index.js.map +1 -1
- package/dist/NotFound/index.mjs.map +1 -1
- package/dist/NotificationCard/index.js.map +1 -1
- package/dist/NotificationCard/index.mjs.map +1 -1
- package/dist/ProgressBar/index.js.map +1 -1
- package/dist/ProgressBar/index.mjs.map +1 -1
- package/dist/ProgressCircle/index.js.map +1 -1
- package/dist/ProgressCircle/index.mjs.map +1 -1
- package/dist/Quiz/index.js.map +1 -1
- package/dist/Quiz/index.mjs.map +1 -1
- package/dist/Radio/index.js.map +1 -1
- package/dist/Radio/index.mjs.map +1 -1
- package/dist/Search/index.js.map +1 -1
- package/dist/Search/index.mjs.map +1 -1
- package/dist/Select/index.js.map +1 -1
- package/dist/Select/index.mjs.map +1 -1
- package/dist/SelectionButton/index.js.map +1 -1
- package/dist/SelectionButton/index.mjs.map +1 -1
- package/dist/SendActivityModal/index.js.map +1 -1
- package/dist/SendActivityModal/index.mjs.map +1 -1
- package/dist/Skeleton/index.js.map +1 -1
- package/dist/Skeleton/index.mjs.map +1 -1
- package/dist/StatisticsCard/index.js.map +1 -1
- package/dist/StatisticsCard/index.mjs.map +1 -1
- package/dist/Stepper/index.js.map +1 -1
- package/dist/Stepper/index.mjs.map +1 -1
- package/dist/Support/TicketModal/index.js.map +1 -1
- package/dist/Support/TicketModal/index.mjs.map +1 -1
- package/dist/Support/index.js.map +1 -1
- package/dist/Support/index.mjs.map +1 -1
- package/dist/Table/TablePagination/index.js.map +1 -1
- package/dist/Table/TablePagination/index.mjs.map +1 -1
- package/dist/Table/index.js.map +1 -1
- package/dist/Table/index.mjs.map +1 -1
- package/dist/TableProvider/index.js.map +1 -1
- package/dist/TableProvider/index.mjs.map +1 -1
- package/dist/Text/index.js.map +1 -1
- package/dist/Text/index.mjs.map +1 -1
- package/dist/TextArea/index.js.map +1 -1
- package/dist/TextArea/index.mjs.map +1 -1
- package/dist/ThemeToggle/index.js.map +1 -1
- package/dist/ThemeToggle/index.mjs.map +1 -1
- package/dist/Toast/Toaster/index.js.map +1 -1
- package/dist/Toast/Toaster/index.mjs.map +1 -1
- package/dist/Toast/index.js.map +1 -1
- package/dist/Toast/index.mjs.map +1 -1
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs.map +1 -1
- package/dist/Whiteboard/index.js.map +1 -1
- package/dist/Whiteboard/index.mjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -55
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -55
- package/dist/index.mjs.map +1 -1
- package/dist/types/activityDetails.d.ts +0 -24
- package/dist/types/activityDetails.d.ts.map +1 -1
- package/dist/utils/activityDetailsUtils.d.ts +30 -0
- package/dist/utils/activityDetailsUtils.d.ts.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +65 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +61 -0
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/VideoPlayer/VideoPlayer.tsx","../../src/utils/utils.ts","../../src/components/IconButton/IconButton.tsx","../../src/components/Text/Text.tsx","../../src/hooks/useMobile.ts","../../src/components/DownloadButton/DownloadButton.tsx"],"sourcesContent":["import {\n useRef,\n useState,\n useEffect,\n useCallback,\n MouseEvent,\n KeyboardEvent,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n Play,\n Pause,\n SpeakerHigh,\n SpeakerSlash,\n ArrowsOutSimple,\n ArrowsInSimple,\n ClosedCaptioning,\n DotsThreeVertical,\n} from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport IconButton from '../IconButton/IconButton';\nimport Text from '../Text/Text';\nimport { useMobile } from '../../hooks/useMobile';\nimport DownloadButton, {\n DownloadContent,\n} from '../DownloadButton/DownloadButton';\n\n// Constants for timeout durations\nconst CONTROLS_HIDE_TIMEOUT = 3000; // 3 seconds for normal control hiding\nconst LEAVE_HIDE_TIMEOUT = 1000; // 1 second when mouse leaves the video area\nconst INIT_DELAY = 100; // ms delay to initialize controls on mount\n\n/**\n * VideoPlayer component props interface\n */\ninterface VideoPlayerProps {\n /** Video source URL */\n src: string;\n /** Video poster/thumbnail URL */\n poster?: string;\n /** Subtitles URL */\n subtitles?: string;\n /** Video title */\n title?: string;\n /** Video subtitle/description */\n subtitle?: string;\n /** Initial playback time in seconds */\n initialTime?: number;\n /** Callback fired when video time updates (seconds) */\n onTimeUpdate?: (seconds: number) => void;\n /** Callback fired with progress percentage (0-100) */\n onProgress?: (progress: number) => void;\n /** Callback fired when video completes (>95% watched) */\n onVideoComplete?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Auto-save progress to localStorage */\n autoSave?: boolean;\n /** localStorage key for saving progress */\n storageKey?: string;\n /** Download content URLs for lesson materials */\n downloadContent?: DownloadContent;\n /** Show download button in header */\n showDownloadButton?: boolean;\n /** Callback fired when download starts */\n onDownloadStart?: (contentType: string) => void;\n /** Callback fired when download completes */\n onDownloadComplete?: (contentType: string) => void;\n /** Callback fired when download fails */\n onDownloadError?: (contentType: string, error: Error) => void;\n}\n\n/**\n * Format seconds to MM:SS display format\n * @param seconds - Time in seconds\n * @returns Formatted time string\n */\nconst formatTime = (seconds: number): string => {\n if (!seconds || Number.isNaN(seconds)) return '0:00';\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Progress bar component props\n */\ninterface ProgressBarProps {\n currentTime: number;\n duration: number;\n progressPercentage: number;\n onSeek: (time: number) => void;\n}\n\n/**\n * Progress bar subcomponent\n */\nconst ProgressBar = ({\n currentTime,\n duration,\n progressPercentage,\n onSeek,\n className = 'px-4 pb-2',\n}: ProgressBarProps & { className?: string }) => (\n <div className={className}>\n <input\n type=\"range\"\n min={0}\n max={duration || 100}\n value={currentTime}\n onChange={(e) => onSeek(Number.parseFloat(e.target.value))}\n 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\"\n aria-label=\"Video progress\"\n style={{\n background: `linear-gradient(to right, var(--color-primary-700) ${progressPercentage}%, var(--color-secondary-300) ${progressPercentage}%)`,\n }}\n />\n </div>\n);\n\n/**\n * Volume controls component props\n */\ninterface VolumeControlsProps {\n volume: number;\n isMuted: boolean;\n onVolumeChange: (volume: number) => void;\n onToggleMute: () => void;\n iconSize?: number;\n showSlider?: boolean;\n}\n\n/**\n * Volume controls subcomponent\n */\nconst VolumeControls = ({\n volume,\n isMuted,\n onVolumeChange,\n onToggleMute,\n iconSize = 24,\n showSlider = true,\n}: VolumeControlsProps) => (\n <div className=\"flex items-center gap-2\">\n <IconButton\n icon={\n isMuted ? (\n <SpeakerSlash size={iconSize} />\n ) : (\n <SpeakerHigh size={iconSize} />\n )\n }\n onClick={onToggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {showSlider && (\n <input\n type=\"range\"\n min={0}\n max={100}\n value={Math.round(volume * 100)}\n onChange={(e) => onVolumeChange(Number.parseInt(e.target.value))}\n className=\"w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Volume control\"\n style={{\n background: `linear-gradient(to right, var(--color-primary-700) ${volume * 100}%, var(--color-secondary-300) ${volume * 100}%)`,\n }}\n />\n )}\n </div>\n);\n\n/**\n * Speed menu component props\n */\ninterface SpeedMenuProps {\n showSpeedMenu: boolean;\n playbackRate: number;\n onToggleMenu: () => void;\n onSpeedChange: (speed: number) => void;\n isFullscreen: boolean;\n iconSize?: number;\n isTinyMobile?: boolean;\n}\n\n/**\n * Speed menu subcomponent\n */\nconst SpeedMenu = ({\n showSpeedMenu,\n playbackRate,\n onToggleMenu,\n onSpeedChange,\n isFullscreen,\n iconSize = 24,\n isTinyMobile = false,\n}: SpeedMenuProps) => {\n const buttonRef = useRef<HTMLButtonElement>(null);\n const speedMenuContainerRef = useRef<HTMLDivElement>(null);\n const speedMenuRef = useRef<HTMLDivElement>(null);\n\n const getMenuPosition = () => {\n if (!buttonRef.current) return { top: 0, left: 0 };\n const rect = buttonRef.current.getBoundingClientRect();\n\n // Adjust positioning for tiny mobile screens\n const menuHeight = isTinyMobile ? 150 : 180;\n const menuWidth = isTinyMobile ? 60 : 80;\n const padding = isTinyMobile ? 4 : 8;\n\n return {\n // Fixed coords are viewport-based — no scroll offsets.\n top: Math.max(padding, rect.top - menuHeight),\n left: Math.max(padding, rect.right - menuWidth),\n };\n };\n\n const position = getMenuPosition();\n\n useEffect(() => {\n const handleClickOutside = (event: Event) => {\n const target = event.target as Node;\n\n // Check if click is outside both the container and the menu\n const isOutsideContainer =\n speedMenuContainerRef.current &&\n !speedMenuContainerRef.current.contains(target);\n const isOutsideMenu =\n speedMenuRef.current && !speedMenuRef.current.contains(target);\n\n // Only close if click is outside both refs (null-safe checks)\n if (isOutsideContainer && isOutsideMenu) {\n onToggleMenu();\n }\n };\n\n if (showSpeedMenu) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [showSpeedMenu, onToggleMenu]);\n\n const menuContent = (\n <div\n ref={speedMenuRef}\n role=\"menu\"\n aria-label=\"Playback speed\"\n className={\n isFullscreen\n ? 'absolute bottom-12 right-0 bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]'\n : 'fixed bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]'\n }\n style={\n isFullscreen\n ? undefined\n : {\n top: `${position.top}px`,\n left: `${position.left}px`,\n }\n }\n >\n {[0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => (\n <button\n key={speed}\n role=\"menuitemradio\"\n aria-checked={playbackRate === speed}\n onClick={() => onSpeedChange(speed)}\n className={`block w-full text-left px-3 py-1 text-sm rounded hover:bg-border-50 transition-colors ${\n playbackRate === speed\n ? 'bg-primary-950 text-secondary-100 font-medium'\n : 'text-text-950'\n }`}\n >\n {speed}x\n </button>\n ))}\n </div>\n );\n\n // SSR-safe portal content\n const portalContent =\n showSpeedMenu &&\n globalThis.window !== undefined &&\n globalThis.document !== undefined &&\n !!globalThis.document?.body\n ? createPortal(menuContent, globalThis.document.body)\n : null;\n\n return (\n <div className=\"relative\" ref={speedMenuContainerRef}>\n <IconButton\n ref={buttonRef}\n icon={<DotsThreeVertical size={iconSize} />}\n onClick={onToggleMenu}\n aria-label=\"Playback speed\"\n aria-haspopup=\"menu\"\n aria-expanded={showSpeedMenu}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n {showSpeedMenu && (isFullscreen ? menuContent : portalContent)}\n </div>\n );\n};\n\n/**\n * Video player component with controls and progress tracking\n * Integrates with backend lesson progress system\n *\n * @param props - VideoPlayer component props\n * @returns Video player element with controls\n */\nconst VideoPlayer = ({\n src,\n poster,\n subtitles,\n title,\n subtitle: subtitleText,\n initialTime = 0,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n className,\n autoSave = true,\n storageKey = 'video-progress',\n downloadContent,\n showDownloadButton = false,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n}: VideoPlayerProps) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const { isUltraSmallMobile, isTinyMobile } = useMobile();\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [isMuted, setIsMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [showControls, setShowControls] = useState(true);\n const [hasCompleted, setHasCompleted] = useState(false);\n const [showCaptions, setShowCaptions] = useState(false);\n const [subtitlesValidation, setSubtitlesValidation] = useState<\n 'idle' | 'validating' | 'valid' | 'invalid'\n >('idle');\n\n // Reset completion flag when changing videos\n useEffect(() => {\n setHasCompleted(false);\n }, [src]);\n const [playbackRate, setPlaybackRate] = useState(1);\n const [showSpeedMenu, setShowSpeedMenu] = useState(false);\n const lastSaveTimeRef = useRef(0);\n const trackRef = useRef<HTMLTrackElement>(null);\n const controlsTimeoutRef = useRef<number | null>(null);\n const lastMousePositionRef = useRef({ x: 0, y: 0 });\n\n /**\n * Check if user is currently interacting with controls\n */\n const isUserInteracting = useCallback(() => {\n // Check if speed menu is open\n if (showSpeedMenu) {\n return true;\n }\n\n // Check if any control element has focus\n const activeElement = document.activeElement;\n const videoContainer = videoRef.current?.parentElement;\n\n if (activeElement && videoContainer?.contains(activeElement)) {\n // Ignore the video element itself - it should not prevent control hiding\n if (activeElement === videoRef.current) {\n return false;\n }\n\n // Check if focused element is a control (button, input, etc.)\n const isControl = activeElement.matches('button, input, [tabindex]');\n if (isControl) {\n return true;\n }\n }\n\n return false;\n }, [showSpeedMenu]);\n\n /**\n * Clear controls timeout\n */\n const clearControlsTimeout = useCallback(() => {\n if (controlsTimeoutRef.current) {\n clearTimeout(controlsTimeoutRef.current);\n controlsTimeoutRef.current = null;\n }\n }, []);\n\n /**\n * Show controls and set auto-hide timer\n */\n const showControlsWithTimer = useCallback(() => {\n setShowControls(true);\n clearControlsTimeout();\n\n // In fullscreen mode, only hide if video is playing\n if (isFullscreen) {\n if (isPlaying) {\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, CONTROLS_HIDE_TIMEOUT) as unknown as number;\n }\n } else {\n // In normal mode, always set a timer to hide controls\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, CONTROLS_HIDE_TIMEOUT) as unknown as number;\n }\n }, [isFullscreen, isPlaying, clearControlsTimeout]);\n\n /**\n * Handle mouse move with position detection\n */\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n const currentX = event.clientX;\n const currentY = event.clientY;\n const lastPos = lastMousePositionRef.current;\n\n // Check if mouse actually moved (minimum 5px threshold)\n const hasMoved =\n Math.abs(currentX - lastPos.x) > 5 ||\n Math.abs(currentY - lastPos.y) > 5;\n\n if (hasMoved) {\n lastMousePositionRef.current = { x: currentX, y: currentY };\n showControlsWithTimer();\n }\n },\n [showControlsWithTimer]\n );\n\n /**\n * Handle mouse enter to show controls with appropriate timer logic\n */\n const handleMouseEnter = useCallback(() => {\n showControlsWithTimer();\n }, [showControlsWithTimer]);\n\n /**\n * Handle mouse leave to hide controls faster\n */\n const handleMouseLeave = useCallback(() => {\n const userInteracting = isUserInteracting();\n clearControlsTimeout();\n\n // Hide controls when mouse leaves, except when in fullscreen or user is interacting\n if (!isFullscreen && !userInteracting) {\n // Use shorter timeout when mouse leaves\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, LEAVE_HIDE_TIMEOUT) as unknown as number;\n }\n }, [isFullscreen, clearControlsTimeout, isUserInteracting]);\n\n /**\n * Initialize video element properties\n */\n useEffect(() => {\n // Set initial volume\n if (videoRef.current) {\n videoRef.current.volume = volume;\n videoRef.current.muted = isMuted;\n }\n }, [volume, isMuted]);\n\n /**\n * Synchronize isPlaying state with media events\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const onPlay = () => setIsPlaying(true);\n const onPause = () => setIsPlaying(false);\n const onEnded = () => setIsPlaying(false);\n\n video.addEventListener('play', onPlay);\n video.addEventListener('pause', onPause);\n video.addEventListener('ended', onEnded);\n\n return () => {\n video.removeEventListener('play', onPlay);\n video.removeEventListener('pause', onPause);\n video.removeEventListener('ended', onEnded);\n };\n }, []);\n\n /**\n * Set iOS/Safari inline playback attributes imperatively\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n // Ensure inline playback on iOS/Safari\n video.setAttribute('playsinline', '');\n video.setAttribute('webkit-playsinline', '');\n }, []);\n\n /**\n * Handle controls auto-hide when play state changes\n */\n useEffect(() => {\n if (isPlaying) {\n // Start timer when video starts playing\n showControlsWithTimer();\n } else {\n // Keep controls visible when paused only in fullscreen\n clearControlsTimeout();\n if (isFullscreen) {\n setShowControls(true);\n } else {\n // In normal mode (not fullscreen), initialize timer even when paused\n // This ensures controls will hide properly from the start\n showControlsWithTimer();\n }\n }\n }, [isPlaying, isFullscreen, showControlsWithTimer, clearControlsTimeout]);\n\n /**\n * Handle fullscreen state changes from browser events\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const handleFullscreenChange = () => {\n const isCurrentlyFullscreen = !!document.fullscreenElement;\n setIsFullscreen(isCurrentlyFullscreen);\n\n // Show controls when entering fullscreen, hide after timeout if playing\n if (isCurrentlyFullscreen) {\n showControlsWithTimer();\n }\n };\n\n // Safari iOS-specific fullscreen event handlers\n const handleWebkitBeginFullscreen = () => {\n setIsFullscreen(true);\n showControlsWithTimer();\n };\n\n const handleWebkitEndFullscreen = () => {\n setIsFullscreen(false);\n };\n\n // Standard fullscreen events\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n // Safari iOS fullscreen events\n video.addEventListener(\n 'webkitbeginfullscreen',\n handleWebkitBeginFullscreen\n );\n video.addEventListener('webkitendfullscreen', handleWebkitEndFullscreen);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n video.removeEventListener(\n 'webkitbeginfullscreen',\n handleWebkitBeginFullscreen\n );\n video.removeEventListener(\n 'webkitendfullscreen',\n handleWebkitEndFullscreen\n );\n };\n }, [showControlsWithTimer]);\n\n /**\n * Initialize controls behavior on component mount\n * This ensures controls work correctly from the first load\n */\n useEffect(() => {\n const init = () => {\n if (!isFullscreen) {\n showControlsWithTimer();\n }\n };\n // Prefer rAF to avoid arbitrary timing if available; fall back to INIT_DELAY.\n let raf1 = 0,\n raf2 = 0,\n tid: number | undefined;\n if (globalThis.requestAnimationFrame === undefined) {\n tid = globalThis.setTimeout(init, INIT_DELAY) as unknown as number;\n return () => {\n if (tid) clearTimeout(tid);\n };\n } else {\n raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(init);\n });\n return () => {\n cancelAnimationFrame(raf1);\n cancelAnimationFrame(raf2);\n };\n }\n }, []); // Run only once on mount\n\n /**\n * Get initial time from props or localStorage\n */\n const getInitialTime = useCallback((): number | undefined => {\n if (!autoSave || !storageKey) {\n return Number.isFinite(initialTime) && initialTime >= 0\n ? initialTime\n : undefined;\n }\n\n const saved = Number(\n localStorage.getItem(`${storageKey}-${src}`) || Number.NaN\n );\n const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;\n const hasValidSaved = Number.isFinite(saved) && saved >= 0;\n\n if (hasValidInitial) return initialTime;\n if (hasValidSaved) return saved;\n return undefined;\n }, [autoSave, storageKey, src, initialTime]);\n\n /**\n * Load saved progress from localStorage\n */\n useEffect(() => {\n const start = getInitialTime();\n if (start !== undefined && videoRef.current) {\n videoRef.current.currentTime = start;\n setCurrentTime(start);\n }\n }, [getInitialTime]);\n\n /**\n * Save progress to localStorage periodically\n */\n const saveProgress = useCallback(\n (time: number) => {\n if (!autoSave || !storageKey) return;\n\n const now = Date.now();\n if (now - lastSaveTimeRef.current > 5000) {\n localStorage.setItem(`${storageKey}-${src}`, time.toString());\n lastSaveTimeRef.current = now;\n }\n },\n [autoSave, storageKey, src]\n );\n\n /**\n * Handle play/pause toggle\n */\n const togglePlayPause = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n\n if (!video.paused) {\n video.pause();\n return;\n }\n\n try {\n await video.play();\n } catch {\n // Playback prevented (e.g., autoplay policy); keep state unchanged.\n }\n }, []);\n\n /**\n * Handle volume change\n */\n const handleVolumeChange = useCallback(\n (newVolume: number) => {\n const video = videoRef.current;\n if (!video) return;\n\n const volumeValue = newVolume / 100; // Convert 0-100 to 0-1\n video.volume = volumeValue;\n setVolume(volumeValue);\n\n // Auto mute/unmute based on volume\n const shouldMute = volumeValue === 0;\n const shouldUnmute = volumeValue > 0 && isMuted;\n\n if (shouldMute) {\n video.muted = true;\n setIsMuted(true);\n } else if (shouldUnmute) {\n video.muted = false;\n setIsMuted(false);\n }\n },\n [isMuted]\n );\n\n /**\n * Handle mute toggle\n */\n const toggleMute = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n\n if (isMuted) {\n // Unmute: restore volume or set to 50% if it was 0\n const restoreVolume = volume > 0 ? volume : 0.5;\n video.volume = restoreVolume;\n video.muted = false;\n setVolume(restoreVolume);\n setIsMuted(false);\n } else {\n // Mute: set volume to 0 and mute\n video.muted = true;\n setIsMuted(true);\n }\n }, [isMuted, volume]);\n\n /**\n * Handle video seek\n */\n const handleSeek = useCallback((newTime: number) => {\n const video = videoRef.current;\n if (video) {\n video.currentTime = newTime;\n }\n }, []);\n\n /**\n * Detect if running on Safari iOS\n */\n const isSafariIOS = useCallback((): boolean => {\n const ua = navigator.userAgent;\n const isIOS = /iPad|iPhone|iPod/.test(ua);\n const isWebKit = /WebKit/.test(ua);\n const isNotChrome = !/CriOS|Chrome/.test(ua);\n return isIOS && isWebKit && isNotChrome;\n }, []);\n\n /**\n * Handle fullscreen toggle\n */\n const toggleFullscreen = useCallback(() => {\n const video = videoRef.current;\n const container = video?.parentElement;\n if (!video || !container) return;\n\n // Safari iOS requires using webkitEnterFullscreen on video element\n if (isSafariIOS()) {\n // Type assertion for Safari-specific API\n const videoElement = video as HTMLVideoElement & {\n webkitEnterFullscreen?: () => void;\n webkitExitFullscreen?: () => void;\n webkitDisplayingFullscreen?: boolean;\n };\n\n if (!isFullscreen && videoElement.webkitEnterFullscreen) {\n videoElement.webkitEnterFullscreen();\n } else if (isFullscreen && videoElement.webkitExitFullscreen) {\n videoElement.webkitExitFullscreen();\n }\n } else if (!isFullscreen && container.requestFullscreen) {\n // Standard fullscreen API for other browsers\n container.requestFullscreen();\n } else if (isFullscreen && document.exitFullscreen) {\n document.exitFullscreen();\n }\n }, [isFullscreen, isSafariIOS]);\n\n /**\n * Handle playback speed change\n */\n const handleSpeedChange = useCallback((speed: number) => {\n if (videoRef.current) {\n videoRef.current.playbackRate = speed;\n setPlaybackRate(speed);\n setShowSpeedMenu(false);\n }\n }, []);\n\n /**\n * Toggle speed menu visibility\n */\n const toggleSpeedMenu = useCallback(() => {\n setShowSpeedMenu(!showSpeedMenu);\n }, [showSpeedMenu]);\n\n /**\n * Toggle captions visibility\n */\n const toggleCaptions = useCallback(() => {\n if (\n !trackRef.current?.track ||\n !subtitles ||\n subtitlesValidation !== 'valid'\n )\n return;\n\n const newShowCaptions = !showCaptions;\n setShowCaptions(newShowCaptions);\n\n // Control track mode programmatically - we already validated subtitles above\n trackRef.current.track.mode = newShowCaptions ? 'showing' : 'hidden';\n }, [showCaptions, subtitles, subtitlesValidation]);\n\n /**\n * Check video completion and fire callback\n */\n const checkVideoCompletion = useCallback(\n (progressPercent: number) => {\n if (progressPercent >= 95 && !hasCompleted) {\n setHasCompleted(true);\n onVideoComplete?.();\n }\n },\n [hasCompleted, onVideoComplete]\n );\n\n /**\n * Handle time update\n */\n const handleTimeUpdate = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const current = video.currentTime;\n setCurrentTime(current);\n\n // Save progress periodically\n saveProgress(current);\n\n // Fire callbacks\n onTimeUpdate?.(current);\n\n if (duration > 0) {\n const progressPercent = (current / duration) * 100;\n onProgress?.(progressPercent);\n checkVideoCompletion(progressPercent);\n }\n }, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]);\n\n /**\n * Handle loaded metadata\n */\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current) {\n setDuration(videoRef.current.duration);\n }\n }, []);\n\n /**\n * Validate subtitles URL before showing the button\n */\n useEffect(() => {\n const controller = new AbortController();\n\n const validateSubtitles = async () => {\n // If no subtitles, mark as idle\n if (!subtitles) {\n setSubtitlesValidation('idle');\n return;\n }\n\n // Start validation\n setSubtitlesValidation('validating');\n\n try {\n // Check if it's a data URL (inline VTT)\n if (subtitles.startsWith('data:')) {\n setSubtitlesValidation('valid');\n return;\n }\n\n // Fetch the subtitles file to validate it\n const response = await fetch(subtitles, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n if (response.ok) {\n // Optionally check content type\n const contentType = response.headers.get('content-type');\n const isValidType =\n !contentType ||\n contentType.includes('text/vtt') ||\n contentType.includes('text/plain') ||\n contentType.includes('application/octet-stream');\n\n if (isValidType) {\n setSubtitlesValidation('valid');\n } else {\n setSubtitlesValidation('invalid');\n console.warn(\n `Subtitles URL has invalid content type: ${contentType}`\n );\n }\n } else {\n setSubtitlesValidation('invalid');\n console.warn(\n `Subtitles URL returned status: ${response.status} ${response.statusText}`\n );\n }\n } catch (error) {\n // Ignore AbortError - it's expected when cleaning up\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n console.warn('Subtitles URL validation failed:', error);\n setSubtitlesValidation('invalid');\n }\n };\n\n validateSubtitles();\n\n // Cleanup: abort ongoing fetch to prevent stale updates\n return () => {\n controller.abort();\n };\n }, [subtitles]);\n\n /**\n * Initialize track mode when track is available\n */\n useEffect(() => {\n if (trackRef.current?.track) {\n // Set initial mode based on showCaptions state and subtitle availability\n trackRef.current.track.mode =\n showCaptions && subtitles && subtitlesValidation === 'valid'\n ? 'showing'\n : 'hidden';\n }\n }, [subtitles, showCaptions, subtitlesValidation]);\n\n /**\n * Handle visibility change and blur to pause video when losing focus\n */\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.hidden && isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n const handleBlur = () => {\n if (isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n globalThis.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n globalThis.removeEventListener('blur', handleBlur);\n // Clean up timers on unmount\n clearControlsTimeout();\n };\n }, [isPlaying, clearControlsTimeout]);\n\n const progressPercentage = duration > 0 ? (currentTime / duration) * 100 : 0;\n\n /**\n * Get responsive icon size based on screen size\n */\n const getIconSize = useCallback(() => {\n if (isTinyMobile) return 18;\n if (isUltraSmallMobile) return 20;\n return 24;\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive padding classes for controls\n */\n const getControlsPadding = useCallback(() => {\n if (isTinyMobile) return 'px-2 pb-2 pt-1';\n if (isUltraSmallMobile) return 'px-3 pb-3 pt-1';\n return 'px-4 pb-4';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive gap classes for controls\n */\n const getControlsGap = useCallback(() => {\n if (isTinyMobile) return 'gap-1';\n if (isUltraSmallMobile) return 'gap-2';\n return 'gap-4';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive padding for progress bar\n */\n const getProgressBarPadding = useCallback(() => {\n if (isTinyMobile) return 'px-2 pb-1';\n if (isUltraSmallMobile) return 'px-3 pb-1';\n return 'px-4 pb-2';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive positioning classes for center play button\n */\n const getCenterPlayButtonPosition = useCallback(() => {\n if (isTinyMobile) return 'items-center justify-center -translate-y-12';\n if (isUltraSmallMobile) return 'items-center justify-center -translate-y-8';\n return 'items-center justify-center';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Calculate top controls opacity based on state\n */\n const getTopControlsOpacity = useCallback(() => {\n return showControls ? 'opacity-100' : 'opacity-0';\n }, [showControls]);\n\n /**\n * Calculate bottom controls opacity based on state\n */\n const getBottomControlsOpacity = useCallback(() => {\n return showControls ? 'opacity-100' : 'opacity-0';\n }, [showControls]);\n\n /**\n * Seek video backward\n */\n const seekBackward = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.currentTime -= 10;\n }\n }, []);\n\n /**\n * Seek video forward\n */\n const seekForward = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.currentTime += 10;\n }\n }, []);\n\n /**\n * Increase volume\n */\n const increaseVolume = useCallback(() => {\n handleVolumeChange(Math.min(100, volume * 100 + 10));\n }, [handleVolumeChange, volume]);\n\n /**\n * Decrease volume\n */\n const decreaseVolume = useCallback(() => {\n handleVolumeChange(Math.max(0, volume * 100 - 10));\n }, [handleVolumeChange, volume]);\n\n /**\n * Handle video element keyboard events\n */\n const handleVideoKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!e.key) return;\n\n // Prevent bubbling to parent handlers to avoid double toggles\n e.stopPropagation();\n showControlsWithTimer();\n\n // Map of key handlers for better maintainability and reduced complexity\n const keyHandlers: Record<string, () => void | Promise<void>> = {\n ' ': togglePlayPause,\n Enter: togglePlayPause,\n ArrowLeft: seekBackward,\n ArrowRight: seekForward,\n ArrowUp: increaseVolume,\n ArrowDown: decreaseVolume,\n m: toggleMute,\n M: toggleMute,\n f: toggleFullscreen,\n F: toggleFullscreen,\n };\n\n const handler = keyHandlers[e.key];\n if (handler) {\n e.preventDefault();\n handler();\n }\n },\n [\n showControlsWithTimer,\n togglePlayPause,\n seekBackward,\n seekForward,\n increaseVolume,\n decreaseVolume,\n toggleMute,\n toggleFullscreen,\n ]\n );\n\n const groupedSubTitleValid = subtitles && subtitlesValidation === 'valid';\n\n return (\n <div className={cn('flex flex-col', className)}>\n {/* Integrated Header */}\n {(title || subtitleText) && (\n <div className=\"bg-subject-1 px-8 py-4 flex items-end justify-between min-h-20\">\n <div className=\"flex flex-col gap-1\">\n {title && (\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n color=\"text-text-900\"\n className=\"leading-5 tracking-wide\"\n >\n {title}\n </Text>\n )}\n {subtitleText && (\n <Text\n as=\"p\"\n size=\"sm\"\n weight=\"normal\"\n color=\"text-text-600\"\n className=\"leading-5\"\n >\n {subtitleText}\n </Text>\n )}\n </div>\n\n {/* Download Button */}\n {showDownloadButton && downloadContent && (\n <DownloadButton\n content={downloadContent}\n lessonTitle={title}\n onDownloadStart={onDownloadStart}\n onDownloadComplete={onDownloadComplete}\n onDownloadError={onDownloadError}\n className=\"flex-shrink-0\"\n />\n )}\n </div>\n )}\n\n {/* Video Container */}\n <section\n className={cn(\n 'relative w-full bg-background overflow-hidden group',\n 'rounded-b-xl',\n // Hide cursor when controls are hidden and video is playing\n isPlaying && !showControls\n ? 'cursor-none group-hover:cursor-default'\n : 'cursor-default'\n )}\n aria-label={title ? `Video player: ${title}` : 'Video player'}\n onMouseMove={handleMouseMove}\n onMouseEnter={handleMouseEnter}\n onTouchStart={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {/* Video Element */}\n <video\n ref={videoRef}\n src={src}\n poster={poster}\n className=\"w-full h-full object-contain analytica-video\"\n controlsList=\"nodownload\"\n playsInline\n onTimeUpdate={handleTimeUpdate}\n onLoadedMetadata={handleLoadedMetadata}\n onClick={togglePlayPause}\n onKeyDown={handleVideoKeyDown}\n tabIndex={0}\n aria-label={title ? `Video: ${title}` : 'Video player'}\n >\n <track\n ref={trackRef}\n kind=\"captions\"\n src={\n groupedSubTitleValid\n ? subtitles\n : 'data:text/vtt;charset=utf-8,WEBVTT'\n }\n srcLang=\"pt-br\"\n label={\n groupedSubTitleValid\n ? 'Legendas em Português'\n : 'Sem legendas disponíveis'\n }\n default={false}\n />\n </video>\n\n {/* Center Play Button */}\n {!isPlaying && (\n <div\n className={cn(\n 'absolute inset-0 flex bg-black/30 transition-opacity',\n getCenterPlayButtonPosition()\n )}\n >\n <IconButton\n icon={<Play size={32} weight=\"regular\" className=\"ml-1\" />}\n onClick={togglePlayPause}\n aria-label=\"Play video\"\n className=\"!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200\"\n />\n </div>\n )}\n\n {/* Top Controls */}\n <div\n className={cn(\n 'absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity',\n getTopControlsOpacity()\n )}\n >\n <div className=\"flex justify-start\">\n <IconButton\n icon={\n isFullscreen ? (\n <ArrowsInSimple size={24} />\n ) : (\n <ArrowsOutSimple size={24} />\n )\n }\n onClick={toggleFullscreen}\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n </div>\n </div>\n\n {/* Bottom Controls */}\n <div\n className={cn(\n 'absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity',\n getBottomControlsOpacity()\n )}\n >\n {/* Progress Bar */}\n <ProgressBar\n currentTime={currentTime}\n duration={duration}\n progressPercentage={progressPercentage}\n onSeek={handleSeek}\n className={getProgressBarPadding()}\n />\n\n {/* Control Buttons */}\n <div\n className={cn(\n 'flex items-center justify-between',\n getControlsPadding()\n )}\n >\n {/* Left Controls */}\n <div className={cn('flex items-center', getControlsGap())}>\n {/* Play/Pause */}\n <IconButton\n icon={\n isPlaying ? (\n <Pause size={getIconSize()} />\n ) : (\n <Play size={getIconSize()} />\n )\n }\n onClick={togglePlayPause}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume */}\n <VolumeControls\n volume={volume}\n isMuted={isMuted}\n onVolumeChange={handleVolumeChange}\n onToggleMute={toggleMute}\n iconSize={getIconSize()}\n showSlider={!isUltraSmallMobile}\n />\n\n {/* Captions - Only show after validation is complete and valid */}\n {groupedSubTitleValid && (\n <IconButton\n icon={<ClosedCaptioning size={getIconSize()} />}\n onClick={toggleCaptions}\n aria-label={showCaptions ? 'Hide captions' : 'Show captions'}\n className={cn(\n '!bg-transparent hover:!bg-white/20',\n showCaptions ? '!text-primary-400' : '!text-white'\n )}\n />\n )}\n\n {/* Time Display */}\n <Text size=\"sm\" weight=\"medium\" color=\"text-white\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </Text>\n </div>\n\n {/* Right Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Speed Control */}\n <SpeedMenu\n showSpeedMenu={showSpeedMenu}\n playbackRate={playbackRate}\n onToggleMenu={toggleSpeedMenu}\n onSpeedChange={handleSpeedChange}\n iconSize={getIconSize()}\n isTinyMobile={isTinyMobile}\n isFullscreen={isFullscreen}\n />\n </div>\n </div>\n </div>\n </section>\n </div>\n );\n};\n\nexport default VideoPlayer;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { useState, useEffect } from 'react';\n\n// Mobile width in pixels\nconst MOBILE_WIDTH = 500;\n// Tablet width in pixels\nconst TABLET_WIDTH = 931;\n// Video responsive breakpoints\nconst SMALL_MOBILE_WIDTH = 425;\nconst EXTRA_SMALL_MOBILE_WIDTH = 375;\nconst ULTRA_SMALL_MOBILE_WIDTH = 375; // For video controls\nconst TINY_MOBILE_WIDTH = 320;\n// Default desktop width for SSR\nconst DEFAULT_WIDTH = 1200;\n\n/**\n * Device type based on screen width\n */\nexport type DeviceType = 'responsive' | 'desktop';\n\n/**\n * Gets the window width safely (SSR compatible)\n * @returns {number} window width or default value for SSR\n */\nconst getWindowWidth = (): number => {\n if (typeof window === 'undefined') {\n return DEFAULT_WIDTH;\n }\n return window.innerWidth;\n};\n\n/**\n * Gets the current device type based on screen width\n * @returns {DeviceType} 'responsive' for mobile/tablet (width < 931px), 'desktop' for larger screens\n */\nexport const getDeviceType = (): DeviceType => {\n const width = getWindowWidth();\n return width < TABLET_WIDTH ? 'responsive' : 'desktop';\n};\n\n/**\n * Hook to detect screen size and get responsive classes\n * @returns object with isMobile, isTablet, responsive class functions and getDeviceType\n */\nexport const useMobile = () => {\n const [isMobile, setIsMobile] = useState(false);\n const [isTablet, setIsTablet] = useState(false);\n const [isSmallMobile, setIsSmallMobile] = useState(false);\n const [isExtraSmallMobile, setIsExtraSmallMobile] = useState(false);\n const [isUltraSmallMobile, setIsUltraSmallMobile] = useState(false);\n const [isTinyMobile, setIsTinyMobile] = useState(false);\n\n useEffect(() => {\n const checkScreenSize = () => {\n const width = getWindowWidth();\n setIsMobile(width < MOBILE_WIDTH);\n setIsTablet(width < TABLET_WIDTH);\n setIsSmallMobile(width < SMALL_MOBILE_WIDTH);\n setIsExtraSmallMobile(width < EXTRA_SMALL_MOBILE_WIDTH);\n setIsUltraSmallMobile(width < ULTRA_SMALL_MOBILE_WIDTH);\n setIsTinyMobile(width < TINY_MOBILE_WIDTH);\n };\n\n checkScreenSize();\n\n window.addEventListener('resize', checkScreenSize);\n\n return () => window.removeEventListener('resize', checkScreenSize);\n }, []);\n\n /**\n * Get responsive classes for the form container\n * @returns className string for form container based on screen size\n */\n const getFormContainerClasses = (): string => {\n if (isMobile) {\n return 'w-full px-4';\n }\n if (isTablet) {\n return 'w-full px-6';\n }\n return 'w-full max-w-[992px] mx-auto px-0';\n };\n\n /**\n * Get mobile-specific classes for the header\n * @returns className string for mobile header layout\n */\n const getMobileHeaderClasses = (): string => {\n return 'flex flex-col items-start gap-4 mb-6';\n };\n\n /**\n * Get desktop-specific classes for the header\n * @returns className string for desktop header layout\n */\n const getDesktopHeaderClasses = (): string => {\n return 'flex flex-row justify-between items-center gap-6 mb-8';\n };\n\n /**\n * Get responsive classes for the header\n * @returns className string for header based on screen size\n */\n const getHeaderClasses = (): string => {\n return isMobile ? getMobileHeaderClasses() : getDesktopHeaderClasses();\n };\n\n /**\n * Get responsive classes for video container\n * @returns className string for video container aspect ratio based on screen size\n */\n const getVideoContainerClasses = (): string => {\n if (isTinyMobile) return 'aspect-square'; // 1:1 ratio for screens < 320px\n if (isExtraSmallMobile) return 'aspect-[4/3]'; // 4:3 ratio for screens < 375px\n if (isSmallMobile) return 'aspect-[16/12]'; // 16:12 ratio for screens < 425px\n return 'aspect-video'; // 16:9 ratio for larger screens\n };\n\n return {\n isMobile,\n isTablet,\n isSmallMobile,\n isExtraSmallMobile,\n isUltraSmallMobile,\n isTinyMobile,\n getFormContainerClasses,\n getHeaderClasses,\n getMobileHeaderClasses,\n getDesktopHeaderClasses,\n getVideoContainerClasses,\n getDeviceType,\n };\n};\n","import { useCallback, useState } from 'react';\nimport { DownloadSimple } from 'phosphor-react';\nimport IconButton from '../IconButton/IconButton';\nimport { cn } from '../../utils/utils';\n\n/**\n * Download content interface for lesson materials\n */\nexport interface DownloadContent {\n /** Document URL (PDF) */\n urlDoc?: string;\n /** Initial frame image URL */\n urlInitialFrame?: string;\n /** Final frame image URL */\n urlFinalFrame?: string;\n /** Podcast audio URL */\n urlPodcast?: string;\n /** Video URL */\n urlVideo?: string;\n}\n\n/**\n * Props for DownloadButton component\n */\nexport interface DownloadButtonProps {\n /** Content URLs to download */\n content: DownloadContent;\n /** Additional CSS classes */\n className?: string;\n /** Callback fired when download starts */\n onDownloadStart?: (contentType: string) => void;\n /** Callback fired when download completes */\n onDownloadComplete?: (contentType: string) => void;\n /** Callback fired when download fails */\n onDownloadError?: (contentType: string, error: Error) => void;\n /** Lesson title for download file naming */\n lessonTitle?: string;\n /** Whether the button is disabled */\n disabled?: boolean;\n}\n\n/**\n * Get MIME type based on file extension\n * @param url - URL to extract extension from\n * @returns MIME type string\n */\nconst getMimeType = (url: string): string => {\n const extension = getFileExtension(url);\n const mimeTypes: Record<string, string> = {\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n mp3: 'audio/mpeg',\n mp4: 'video/mp4',\n vtt: 'text/vtt',\n };\n return mimeTypes[extension] || 'application/octet-stream';\n};\n\n/**\n * Download file via fetch and blob to ensure proper download behavior\n * @param url - URL to download\n * @param filename - Filename for the download\n * @returns Promise<void>\n */\nconst triggerDownload = async (\n url: string,\n filename: string\n): Promise<void> => {\n try {\n // Fetch the file as blob\n const response = await fetch(url, {\n mode: 'cors',\n credentials: 'same-origin',\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch file: ${response.status} ${response.statusText}`\n );\n }\n\n const blob = await response.blob();\n const mimeType = getMimeType(url);\n\n // Create a blob with the correct MIME type\n const typedBlob = new Blob([blob], { type: mimeType });\n\n // Create object URL\n const blobUrl = URL.createObjectURL(typedBlob);\n\n // Create download link\n const link = document.createElement('a');\n link.href = blobUrl;\n link.download = filename;\n link.rel = 'noopener noreferrer';\n\n // Add to DOM, click, and remove\n document.body.appendChild(link);\n link.click();\n link.remove();\n\n // Clean up object URL after a short delay\n setTimeout(() => {\n URL.revokeObjectURL(blobUrl);\n }, 1000);\n } catch (error) {\n // Fallback to direct link if fetch fails\n console.warn('Fetch download failed, falling back to direct link:', error);\n\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.rel = 'noopener noreferrer';\n link.target = '_blank'; // Open in new tab as fallback\n\n document.body.appendChild(link);\n link.click();\n link.remove();\n }\n};\n\n/**\n * Get file extension from URL\n * @param url - URL to extract extension from\n * @returns File extension or default\n */\nconst getFileExtension = (url: string): string => {\n try {\n const u = new URL(url, globalThis.location?.origin || 'http://localhost');\n url = u.pathname;\n } catch {\n // keep original url (likely relative)\n }\n const path = url.split(/[?#]/)[0];\n const dot = path.lastIndexOf('.');\n return dot > -1 ? path.slice(dot + 1).toLowerCase() : 'file';\n};\n\n/**\n * Generate filename for download\n * @param contentType - Type of content being downloaded\n * @param lessonTitle - Title of the lesson\n * @param url - URL to get extension from\n * @returns Generated filename\n */\nconst generateFilename = (\n contentType: string,\n url: string,\n lessonTitle: string = 'aula'\n): string => {\n const sanitizedTitle = lessonTitle\n .toLowerCase()\n .replaceAll(/[^a-z0-9\\s]/g, '')\n .replaceAll(/\\s+/g, '-')\n .substring(0, 50);\n\n const extension = getFileExtension(url);\n return `${sanitizedTitle}-${contentType}.${extension}`;\n};\n\n/**\n * DownloadButton component for downloading lesson content\n * Provides a single button that downloads all available content for a lesson\n *\n * @param props - DownloadButton component props\n * @returns Download button element\n */\nconst DownloadButton = ({\n content,\n className,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n lessonTitle = 'aula',\n disabled = false,\n}: DownloadButtonProps) => {\n const [isDownloading, setIsDownloading] = useState(false);\n\n /**\n * Check if URL is valid and not empty\n * @param url - URL to validate\n * @returns Whether URL is valid\n */\n const isValidUrl = useCallback((url?: string): boolean => {\n return Boolean(\n url && url.trim() !== '' && url !== 'undefined' && url !== 'null'\n );\n }, []);\n\n /**\n * Get available download content\n * @returns Array of available download items\n */\n const getAvailableContent = useCallback(() => {\n const downloads: Array<{ type: string; url: string; label: string }> = [];\n\n if (isValidUrl(content.urlDoc)) {\n downloads.push({\n type: 'documento',\n url: content.urlDoc!,\n label: 'Documento',\n });\n }\n\n if (isValidUrl(content.urlInitialFrame)) {\n downloads.push({\n type: 'quadro-inicial',\n url: content.urlInitialFrame!,\n label: 'Quadro Inicial',\n });\n }\n\n if (isValidUrl(content.urlFinalFrame)) {\n downloads.push({\n type: 'quadro-final',\n url: content.urlFinalFrame!,\n label: 'Quadro Final',\n });\n }\n\n if (isValidUrl(content.urlPodcast)) {\n downloads.push({\n type: 'podcast',\n url: content.urlPodcast!,\n label: 'Podcast',\n });\n }\n\n if (isValidUrl(content.urlVideo)) {\n downloads.push({ type: 'video', url: content.urlVideo!, label: 'Vídeo' });\n }\n\n return downloads;\n }, [content, isValidUrl]);\n\n /**\n * Handle download of all available content\n */\n const handleDownload = useCallback(async () => {\n if (disabled || isDownloading) return;\n\n const availableContent = getAvailableContent();\n\n if (availableContent.length === 0) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n // Download each available content sequentially with small delay\n for (let i = 0; i < availableContent.length; i++) {\n const item = availableContent[i];\n\n try {\n onDownloadStart?.(item.type);\n\n const filename = generateFilename(item.type, item.url, lessonTitle);\n await triggerDownload(item.url, filename);\n\n onDownloadComplete?.(item.type);\n\n // Add small delay between downloads to prevent browser blocking\n if (i < availableContent.length - 1) {\n await new Promise((resolve) => setTimeout(resolve, 200));\n }\n } catch (error) {\n // Silent error handling - delegate to callback\n onDownloadError?.(\n item.type,\n error instanceof Error\n ? error\n : new Error(`Falha ao baixar ${item.label}`)\n );\n }\n }\n } finally {\n setIsDownloading(false);\n }\n }, [\n disabled,\n isDownloading,\n getAvailableContent,\n lessonTitle,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n ]);\n\n // Don't render if no content is available\n const hasContent = getAvailableContent().length > 0;\n\n if (!hasContent) {\n return null;\n }\n\n return (\n <div className={cn('flex items-center', className)}>\n <IconButton\n icon={<DownloadSimple size={24} />}\n onClick={handleDownload}\n disabled={disabled || isDownloading}\n aria-label={(() => {\n if (isDownloading) {\n return 'Baixando conteúdo...';\n }\n const contentCount = getAvailableContent().length;\n const suffix = contentCount > 1 ? 's' : '';\n return `Baixar conteúdo da aula (${contentCount} arquivo${suffix})`;\n })()}\n className={cn(\n '!bg-transparent hover:!bg-black/10 transition-colors',\n isDownloading && 'opacity-60 cursor-not-allowed'\n )}\n />\n </div>\n );\n};\n\nexport default DownloadButton;\n"],"mappings":"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","names":["useState","useEffect","useCallback","jsx","useState","jsx","useState","jsx","useEffect","useState","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/VideoPlayer/VideoPlayer.tsx","../../src/utils/utils.ts","../../src/components/IconButton/IconButton.tsx","../../src/components/Text/Text.tsx","../../src/hooks/useMobile.ts","../../src/components/DownloadButton/DownloadButton.tsx"],"sourcesContent":["import {\n useRef,\n useState,\n useEffect,\n useCallback,\n MouseEvent,\n KeyboardEvent,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n Play,\n Pause,\n SpeakerHigh,\n SpeakerSlash,\n ArrowsOutSimple,\n ArrowsInSimple,\n ClosedCaptioning,\n DotsThreeVertical,\n} from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport IconButton from '../IconButton/IconButton';\nimport Text from '../Text/Text';\nimport { useMobile } from '../../hooks/useMobile';\nimport DownloadButton, {\n DownloadContent,\n} from '../DownloadButton/DownloadButton';\n\n// Constants for timeout durations\nconst CONTROLS_HIDE_TIMEOUT = 3000; // 3 seconds for normal control hiding\nconst LEAVE_HIDE_TIMEOUT = 1000; // 1 second when mouse leaves the video area\nconst INIT_DELAY = 100; // ms delay to initialize controls on mount\n\n/**\n * VideoPlayer component props interface\n */\ninterface VideoPlayerProps {\n /** Video source URL */\n src: string;\n /** Video poster/thumbnail URL */\n poster?: string;\n /** Subtitles URL */\n subtitles?: string;\n /** Video title */\n title?: string;\n /** Video subtitle/description */\n subtitle?: string;\n /** Initial playback time in seconds */\n initialTime?: number;\n /** Callback fired when video time updates (seconds) */\n onTimeUpdate?: (seconds: number) => void;\n /** Callback fired with progress percentage (0-100) */\n onProgress?: (progress: number) => void;\n /** Callback fired when video completes (>95% watched) */\n onVideoComplete?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Auto-save progress to localStorage */\n autoSave?: boolean;\n /** localStorage key for saving progress */\n storageKey?: string;\n /** Download content URLs for lesson materials */\n downloadContent?: DownloadContent;\n /** Show download button in header */\n showDownloadButton?: boolean;\n /** Callback fired when download starts */\n onDownloadStart?: (contentType: string) => void;\n /** Callback fired when download completes */\n onDownloadComplete?: (contentType: string) => void;\n /** Callback fired when download fails */\n onDownloadError?: (contentType: string, error: Error) => void;\n}\n\n/**\n * Format seconds to MM:SS display format\n * @param seconds - Time in seconds\n * @returns Formatted time string\n */\nconst formatTime = (seconds: number): string => {\n if (!seconds || Number.isNaN(seconds)) return '0:00';\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Progress bar component props\n */\ninterface ProgressBarProps {\n currentTime: number;\n duration: number;\n progressPercentage: number;\n onSeek: (time: number) => void;\n}\n\n/**\n * Progress bar subcomponent\n */\nconst ProgressBar = ({\n currentTime,\n duration,\n progressPercentage,\n onSeek,\n className = 'px-4 pb-2',\n}: ProgressBarProps & { className?: string }) => (\n <div className={className}>\n <input\n type=\"range\"\n min={0}\n max={duration || 100}\n value={currentTime}\n onChange={(e) => onSeek(Number.parseFloat(e.target.value))}\n 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\"\n aria-label=\"Video progress\"\n style={{\n background: `linear-gradient(to right, var(--color-primary-700) ${progressPercentage}%, var(--color-secondary-300) ${progressPercentage}%)`,\n }}\n />\n </div>\n);\n\n/**\n * Volume controls component props\n */\ninterface VolumeControlsProps {\n volume: number;\n isMuted: boolean;\n onVolumeChange: (volume: number) => void;\n onToggleMute: () => void;\n iconSize?: number;\n showSlider?: boolean;\n}\n\n/**\n * Volume controls subcomponent\n */\nconst VolumeControls = ({\n volume,\n isMuted,\n onVolumeChange,\n onToggleMute,\n iconSize = 24,\n showSlider = true,\n}: VolumeControlsProps) => (\n <div className=\"flex items-center gap-2\">\n <IconButton\n icon={\n isMuted ? (\n <SpeakerSlash size={iconSize} />\n ) : (\n <SpeakerHigh size={iconSize} />\n )\n }\n onClick={onToggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {showSlider && (\n <input\n type=\"range\"\n min={0}\n max={100}\n value={Math.round(volume * 100)}\n onChange={(e) => onVolumeChange(Number.parseInt(e.target.value))}\n className=\"w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Volume control\"\n style={{\n background: `linear-gradient(to right, var(--color-primary-700) ${volume * 100}%, var(--color-secondary-300) ${volume * 100}%)`,\n }}\n />\n )}\n </div>\n);\n\n/**\n * Speed menu component props\n */\ninterface SpeedMenuProps {\n showSpeedMenu: boolean;\n playbackRate: number;\n onToggleMenu: () => void;\n onSpeedChange: (speed: number) => void;\n isFullscreen: boolean;\n iconSize?: number;\n isTinyMobile?: boolean;\n}\n\n/**\n * Speed menu subcomponent\n */\nconst SpeedMenu = ({\n showSpeedMenu,\n playbackRate,\n onToggleMenu,\n onSpeedChange,\n isFullscreen,\n iconSize = 24,\n isTinyMobile = false,\n}: SpeedMenuProps) => {\n const buttonRef = useRef<HTMLButtonElement>(null);\n const speedMenuContainerRef = useRef<HTMLDivElement>(null);\n const speedMenuRef = useRef<HTMLDivElement>(null);\n\n const getMenuPosition = () => {\n if (!buttonRef.current) return { top: 0, left: 0 };\n const rect = buttonRef.current.getBoundingClientRect();\n\n // Adjust positioning for tiny mobile screens\n const menuHeight = isTinyMobile ? 150 : 180;\n const menuWidth = isTinyMobile ? 60 : 80;\n const padding = isTinyMobile ? 4 : 8;\n\n return {\n // Fixed coords are viewport-based — no scroll offsets.\n top: Math.max(padding, rect.top - menuHeight),\n left: Math.max(padding, rect.right - menuWidth),\n };\n };\n\n const position = getMenuPosition();\n\n useEffect(() => {\n const handleClickOutside = (event: Event) => {\n const target = event.target as Node;\n\n // Check if click is outside both the container and the menu\n const isOutsideContainer =\n speedMenuContainerRef.current &&\n !speedMenuContainerRef.current.contains(target);\n const isOutsideMenu =\n speedMenuRef.current && !speedMenuRef.current.contains(target);\n\n // Only close if click is outside both refs (null-safe checks)\n if (isOutsideContainer && isOutsideMenu) {\n onToggleMenu();\n }\n };\n\n if (showSpeedMenu) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [showSpeedMenu, onToggleMenu]);\n\n const menuContent = (\n <div\n ref={speedMenuRef}\n role=\"menu\"\n aria-label=\"Playback speed\"\n className={\n isFullscreen\n ? 'absolute bottom-12 right-0 bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]'\n : 'fixed bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]'\n }\n style={\n isFullscreen\n ? undefined\n : {\n top: `${position.top}px`,\n left: `${position.left}px`,\n }\n }\n >\n {[0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => (\n <button\n key={speed}\n role=\"menuitemradio\"\n aria-checked={playbackRate === speed}\n onClick={() => onSpeedChange(speed)}\n className={`block w-full text-left px-3 py-1 text-sm rounded hover:bg-border-50 transition-colors ${\n playbackRate === speed\n ? 'bg-primary-950 text-secondary-100 font-medium'\n : 'text-text-950'\n }`}\n >\n {speed}x\n </button>\n ))}\n </div>\n );\n\n // SSR-safe portal content\n const portalContent =\n showSpeedMenu &&\n globalThis.window !== undefined &&\n globalThis.document !== undefined &&\n !!globalThis.document?.body\n ? createPortal(menuContent, globalThis.document.body)\n : null;\n\n return (\n <div className=\"relative\" ref={speedMenuContainerRef}>\n <IconButton\n ref={buttonRef}\n icon={<DotsThreeVertical size={iconSize} />}\n onClick={onToggleMenu}\n aria-label=\"Playback speed\"\n aria-haspopup=\"menu\"\n aria-expanded={showSpeedMenu}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n {showSpeedMenu && (isFullscreen ? menuContent : portalContent)}\n </div>\n );\n};\n\n/**\n * Video player component with controls and progress tracking\n * Integrates with backend lesson progress system\n *\n * @param props - VideoPlayer component props\n * @returns Video player element with controls\n */\nconst VideoPlayer = ({\n src,\n poster,\n subtitles,\n title,\n subtitle: subtitleText,\n initialTime = 0,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n className,\n autoSave = true,\n storageKey = 'video-progress',\n downloadContent,\n showDownloadButton = false,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n}: VideoPlayerProps) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const { isUltraSmallMobile, isTinyMobile } = useMobile();\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [isMuted, setIsMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [showControls, setShowControls] = useState(true);\n const [hasCompleted, setHasCompleted] = useState(false);\n const [showCaptions, setShowCaptions] = useState(false);\n const [subtitlesValidation, setSubtitlesValidation] = useState<\n 'idle' | 'validating' | 'valid' | 'invalid'\n >('idle');\n\n // Reset completion flag when changing videos\n useEffect(() => {\n setHasCompleted(false);\n }, [src]);\n const [playbackRate, setPlaybackRate] = useState(1);\n const [showSpeedMenu, setShowSpeedMenu] = useState(false);\n const lastSaveTimeRef = useRef(0);\n const trackRef = useRef<HTMLTrackElement>(null);\n const controlsTimeoutRef = useRef<number | null>(null);\n const lastMousePositionRef = useRef({ x: 0, y: 0 });\n\n /**\n * Check if user is currently interacting with controls\n */\n const isUserInteracting = useCallback(() => {\n // Check if speed menu is open\n if (showSpeedMenu) {\n return true;\n }\n\n // Check if any control element has focus\n const activeElement = document.activeElement;\n const videoContainer = videoRef.current?.parentElement;\n\n if (activeElement && videoContainer?.contains(activeElement)) {\n // Ignore the video element itself - it should not prevent control hiding\n if (activeElement === videoRef.current) {\n return false;\n }\n\n // Check if focused element is a control (button, input, etc.)\n const isControl = activeElement.matches('button, input, [tabindex]');\n if (isControl) {\n return true;\n }\n }\n\n return false;\n }, [showSpeedMenu]);\n\n /**\n * Clear controls timeout\n */\n const clearControlsTimeout = useCallback(() => {\n if (controlsTimeoutRef.current) {\n clearTimeout(controlsTimeoutRef.current);\n controlsTimeoutRef.current = null;\n }\n }, []);\n\n /**\n * Show controls and set auto-hide timer\n */\n const showControlsWithTimer = useCallback(() => {\n setShowControls(true);\n clearControlsTimeout();\n\n // In fullscreen mode, only hide if video is playing\n if (isFullscreen) {\n if (isPlaying) {\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, CONTROLS_HIDE_TIMEOUT) as unknown as number;\n }\n } else {\n // In normal mode, always set a timer to hide controls\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, CONTROLS_HIDE_TIMEOUT) as unknown as number;\n }\n }, [isFullscreen, isPlaying, clearControlsTimeout]);\n\n /**\n * Handle mouse move with position detection\n */\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n const currentX = event.clientX;\n const currentY = event.clientY;\n const lastPos = lastMousePositionRef.current;\n\n // Check if mouse actually moved (minimum 5px threshold)\n const hasMoved =\n Math.abs(currentX - lastPos.x) > 5 ||\n Math.abs(currentY - lastPos.y) > 5;\n\n if (hasMoved) {\n lastMousePositionRef.current = { x: currentX, y: currentY };\n showControlsWithTimer();\n }\n },\n [showControlsWithTimer]\n );\n\n /**\n * Handle mouse enter to show controls with appropriate timer logic\n */\n const handleMouseEnter = useCallback(() => {\n showControlsWithTimer();\n }, [showControlsWithTimer]);\n\n /**\n * Handle mouse leave to hide controls faster\n */\n const handleMouseLeave = useCallback(() => {\n const userInteracting = isUserInteracting();\n clearControlsTimeout();\n\n // Hide controls when mouse leaves, except when in fullscreen or user is interacting\n if (!isFullscreen && !userInteracting) {\n // Use shorter timeout when mouse leaves\n controlsTimeoutRef.current = globalThis.setTimeout(() => {\n setShowControls(false);\n }, LEAVE_HIDE_TIMEOUT) as unknown as number;\n }\n }, [isFullscreen, clearControlsTimeout, isUserInteracting]);\n\n /**\n * Initialize video element properties\n */\n useEffect(() => {\n // Set initial volume\n if (videoRef.current) {\n videoRef.current.volume = volume;\n videoRef.current.muted = isMuted;\n }\n }, [volume, isMuted]);\n\n /**\n * Synchronize isPlaying state with media events\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const onPlay = () => setIsPlaying(true);\n const onPause = () => setIsPlaying(false);\n const onEnded = () => setIsPlaying(false);\n\n video.addEventListener('play', onPlay);\n video.addEventListener('pause', onPause);\n video.addEventListener('ended', onEnded);\n\n return () => {\n video.removeEventListener('play', onPlay);\n video.removeEventListener('pause', onPause);\n video.removeEventListener('ended', onEnded);\n };\n }, []);\n\n /**\n * Set iOS/Safari inline playback attributes imperatively\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n // Ensure inline playback on iOS/Safari\n video.setAttribute('playsinline', '');\n video.setAttribute('webkit-playsinline', '');\n }, []);\n\n /**\n * Handle controls auto-hide when play state changes\n */\n useEffect(() => {\n if (isPlaying) {\n // Start timer when video starts playing\n showControlsWithTimer();\n } else {\n // Keep controls visible when paused only in fullscreen\n clearControlsTimeout();\n if (isFullscreen) {\n setShowControls(true);\n } else {\n // In normal mode (not fullscreen), initialize timer even when paused\n // This ensures controls will hide properly from the start\n showControlsWithTimer();\n }\n }\n }, [isPlaying, isFullscreen, showControlsWithTimer, clearControlsTimeout]);\n\n /**\n * Handle fullscreen state changes from browser events\n */\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const handleFullscreenChange = () => {\n const isCurrentlyFullscreen = !!document.fullscreenElement;\n setIsFullscreen(isCurrentlyFullscreen);\n\n // Show controls when entering fullscreen, hide after timeout if playing\n if (isCurrentlyFullscreen) {\n showControlsWithTimer();\n }\n };\n\n // Safari iOS-specific fullscreen event handlers\n const handleWebkitBeginFullscreen = () => {\n setIsFullscreen(true);\n showControlsWithTimer();\n };\n\n const handleWebkitEndFullscreen = () => {\n setIsFullscreen(false);\n };\n\n // Standard fullscreen events\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n // Safari iOS fullscreen events\n video.addEventListener(\n 'webkitbeginfullscreen',\n handleWebkitBeginFullscreen\n );\n video.addEventListener('webkitendfullscreen', handleWebkitEndFullscreen);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n video.removeEventListener(\n 'webkitbeginfullscreen',\n handleWebkitBeginFullscreen\n );\n video.removeEventListener(\n 'webkitendfullscreen',\n handleWebkitEndFullscreen\n );\n };\n }, [showControlsWithTimer]);\n\n /**\n * Initialize controls behavior on component mount\n * This ensures controls work correctly from the first load\n */\n useEffect(() => {\n const init = () => {\n if (!isFullscreen) {\n showControlsWithTimer();\n }\n };\n // Prefer rAF to avoid arbitrary timing if available; fall back to INIT_DELAY.\n let raf1 = 0,\n raf2 = 0,\n tid: number | undefined;\n if (globalThis.requestAnimationFrame === undefined) {\n tid = globalThis.setTimeout(init, INIT_DELAY) as unknown as number;\n return () => {\n if (tid) clearTimeout(tid);\n };\n } else {\n raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(init);\n });\n return () => {\n cancelAnimationFrame(raf1);\n cancelAnimationFrame(raf2);\n };\n }\n }, []); // Run only once on mount\n\n /**\n * Get initial time from props or localStorage\n */\n const getInitialTime = useCallback((): number | undefined => {\n if (!autoSave || !storageKey) {\n return Number.isFinite(initialTime) && initialTime >= 0\n ? initialTime\n : undefined;\n }\n\n const saved = Number(\n localStorage.getItem(`${storageKey}-${src}`) || Number.NaN\n );\n const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;\n const hasValidSaved = Number.isFinite(saved) && saved >= 0;\n\n if (hasValidInitial) return initialTime;\n if (hasValidSaved) return saved;\n return undefined;\n }, [autoSave, storageKey, src, initialTime]);\n\n /**\n * Load saved progress from localStorage\n */\n useEffect(() => {\n const start = getInitialTime();\n if (start !== undefined && videoRef.current) {\n videoRef.current.currentTime = start;\n setCurrentTime(start);\n }\n }, [getInitialTime]);\n\n /**\n * Save progress to localStorage periodically\n */\n const saveProgress = useCallback(\n (time: number) => {\n if (!autoSave || !storageKey) return;\n\n const now = Date.now();\n if (now - lastSaveTimeRef.current > 5000) {\n localStorage.setItem(`${storageKey}-${src}`, time.toString());\n lastSaveTimeRef.current = now;\n }\n },\n [autoSave, storageKey, src]\n );\n\n /**\n * Handle play/pause toggle\n */\n const togglePlayPause = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n\n if (!video.paused) {\n video.pause();\n return;\n }\n\n try {\n await video.play();\n } catch {\n // Playback prevented (e.g., autoplay policy); keep state unchanged.\n }\n }, []);\n\n /**\n * Handle volume change\n */\n const handleVolumeChange = useCallback(\n (newVolume: number) => {\n const video = videoRef.current;\n if (!video) return;\n\n const volumeValue = newVolume / 100; // Convert 0-100 to 0-1\n video.volume = volumeValue;\n setVolume(volumeValue);\n\n // Auto mute/unmute based on volume\n const shouldMute = volumeValue === 0;\n const shouldUnmute = volumeValue > 0 && isMuted;\n\n if (shouldMute) {\n video.muted = true;\n setIsMuted(true);\n } else if (shouldUnmute) {\n video.muted = false;\n setIsMuted(false);\n }\n },\n [isMuted]\n );\n\n /**\n * Handle mute toggle\n */\n const toggleMute = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n\n if (isMuted) {\n // Unmute: restore volume or set to 50% if it was 0\n const restoreVolume = volume > 0 ? volume : 0.5;\n video.volume = restoreVolume;\n video.muted = false;\n setVolume(restoreVolume);\n setIsMuted(false);\n } else {\n // Mute: set volume to 0 and mute\n video.muted = true;\n setIsMuted(true);\n }\n }, [isMuted, volume]);\n\n /**\n * Handle video seek\n */\n const handleSeek = useCallback((newTime: number) => {\n const video = videoRef.current;\n if (video) {\n video.currentTime = newTime;\n }\n }, []);\n\n /**\n * Detect if running on Safari iOS\n */\n const isSafariIOS = useCallback((): boolean => {\n const ua = navigator.userAgent;\n const isIOS = /iPad|iPhone|iPod/.test(ua);\n const isWebKit = /WebKit/.test(ua);\n const isNotChrome = !/CriOS|Chrome/.test(ua);\n return isIOS && isWebKit && isNotChrome;\n }, []);\n\n /**\n * Handle fullscreen toggle\n */\n const toggleFullscreen = useCallback(() => {\n const video = videoRef.current;\n const container = video?.parentElement;\n if (!video || !container) return;\n\n // Safari iOS requires using webkitEnterFullscreen on video element\n if (isSafariIOS()) {\n // Type assertion for Safari-specific API\n const videoElement = video as HTMLVideoElement & {\n webkitEnterFullscreen?: () => void;\n webkitExitFullscreen?: () => void;\n webkitDisplayingFullscreen?: boolean;\n };\n\n if (!isFullscreen && videoElement.webkitEnterFullscreen) {\n videoElement.webkitEnterFullscreen();\n } else if (isFullscreen && videoElement.webkitExitFullscreen) {\n videoElement.webkitExitFullscreen();\n }\n } else if (!isFullscreen && container.requestFullscreen) {\n // Standard fullscreen API for other browsers\n container.requestFullscreen();\n } else if (isFullscreen && document.exitFullscreen) {\n document.exitFullscreen();\n }\n }, [isFullscreen, isSafariIOS]);\n\n /**\n * Handle playback speed change\n */\n const handleSpeedChange = useCallback((speed: number) => {\n if (videoRef.current) {\n videoRef.current.playbackRate = speed;\n setPlaybackRate(speed);\n setShowSpeedMenu(false);\n }\n }, []);\n\n /**\n * Toggle speed menu visibility\n */\n const toggleSpeedMenu = useCallback(() => {\n setShowSpeedMenu(!showSpeedMenu);\n }, [showSpeedMenu]);\n\n /**\n * Toggle captions visibility\n */\n const toggleCaptions = useCallback(() => {\n if (\n !trackRef.current?.track ||\n !subtitles ||\n subtitlesValidation !== 'valid'\n )\n return;\n\n const newShowCaptions = !showCaptions;\n setShowCaptions(newShowCaptions);\n\n // Control track mode programmatically - we already validated subtitles above\n trackRef.current.track.mode = newShowCaptions ? 'showing' : 'hidden';\n }, [showCaptions, subtitles, subtitlesValidation]);\n\n /**\n * Check video completion and fire callback\n */\n const checkVideoCompletion = useCallback(\n (progressPercent: number) => {\n if (progressPercent >= 95 && !hasCompleted) {\n setHasCompleted(true);\n onVideoComplete?.();\n }\n },\n [hasCompleted, onVideoComplete]\n );\n\n /**\n * Handle time update\n */\n const handleTimeUpdate = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const current = video.currentTime;\n setCurrentTime(current);\n\n // Save progress periodically\n saveProgress(current);\n\n // Fire callbacks\n onTimeUpdate?.(current);\n\n if (duration > 0) {\n const progressPercent = (current / duration) * 100;\n onProgress?.(progressPercent);\n checkVideoCompletion(progressPercent);\n }\n }, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]);\n\n /**\n * Handle loaded metadata\n */\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current) {\n setDuration(videoRef.current.duration);\n }\n }, []);\n\n /**\n * Validate subtitles URL before showing the button\n */\n useEffect(() => {\n const controller = new AbortController();\n\n const validateSubtitles = async () => {\n // If no subtitles, mark as idle\n if (!subtitles) {\n setSubtitlesValidation('idle');\n return;\n }\n\n // Start validation\n setSubtitlesValidation('validating');\n\n try {\n // Check if it's a data URL (inline VTT)\n if (subtitles.startsWith('data:')) {\n setSubtitlesValidation('valid');\n return;\n }\n\n // Fetch the subtitles file to validate it\n const response = await fetch(subtitles, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n if (response.ok) {\n // Optionally check content type\n const contentType = response.headers.get('content-type');\n const isValidType =\n !contentType ||\n contentType.includes('text/vtt') ||\n contentType.includes('text/plain') ||\n contentType.includes('application/octet-stream');\n\n if (isValidType) {\n setSubtitlesValidation('valid');\n } else {\n setSubtitlesValidation('invalid');\n console.warn(\n `Subtitles URL has invalid content type: ${contentType}`\n );\n }\n } else {\n setSubtitlesValidation('invalid');\n console.warn(\n `Subtitles URL returned status: ${response.status} ${response.statusText}`\n );\n }\n } catch (error) {\n // Ignore AbortError - it's expected when cleaning up\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n console.warn('Subtitles URL validation failed:', error);\n setSubtitlesValidation('invalid');\n }\n };\n\n validateSubtitles();\n\n // Cleanup: abort ongoing fetch to prevent stale updates\n return () => {\n controller.abort();\n };\n }, [subtitles]);\n\n /**\n * Initialize track mode when track is available\n */\n useEffect(() => {\n if (trackRef.current?.track) {\n // Set initial mode based on showCaptions state and subtitle availability\n trackRef.current.track.mode =\n showCaptions && subtitles && subtitlesValidation === 'valid'\n ? 'showing'\n : 'hidden';\n }\n }, [subtitles, showCaptions, subtitlesValidation]);\n\n /**\n * Handle visibility change and blur to pause video when losing focus\n */\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.hidden && isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n const handleBlur = () => {\n if (isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n globalThis.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n globalThis.removeEventListener('blur', handleBlur);\n // Clean up timers on unmount\n clearControlsTimeout();\n };\n }, [isPlaying, clearControlsTimeout]);\n\n const progressPercentage = duration > 0 ? (currentTime / duration) * 100 : 0;\n\n /**\n * Get responsive icon size based on screen size\n */\n const getIconSize = useCallback(() => {\n if (isTinyMobile) return 18;\n if (isUltraSmallMobile) return 20;\n return 24;\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive padding classes for controls\n */\n const getControlsPadding = useCallback(() => {\n if (isTinyMobile) return 'px-2 pb-2 pt-1';\n if (isUltraSmallMobile) return 'px-3 pb-3 pt-1';\n return 'px-4 pb-4';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive gap classes for controls\n */\n const getControlsGap = useCallback(() => {\n if (isTinyMobile) return 'gap-1';\n if (isUltraSmallMobile) return 'gap-2';\n return 'gap-4';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive padding for progress bar\n */\n const getProgressBarPadding = useCallback(() => {\n if (isTinyMobile) return 'px-2 pb-1';\n if (isUltraSmallMobile) return 'px-3 pb-1';\n return 'px-4 pb-2';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Get responsive positioning classes for center play button\n */\n const getCenterPlayButtonPosition = useCallback(() => {\n if (isTinyMobile) return 'items-center justify-center -translate-y-12';\n if (isUltraSmallMobile) return 'items-center justify-center -translate-y-8';\n return 'items-center justify-center';\n }, [isTinyMobile, isUltraSmallMobile]);\n\n /**\n * Calculate top controls opacity based on state\n */\n const getTopControlsOpacity = useCallback(() => {\n return showControls ? 'opacity-100' : 'opacity-0';\n }, [showControls]);\n\n /**\n * Calculate bottom controls opacity based on state\n */\n const getBottomControlsOpacity = useCallback(() => {\n return showControls ? 'opacity-100' : 'opacity-0';\n }, [showControls]);\n\n /**\n * Seek video backward\n */\n const seekBackward = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.currentTime -= 10;\n }\n }, []);\n\n /**\n * Seek video forward\n */\n const seekForward = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.currentTime += 10;\n }\n }, []);\n\n /**\n * Increase volume\n */\n const increaseVolume = useCallback(() => {\n handleVolumeChange(Math.min(100, volume * 100 + 10));\n }, [handleVolumeChange, volume]);\n\n /**\n * Decrease volume\n */\n const decreaseVolume = useCallback(() => {\n handleVolumeChange(Math.max(0, volume * 100 - 10));\n }, [handleVolumeChange, volume]);\n\n /**\n * Handle video element keyboard events\n */\n const handleVideoKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!e.key) return;\n\n // Prevent bubbling to parent handlers to avoid double toggles\n e.stopPropagation();\n showControlsWithTimer();\n\n // Map of key handlers for better maintainability and reduced complexity\n const keyHandlers: Record<string, () => void | Promise<void>> = {\n ' ': togglePlayPause,\n Enter: togglePlayPause,\n ArrowLeft: seekBackward,\n ArrowRight: seekForward,\n ArrowUp: increaseVolume,\n ArrowDown: decreaseVolume,\n m: toggleMute,\n M: toggleMute,\n f: toggleFullscreen,\n F: toggleFullscreen,\n };\n\n const handler = keyHandlers[e.key];\n if (handler) {\n e.preventDefault();\n handler();\n }\n },\n [\n showControlsWithTimer,\n togglePlayPause,\n seekBackward,\n seekForward,\n increaseVolume,\n decreaseVolume,\n toggleMute,\n toggleFullscreen,\n ]\n );\n\n const groupedSubTitleValid = subtitles && subtitlesValidation === 'valid';\n\n return (\n <div className={cn('flex flex-col', className)}>\n {/* Integrated Header */}\n {(title || subtitleText) && (\n <div className=\"bg-subject-1 px-8 py-4 flex items-end justify-between min-h-20\">\n <div className=\"flex flex-col gap-1\">\n {title && (\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n color=\"text-text-900\"\n className=\"leading-5 tracking-wide\"\n >\n {title}\n </Text>\n )}\n {subtitleText && (\n <Text\n as=\"p\"\n size=\"sm\"\n weight=\"normal\"\n color=\"text-text-600\"\n className=\"leading-5\"\n >\n {subtitleText}\n </Text>\n )}\n </div>\n\n {/* Download Button */}\n {showDownloadButton && downloadContent && (\n <DownloadButton\n content={downloadContent}\n lessonTitle={title}\n onDownloadStart={onDownloadStart}\n onDownloadComplete={onDownloadComplete}\n onDownloadError={onDownloadError}\n className=\"flex-shrink-0\"\n />\n )}\n </div>\n )}\n\n {/* Video Container */}\n <section\n className={cn(\n 'relative w-full bg-background overflow-hidden group',\n 'rounded-b-xl',\n // Hide cursor when controls are hidden and video is playing\n isPlaying && !showControls\n ? 'cursor-none group-hover:cursor-default'\n : 'cursor-default'\n )}\n aria-label={title ? `Video player: ${title}` : 'Video player'}\n onMouseMove={handleMouseMove}\n onMouseEnter={handleMouseEnter}\n onTouchStart={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {/* Video Element */}\n <video\n ref={videoRef}\n src={src}\n poster={poster}\n className=\"w-full h-full object-contain analytica-video\"\n controlsList=\"nodownload\"\n playsInline\n onTimeUpdate={handleTimeUpdate}\n onLoadedMetadata={handleLoadedMetadata}\n onClick={togglePlayPause}\n onKeyDown={handleVideoKeyDown}\n tabIndex={0}\n aria-label={title ? `Video: ${title}` : 'Video player'}\n >\n <track\n ref={trackRef}\n kind=\"captions\"\n src={\n groupedSubTitleValid\n ? subtitles\n : 'data:text/vtt;charset=utf-8,WEBVTT'\n }\n srcLang=\"pt-br\"\n label={\n groupedSubTitleValid\n ? 'Legendas em Português'\n : 'Sem legendas disponíveis'\n }\n default={false}\n />\n </video>\n\n {/* Center Play Button */}\n {!isPlaying && (\n <div\n className={cn(\n 'absolute inset-0 flex bg-black/30 transition-opacity',\n getCenterPlayButtonPosition()\n )}\n >\n <IconButton\n icon={<Play size={32} weight=\"regular\" className=\"ml-1\" />}\n onClick={togglePlayPause}\n aria-label=\"Play video\"\n className=\"!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200\"\n />\n </div>\n )}\n\n {/* Top Controls */}\n <div\n className={cn(\n 'absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity',\n getTopControlsOpacity()\n )}\n >\n <div className=\"flex justify-start\">\n <IconButton\n icon={\n isFullscreen ? (\n <ArrowsInSimple size={24} />\n ) : (\n <ArrowsOutSimple size={24} />\n )\n }\n onClick={toggleFullscreen}\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n </div>\n </div>\n\n {/* Bottom Controls */}\n <div\n className={cn(\n 'absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity',\n getBottomControlsOpacity()\n )}\n >\n {/* Progress Bar */}\n <ProgressBar\n currentTime={currentTime}\n duration={duration}\n progressPercentage={progressPercentage}\n onSeek={handleSeek}\n className={getProgressBarPadding()}\n />\n\n {/* Control Buttons */}\n <div\n className={cn(\n 'flex items-center justify-between',\n getControlsPadding()\n )}\n >\n {/* Left Controls */}\n <div className={cn('flex items-center', getControlsGap())}>\n {/* Play/Pause */}\n <IconButton\n icon={\n isPlaying ? (\n <Pause size={getIconSize()} />\n ) : (\n <Play size={getIconSize()} />\n )\n }\n onClick={togglePlayPause}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume */}\n <VolumeControls\n volume={volume}\n isMuted={isMuted}\n onVolumeChange={handleVolumeChange}\n onToggleMute={toggleMute}\n iconSize={getIconSize()}\n showSlider={!isUltraSmallMobile}\n />\n\n {/* Captions - Only show after validation is complete and valid */}\n {groupedSubTitleValid && (\n <IconButton\n icon={<ClosedCaptioning size={getIconSize()} />}\n onClick={toggleCaptions}\n aria-label={showCaptions ? 'Hide captions' : 'Show captions'}\n className={cn(\n '!bg-transparent hover:!bg-white/20',\n showCaptions ? '!text-primary-400' : '!text-white'\n )}\n />\n )}\n\n {/* Time Display */}\n <Text size=\"sm\" weight=\"medium\" color=\"text-white\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </Text>\n </div>\n\n {/* Right Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Speed Control */}\n <SpeedMenu\n showSpeedMenu={showSpeedMenu}\n playbackRate={playbackRate}\n onToggleMenu={toggleSpeedMenu}\n onSpeedChange={handleSpeedChange}\n iconSize={getIconSize()}\n isTinyMobile={isTinyMobile}\n isFullscreen={isFullscreen}\n />\n </div>\n </div>\n </div>\n </section>\n </div>\n );\n};\n\nexport default VideoPlayer;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { useState, useEffect } from 'react';\n\n// Mobile width in pixels\nconst MOBILE_WIDTH = 500;\n// Tablet width in pixels\nconst TABLET_WIDTH = 931;\n// Video responsive breakpoints\nconst SMALL_MOBILE_WIDTH = 425;\nconst EXTRA_SMALL_MOBILE_WIDTH = 375;\nconst ULTRA_SMALL_MOBILE_WIDTH = 375; // For video controls\nconst TINY_MOBILE_WIDTH = 320;\n// Default desktop width for SSR\nconst DEFAULT_WIDTH = 1200;\n\n/**\n * Device type based on screen width\n */\nexport type DeviceType = 'responsive' | 'desktop';\n\n/**\n * Gets the window width safely (SSR compatible)\n * @returns {number} window width or default value for SSR\n */\nconst getWindowWidth = (): number => {\n if (typeof window === 'undefined') {\n return DEFAULT_WIDTH;\n }\n return window.innerWidth;\n};\n\n/**\n * Gets the current device type based on screen width\n * @returns {DeviceType} 'responsive' for mobile/tablet (width < 931px), 'desktop' for larger screens\n */\nexport const getDeviceType = (): DeviceType => {\n const width = getWindowWidth();\n return width < TABLET_WIDTH ? 'responsive' : 'desktop';\n};\n\n/**\n * Hook to detect screen size and get responsive classes\n * @returns object with isMobile, isTablet, responsive class functions and getDeviceType\n */\nexport const useMobile = () => {\n const [isMobile, setIsMobile] = useState(false);\n const [isTablet, setIsTablet] = useState(false);\n const [isSmallMobile, setIsSmallMobile] = useState(false);\n const [isExtraSmallMobile, setIsExtraSmallMobile] = useState(false);\n const [isUltraSmallMobile, setIsUltraSmallMobile] = useState(false);\n const [isTinyMobile, setIsTinyMobile] = useState(false);\n\n useEffect(() => {\n const checkScreenSize = () => {\n const width = getWindowWidth();\n setIsMobile(width < MOBILE_WIDTH);\n setIsTablet(width < TABLET_WIDTH);\n setIsSmallMobile(width < SMALL_MOBILE_WIDTH);\n setIsExtraSmallMobile(width < EXTRA_SMALL_MOBILE_WIDTH);\n setIsUltraSmallMobile(width < ULTRA_SMALL_MOBILE_WIDTH);\n setIsTinyMobile(width < TINY_MOBILE_WIDTH);\n };\n\n checkScreenSize();\n\n window.addEventListener('resize', checkScreenSize);\n\n return () => window.removeEventListener('resize', checkScreenSize);\n }, []);\n\n /**\n * Get responsive classes for the form container\n * @returns className string for form container based on screen size\n */\n const getFormContainerClasses = (): string => {\n if (isMobile) {\n return 'w-full px-4';\n }\n if (isTablet) {\n return 'w-full px-6';\n }\n return 'w-full max-w-[992px] mx-auto px-0';\n };\n\n /**\n * Get mobile-specific classes for the header\n * @returns className string for mobile header layout\n */\n const getMobileHeaderClasses = (): string => {\n return 'flex flex-col items-start gap-4 mb-6';\n };\n\n /**\n * Get desktop-specific classes for the header\n * @returns className string for desktop header layout\n */\n const getDesktopHeaderClasses = (): string => {\n return 'flex flex-row justify-between items-center gap-6 mb-8';\n };\n\n /**\n * Get responsive classes for the header\n * @returns className string for header based on screen size\n */\n const getHeaderClasses = (): string => {\n return isMobile ? getMobileHeaderClasses() : getDesktopHeaderClasses();\n };\n\n /**\n * Get responsive classes for video container\n * @returns className string for video container aspect ratio based on screen size\n */\n const getVideoContainerClasses = (): string => {\n if (isTinyMobile) return 'aspect-square'; // 1:1 ratio for screens < 320px\n if (isExtraSmallMobile) return 'aspect-[4/3]'; // 4:3 ratio for screens < 375px\n if (isSmallMobile) return 'aspect-[16/12]'; // 16:12 ratio for screens < 425px\n return 'aspect-video'; // 16:9 ratio for larger screens\n };\n\n return {\n isMobile,\n isTablet,\n isSmallMobile,\n isExtraSmallMobile,\n isUltraSmallMobile,\n isTinyMobile,\n getFormContainerClasses,\n getHeaderClasses,\n getMobileHeaderClasses,\n getDesktopHeaderClasses,\n getVideoContainerClasses,\n getDeviceType,\n };\n};\n","import { useCallback, useState } from 'react';\nimport { DownloadSimple } from 'phosphor-react';\nimport IconButton from '../IconButton/IconButton';\nimport { cn } from '../../utils/utils';\n\n/**\n * Download content interface for lesson materials\n */\nexport interface DownloadContent {\n /** Document URL (PDF) */\n urlDoc?: string;\n /** Initial frame image URL */\n urlInitialFrame?: string;\n /** Final frame image URL */\n urlFinalFrame?: string;\n /** Podcast audio URL */\n urlPodcast?: string;\n /** Video URL */\n urlVideo?: string;\n}\n\n/**\n * Props for DownloadButton component\n */\nexport interface DownloadButtonProps {\n /** Content URLs to download */\n content: DownloadContent;\n /** Additional CSS classes */\n className?: string;\n /** Callback fired when download starts */\n onDownloadStart?: (contentType: string) => void;\n /** Callback fired when download completes */\n onDownloadComplete?: (contentType: string) => void;\n /** Callback fired when download fails */\n onDownloadError?: (contentType: string, error: Error) => void;\n /** Lesson title for download file naming */\n lessonTitle?: string;\n /** Whether the button is disabled */\n disabled?: boolean;\n}\n\n/**\n * Get MIME type based on file extension\n * @param url - URL to extract extension from\n * @returns MIME type string\n */\nconst getMimeType = (url: string): string => {\n const extension = getFileExtension(url);\n const mimeTypes: Record<string, string> = {\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n mp3: 'audio/mpeg',\n mp4: 'video/mp4',\n vtt: 'text/vtt',\n };\n return mimeTypes[extension] || 'application/octet-stream';\n};\n\n/**\n * Download file via fetch and blob to ensure proper download behavior\n * @param url - URL to download\n * @param filename - Filename for the download\n * @returns Promise<void>\n */\nconst triggerDownload = async (\n url: string,\n filename: string\n): Promise<void> => {\n try {\n // Fetch the file as blob\n const response = await fetch(url, {\n mode: 'cors',\n credentials: 'same-origin',\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch file: ${response.status} ${response.statusText}`\n );\n }\n\n const blob = await response.blob();\n const mimeType = getMimeType(url);\n\n // Create a blob with the correct MIME type\n const typedBlob = new Blob([blob], { type: mimeType });\n\n // Create object URL\n const blobUrl = URL.createObjectURL(typedBlob);\n\n // Create download link\n const link = document.createElement('a');\n link.href = blobUrl;\n link.download = filename;\n link.rel = 'noopener noreferrer';\n\n // Add to DOM, click, and remove\n document.body.appendChild(link);\n link.click();\n link.remove();\n\n // Clean up object URL after a short delay\n setTimeout(() => {\n URL.revokeObjectURL(blobUrl);\n }, 1000);\n } catch (error) {\n // Fallback to direct link if fetch fails\n console.warn('Fetch download failed, falling back to direct link:', error);\n\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.rel = 'noopener noreferrer';\n link.target = '_blank'; // Open in new tab as fallback\n\n document.body.appendChild(link);\n link.click();\n link.remove();\n }\n};\n\n/**\n * Get file extension from URL\n * @param url - URL to extract extension from\n * @returns File extension or default\n */\nconst getFileExtension = (url: string): string => {\n try {\n const u = new URL(url, globalThis.location?.origin || 'http://localhost');\n url = u.pathname;\n } catch {\n // keep original url (likely relative)\n }\n const path = url.split(/[?#]/)[0];\n const dot = path.lastIndexOf('.');\n return dot > -1 ? path.slice(dot + 1).toLowerCase() : 'file';\n};\n\n/**\n * Generate filename for download\n * @param contentType - Type of content being downloaded\n * @param lessonTitle - Title of the lesson\n * @param url - URL to get extension from\n * @returns Generated filename\n */\nconst generateFilename = (\n contentType: string,\n url: string,\n lessonTitle: string = 'aula'\n): string => {\n const sanitizedTitle = lessonTitle\n .toLowerCase()\n .replaceAll(/[^a-z0-9\\s]/g, '')\n .replaceAll(/\\s+/g, '-')\n .substring(0, 50);\n\n const extension = getFileExtension(url);\n return `${sanitizedTitle}-${contentType}.${extension}`;\n};\n\n/**\n * DownloadButton component for downloading lesson content\n * Provides a single button that downloads all available content for a lesson\n *\n * @param props - DownloadButton component props\n * @returns Download button element\n */\nconst DownloadButton = ({\n content,\n className,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n lessonTitle = 'aula',\n disabled = false,\n}: DownloadButtonProps) => {\n const [isDownloading, setIsDownloading] = useState(false);\n\n /**\n * Check if URL is valid and not empty\n * @param url - URL to validate\n * @returns Whether URL is valid\n */\n const isValidUrl = useCallback((url?: string): boolean => {\n return Boolean(\n url && url.trim() !== '' && url !== 'undefined' && url !== 'null'\n );\n }, []);\n\n /**\n * Get available download content\n * @returns Array of available download items\n */\n const getAvailableContent = useCallback(() => {\n const downloads: Array<{ type: string; url: string; label: string }> = [];\n\n if (isValidUrl(content.urlDoc)) {\n downloads.push({\n type: 'documento',\n url: content.urlDoc!,\n label: 'Documento',\n });\n }\n\n if (isValidUrl(content.urlInitialFrame)) {\n downloads.push({\n type: 'quadro-inicial',\n url: content.urlInitialFrame!,\n label: 'Quadro Inicial',\n });\n }\n\n if (isValidUrl(content.urlFinalFrame)) {\n downloads.push({\n type: 'quadro-final',\n url: content.urlFinalFrame!,\n label: 'Quadro Final',\n });\n }\n\n if (isValidUrl(content.urlPodcast)) {\n downloads.push({\n type: 'podcast',\n url: content.urlPodcast!,\n label: 'Podcast',\n });\n }\n\n if (isValidUrl(content.urlVideo)) {\n downloads.push({ type: 'video', url: content.urlVideo!, label: 'Vídeo' });\n }\n\n return downloads;\n }, [content, isValidUrl]);\n\n /**\n * Handle download of all available content\n */\n const handleDownload = useCallback(async () => {\n if (disabled || isDownloading) return;\n\n const availableContent = getAvailableContent();\n\n if (availableContent.length === 0) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n // Download each available content sequentially with small delay\n for (let i = 0; i < availableContent.length; i++) {\n const item = availableContent[i];\n\n try {\n onDownloadStart?.(item.type);\n\n const filename = generateFilename(item.type, item.url, lessonTitle);\n await triggerDownload(item.url, filename);\n\n onDownloadComplete?.(item.type);\n\n // Add small delay between downloads to prevent browser blocking\n if (i < availableContent.length - 1) {\n await new Promise((resolve) => setTimeout(resolve, 200));\n }\n } catch (error) {\n // Silent error handling - delegate to callback\n onDownloadError?.(\n item.type,\n error instanceof Error\n ? error\n : new Error(`Falha ao baixar ${item.label}`)\n );\n }\n }\n } finally {\n setIsDownloading(false);\n }\n }, [\n disabled,\n isDownloading,\n getAvailableContent,\n lessonTitle,\n onDownloadStart,\n onDownloadComplete,\n onDownloadError,\n ]);\n\n // Don't render if no content is available\n const hasContent = getAvailableContent().length > 0;\n\n if (!hasContent) {\n return null;\n }\n\n return (\n <div className={cn('flex items-center', className)}>\n <IconButton\n icon={<DownloadSimple size={24} />}\n onClick={handleDownload}\n disabled={disabled || isDownloading}\n aria-label={(() => {\n if (isDownloading) {\n return 'Baixando conteúdo...';\n }\n const contentCount = getAvailableContent().length;\n const suffix = contentCount > 1 ? 's' : '';\n return `Baixar conteúdo da aula (${contentCount} arquivo${suffix})`;\n })()}\n className={cn(\n '!bg-transparent hover:!bg-black/10 transition-colors',\n isDownloading && 'opacity-60 cursor-not-allowed'\n )}\n />\n </div>\n );\n};\n\nexport default DownloadButton;\n"],"mappings":"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","names":["useState","useEffect","useCallback","jsx","useState","jsx","useState","jsx","useEffect","useState","useCallback"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { ArrowsOut } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-background border border-border-50 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"cursor-pointer absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <ArrowsOut\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { ArrowsOut } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-background border border-border-50 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"cursor-pointer absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <ArrowsOut\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { ArrowsOut } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-background border border-border-50 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"cursor-pointer absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <ArrowsOut\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { ArrowsOut } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-background border border-border-50 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"cursor-pointer absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <ArrowsOut\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n} from './activityFilters';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -134,7 +134,8 @@ export type { ActivityPreviewProps, PreviewQuestion, } from './components/Activi
|
|
|
134
134
|
export { QuestionsPdfGenerator, QuestionsPdfContent, useQuestionsPdfPrint, } from './components/QuestionsPdfGenerator';
|
|
135
135
|
export type { QuestionsPdfGeneratorProps } from './components/QuestionsPdfGenerator';
|
|
136
136
|
export { cn, getSubjectColorWithOpacity, syncDropdownState, getSelectedIdsFromCategories, toggleArrayItem, toggleSingleValue, } from './utils/utils';
|
|
137
|
-
export { STUDENT_ACTIVITY_STATUS, ACTIVITY_AVAILABILITY,
|
|
137
|
+
export { STUDENT_ACTIVITY_STATUS, ACTIVITY_AVAILABILITY, } from './types/activityDetails';
|
|
138
|
+
export { getStatusBadgeConfig, formatTimeSpent, formatQuestionNumbers, formatDateToBrazilian, } from './utils/utils';
|
|
138
139
|
export type { StudentActivityStatus, ActivityAvailability, ActivityStudentData, Pagination as ActivityDetailsPagination, GeneralStats, QuestionStats, ActivityMetadata, ActivityDetailsData, ActivityDetailsQueryParams, ActivityStudentTableItem, StatusBadgeConfig, } from './types/activityDetails';
|
|
139
140
|
export { ActivityDetails } from './components/ActivityDetails/ActivityDetails';
|
|
140
141
|
export type { ActivityDetailsProps } from './components/ActivityDetails/ActivityDetails';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,0CAA0C,CAAC;AACpF,YAAY,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAChG,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAC1F,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAC9E,YAAY,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,gBAAgB,GACjB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,IAAI,GACV,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AACpF,YAAY,EACV,aAAa,EACb,eAAe,GAChB,MAAM,iDAAiD,CAAC;AACzD,YAAY,EACV,YAAY,EACZ,SAAS,EACT,aAAa,GACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,OAAO,IAAI,KAAK,EAChB,UAAU,EACV,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wDAAwD,CAAC;AACzG,YAAY,EAAE,yBAAyB,EAAE,MAAM,wDAAwD,CAAC;AACxG,OAAO,EACL,eAAe,IAAI,0BAA0B,EAC7C,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,cAAc,IAAI,wBAAwB,EAC1C,eAAe,EACf,6BAA6B,GAC9B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,YAAY,EACV,mBAAmB,EACnB,YAAY,GACb,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAGnE,YAAY,EACV,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gDAAgD,CAAC;AAGxD,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,cAAc,GACf,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,WAAW,EACX,SAAS,IAAI,gBAAgB,GAC9B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EACL,uBAAuB,EACvB,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,4BAA4B,EAC5B,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,YAAY,EACV,QAAQ,IAAI,gBAAgB,EAC5B,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,EAClB,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGhF,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAG/E,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,SAAS,EACT,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EACL,OAAO,IAAI,KAAK,EAChB,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,mBAAmB,EACnB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,eAAe,EACf,sBAAsB,GACvB,MAAM,8CAA8C,CAAC;AACtD,YAAY,EACV,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,8CAA8C,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,aAAa,EACb,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,YAAY,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,OAAO,IAAI,MAAM,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,OAAO,IAAI,IAAI,EACf,QAAQ,EACR,YAAY,EACZ,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,SAAS,EACT,QAAQ,EACR,qBAAqB,EACrB,SAAS,GACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAG5E,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACvF,YAAY,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACvF,YAAY,EACV,eAAe,EACf,mBAAmB,GACpB,MAAM,4CAA4C,CAAC;AAGpD,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAG9D,OAAO,EACL,SAAS,EACT,IAAI,EACJ,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAG9D,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,SAAS,GACV,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,YAAY,EACV,cAAc,EACd,QAAQ,EACR,cAAc,EACd,SAAS,EACT,aAAa,GACd,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAG3E,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,YAAY,EACV,uBAAuB,EACvB,eAAe,EACf,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AACxE,YAAY,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAElF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,kEAAkE,CAAC;AAC7G,OAAO,EAAE,2BAA2B,EAAE,MAAM,sEAAsE,CAAC;AACnH,YAAY,EAAE,gCAAgC,EAAE,MAAM,sEAAsE,CAAC;AAC7H,OAAO,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAC/E,YAAY,EACV,oBAAoB,EACpB,eAAe,GAChB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAErF,OAAO,EACL,EAAE,EACF,0BAA0B,EAC1B,iBAAiB,EACjB,4BAA4B,EAC5B,eAAe,EACf,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,IAAI,yBAAyB,EACvC,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAC/E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AAGzF,OAAO,EACL,OAAO,EACP,WAAW,EACX,eAAe,EACf,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,OAAO,EACP,eAAe,EACf,aAAa,EACb,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EACzB,gBAAgB,EAChB,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,YAAY,EACV,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,SAAS,EACT,UAAU,GACX,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,cAAc,GACf,MAAM,gCAAgC,CAAC"}
|