All files / src/components/ui slider.tsx

0% Statements 0/31
0% Branches 0/1
0% Functions 0/1
0% Lines 0/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51                                                                                                     
import * as React from "react";
import { cn } from "@/lib/utils";
 
interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange"> {
	value: number;
	min?: number;
	max?: number;
	step?: number;
	onValueChange?: (value: number) => void;
}
 
// Half the thumb width in px (thumb is w-4 = 16px). The browser offsets the thumb
// so its centre sits exactly at the theoretical percentage position, but it can't
// move past the track edges — at 0 % the centre is 8 px in, at 100 % it is 8 px
// from the right. We apply the same correction to the gradient stop so the filled
// portion always ends exactly at the thumb centre.
const THUMB_HALF = 8;
 
const Slider = React.forwardRef<HTMLInputElement, SliderProps>(
	({ className, value, min = 0, max = 100, step = 1, onValueChange, ...props }, ref) => {
		const range = max - min;
		const percent = range > 0 ? ((value - min) / range) * 100 : 0;
		// offset shrinks from +THUMB_HALF at 0 % to −THUMB_HALF at 100 %
		const offset = THUMB_HALF - (percent / 100) * (THUMB_HALF * 2);
		const stop = `calc(${percent}% + ${offset}px)`;
		return (
			<input
				ref={ref}
				type="range"
				min={min}
				max={max}
				step={step}
				value={value}
				onChange={(e) => onValueChange?.(Number(e.target.value))}
				className={cn(
					"w-full h-2 rounded-full appearance-none cursor-pointer bg-secondary",
					"[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary",
					className
				)}
				style={{
					background: `linear-gradient(to right, hsl(var(--primary)) ${stop}, hsl(var(--secondary)) ${stop})`,
				}}
				{...props}
			/>
		);
	}
);
Slider.displayName = "Slider";
 
export { Slider };