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