select.tsx raw

   1  import * as React from 'react'
   2  import * as SelectPrimitive from '@radix-ui/react-select'
   3  import { Check, ChevronDown, ChevronUp } from 'lucide-react'
   4  
   5  import { cn } from '@/lib/utils'
   6  
   7  const Select = SelectPrimitive.Root
   8  
   9  const SelectGroup = SelectPrimitive.Group
  10  
  11  const SelectValue = SelectPrimitive.Value
  12  
  13  const SelectTrigger = React.forwardRef<
  14    React.ElementRef<typeof SelectPrimitive.Trigger>,
  15    React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
  16  >(({ className, children, ...props }, ref) => (
  17    <SelectPrimitive.Trigger
  18      ref={ref}
  19      className={cn(
  20        'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 transition-all duration-200 hover:border-ring/50',
  21        className
  22      )}
  23      {...props}
  24    >
  25      {children}
  26      <SelectPrimitive.Icon asChild>
  27        <ChevronDown className="h-4 w-4 opacity-50 transition-transform duration-200" />
  28      </SelectPrimitive.Icon>
  29    </SelectPrimitive.Trigger>
  30  ))
  31  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
  32  
  33  const SelectScrollUpButton = React.forwardRef<
  34    React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
  35    React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
  36  >(({ className, ...props }, ref) => (
  37    <SelectPrimitive.ScrollUpButton
  38      ref={ref}
  39      className={cn('flex cursor-default items-center justify-center py-1', className)}
  40      {...props}
  41    >
  42      <ChevronUp className="h-4 w-4" />
  43    </SelectPrimitive.ScrollUpButton>
  44  ))
  45  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
  46  
  47  const SelectScrollDownButton = React.forwardRef<
  48    React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
  49    React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
  50  >(({ className, ...props }, ref) => (
  51    <SelectPrimitive.ScrollDownButton
  52      ref={ref}
  53      className={cn('flex cursor-default items-center justify-center py-1', className)}
  54      {...props}
  55    >
  56      <ChevronDown className="h-4 w-4" />
  57    </SelectPrimitive.ScrollDownButton>
  58  ))
  59  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
  60  
  61  const SelectContent = React.forwardRef<
  62    React.ElementRef<typeof SelectPrimitive.Content>,
  63    React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
  64  >(({ className, children, position = 'popper', ...props }, ref) => (
  65    <SelectPrimitive.Portal>
  66      <SelectPrimitive.Content
  67        ref={ref}
  68        className={cn(
  69          'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-xl border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
  70          position === 'popper' &&
  71            'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
  72          className
  73        )}
  74        position={position}
  75        {...props}
  76      >
  77        <SelectScrollUpButton />
  78        <SelectPrimitive.Viewport
  79          className={cn(
  80            'p-1',
  81            position === 'popper' &&
  82              'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
  83          )}
  84        >
  85          {children}
  86        </SelectPrimitive.Viewport>
  87        <SelectScrollDownButton />
  88      </SelectPrimitive.Content>
  89    </SelectPrimitive.Portal>
  90  ))
  91  SelectContent.displayName = SelectPrimitive.Content.displayName
  92  
  93  const SelectLabel = React.forwardRef<
  94    React.ElementRef<typeof SelectPrimitive.Label>,
  95    React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
  96  >(({ className, ...props }, ref) => (
  97    <SelectPrimitive.Label
  98      ref={ref}
  99      className={cn('px-2 py-1.5 text-sm font-semibold', className)}
 100      {...props}
 101    />
 102  ))
 103  SelectLabel.displayName = SelectPrimitive.Label.displayName
 104  
 105  const SelectItem = React.forwardRef<
 106    React.ElementRef<typeof SelectPrimitive.Item>,
 107    React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
 108  >(({ className, children, ...props }, ref) => (
 109    <SelectPrimitive.Item
 110      ref={ref}
 111      className={cn(
 112        'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
 113        className
 114      )}
 115      {...props}
 116    >
 117      <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
 118        <SelectPrimitive.ItemIndicator>
 119          <Check className="h-4 w-4" />
 120        </SelectPrimitive.ItemIndicator>
 121      </span>
 122      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
 123    </SelectPrimitive.Item>
 124  ))
 125  SelectItem.displayName = SelectPrimitive.Item.displayName
 126  
 127  const SelectSeparator = React.forwardRef<
 128    React.ElementRef<typeof SelectPrimitive.Separator>,
 129    React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
 130  >(({ className, ...props }, ref) => (
 131    <SelectPrimitive.Separator
 132      ref={ref}
 133      className={cn('-mx-1 my-1 h-px bg-muted', className)}
 134      {...props}
 135    />
 136  ))
 137  SelectSeparator.displayName = SelectPrimitive.Separator.displayName
 138  
 139  export {
 140    Select,
 141    SelectGroup,
 142    SelectValue,
 143    SelectTrigger,
 144    SelectContent,
 145    SelectLabel,
 146    SelectItem,
 147    SelectSeparator,
 148    SelectScrollUpButton,
 149    SelectScrollDownButton
 150  }
 151