dropdown-menu.tsx raw
1 import * as React from "react";
2 import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3 import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
4
5 import { cn } from "src/lib/utils";
6
7 function DropdownMenu({
8 ...props
9 }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10 return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
11 }
12
13 function DropdownMenuPortal({
14 ...props
15 }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16 return (
17 <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
18 );
19 }
20
21 function DropdownMenuTrigger({
22 ...props
23 }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
24 return (
25 <DropdownMenuPrimitive.Trigger
26 data-slot="dropdown-menu-trigger"
27 {...props}
28 />
29 );
30 }
31
32 function DropdownMenuContent({
33 className,
34 sideOffset = 4,
35 ...props
36 }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
37 return (
38 <DropdownMenuPrimitive.Portal>
39 <DropdownMenuPrimitive.Content
40 data-slot="dropdown-menu-content"
41 sideOffset={sideOffset}
42 className={cn(
43 "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 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",
44 className
45 )}
46 {...props}
47 />
48 </DropdownMenuPrimitive.Portal>
49 );
50 }
51
52 function DropdownMenuGroup({
53 ...props
54 }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
55 return (
56 <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
57 );
58 }
59
60 function DropdownMenuItem({
61 className,
62 inset,
63 variant = "default",
64 ...props
65 }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
66 inset?: boolean;
67 variant?: "default" | "destructive";
68 }) {
69 return (
70 <DropdownMenuPrimitive.Item
71 data-slot="dropdown-menu-item"
72 data-inset={inset}
73 data-variant={variant}
74 className={cn(
75 "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
76 className
77 )}
78 {...props}
79 />
80 );
81 }
82
83 function DropdownMenuCheckboxItem({
84 className,
85 children,
86 checked,
87 ...props
88 }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
89 return (
90 <DropdownMenuPrimitive.CheckboxItem
91 data-slot="dropdown-menu-checkbox-item"
92 className={cn(
93 "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 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",
94 className
95 )}
96 checked={checked}
97 {...props}
98 >
99 <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
100 <DropdownMenuPrimitive.ItemIndicator>
101 <CheckIcon className="size-4" />
102 </DropdownMenuPrimitive.ItemIndicator>
103 </span>
104 {children}
105 </DropdownMenuPrimitive.CheckboxItem>
106 );
107 }
108
109 function DropdownMenuRadioGroup({
110 ...props
111 }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
112 return (
113 <DropdownMenuPrimitive.RadioGroup
114 data-slot="dropdown-menu-radio-group"
115 {...props}
116 />
117 );
118 }
119
120 function DropdownMenuRadioItem({
121 className,
122 children,
123 ...props
124 }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
125 return (
126 <DropdownMenuPrimitive.RadioItem
127 data-slot="dropdown-menu-radio-item"
128 className={cn(
129 "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 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",
130 className
131 )}
132 {...props}
133 >
134 <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
135 <DropdownMenuPrimitive.ItemIndicator>
136 <CircleIcon className="size-2 fill-current" />
137 </DropdownMenuPrimitive.ItemIndicator>
138 </span>
139 {children}
140 </DropdownMenuPrimitive.RadioItem>
141 );
142 }
143
144 function DropdownMenuLabel({
145 className,
146 inset,
147 ...props
148 }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
149 inset?: boolean;
150 }) {
151 return (
152 <DropdownMenuPrimitive.Label
153 data-slot="dropdown-menu-label"
154 data-inset={inset}
155 className={cn(
156 "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
157 className
158 )}
159 {...props}
160 />
161 );
162 }
163
164 function DropdownMenuSeparator({
165 className,
166 ...props
167 }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
168 return (
169 <DropdownMenuPrimitive.Separator
170 data-slot="dropdown-menu-separator"
171 className={cn("-mx-1 my-1 h-px bg-border", className)}
172 {...props}
173 />
174 );
175 }
176
177 function DropdownMenuShortcut({
178 className,
179 ...props
180 }: React.ComponentProps<"span">) {
181 return (
182 <span
183 data-slot="dropdown-menu-shortcut"
184 className={cn(
185 "ml-auto text-xs tracking-widest text-muted-foreground",
186 className
187 )}
188 {...props}
189 />
190 );
191 }
192
193 function DropdownMenuSub({
194 ...props
195 }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
196 return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
197 }
198
199 function DropdownMenuSubTrigger({
200 className,
201 inset,
202 children,
203 ...props
204 }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
205 inset?: boolean;
206 }) {
207 return (
208 <DropdownMenuPrimitive.SubTrigger
209 data-slot="dropdown-menu-sub-trigger"
210 data-inset={inset}
211 className={cn(
212 "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
213 className
214 )}
215 {...props}
216 >
217 {children}
218 <ChevronRightIcon className="ml-auto size-4" />
219 </DropdownMenuPrimitive.SubTrigger>
220 );
221 }
222
223 function DropdownMenuSubContent({
224 className,
225 ...props
226 }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
227 return (
228 <DropdownMenuPrimitive.SubContent
229 data-slot="dropdown-menu-sub-content"
230 className={cn(
231 "z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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",
232 className
233 )}
234 {...props}
235 />
236 );
237 }
238
239 export {
240 DropdownMenu,
241 DropdownMenuPortal,
242 DropdownMenuTrigger,
243 DropdownMenuContent,
244 DropdownMenuGroup,
245 DropdownMenuLabel,
246 DropdownMenuItem,
247 DropdownMenuCheckboxItem,
248 DropdownMenuRadioGroup,
249 DropdownMenuRadioItem,
250 DropdownMenuSeparator,
251 DropdownMenuShortcut,
252 DropdownMenuSub,
253 DropdownMenuSubTrigger,
254 DropdownMenuSubContent,
255 };
256