import React, { useCallback, useMemo, useRef, useState } from "react"; import Slider from "../Slider"; const clamp01 = (value) => Math.min(1, Math.max(0, value || 0)); const formatTime = (ms) => { const totalSeconds = Math.max(0, Math.floor((Number(ms) || 0) / 1000)); const minutes = Math.floor(totalSeconds / 60) .toString() .padStart(1, "0"); const seconds = (totalSeconds % 60).toString().padStart(2, "0"); return `${minutes}:${seconds}`; }; const ProgressSlider = ({ positionMs = 0, durationMs = 0, isPlaying = false, onSeek, // (targetMs) => void | Promise onPause, // () => void | Promise onPlay, // () => void | Promise onSeekStart, // () => void | Promise onSeekEnd, // () => void | Promise disabled = false, }) => { const wasPlayingRef = useRef(false); const [isSeeking, setIsSeeking] = useState(false); const [previewRatio, setPreviewRatio] = useState(null); const { safePosition, safeDuration, progress } = useMemo(() => { const dur = Math.max(0, Number(durationMs) || 0); const pos = Math.max(0, Math.min(dur, Number(positionMs) || 0)); const ratio = dur > 0 ? clamp01(pos / dur) : 0; return { safePosition: pos, safeDuration: dur, progress: ratio }; }, [durationMs, positionMs]); const handleSeekStart = useCallback(() => { if (disabled || !safeDuration) return; wasPlayingRef.current = !!isPlaying; setIsSeeking(true); setPreviewRatio(progress); if (typeof onSeekStart === "function") { onSeekStart(); } if (isPlaying && typeof onPause === "function") { onPause(); } }, [disabled, safeDuration, isPlaying, onPause, onSeekStart]); const handleSeek = useCallback( (ratio) => { if (disabled || !safeDuration || typeof onSeek !== "function") return; const bounded = clamp01(ratio); setPreviewRatio(bounded); const targetMs = safeDuration * bounded; onSeek(targetMs); }, [disabled, onSeek, safeDuration], ); const handleSeekEnd = useCallback(() => { if (disabled || !safeDuration) return; setIsSeeking(false); setPreviewRatio(null); if (typeof onSeekEnd === "function") { onSeekEnd(); } if (wasPlayingRef.current && typeof onPlay === "function") { onPlay(); } wasPlayingRef.current = false; }, [disabled, onPlay, onSeekEnd, safeDuration]); return ( 0} onSeekStart={handleSeekStart} onSeek={handleSeek} onSeekEnd={handleSeekEnd} /> ); }; export default ProgressSlider;