a programming education platform
www.hypercommit.com
education
1"use client"
2
3import * as React from "react"
4import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
5import { HugeiconsIcon } from "@hugeicons/react"
6import {
7 ArrowRight01Icon,
8 Tick02Icon,
9} from "@hugeicons/core-free-icons"
10
11import { cn } from "@workspace/ui/lib/utils"
12
13function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
14 return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
15}
16
17function ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {
18 return (
19 <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
20 )
21}
22
23function ContextMenuTrigger({
24 className,
25 ...props
26}: ContextMenuPrimitive.Trigger.Props) {
27 return (
28 <ContextMenuPrimitive.Trigger
29 data-slot="context-menu-trigger"
30 className={cn("select-none", className)}
31 {...props}
32 />
33 )
34}
35
36function ContextMenuContent({
37 className,
38 align = "start",
39 alignOffset = 4,
40 side = "inline-end",
41 sideOffset = 0,
42 ...props
43}: ContextMenuPrimitive.Popup.Props &
44 Pick<
45 ContextMenuPrimitive.Positioner.Props,
46 "align" | "alignOffset" | "side" | "sideOffset"
47 >) {
48 return (
49 <ContextMenuPrimitive.Portal>
50 <ContextMenuPrimitive.Positioner
51 className="isolate z-50 outline-none"
52 align={align}
53 alignOffset={alignOffset}
54 side={side}
55 sideOffset={sideOffset}
56 >
57 <ContextMenuPrimitive.Popup
58 data-slot="context-menu-content"
59 className={cn(
60 "z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-start-2 data-[side=inline-start]:slide-in-from-end-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-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 dark:bg-popover/80 dark:ring-white/10 dark:backdrop-blur-md",
61 className
62 )}
63 {...props}
64 />
65 </ContextMenuPrimitive.Positioner>
66 </ContextMenuPrimitive.Portal>
67 )
68}
69
70function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
71 return (
72 <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
73 )
74}
75
76function ContextMenuLabel({
77 className,
78 inset,
79 ...props
80}: ContextMenuPrimitive.GroupLabel.Props & {
81 inset?: boolean
82}) {
83 return (
84 <ContextMenuPrimitive.GroupLabel
85 data-slot="context-menu-label"
86 data-inset={inset}
87 className={cn(
88 "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:ps-7",
89 className
90 )}
91 {...props}
92 />
93 )
94}
95
96function ContextMenuItem({
97 className,
98 inset,
99 variant = "default",
100 ...props
101}: ContextMenuPrimitive.Item.Props & {
102 inset?: boolean
103 variant?: "default" | "destructive"
104}) {
105 return (
106 <ContextMenuPrimitive.Item
107 data-slot="context-menu-item"
108 data-inset={inset}
109 data-variant={variant}
110 className={cn(
111 "group/context-menu-item relative flex cursor-pointer items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:ps-7 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 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
112 className
113 )}
114 {...props}
115 />
116 )
117}
118
119function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
120 return (
121 <ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
122 )
123}
124
125function ContextMenuSubTrigger({
126 className,
127 inset,
128 children,
129 ...props
130}: ContextMenuPrimitive.SubmenuTrigger.Props & {
131 inset?: boolean
132}) {
133 return (
134 <ContextMenuPrimitive.SubmenuTrigger
135 data-slot="context-menu-sub-trigger"
136 data-inset={inset}
137 className={cn(
138 "flex cursor-pointer items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:ps-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
139 className
140 )}
141 {...props}
142 >
143 {children}
144 <HugeiconsIcon icon={ArrowRight01Icon} className="ms-auto rtl:rotate-180" />
145 </ContextMenuPrimitive.SubmenuTrigger>
146 )
147}
148
149function ContextMenuSubContent({
150 ...props
151}: React.ComponentProps<typeof ContextMenuContent>) {
152 return (
153 <ContextMenuContent
154 data-slot="context-menu-sub-content"
155 className="shadow-lg"
156 side="inline-end"
157 {...props}
158 />
159 )
160}
161
162function ContextMenuCheckboxItem({
163 className,
164 children,
165 checked,
166 inset,
167 ...props
168}: ContextMenuPrimitive.CheckboxItem.Props & {
169 inset?: boolean
170}) {
171 return (
172 <ContextMenuPrimitive.CheckboxItem
173 data-slot="context-menu-checkbox-item"
174 data-inset={inset}
175 className={cn(
176 "relative flex cursor-pointer items-center gap-1.5 rounded-md py-1 ps-1.5 pe-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:ps-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
177 className
178 )}
179 checked={checked}
180 {...props}
181 >
182 <span className="pointer-events-none absolute end-2">
183 <ContextMenuPrimitive.CheckboxItemIndicator>
184 <HugeiconsIcon icon={Tick02Icon} />
185 </ContextMenuPrimitive.CheckboxItemIndicator>
186 </span>
187 {children}
188 </ContextMenuPrimitive.CheckboxItem>
189 )
190}
191
192function ContextMenuRadioGroup({
193 ...props
194}: ContextMenuPrimitive.RadioGroup.Props) {
195 return (
196 <ContextMenuPrimitive.RadioGroup
197 data-slot="context-menu-radio-group"
198 {...props}
199 />
200 )
201}
202
203function ContextMenuRadioItem({
204 className,
205 children,
206 inset,
207 ...props
208}: ContextMenuPrimitive.RadioItem.Props & {
209 inset?: boolean
210}) {
211 return (
212 <ContextMenuPrimitive.RadioItem
213 data-slot="context-menu-radio-item"
214 data-inset={inset}
215 className={cn(
216 "relative flex cursor-pointer items-center gap-1.5 rounded-md py-1 ps-1.5 pe-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:ps-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
217 className
218 )}
219 {...props}
220 >
221 <span className="pointer-events-none absolute end-2">
222 <ContextMenuPrimitive.RadioItemIndicator>
223 <HugeiconsIcon icon={Tick02Icon} />
224 </ContextMenuPrimitive.RadioItemIndicator>
225 </span>
226 {children}
227 </ContextMenuPrimitive.RadioItem>
228 )
229}
230
231function ContextMenuSeparator({
232 className,
233 ...props
234}: ContextMenuPrimitive.Separator.Props) {
235 return (
236 <ContextMenuPrimitive.Separator
237 data-slot="context-menu-separator"
238 className={cn("-mx-1 my-1 h-px bg-border", className)}
239 {...props}
240 />
241 )
242}
243
244function ContextMenuShortcut({
245 className,
246 ...props
247}: React.ComponentProps<"span">) {
248 return (
249 <span
250 data-slot="context-menu-shortcut"
251 className={cn(
252 "ms-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground",
253 className
254 )}
255 {...props}
256 />
257 )
258}
259
260export {
261 ContextMenu,
262 ContextMenuTrigger,
263 ContextMenuContent,
264 ContextMenuItem,
265 ContextMenuCheckboxItem,
266 ContextMenuRadioItem,
267 ContextMenuLabel,
268 ContextMenuSeparator,
269 ContextMenuShortcut,
270 ContextMenuGroup,
271 ContextMenuPortal,
272 ContextMenuSub,
273 ContextMenuSubContent,
274 ContextMenuSubTrigger,
275 ContextMenuRadioGroup,
276}