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