import { useMemo, ComponentProps, useState, ChangeEvent, PointerEvent, TouchEvent, KeyboardEvent, } from "react"; import clsx from "clsx"; import { ThemeColor } from "../../../types"; export interface RangeProps extends Omit< ComponentProps<"input">, | "value" | "defaultValue" | "min" | "max" | "step" | "onPointerDown" | "onPointerUp" | "onTouchEnd" | "ref" > { value: number; additionalBarValue?: number; min?: number; max?: number; step?: number; showMinMax?: "onHover" | "always" | "never"; showValue?: boolean; ticks?: boolean; valuesByTick?: number; color?: ThemeColor; formatter?: (str: number) => string; onChangeCommitted?: (valueAsNumber: number) => void; } const trackBase = "pointer-events-none absolute top-0 left-0 h-full"; export function Range({ className, additionalBarValue, value: controlledValue, min = 0, max = 100, color = "yellow", showMinMax = "onHover", step = 1, valuesByTick, showValue = true, ticks = false, formatter = (str) => str?.toString(), onChange, onChangeCommitted, ...rest }: RangeProps) { const [tempValue, setTempValue] = useState(undefined); const isTemp = typeof tempValue !== "undefined"; const value = isTemp ? tempValue : controlledValue; const range = max - min; const percent = (value - min) / range; const additionalPercent = additionalBarValue ? (additionalBarValue - min) / range : 0; const nbOfTicks = 1 + Math.floor(range / (valuesByTick ?? step ?? 5)); const cssVars = useMemo( () => ({ "--p8n-range-progress-percent": `${percent * 100}%`, "--p8n-range-additional-percent": `${additionalPercent * 100}%`, }), [percent, additionalPercent], ); const handleChange = (e: ChangeEvent) => { if (isTemp) { setTempValue(e.target.valueAsNumber); } onChange?.(e); }; function handleSeekStart(event: PointerEvent) { setTempValue((event.target as HTMLInputElement).valueAsNumber); } function handleSeekEnd(event: TouchEvent | PointerEvent) { setTempValue(undefined); onChangeCommitted?.((event.target as HTMLInputElement).valueAsNumber); } function handleKeyUp(event: KeyboardEvent) { onChangeCommitted?.((event.target as HTMLInputElement).valueAsNumber); } return (
{showMinMax !== "never" && ( <>
{formatter(min)}
{formatter(max)}
)}
{/* track full */}
{typeof additionalBarValue !== "undefined" && (
)} {showValue && (
{formatter(value)}
)} {/* active zone */}
{/* ticks */} {ticks && (
{Array.from({ length: nbOfTicks }).map((_, i) => ( ))}
)}
); }