select.tsx raw

   1  "use client";
   2  
   3  import * as React from "react";
   4  import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
   5  import { Select as SelectPrimitive } from "radix-ui";
   6  
   7  import { cn } from "src/lib/utils";
   8  
   9  function Select({
  10    ...props
  11  }: React.ComponentProps<typeof SelectPrimitive.Root>) {
  12    return <SelectPrimitive.Root data-slot="select" {...props} />;
  13  }
  14  
  15  function SelectGroup({
  16    ...props
  17  }: React.ComponentProps<typeof SelectPrimitive.Group>) {
  18    return <SelectPrimitive.Group data-slot="select-group" {...props} />;
  19  }
  20  
  21  function SelectValue({
  22    ...props
  23  }: React.ComponentProps<typeof SelectPrimitive.Value>) {
  24    return <SelectPrimitive.Value data-slot="select-value" {...props} />;
  25  }
  26  
  27  function SelectTrigger({
  28    className,
  29    size = "default",
  30    children,
  31    ...props
  32  }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
  33    size?: "sm" | "default";
  34  }) {
  35    return (
  36      <SelectPrimitive.Trigger
  37        data-slot="select-trigger"
  38        data-size={size}
  39        className={cn(
  40          "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
  41          className
  42        )}
  43        {...props}
  44      >
  45        {children}
  46        <SelectPrimitive.Icon asChild>
  47          <ChevronDownIcon className="size-4 opacity-50" />
  48        </SelectPrimitive.Icon>
  49      </SelectPrimitive.Trigger>
  50    );
  51  }
  52  
  53  function SelectContent({
  54    className,
  55    children,
  56    position = "item-aligned",
  57    align = "center",
  58    ...props
  59  }: React.ComponentProps<typeof SelectPrimitive.Content>) {
  60    return (
  61      <SelectPrimitive.Portal>
  62        <SelectPrimitive.Content
  63          data-slot="select-content"
  64          className={cn(
  65            "relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md 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 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
  66            position === "popper" &&
  67              "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
  68            className
  69          )}
  70          position={position}
  71          align={align}
  72          {...props}
  73        >
  74          <SelectScrollUpButton />
  75          <SelectPrimitive.Viewport
  76            className={cn(
  77              "p-1",
  78              position === "popper" &&
  79                "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
  80            )}
  81          >
  82            {children}
  83          </SelectPrimitive.Viewport>
  84          <SelectScrollDownButton />
  85        </SelectPrimitive.Content>
  86      </SelectPrimitive.Portal>
  87    );
  88  }
  89  
  90  function SelectLabel({
  91    className,
  92    ...props
  93  }: React.ComponentProps<typeof SelectPrimitive.Label>) {
  94    return (
  95      <SelectPrimitive.Label
  96        data-slot="select-label"
  97        className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
  98        {...props}
  99      />
 100    );
 101  }
 102  
 103  function SelectItem({
 104    className,
 105    children,
 106    ...props
 107  }: React.ComponentProps<typeof SelectPrimitive.Item>) {
 108    return (
 109      <SelectPrimitive.Item
 110        data-slot="select-item"
 111        className={cn(
 112          "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
 113          className
 114        )}
 115        {...props}
 116      >
 117        <span
 118          data-slot="select-item-indicator"
 119          className="absolute right-2 flex size-3.5 items-center justify-center"
 120        >
 121          <SelectPrimitive.ItemIndicator>
 122            <CheckIcon className="size-4" />
 123          </SelectPrimitive.ItemIndicator>
 124        </span>
 125        <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
 126      </SelectPrimitive.Item>
 127    );
 128  }
 129  
 130  function SelectSeparator({
 131    className,
 132    ...props
 133  }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
 134    return (
 135      <SelectPrimitive.Separator
 136        data-slot="select-separator"
 137        className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
 138        {...props}
 139      />
 140    );
 141  }
 142  
 143  function SelectScrollUpButton({
 144    className,
 145    ...props
 146  }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
 147    return (
 148      <SelectPrimitive.ScrollUpButton
 149        data-slot="select-scroll-up-button"
 150        className={cn(
 151          "flex cursor-default items-center justify-center py-1",
 152          className
 153        )}
 154        {...props}
 155      >
 156        <ChevronUpIcon className="size-4" />
 157      </SelectPrimitive.ScrollUpButton>
 158    );
 159  }
 160  
 161  function SelectScrollDownButton({
 162    className,
 163    ...props
 164  }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
 165    return (
 166      <SelectPrimitive.ScrollDownButton
 167        data-slot="select-scroll-down-button"
 168        className={cn(
 169          "flex cursor-default items-center justify-center py-1",
 170          className
 171        )}
 172        {...props}
 173      >
 174        <ChevronDownIcon className="size-4" />
 175      </SelectPrimitive.ScrollDownButton>
 176    );
 177  }
 178  
 179  export {
 180    Select,
 181    SelectContent,
 182    SelectGroup,
 183    SelectItem,
 184    SelectLabel,
 185    SelectScrollDownButton,
 186    SelectScrollUpButton,
 187    SelectSeparator,
 188    SelectTrigger,
 189    SelectValue,
 190  };
 191