diff options
| author | Yuval Adam <_@yuv.al> | 2025-09-24 10:52:38 +0200 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2025-09-24 10:52:38 +0200 |
| commit | 2a050b383bcafefb8fc378531d6f15f352fdb5a5 (patch) | |
| tree | 3170255e4a0b6e5488a3b851d8d9a0873e95747e /src/components/Slider.tsx | |
| parent | c9d9d3a49aed068a7fe3dadcd8dd06f938e6d9b5 (diff) | |
Override all files with v8 implementation
Diffstat (limited to 'src/components/Slider.tsx')
| -rw-r--r-- | src/components/Slider.tsx | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/components/Slider.tsx b/src/components/Slider.tsx new file mode 100644 index 0000000..22a8f8f --- /dev/null +++ b/src/components/Slider.tsx @@ -0,0 +1,86 @@ +import React, { useCallback, useRef, useState } from "react"; +import { PLAYBACK_SLOTS } from "../config"; + +interface SliderProps { + playback?: string; + slider: number; + setSlider: React.Dispatch<React.SetStateAction<number>>; +} + +export function Slider({ playback, slider, setSlider }: SliderProps) { + const sliderRef = useRef<HTMLDivElement>(null); + const [isDragging, setIsDragging] = useState(false); + + const max = playback ? PLAYBACK_SLOTS : 9; + + const handleStart = useCallback((e: React.MouseEvent | React.TouchEvent) => { + setIsDragging(true); + updateSliderValue(e); + }, []); + + const updateSliderValue = useCallback((e: MouseEvent | React.MouseEvent | TouchEvent | React.TouchEvent) => { + if (!sliderRef.current) return; + + const rect = sliderRef.current.getBoundingClientRect(); + let clientX: number; + + if ('touches' in e) { + clientX = e.touches?.[0]?.clientX || (e as TouchEvent).changedTouches?.[0]?.clientX || 0; + } else { + clientX = e.clientX; + } + + const percentage = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)); + const value = Math.round(percentage * max); + setSlider(value); + }, [max, setSlider]); + + const handleMove = useCallback((e: MouseEvent | TouchEvent) => { + if (isDragging) { + e.preventDefault(); + updateSliderValue(e); + } + }, [isDragging, updateSliderValue]); + + const handleEnd = useCallback(() => { + setIsDragging(false); + }, []); + + React.useEffect(() => { + if (isDragging) { + document.addEventListener('mousemove', handleMove); + document.addEventListener('mouseup', handleEnd); + document.addEventListener('touchmove', handleMove, { passive: false }); + document.addEventListener('touchend', handleEnd); + + return () => { + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('mouseup', handleEnd); + document.removeEventListener('touchmove', handleMove); + document.removeEventListener('touchend', handleEnd); + }; + } + }, [isDragging, handleMove, handleEnd]); + + const percentage = (slider / max) * 100; + + return ( + <div id="slider" className="py-2.5 fixed bottom-[8vh] w-[30vw] ml-[35vw] max-[812px]:w-[80vw] max-[812px]:ml-[8vw] max-[812px]:z-10"> + <div + ref={sliderRef} + className="relative h-5 bg-blue-500 rounded-full cursor-pointer touch-none" + onMouseDown={handleStart} + onTouchStart={handleStart} + > + <div + className={`absolute -top-2.5 w-10 h-10 bg-white rounded-full shadow-md z-[1] ${ + isDragging ? 'cursor-grabbing' : 'cursor-grab' + }`} + style={{ + left: `calc(${percentage}% - 20px)` + }} + /> + </div> + </div> + ); +}
\ No newline at end of file |
