import React, { Dispatch, HTMLAttributes, SetStateAction, useEffect, useRef } from 'react'; import './index.scss'; export interface SliderProps extends HTMLAttributes { children?: React.ReactNode; format?: (num: number) => string; defaultPercent: number; setPercent: Dispatch>; } const Slider: React.FC = (props) => { const { children, defaultPercent, setPercent, format, ...rest } = props; const isMove = useRef(false); const spanRef = useRef(null); const dotFocus = () => { spanRef.current?.classList.add('dotFocus'); }; const dotBlur = () => { spanRef.current?.classList.remove('dotFocus'); }; const moveHandle: React.EventHandler = (e) => { const isTouchDevice = 'ontouchstart' in document.documentElement; let clientX = 0; if (!isTouchDevice) { clientX = e.clientX; } else { clientX = e.touches[0].clientX; } if (isMove.current) { const parentNode = spanRef.current!.parentNode as HTMLDivElement; const parentClientX = parentNode.getBoundingClientRect().left; const parentWidth = parentNode.getBoundingClientRect().width; const percent = ((clientX - parentClientX) / parentWidth) * 100; if (percent < 100 && percent > 0) { spanRef.current!.style.transform = `translate(calc(${ clientX - parentClientX }px - 50%),-50%)`; setPercent(() => { return percent; }); } else if (percent > 100) { setPercent(100); } else if (percent < 0) { setPercent(0); } } }; const cancelHandle = () => { isMove.current = false; document.body.removeEventListener('mousemove', moveHandle); document.body.removeEventListener('touchmove', moveHandle); }; const outHandle = () => { document.body.removeEventListener('mousemove', moveHandle); document.body.removeEventListener('mouseup', cancelHandle); isMove.current = false; }; const dotDown = () => { isMove.current = true; const isTouchDevice = 'ontouchstart' in document.documentElement; if (!isTouchDevice) { document.body.removeEventListener('mouseleave', outHandle); document.body.addEventListener('mousemove', moveHandle); document.body.addEventListener('mouseup', cancelHandle); document.body.addEventListener('mouseleave', outHandle); } else { document.body.addEventListener('touchmove', moveHandle); document.body.addEventListener('touchend', cancelHandle); } }; useEffect(() => { const parentNode = spanRef.current!.parentNode as HTMLDivElement; const parentWidth = parentNode.getBoundingClientRect().width; spanRef.current!.style.transform = `translate(calc(${ (parentWidth * defaultPercent) / 100 }px - 50%),-50%)`; }, []); useEffect(() => { return () => { document.body.removeEventListener('mousemove', moveHandle); document.body.removeEventListener('mouseup', cancelHandle); document.body.removeEventListener('mouseleave', outHandle); }; }, []); return (
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
{format!(Math.ceil(defaultPercent))}
); }; Slider.defaultProps = { children: '', format: (num: number) => `${num}%` }; export default Slider;