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