clear last tickets
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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<void>
|
||||
onPause, // () => void | Promise<void>
|
||||
onPlay, // () => void | Promise<void>
|
||||
onSeekStart, // () => void | Promise<void>
|
||||
onSeekEnd, // () => void | Promise<void>
|
||||
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 (
|
||||
<Slider
|
||||
value={formatTime(
|
||||
isSeeking && previewRatio != null
|
||||
? safeDuration * previewRatio
|
||||
: safePosition,
|
||||
)}
|
||||
maxValue={formatTime(safeDuration)}
|
||||
progress={progress}
|
||||
seekEnabled={!disabled && safeDuration > 0}
|
||||
onSeekStart={handleSeekStart}
|
||||
onSeek={handleSeek}
|
||||
onSeekEnd={handleSeekEnd}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressSlider;
|
||||
Reference in New Issue
Block a user