Animated Slider
Slider Tag Wrapper with shadcn slider and numberflow.
Slider Tag Wrapper with shadcn slider and numberflow.
npm install @number-flow/react @radix-ui/react-slider
1'use client';23import * as React from 'react';4import * as SliderPrimitive from '@radix-ui/react-slider';5import { cn } from '@/lib/utils';6import NumberFlow from '@number-flow/react';78interface DualRangeSliderProps9extends React.ComponentProps<typeof SliderPrimitive.Root> {10labelPosition?: 'top' | 'bottom' | 'static';11lableContenPos?: 'left' | 'right';12label?: React.ReactNode | ((value: number | undefined) => React.ReactNode);13}1415const DualRangeSlider = React.forwardRef<16React.ElementRef<typeof SliderPrimitive.Root>,17DualRangeSliderProps18>(19(20{21className,22label,23labelPosition = 'top',24lableContenPos = 'right',25...props26},27ref28) => {29const initialValue = Array.isArray(props.value)30? props.value31: [props.min, props.max];3233return (34<SliderPrimitive.Root35ref={ref}36className={cn(37'relative flex w-full touch-none select-none items-center',38className39)}40{...props}41>42<SliderPrimitive.Track className='relative h-2 w-full grow overflow-hidden rounded-full dark:bg-gray-800 bg-gray-300'>43<SliderPrimitive.Range className='absolute h-full bg-primary' />44</SliderPrimitive.Track>45<>46{initialValue.map((value, index) => (47<React.Fragment key={index}>48<SliderPrimitive.Thumb className='relative block h-4 w-4 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50'>49{label && labelPosition !== 'static' && (50<div51className={cn(52'absolute flex w-full justify-center items-start gap-0.5',53labelPosition === 'top' && '-top-7',54labelPosition === 'bottom' && 'top-4'55)}56>57{lableContenPos === 'left' && (58<>59{typeof label === 'function' ? (60<span className='inline-block -translate-y-0.5'>61{label(value)}62</span>63) : (64label && (65<span className='inline-block '>{label}</span>66)67)}68</>69)}70<NumberFlow71willChange72// @ts-ignore73value={value}74isolate75opacityTiming={{76duration: 250,77easing: 'ease-out',78}}79transformTiming={{80easing: `linear(0, 0.0033 0.8%, 0.0263 2.39%, 0.0896 4.77%, 0.4676 15.12%, 0.5688, 0.6553, 0.7274, 0.7862, 0.8336 31.04%, 0.8793, 0.9132 38.99%, 0.9421 43.77%, 0.9642 49.34%, 0.9796 55.71%, 0.9893 62.87%, 0.9952 71.62%, 0.9983 82.76%, 0.9996 99.47%)`,81duration: 500,82}}83/>84{lableContenPos === 'right' && (85<>86{typeof label === 'function' ? (87<span className='inline-block -translate-y-1'>88{label(value)}89</span>90) : (91label && (92<span className='inline-block '>{label}</span>93)94)}95</>96)}97</div>98)}99</SliderPrimitive.Thumb>100</React.Fragment>101))}102</>103104{label && labelPosition === 'static' && (105<>106{initialValue.map((value, index) => (107<div108className={cn(109'absolute -top-7 w-fit right-0 flex justify-center items-start gap-0.5'110)}111>112{lableContenPos === 'left' && (113<>114{typeof label === 'function' ? (115<span className='inline-block -translate-y-0.5'>116{label(value)}117</span>118) : (119label && <span className='inline-block '>{label}</span>120)}121</>122)}123<NumberFlow124willChange125// @ts-ignore126value={value}127isolate128opacityTiming={{129duration: 250,130easing: 'ease-out',131}}132transformTiming={{133easing: `linear(0, 0.0033 0.8%, 0.0263 2.39%, 0.0896 4.77%, 0.4676 15.12%, 0.5688, 0.6553, 0.7274, 0.7862, 0.8336 31.04%, 0.8793, 0.9132 38.99%, 0.9421 43.77%, 0.9642 49.34%, 0.9796 55.71%, 0.9893 62.87%, 0.9952 71.62%, 0.9983 82.76%, 0.9996 99.47%)`,134duration: 500,135}}136/>137{lableContenPos === 'right' && (138<>139{typeof label === 'function' ? (140<span className='inline-block -translate-y-1'>141{label(value)}142</span>143) : (144label && <span className='inline-block '>{label}</span>145)}146</>147)}148</div>149))}150</>151)}152</SliderPrimitive.Root>153);154}155);156DualRangeSlider.displayName = 'DualRangeSlider';157158export { DualRangeSlider };
Prop | Type | Default | Description |
---|---|---|---|
label | React.ReactNode or (value?: number) => ReactNode | undefined | The content for the label, either as a node or a function receiving the current value. |
labelPosition | 'top' | 'bottom' | 'static' | 'top' | The position of the label relative to the slider thumb. |
lableContenPos | 'left' | 'right' | 'right' | The position of the label content within the label block. |
className | string | undefined | Additional class name(s) for the outer container of the slider. |
value | number[] | undefined | The current value(s) of the slider. Can be a single value or a range (array). |
min | number | undefined | The minimum value allowed for the slider. |
max | number | undefined | The maximum value allowed for the slider. |
step | number | undefined | The step increment for the slider values. |
ref | React.RefObject | undefined | A React ref object for the slider's root element. |
Inspire from shadcn/ui expansions