Skip to content

Commit 10579a9

Browse files
committed
Add select component
1 parent e190944 commit 10579a9

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

components/ui/select.tsx

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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 "@/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+
}

0 commit comments

Comments
 (0)