import { useMemo, useState, ChangeEvent, useRef, KeyboardEvent, ComponentProps } from "react"; import clsx from "clsx"; import { ThemeColor } from "../../../types"; export const formatTime = (time: number) => { let seconds: string | number = time % 60; const foo = time - seconds; const minutes = foo / 60; if (seconds < 10) { seconds = `0${Math.floor(seconds)}`; } else { seconds = Math.floor(seconds).toFixed(0); } return minutes + ":" + seconds; }; export interface RangeProps extends Omit< ComponentProps<"input">, | "value" | "defaultValue" | "min" | "max" | "step" | "onPointerDown" | "onPointerUp" | "onTouchEnd" | "onKeyDown" | "ref" > { value: number; min?: number; max?: number; step?: number; color?: ThemeColor; formatter?: (str: number) => string; valueClassName?: string; onChangeCommitted?: (valueAsNumber: number) => void; } const trackBase = "pointer-events-none absolute top-0 left-0 h-full"; export function AudioRange({ className, valueClassName, value: controlledValue, min = 0, max = 100, color = "yellow", step = 1, formatter = formatTime, onChange, onChangeCommitted, ...rest }: RangeProps) { const rangeRef = useRef(null!); const [tempValue, setTempValue] = useState(undefined); const isTemp = typeof tempValue !== "undefined"; const value = isTemp ? tempValue : controlledValue; const range = max - min; const percent = (value - min) / range; const cssVars = useMemo( () => ({ "--p8n-range-progress-percent": `${percent * 100}%`, }), [percent], ); const handleChange = (e: ChangeEvent) => { if (isTemp) { setTempValue(e.target.valueAsNumber); } onChange?.(e); }; function handleSeekStart() { setTempValue(rangeRef.current.valueAsNumber); } function handleSeekEnd() { setTempValue(undefined); onChangeCommitted?.(rangeRef.current.valueAsNumber); } function handleKeyDown(e: KeyboardEvent) { const currentVal = rangeRef.current.valueAsNumber; switch (e.code) { case "ArrowLeft": { e.preventDefault(); onChangeCommitted?.(Math.max(min, currentVal - 5)); break; } case "ArrowRight": { e.preventDefault(); onChangeCommitted?.(Math.min(max, currentVal + 5)); break; } } } return (
{formatter(value)}
{/* track full */}
{/* active zone */}
{formatter(max)}
); }