slider

PreviousNext
Docs
ui-layoutscomponent

Preview

Loading preview…
./components/ui/slider.tsx
'use client';

import * as React from 'react';
import * as SliderPrimitive from '@radix-ui/react-slider';
import { cn } from '@/lib/utils';
import NumberFlow from '@number-flow/react';
import { GripVertical } from 'lucide-react';

interface DualRangeSliderProps
  extends React.ComponentProps<typeof SliderPrimitive.Root> {
  labelPosition?: 'top' | 'bottom' | 'static';
  lableContenPos?: 'left' | 'right';
  label?: React.ReactNode | ((value: number | undefined) => React.ReactNode);
}

const DualRangeSlider = React.forwardRef<
  React.ElementRef<typeof SliderPrimitive.Root>,
  DualRangeSliderProps
>(
  (
    {
      className,
      label,
      labelPosition = 'top',
      lableContenPos = 'right',
      ...props
    },
    ref
  ) => {
    const initialValue = Array.isArray(props.value)
      ? props.value
      : [props.min, props.max];

    return (
      <SliderPrimitive.Root
        ref={ref}
        className={cn(
          'relative flex w-full touch-none select-none items-center',
          className
        )}
        {...props}
      >
        <SliderPrimitive.Track className='relative h-6 w-full grow overflow-hidden bg-[linear-gradient(to_right,#e2e2e22c_1px,transparent_1px),linear-gradient(to_bottom,#9c9c9c2c_1px,transparent_1px)] bg-size-[4px_4px] dark:bg-neutral-950 bg-neutral-50 rounded-md dark:bg-[linear-gradient(to_right,#4f4f4f2e_1px,transparent_1px),linear-gradient(to_bottom,#4f4f4f2e_1px,transparent_1px)]'>
          <SliderPrimitive.Range className='absolute h-full dark:bg-neutral-100 bg-neutral-950' />
        </SliderPrimitive.Track>
        <>
          {initialValue.map((value, index) => (
            <React.Fragment key={index}>
              <SliderPrimitive.Thumb className='relative grid h-6 w-3 cursor-grab place-content-center dark:bg-neutral-100 bg-neutral-950 shadow-sm focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-white'>
                {label && labelPosition !== 'static' && (
                  <div
                    className={cn(
                      'absolute flex w-full justify-center items-start gap-0.5',
                      labelPosition === 'top' && '-top-7',
                      labelPosition === 'bottom' && 'top-4'
                    )}
                  >
                    {lableContenPos === 'left' && (
                      <>
                        {typeof label === 'function' ? (
                          <span className='inline-block  -translate-y-0.5'>
                            {label(value)}
                          </span>
                        ) : (
                          label && (
                            <span className='inline-block '>{label}</span>
                          )
                        )}
                      </>
                    )}
                    <NumberFlow
                      willChange
                      // @ts-ignore
                      value={value}
                      isolate
                      opacityTiming={{
                        duration: 250,
                        easing: 'ease-out',
                      }}
                      transformTiming={{
                        easing: `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%)`,
                        duration: 500,
                      }}
                    />
                    {lableContenPos === 'right' && (
                      <>
                        {typeof label === 'function' ? (
                          <span className='inline-block  -translate-y-1'>
                            {label(value)}
                          </span>
                        ) : (
                          label && (
                            <span className='inline-block '>{label}</span>
                          )
                        )}
                      </>
                    )}
                  </div>
                )}
                <GripVertical
                  size={16}
                  className='px-0.5 text-primary-foreground'
                />
              </SliderPrimitive.Thumb>
            </React.Fragment>
          ))}
        </>

        {label && labelPosition === 'static' && (
          <>
            {initialValue.map((value, index) => (
              <div
                className={cn(
                  'absolute -top-7 w-fit right-0 flex  justify-center items-start gap-0.5'
                )}
              >
                {lableContenPos === 'left' && (
                  <>
                    {typeof label === 'function' ? (
                      <span className='inline-block  -translate-y-0.5'>
                        {label(value)}
                      </span>
                    ) : (
                      label && <span className='inline-block '>{label}</span>
                    )}
                  </>
                )}
                <NumberFlow
                  willChange
                  // @ts-ignore
                  value={value}
                  isolate
                  opacityTiming={{
                    duration: 250,
                    easing: 'ease-out',
                  }}
                  transformTiming={{
                    easing: `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%)`,
                    duration: 500,
                  }}
                />
                {lableContenPos === 'right' && (
                  <>
                    {typeof label === 'function' ? (
                      <span className='inline-block  -translate-y-1'>
                        {label(value)}
                      </span>
                    ) : (
                      label && <span className='inline-block '>{label}</span>
                    )}
                  </>
                )}
              </div>
            ))}
          </>
        )}
      </SliderPrimitive.Root>
    );
  }
);
DualRangeSlider.displayName = 'DualRangeSlider';

export { DualRangeSlider };

Installation

npx shadcn@latest add @ui-layouts/slider

Usage

import { Slider } from "@/components/slider"
<Slider />