import { useRef, useCallback, useEffect, useState } from 'react' interface SliderProps { value: number min: number max: number step?: number onChange: (value: number) => void } export default function Slider({ value, min, max, step = 1, onChange }: SliderProps) { const trackRef = useRef(null) const [dragging, setDragging] = useState(false) const pct = ((value - min) / (max - min)) * 100 const updateFromClient = useCallback((clientX: number) => { const rect = trackRef.current?.getBoundingClientRect() if (!rect) return const raw = (clientX - rect.left) / rect.width const clamped = Math.max(0, Math.min(1, raw)) const stepped = Math.round((min + clamped * (max - min)) / step) * step onChange(Math.max(min, Math.min(max, stepped))) }, [min, max, step, onChange]) const handleMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault() setDragging(true) updateFromClient(e.clientX) }, [updateFromClient]) useEffect(() => { if (!dragging) return const handleMove = (e: MouseEvent) => { e.preventDefault(); updateFromClient(e.clientX) } const handleUp = () => setDragging(false) window.addEventListener('mousemove', handleMove) window.addEventListener('mouseup', handleUp) return () => { window.removeEventListener('mousemove', handleMove); window.removeEventListener('mouseup', handleUp) } }, [dragging, updateFromClient]) return (
{ if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { e.preventDefault(); onChange(Math.min(max, value + step)) } if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { e.preventDefault(); onChange(Math.max(min, value - step)) } }} >
) }