/* eslint-disable react-hooks/exhaustive-deps */ import clsx from "clsx"; import { useEffect, useRef, useState, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent, } from "react"; interface IMultiRangeSlider { min: number; max: number; currentMin: number; currentMax: number; offset: number; disabled?: boolean; label: string; onChange: (value: [number, number]) => void; } function MultiRangeSlider({ currentMax, currentMin, max, min, onChange, offset, label, disabled = false, }: IMultiRangeSlider) { const [current, setCurrent] = useState<"min" | "max" | null>(null); const rangeRef = useRef(null); function calculateValue(clientX: number, isMin: boolean) { if (!rangeRef.current) return; const rect = rangeRef.current.getBoundingClientRect(); const percentage = (clientX - rect.x) / rect.width; const newValue = min + percentage * (max - min); return isMin ? Math.max(Math.min(newValue, currentMax - offset), min) : Math.min(Math.max(newValue, currentMin + offset), max); } const [value, setValue] = useState<[number, number]>([ currentMin, currentMax, ]); useEffect(() => { setValue([currentMin, currentMax]); }, [currentMin, currentMax]); function handleChange( e: MouseEvent | TouchEvent | ReactMouseEvent | ReactTouchEvent ) { if (!current || disabled) return; const { clientX } = "touches" in e ? e.touches[0] : e; const newValue = calculateValue(clientX, current === "min"); if (newValue !== undefined) { if (current === "min") setValue([newValue, currentMax]); else setValue([currentMin, newValue]); } } function handleMouseUp() { setCurrent(null); onChange(value.map(Math.ceil) as [number, number]); } useEffect(() => { if (!current) return; document.addEventListener("mousemove", handleChange as EventListener); document.addEventListener("mouseup", handleMouseUp); document.addEventListener("mouseleave", handleMouseUp); document.addEventListener("touchmove", handleChange as EventListener); document.addEventListener("touchend", handleMouseUp); document.addEventListener("touchcancel", handleMouseUp); return () => { document.removeEventListener("mousemove", handleChange as EventListener); document.removeEventListener("mouseup", handleMouseUp); document.removeEventListener("mouseleave", handleMouseUp); document.removeEventListener("touchmove", handleChange as EventListener); document.removeEventListener("touchend", handleMouseUp); document.removeEventListener("touchcancel", handleMouseUp); }; }, [current, value]); const getThumbStyle = (newValue: number) => ({ left: `${((newValue - min) / (max - min)) * 100}%`, }); return (

{label}

{Intl.NumberFormat("en").format(Math.ceil(value[0]))}

{Intl.NumberFormat("en").format(Math.ceil(value[1]))}

} onTouchMove={ handleChange as React.TouchEventHandler } >
{["min", "max"].map((type) => (
setCurrent(type as "min" | "max")} onTouchStart={() => setCurrent(type as "min" | "max")} style={getThumbStyle(type === "min" ? value[0] : value[1])} className={clsx( "rounded-full 2xl:w-[1.111vw] 2xl:h-[1.111vw] w-4 h-4 absolute bottom-0 -translate-x-1/2", current === type ? "cursor-grabbing" : "cursor-grab", disabled ? "bg-[#D2D2D2] !cursor-default" : "bg-[#00BED7]" )} /> ))}
); } export default MultiRangeSlider;