Slider

Slider allow users to make selections from a range of values.

slider-demo
JSX
import { useRef, useState } from 'react';

const clamp = ({ value, min, max }) => {
  return Math.min(Math.max(value, min), max);
};

export default function Slider(props) {
  const { min = 0, max = 100 } = props;

  const sliderRef = useRef();
  const [isDragging, setIsDragging] = useState(false);
  const [progress, setProgress] = useState(0);

  const onMouseMove = (event) => {
    event.stopPropagation();

    if (isDragging) {
      const { left, width } = sliderRef?.current?.getBoundingClientRect();

      const _x = clamp({ value: (event.clientX - left) / width, min: 0, max: 1 });
      let value = (_x * 100).toFixed(1);

      if (value < min) value = min;
      if (value > max) value = max;

      setProgress(value);
    }
  };

  const onMouseDown = (event) => {
    setIsDragging(true);
    event.stopPropagation();
  };

  const onMouseUp = (event) => {
    setIsDragging(false);
    event.stopPropagation();
  };

  return (
    <div className="relative w-full">
      <div ref={sliderRef} className="h-1 overflow-hidden rounded-full bg-zinc-600">
        <div
          className="absolute inset-0 rounded-full bg-gradient-to-r from-sky-700 via-sky-500 to-sky-800"
          style={{ width: `${progress}%` }}
        />
      </div>
      <button
        onMouseLeave={() => setIsDragging(false)}
        onMouseUp={onMouseUp}
        onMouseDown={onMouseDown}
        onMouseMove={onMouseMove}
        className="absolute -bottom-2.5 -translate-x-1/2 transform"
        style={{ left: `${progress}%` }}
      >
        <svg viewBox="0 0 26 38" fill="none" className="h-10 w-7 fill-sky-500 active:fill-sky-600">
          <g>
            <path d="M7.264 20.625L13 28l5.736-7.375A6 6 0 0020 16.941V6a4 4 0 00-4-4h-6a4 4 0 00-4 4v10.941a6 6 0 001.264 3.684z"></path>
          </g>
        </svg>

        <span className="absolute -left-2">{`${progress}%`}</span>
      </button>
    </div>
  );
}
Last Updated: