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