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