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