const Slider = ({ value, min, max, onChange, onlyCallOnChangeAfterDragEnded = false, orientation = 'horizontal', }: { value: number min: number max: number onChange: (value: number) => void onlyCallOnChangeAfterDragEnded?: boolean orientation?: 'horizontal' | 'vertical' }) => { const sliderRef = useRef(null) const [isDragging, setIsDragging] = useState(false) const [draggingValue, setDraggingValue] = useState(value) const memoedValue = useMemo( () => isDragging && onlyCallOnChangeAfterDragEnded ? draggingValue : value, [isDragging, draggingValue, value, onlyCallOnChangeAfterDragEnded] ) /** * Get the value of the slider based on the position of the pointer */ const getNewValue = useCallback( (val: number) => { if (!sliderRef?.current) return 0 const sliderWidth = sliderRef.current.getBoundingClientRect().width const newValue = (val / sliderWidth) * max if (newValue < min) return min if (newValue > max) return max return newValue }, [sliderRef, max, min] ) /** * Handle slider click event */ const handleClick = useCallback( (e: React.MouseEvent) => { onChange(getNewValue(e.clientX)) }, [getNewValue, onChange] ) /** * Handle pointer down event */ const handlePointerDown = () => { setIsDragging(true) } /** * Handle pointer move events */ useEffect(() => { const handlePointerMove = (e: { clientX: number; clientY: number }) => { if (!isDragging) return const newValue = getNewValue(e.clientX) onlyCallOnChangeAfterDragEnded ? setDraggingValue(newValue) : onChange(newValue) } document.addEventListener('pointermove', handlePointerMove) return () => { document.removeEventListener('pointermove', handlePointerMove) } }, [ isDragging, onChange, setDraggingValue, onlyCallOnChangeAfterDragEnded, getNewValue, ]) /** * Handle pointer up events */ useEffect(() => { const handlePointerUp = () => { if (!isDragging) return setIsDragging(false) if (onlyCallOnChangeAfterDragEnded) { console.log('draggingValue', draggingValue) onChange(draggingValue) } } document.addEventListener('pointerup', handlePointerUp) return () => { document.removeEventListener('pointerup', handlePointerUp) } }, [ isDragging, setIsDragging, onlyCallOnChangeAfterDragEnded, draggingValue, onChange, ]) /** * Track and thumb styles */ const usedTrackStyle = useMemo( () => ({ width: `${(memoedValue / max) * 100}%` }), [max, memoedValue] ) const thumbStyle = useMemo( () => ({ left: `${(memoedValue / max) * 100}%`, transform: `translateX(-10px)`, }), [max, memoedValue] ) return (
{/* Track */}
{/* Passed track */}
{/* Thumb */}
e.stopPropagation()} onPointerDown={handlePointerDown} >
) } export default Slider