| import cn from "@utils/classnames"; | |
| import { type ChangeEvent, type ReactNode, forwardRef } from "react"; | |
| import { FormError } from "../index.ts"; | |
| import LabelTooltip from "./LabelTooltip.tsx"; | |
| interface InputSliderProps { | |
| label: string; | |
| error?: string; | |
| required?: boolean; | |
| className?: string; | |
| id?: string; | |
| disabled?: boolean; | |
| value?: number; | |
| onChange?: (e: ChangeEvent<HTMLInputElement>) => void; | |
| hideLabel?: boolean; | |
| tooltip?: string | ReactNode; | |
| min?: number; | |
| max?: number; | |
| step?: number; | |
| showValue?: boolean; | |
| } | |
| const InputSlider = forwardRef<HTMLInputElement, InputSliderProps>( | |
| ( | |
| { | |
| label, | |
| error, | |
| required, | |
| className = "", | |
| id, | |
| hideLabel = false, | |
| tooltip = "", | |
| min = 0, | |
| max = 100, | |
| step = 1, | |
| showValue = true, | |
| value = min, | |
| ...props | |
| }, | |
| ref | |
| ) => { | |
| return ( | |
| <div className={cn("flex flex-col gap-2", className)}> | |
| <div className="flex items-center justify-between"> | |
| <label | |
| htmlFor={id} | |
| className={cn( | |
| "relative text-sm font-medium text-gray-900 dark:text-gray-100", | |
| { | |
| "clip-[rect(0,0,0,0)] sr-only absolute m-[-1px] h-px w-px overflow-hidden border-0 p-0 whitespace-nowrap": | |
| hideLabel, | |
| } | |
| )} | |
| > | |
| {label} | |
| {required && ( | |
| <span className="ml-1 text-blue-500 dark:text-blue-400">*</span> | |
| )} | |
| {tooltip !== "" && <LabelTooltip text={<>{tooltip}</>} />} | |
| </label> | |
| {showValue && ( | |
| <span className="text-sm font-medium text-gray-700 dark:text-gray-300"> | |
| {value} | |
| </span> | |
| )} | |
| </div> | |
| <input | |
| ref={ref} | |
| type="range" | |
| id={id} | |
| min={min} | |
| max={max} | |
| step={step} | |
| value={value} | |
| className={cn( | |
| "w-full h-2 rounded-lg appearance-none cursor-pointer", | |
| "bg-gray-200 dark:bg-gray-700", | |
| "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-yellow-500 dark:[&::-webkit-slider-thumb]:bg-yellow-400", | |
| "[&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:transition-all", | |
| "[&::-webkit-slider-thumb]:hover:bg-yellow-600 dark:[&::-webkit-slider-thumb]:hover:bg-yellow-500", | |
| "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-yellow-500 dark:[&::-moz-range-thumb]:bg-yellow-400", | |
| "[&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:transition-all", | |
| "[&::-moz-range-thumb]:hover:bg-yellow-600 dark:[&::-moz-range-thumb]:hover:bg-yellow-500", | |
| "focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2 dark:focus:ring-yellow-400", | |
| "focus:ring-offset-white dark:focus:ring-offset-gray-900", | |
| error && | |
| "ring-2 ring-red-500 dark:ring-red-600", | |
| props.disabled && "opacity-50 cursor-not-allowed" | |
| )} | |
| {...props} | |
| /> | |
| {error && <FormError>{error}</FormError>} | |
| </div> | |
| ); | |
| } | |
| ); | |
| InputSlider.displayName = "InputSlider"; | |
| export default InputSlider; | |