1'use client'
2
3import * as React from 'react'
4import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
5import {
6 CheckIcon,
7 ChevronRightIcon,
8 DotFilledIcon,
9} from '@radix-ui/react-icons'
10
11import { cn } from '@/lib/utils'
12
13const DropdownMenu = DropdownMenuPrimitive.Root
14
15const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
16
17const DropdownMenuGroup = DropdownMenuPrimitive.Group
18
19const DropdownMenuPortal = DropdownMenuPrimitive.Portal
20
21const DropdownMenuSub = DropdownMenuPrimitive.Sub
22
23const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
24
25const DropdownMenuSubTrigger = React.forwardRef<
26 React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
27 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
28 inset?: boolean
29 }
30>(({ className, inset, children, ...props }, ref) => (
31 <DropdownMenuPrimitive.SubTrigger
32 ref={ref}
33 className={cn(
34 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
35 inset && 'pl-8',
36 className,
37 )}
38 {...props}
39 >
40 {children}
41 <ChevronRightIcon className="ml-auto" />
42 </DropdownMenuPrimitive.SubTrigger>
43))
44DropdownMenuSubTrigger.displayName =
45 DropdownMenuPrimitive.SubTrigger.displayName
46
47const DropdownMenuSubContent = React.forwardRef<
48 React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
49 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
50>(({ className, ...props }, ref) => (
51 <DropdownMenuPrimitive.SubContent
52 ref={ref}
53 className={cn(
54 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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',
55 className,
56 )}
57 {...props}
58 />
59))
60DropdownMenuSubContent.displayName =
61 DropdownMenuPrimitive.SubContent.displayName
62
63const DropdownMenuContent = React.forwardRef<
64 React.ElementRef<typeof DropdownMenuPrimitive.Content>,
65 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
66>(({ className, sideOffset = 4, ...props }, ref) => (
67 <DropdownMenuPrimitive.Portal>
68 <DropdownMenuPrimitive.Content
69 ref={ref}
70 sideOffset={sideOffset}
71 className={cn(
72 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
73 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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',
74 className,
75 )}
76 {...props}
77 />
78 </DropdownMenuPrimitive.Portal>
79))
80DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
81
82const DropdownMenuItem = React.forwardRef<
83 React.ElementRef<typeof DropdownMenuPrimitive.Item>,
84 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
85 inset?: boolean
86 }
87>(({ className, inset, ...props }, ref) => (
88 <DropdownMenuPrimitive.Item
89 ref={ref}
90 className={cn(
91 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
92 inset && 'pl-8',
93 className,
94 )}
95 {...props}
96 />
97))
98DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
99
100const DropdownMenuCheckboxItem = React.forwardRef<
101 React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
102 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
103>(({ className, children, checked, ...props }, ref) => (
104 <DropdownMenuPrimitive.CheckboxItem
105 ref={ref}
106 className={cn(
107 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
108 className,
109 )}
110 checked={checked}
111 {...props}
112 >
113 <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
114 <DropdownMenuPrimitive.ItemIndicator>
115 <CheckIcon className="h-4 w-4" />
116 </DropdownMenuPrimitive.ItemIndicator>
117 </span>
118 {children}
119 </DropdownMenuPrimitive.CheckboxItem>
120))
121DropdownMenuCheckboxItem.displayName =
122 DropdownMenuPrimitive.CheckboxItem.displayName
123
124const DropdownMenuRadioItem = React.forwardRef<
125 React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
126 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
127>(({ className, children, ...props }, ref) => (
128 <DropdownMenuPrimitive.RadioItem
129 ref={ref}
130 className={cn(
131 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
132 className,
133 )}
134 {...props}
135 >
136 <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
137 <DropdownMenuPrimitive.ItemIndicator>
138 <DotFilledIcon className="h-4 w-4 fill-current" />
139 </DropdownMenuPrimitive.ItemIndicator>
140 </span>
141 {children}
142 </DropdownMenuPrimitive.RadioItem>
143))
144DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
145
146const DropdownMenuLabel = React.forwardRef<
147 React.ElementRef<typeof DropdownMenuPrimitive.Label>,
148 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
149 inset?: boolean
150 }
151>(({ className, inset, ...props }, ref) => (
152 <DropdownMenuPrimitive.Label
153 ref={ref}
154 className={cn(
155 'px-2 py-1.5 text-sm font-semibold',
156 inset && 'pl-8',
157 className,
158 )}
159 {...props}
160 />
161))
162DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
163
164const DropdownMenuSeparator = React.forwardRef<
165 React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
166 React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
167>(({ className, ...props }, ref) => (
168 <DropdownMenuPrimitive.Separator
169 ref={ref}
170 className={cn('-mx-1 my-1 h-px bg-muted', className)}
171 {...props}
172 />
173))
174DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
175
176const DropdownMenuShortcut = ({
177 className,
178 ...props
179}: React.HTMLAttributes<HTMLSpanElement>) => {
180 return (
181 <span
182 className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
183 {...props}
184 />
185 )
186}
187DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
188
189export {
190 DropdownMenu,
191 DropdownMenuTrigger,
192 DropdownMenuContent,
193 DropdownMenuItem,
194 DropdownMenuCheckboxItem,
195 DropdownMenuRadioItem,
196 DropdownMenuLabel,
197 DropdownMenuSeparator,
198 DropdownMenuShortcut,
199 DropdownMenuGroup,
200 DropdownMenuPortal,
201 DropdownMenuSub,
202 DropdownMenuSubContent,
203 DropdownMenuSubTrigger,
204 DropdownMenuRadioGroup,
205}
206