|
| 1 | +"use client"; |
| 2 | +import { forwardRef, useMemo } from "react"; |
| 3 | +import * as SliderPrimitive from "@radix-ui/react-slider"; |
| 4 | +import { cn } from "@/lib/utils"; |
| 5 | +import { cva } from "class-variance-authority"; |
| 6 | + |
| 7 | +const sliderVariants = cva("relative w-full grow", { |
| 8 | + variants: { |
| 9 | + variant: { |
| 10 | + default: "h-5.5", |
| 11 | + thin: "h-4", |
| 12 | + }, |
| 13 | + }, |
| 14 | + defaultVariants: { |
| 15 | + variant: "default", |
| 16 | + }, |
| 17 | +}); |
| 18 | + |
| 19 | +interface SliderProps |
| 20 | + extends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> { |
| 21 | + variant?: "default" | "thin"; |
| 22 | +} |
| 23 | + |
| 24 | +const Slider = forwardRef< |
| 25 | + React.ComponentRef<typeof SliderPrimitive.Root>, |
| 26 | + SliderProps |
| 27 | +>( |
| 28 | + ( |
| 29 | + { |
| 30 | + variant = "default", |
| 31 | + className, |
| 32 | + defaultValue, |
| 33 | + value, |
| 34 | + min = 0, |
| 35 | + max = 100, |
| 36 | + ...props |
| 37 | + }, |
| 38 | + ref |
| 39 | + ) => { |
| 40 | + const _values = useMemo( |
| 41 | + () => |
| 42 | + Array.isArray(value) |
| 43 | + ? value |
| 44 | + : Array.isArray(defaultValue) |
| 45 | + ? defaultValue |
| 46 | + : [min, max], |
| 47 | + [value, defaultValue, min, max] |
| 48 | + ); |
| 49 | + |
| 50 | + return ( |
| 51 | + <div className="space-y-2"> |
| 52 | + <div className="relative"> |
| 53 | + <SliderPrimitive.Root |
| 54 | + ref={ref} |
| 55 | + defaultValue={defaultValue} |
| 56 | + value={value} |
| 57 | + min={min} |
| 58 | + max={max} |
| 59 | + className={cn( |
| 60 | + "relative flex w-full touch-none select-none items-center data-[disabled]:opacity-50", |
| 61 | + className |
| 62 | + )} |
| 63 | + {...props} |
| 64 | + > |
| 65 | + <SliderPrimitive.Track |
| 66 | + className={cn( |
| 67 | + sliderVariants({ variant }), |
| 68 | + "overflow-hidden border-2 border-black bg-white shadow-[4px_4px_0_0_rgba(0,0,0,1)]" |
| 69 | + )} |
| 70 | + > |
| 71 | + <SliderPrimitive.Range className="absolute h-full bg-primary" /> |
| 72 | + </SliderPrimitive.Track> |
| 73 | + {_values.map((_, index) => ( |
| 74 | + <SliderPrimitive.Thumb |
| 75 | + key={index} |
| 76 | + className={cn( |
| 77 | + "block transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50", |
| 78 | + variant === "default" |
| 79 | + ? "h-5 w-5 border-2 border-black bg-white" |
| 80 | + : "h-4 w-4 border-2 border-black bg-white" |
| 81 | + )} |
| 82 | + > |
| 83 | + <div className="absolute inset-0 -translate-x-[2px] -translate-y-[2px] border-2 border-black transition-transform hover:-translate-y-1 active:translate-y-0 bg-white group-data-[disabled]:pointer-events-none"> |
| 84 | + <div className="absolute inset-x-0 -top-1 h-[2px] bg-black/10" /> |
| 85 | + <div className="absolute inset-x-0 -bottom-1 h-[2px] bg-black/10" /> |
| 86 | + </div> |
| 87 | + </SliderPrimitive.Thumb> |
| 88 | + ))} |
| 89 | + </SliderPrimitive.Root> |
| 90 | + </div> |
| 91 | + </div> |
| 92 | + ); |
| 93 | + } |
| 94 | +); |
| 95 | + |
| 96 | +Slider.displayName = "Slider"; |
| 97 | + |
| 98 | +export { Slider }; |
0 commit comments