torch-glare 2.1.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/lib/components/BadgeField.tsx +131 -12
- package/apps/lib/components/ContextMenu.tsx +524 -0
- package/apps/lib/components/DropdownMenu.tsx +254 -102
- package/apps/lib/components/SearchableSelect.tsx +308 -0
- package/apps/lib/components/SearchableTable.tsx +363 -0
- package/apps/lib/components/Table.tsx +6 -6
- package/docs/components/context-menu.md +455 -0
- package/docs/components/dropdown-menu.md +37 -34
- package/docs/components/searchable-select.md +359 -0
- package/docs/components/searchable-table.md +419 -0
- package/docs/reference/tailwind-plugins.md +21 -1
- package/docs/tutorials/getting-started.md +15 -1
- package/package.json +1 -1
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cva, VariantProps } from "class-variance-authority";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
|
6
|
+
import { cn } from "../utils/cn";
|
|
7
|
+
import { Themes } from "../utils/types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ContextMenu — a right-click (or long-press) menu.
|
|
11
|
+
*
|
|
12
|
+
* Same surface as DropdownMenu (items, groups, the boxed look, auto-grouping),
|
|
13
|
+
* built on @radix-ui/react-context-menu so it opens at the pointer on
|
|
14
|
+
* right-click instead of anchoring to a clicked trigger button. The styling and
|
|
15
|
+
* auto-group logic are kept self-contained here (mirrored in DropdownMenu).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface ContextMenuContentProps {
|
|
19
|
+
variant?: "PresentationStyle";
|
|
20
|
+
className?: string;
|
|
21
|
+
theme?: Themes;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// A second right-click while the menu is open should simply close it. Radix
|
|
25
|
+
// would otherwise keep it open (re-anchoring is unreliable), so we make the Root
|
|
26
|
+
// controlled, track `open` in context, and let the Trigger close + swallow the
|
|
27
|
+
// event on re-click.
|
|
28
|
+
const ContextMenuOpenContext = React.createContext<{
|
|
29
|
+
open: boolean;
|
|
30
|
+
setOpen: (open: boolean) => void;
|
|
31
|
+
} | null>(null);
|
|
32
|
+
|
|
33
|
+
const ContextMenu = ({
|
|
34
|
+
open: openProp,
|
|
35
|
+
onOpenChange,
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Root>) => {
|
|
39
|
+
const [open, setOpenState] = React.useState(false);
|
|
40
|
+
const isControlled = openProp !== undefined;
|
|
41
|
+
const actualOpen = isControlled ? openProp : open;
|
|
42
|
+
|
|
43
|
+
const setOpen = React.useCallback(
|
|
44
|
+
(next: boolean) => {
|
|
45
|
+
if (!isControlled) setOpenState(next);
|
|
46
|
+
onOpenChange?.(next);
|
|
47
|
+
},
|
|
48
|
+
[isControlled, onOpenChange]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<ContextMenuOpenContext.Provider value={{ open: actualOpen, setOpen }}>
|
|
53
|
+
<ContextMenuPrimitive.Root
|
|
54
|
+
open={actualOpen}
|
|
55
|
+
onOpenChange={setOpen}
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</ContextMenuPrimitive.Root>
|
|
60
|
+
</ContextMenuOpenContext.Provider>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
ContextMenu.displayName = "ContextMenu";
|
|
64
|
+
|
|
65
|
+
// The right-click zone. Wrap it around the target the menu should open from.
|
|
66
|
+
const ContextMenuTrigger = React.forwardRef<
|
|
67
|
+
React.ElementRef<typeof ContextMenuPrimitive.Trigger>,
|
|
68
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Trigger>
|
|
69
|
+
>(({ onContextMenuCapture, ...props }, ref) => {
|
|
70
|
+
const ctx = React.useContext(ContextMenuOpenContext);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<ContextMenuPrimitive.Trigger
|
|
74
|
+
ref={ref}
|
|
75
|
+
// Capture phase runs before Radix's bubble-phase open handler. If the menu
|
|
76
|
+
// is already open, close it and swallow the event so Radix doesn't reopen
|
|
77
|
+
// it — a second right-click just dismisses the menu.
|
|
78
|
+
onContextMenuCapture={(event) => {
|
|
79
|
+
onContextMenuCapture?.(event);
|
|
80
|
+
if (ctx?.open) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
event.stopPropagation();
|
|
83
|
+
ctx.setOpen(false);
|
|
84
|
+
}
|
|
85
|
+
}}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
ContextMenuTrigger.displayName = ContextMenuPrimitive.Trigger.displayName;
|
|
91
|
+
|
|
92
|
+
const ContextMenuPortal = ContextMenuPrimitive.Portal;
|
|
93
|
+
|
|
94
|
+
const ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
95
|
+
|
|
96
|
+
const ContextMenuGroup = React.forwardRef<
|
|
97
|
+
React.ElementRef<typeof ContextMenuPrimitive.Group>,
|
|
98
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Group> &
|
|
99
|
+
VariantProps<typeof menuGroupStyles>
|
|
100
|
+
>(({ className, variant = "Boxed", ...props }, ref) => (
|
|
101
|
+
<ContextMenuPrimitive.Group
|
|
102
|
+
ref={ref}
|
|
103
|
+
className={cn(menuGroupStyles({ variant }), className)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
106
|
+
));
|
|
107
|
+
ContextMenuGroup.displayName = ContextMenuPrimitive.Group.displayName;
|
|
108
|
+
|
|
109
|
+
const ContextMenuRadioGroup = React.forwardRef<
|
|
110
|
+
React.ElementRef<typeof ContextMenuPrimitive.RadioGroup>,
|
|
111
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioGroup> &
|
|
112
|
+
VariantProps<typeof menuGroupStyles>
|
|
113
|
+
>(({ className, variant = "Boxed", ...props }, ref) => (
|
|
114
|
+
<ContextMenuPrimitive.RadioGroup
|
|
115
|
+
ref={ref}
|
|
116
|
+
className={cn(menuGroupStyles({ variant }), className)}
|
|
117
|
+
{...props}
|
|
118
|
+
/>
|
|
119
|
+
));
|
|
120
|
+
ContextMenuRadioGroup.displayName = ContextMenuPrimitive.RadioGroup.displayName;
|
|
121
|
+
|
|
122
|
+
const ContextMenuContent = React.forwardRef<
|
|
123
|
+
React.ElementRef<typeof ContextMenuPrimitive.Content>,
|
|
124
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> &
|
|
125
|
+
ContextMenuContentProps & { autoGroup?: boolean }
|
|
126
|
+
>(
|
|
127
|
+
(
|
|
128
|
+
{
|
|
129
|
+
theme,
|
|
130
|
+
className,
|
|
131
|
+
variant = "PresentationStyle",
|
|
132
|
+
autoGroup = true,
|
|
133
|
+
collisionPadding = 8,
|
|
134
|
+
children,
|
|
135
|
+
...props
|
|
136
|
+
},
|
|
137
|
+
ref
|
|
138
|
+
) => (
|
|
139
|
+
<ContextMenuPrimitive.Portal>
|
|
140
|
+
<ContextMenuPrimitive.Content
|
|
141
|
+
data-theme={theme}
|
|
142
|
+
ref={ref}
|
|
143
|
+
collisionPadding={collisionPadding}
|
|
144
|
+
className={cn(
|
|
145
|
+
menuContentStyles({ variant }),
|
|
146
|
+
// Cap to the space Radix has after collision handling so a tall menu
|
|
147
|
+
// scrolls instead of overflowing off-screen.
|
|
148
|
+
"max-h-[var(--radix-context-menu-content-available-height)]",
|
|
149
|
+
className
|
|
150
|
+
)}
|
|
151
|
+
{...props}
|
|
152
|
+
>
|
|
153
|
+
{autoGroup ? autoGroupChildren(children) : children}
|
|
154
|
+
</ContextMenuPrimitive.Content>
|
|
155
|
+
</ContextMenuPrimitive.Portal>
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
|
159
|
+
|
|
160
|
+
const ContextMenuSubTrigger = React.forwardRef<
|
|
161
|
+
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
|
|
162
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
|
|
163
|
+
inset?: boolean;
|
|
164
|
+
} & VariantProps<typeof MenuItemStyles>
|
|
165
|
+
>(
|
|
166
|
+
({ className, inset, children, variant = "Default", size = "M", ...props }, ref) => (
|
|
167
|
+
<ContextMenuPrimitive.SubTrigger
|
|
168
|
+
ref={ref}
|
|
169
|
+
className={cn(MenuItemStyles({ variant, size }), "justify-between", className)}
|
|
170
|
+
{...props}
|
|
171
|
+
>
|
|
172
|
+
<div className="justify-between">
|
|
173
|
+
<div className="flex gap-2">{children}</div>
|
|
174
|
+
<i className="ri-arrow-right-s-line text-[16px] rtl:rotate-180"></i>
|
|
175
|
+
</div>
|
|
176
|
+
</ContextMenuPrimitive.SubTrigger>
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
|
180
|
+
|
|
181
|
+
const ContextMenuSubContent = React.forwardRef<
|
|
182
|
+
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
|
|
183
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> & {
|
|
184
|
+
variant?: "PresentationStyle";
|
|
185
|
+
autoGroup?: boolean;
|
|
186
|
+
}
|
|
187
|
+
>(
|
|
188
|
+
({ className, variant = "PresentationStyle", autoGroup = true, children, ...props }, ref) => (
|
|
189
|
+
<ContextMenuPrimitive.Portal>
|
|
190
|
+
<ContextMenuPrimitive.SubContent
|
|
191
|
+
ref={ref}
|
|
192
|
+
className={cn(menuContentStyles({ variant }), className)}
|
|
193
|
+
{...props}
|
|
194
|
+
>
|
|
195
|
+
{autoGroup ? autoGroupChildren(children) : children}
|
|
196
|
+
</ContextMenuPrimitive.SubContent>
|
|
197
|
+
</ContextMenuPrimitive.Portal>
|
|
198
|
+
)
|
|
199
|
+
);
|
|
200
|
+
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
|
201
|
+
|
|
202
|
+
const ContextMenuItem = React.forwardRef<
|
|
203
|
+
React.ElementRef<typeof ContextMenuPrimitive.Item>,
|
|
204
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
|
205
|
+
inset?: boolean;
|
|
206
|
+
} & VariantProps<typeof MenuItemStyles>
|
|
207
|
+
>(
|
|
208
|
+
({ className, inset, children, variant = "Default", size = "M", active, ...props }, ref) => (
|
|
209
|
+
<ContextMenuPrimitive.Item
|
|
210
|
+
{...props}
|
|
211
|
+
ref={ref}
|
|
212
|
+
className={cn(MenuItemStyles({ variant, size, active }), className)}
|
|
213
|
+
>
|
|
214
|
+
<div>{children}</div>
|
|
215
|
+
</ContextMenuPrimitive.Item>
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
|
219
|
+
|
|
220
|
+
const ContextMenuCheckboxItem = React.forwardRef<
|
|
221
|
+
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
|
|
222
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> &
|
|
223
|
+
VariantProps<typeof MenuItemStyles>
|
|
224
|
+
>(
|
|
225
|
+
({ className, children, checked, variant = "Default", size = "M", onSelect, ...props }, ref) => (
|
|
226
|
+
<ContextMenuPrimitive.CheckboxItem
|
|
227
|
+
ref={ref}
|
|
228
|
+
className={cn(MenuItemStyles({ variant, size }), "relative", className)}
|
|
229
|
+
checked={checked}
|
|
230
|
+
// Keep the menu open when toggling; preventDefault stops Radix's auto-close.
|
|
231
|
+
onSelect={(event) => {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
onSelect?.(event);
|
|
234
|
+
}}
|
|
235
|
+
{...props}
|
|
236
|
+
>
|
|
237
|
+
<div className="relative flex items-center">
|
|
238
|
+
<span className="h-full flex items-center justify-center">
|
|
239
|
+
{/* Unchecked box; hidden once the item is checked. */}
|
|
240
|
+
<div className="flex justify-center items-center h-full [[data-state=checked]_&]:hidden">
|
|
241
|
+
<div className="w-[16px] h-[16px] rounded-[3px] border border-white-alpha-40 bg-black-alpha-15 group-hover:border-white-700 group-hover:bg-black-alpha-075"></div>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
{/* Checked indicator only renders when checked. */}
|
|
245
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
246
|
+
<div className="bg-blue-sparkle-600 flex justify-center items-center w-[16px] h-[16px] rounded-[3px]">
|
|
247
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
248
|
+
<path d="M5.8339 8.84977L11.1961 3.48755L12.0211 4.3125L5.8339 10.4997L2.12158 6.7874L2.94654 5.96245L5.8339 8.84977Z" fill="#F9F9F9" />
|
|
249
|
+
</svg>
|
|
250
|
+
</div>
|
|
251
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
252
|
+
</span>{" "}
|
|
253
|
+
{children}
|
|
254
|
+
</div>
|
|
255
|
+
</ContextMenuPrimitive.CheckboxItem>
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
|
|
259
|
+
|
|
260
|
+
const ContextMenuRadioItem = React.forwardRef<
|
|
261
|
+
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
|
|
262
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> &
|
|
263
|
+
VariantProps<typeof MenuItemStyles>
|
|
264
|
+
>(({ className, children, variant = "Default", size = "M", onSelect, ...props }, ref) => (
|
|
265
|
+
<ContextMenuPrimitive.RadioItem
|
|
266
|
+
ref={ref}
|
|
267
|
+
className={cn(MenuItemStyles({ variant, size }), "relative", className)}
|
|
268
|
+
// Keep the menu open when selecting; preventDefault stops Radix's auto-close.
|
|
269
|
+
onSelect={(event) => {
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
onSelect?.(event);
|
|
272
|
+
}}
|
|
273
|
+
{...props}
|
|
274
|
+
>
|
|
275
|
+
<div className="relative flex items-center">
|
|
276
|
+
<span className="h-full left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
277
|
+
{/* Unselected dot; hidden once the item is selected. */}
|
|
278
|
+
<div className="flex justify-center items-center h-full [[data-state=checked]_&]:hidden">
|
|
279
|
+
<div className="w-[14px] h-[14px] rounded-[100px] border border-white-alpha-40 bg-black-alpha-15 group-hover:border-white-700 group-hover:bg-black-alpha-075"></div>
|
|
280
|
+
</div>
|
|
281
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
282
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 14" fill="none">
|
|
283
|
+
<rect width="14" height="14" rx="7" fill="#005ECC" />
|
|
284
|
+
<rect x="5" y="5" width="4" height="4" rx="2" fill="white" />
|
|
285
|
+
</svg>
|
|
286
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
287
|
+
</span>
|
|
288
|
+
{children}
|
|
289
|
+
</div>
|
|
290
|
+
</ContextMenuPrimitive.RadioItem>
|
|
291
|
+
));
|
|
292
|
+
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
|
293
|
+
|
|
294
|
+
// Item types that should be boxed together when sitting loose in the menu.
|
|
295
|
+
// ContextMenuSub is included because its trigger renders as an inline row.
|
|
296
|
+
const GROUPABLE_TYPES = [
|
|
297
|
+
ContextMenuItem,
|
|
298
|
+
ContextMenuCheckboxItem,
|
|
299
|
+
ContextMenuRadioItem,
|
|
300
|
+
ContextMenuSub,
|
|
301
|
+
] as const;
|
|
302
|
+
|
|
303
|
+
const isGroupable = (child: React.ReactNode): child is React.ReactElement =>
|
|
304
|
+
React.isValidElement(child) &&
|
|
305
|
+
(GROUPABLE_TYPES as readonly React.ElementType[]).includes(
|
|
306
|
+
child.type as React.ElementType
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Wraps consecutive runs of loose items in a Boxed ContextMenuGroup so items
|
|
310
|
+
// render inside a container even when the consumer doesn't write one. Labels,
|
|
311
|
+
// separators and explicit groups act as boundaries and pass through unchanged.
|
|
312
|
+
function autoGroupChildren(children: React.ReactNode): React.ReactNode {
|
|
313
|
+
const out: React.ReactNode[] = [];
|
|
314
|
+
let run: React.ReactElement[] = [];
|
|
315
|
+
|
|
316
|
+
const flush = (key: string) => {
|
|
317
|
+
if (run.length === 0) return;
|
|
318
|
+
out.push(
|
|
319
|
+
<ContextMenuGroup key={key} variant="Boxed">
|
|
320
|
+
{run}
|
|
321
|
+
</ContextMenuGroup>
|
|
322
|
+
);
|
|
323
|
+
run = [];
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
React.Children.toArray(children).forEach((child, index) => {
|
|
327
|
+
if (isGroupable(child)) {
|
|
328
|
+
run.push(child);
|
|
329
|
+
} else {
|
|
330
|
+
flush(`auto-group-${index}`);
|
|
331
|
+
out.push(child);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
flush("auto-group-last");
|
|
335
|
+
|
|
336
|
+
return out;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const ContextMenuLabel = React.forwardRef<
|
|
340
|
+
React.ElementRef<typeof ContextMenuPrimitive.Label>,
|
|
341
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
|
|
342
|
+
inset?: boolean;
|
|
343
|
+
}
|
|
344
|
+
>(({ className, inset, ...props }, ref) => (
|
|
345
|
+
<ContextMenuPrimitive.Label
|
|
346
|
+
ref={ref}
|
|
347
|
+
className={cn(
|
|
348
|
+
"text-content-presentation-global-primary-light typography-body-small-medium px-[12px] pt-1 flex justify-start items-center",
|
|
349
|
+
className
|
|
350
|
+
)}
|
|
351
|
+
{...props}
|
|
352
|
+
/>
|
|
353
|
+
));
|
|
354
|
+
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
|
355
|
+
|
|
356
|
+
const ContextMenuShortcut = ({
|
|
357
|
+
className,
|
|
358
|
+
...props
|
|
359
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => (
|
|
360
|
+
<span
|
|
361
|
+
className={cn("ml-auto ltr:ml-0 rtl:mr-auto text-xs tracking-widest opacity-60", className)}
|
|
362
|
+
{...props}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
ContextMenuShortcut.displayName = "ContextMenuShortcut";
|
|
366
|
+
|
|
367
|
+
export {
|
|
368
|
+
ContextMenu,
|
|
369
|
+
ContextMenuTrigger,
|
|
370
|
+
ContextMenuContent,
|
|
371
|
+
ContextMenuItem,
|
|
372
|
+
ContextMenuGroup,
|
|
373
|
+
ContextMenuPortal,
|
|
374
|
+
ContextMenuSub,
|
|
375
|
+
ContextMenuSubTrigger,
|
|
376
|
+
ContextMenuSubContent,
|
|
377
|
+
ContextMenuRadioGroup,
|
|
378
|
+
ContextMenuCheckboxItem,
|
|
379
|
+
ContextMenuRadioItem,
|
|
380
|
+
ContextMenuLabel,
|
|
381
|
+
ContextMenuShortcut,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const MenuItemStyles = cva(
|
|
385
|
+
[
|
|
386
|
+
"text-content-presentation-global-primary-light typography-body-medium-regular",
|
|
387
|
+
"outline-none",
|
|
388
|
+
"border",
|
|
389
|
+
"border-transparent",
|
|
390
|
+
"flex",
|
|
391
|
+
"items-center",
|
|
392
|
+
"justify-start",
|
|
393
|
+
"text-overflow",
|
|
394
|
+
"overflow-hidden",
|
|
395
|
+
"p-[2px]",
|
|
396
|
+
"transition-all",
|
|
397
|
+
"bg-[rgba(184,192,204,0.36)]",
|
|
398
|
+
"ease-in-out",
|
|
399
|
+
"duration-300",
|
|
400
|
+
"[&>div]:flex",
|
|
401
|
+
"[&>div]:px-[12px]",
|
|
402
|
+
"[&>div]:py-[4px]",
|
|
403
|
+
"[&>div]:gap-2",
|
|
404
|
+
"[&>div]:w-full",
|
|
405
|
+
"[&>div]:rounded-[8px]",
|
|
406
|
+
"[&>div]:items-center ",
|
|
407
|
+
"group",
|
|
408
|
+
],
|
|
409
|
+
{
|
|
410
|
+
variants: {
|
|
411
|
+
variant: {
|
|
412
|
+
Default: [
|
|
413
|
+
"text-content-presentation-global-primary-light",
|
|
414
|
+
"[&>div]:hover:bg-white-50 [&>div]:hover:shadow-[0_0_16px_0_rgba(0,0,0,0.36)]",
|
|
415
|
+
"[&>div]:hover:text-black-1000",
|
|
416
|
+
"[&[data-highlighted]>div]:bg-white-alpha-75",
|
|
417
|
+
"[&[data-highlighted]>div]:text-black-1000",
|
|
418
|
+
"[&[data-disabled]>div]:text-content-presentation-global-primary-light",
|
|
419
|
+
"[&[data-disabled]>div]:opacity-50",
|
|
420
|
+
// Disabled items are pointer-events:none by default (Radix), so
|
|
421
|
+
// re-enable them to allow hover styling without making them selectable.
|
|
422
|
+
"[&[data-disabled]]:pointer-events-auto",
|
|
423
|
+
"[&[data-disabled]>div]:hover:text-content-presentation-global-primary-light",
|
|
424
|
+
"[&[data-disabled]>div]:hover:bg-transparent",
|
|
425
|
+
"[&[data-disabled]>div]:hover:shadow-none",
|
|
426
|
+
],
|
|
427
|
+
info: [
|
|
428
|
+
"text-blue-sparkle-200",
|
|
429
|
+
"[&>div]:hover:bg-white-50 [&>div]:hover:shadow-[0_0_16px_0_rgba(0,0,0,0.36)]",
|
|
430
|
+
"[&>div]:hover:text-blue-sparkle-700",
|
|
431
|
+
"[&[data-highlighted]>div]:bg-white-alpha-75",
|
|
432
|
+
"[&[data-highlighted]>div]:text-blue-sparkle-700",
|
|
433
|
+
"[&[data-disabled]>div]:text-content-presentation-global-primary-light",
|
|
434
|
+
"[&[data-disabled]>div]:opacity-50",
|
|
435
|
+
"[&[data-disabled]]:pointer-events-auto",
|
|
436
|
+
"[&[data-disabled]>div]:hover:text-content-presentation-global-primary-light",
|
|
437
|
+
"[&[data-disabled]>div]:hover:bg-transparent",
|
|
438
|
+
"[&[data-disabled]>div]:hover:shadow-none",
|
|
439
|
+
],
|
|
440
|
+
Negative: [
|
|
441
|
+
"text-medium-red-200",
|
|
442
|
+
"[&>div]:hover:bg-white-50 [&>div]:hover:shadow-[0_0_16px_0_rgba(0,0,0,0.36)]",
|
|
443
|
+
"[&>div]:hover:text-medium-red-600",
|
|
444
|
+
"[&[data-highlighted]>div]:bg-white-alpha-75",
|
|
445
|
+
"[&[data-highlighted]>div]:text-medium-red-600",
|
|
446
|
+
"[&[data-disabled]>div]:text-content-presentation-global-primary-light",
|
|
447
|
+
"[&[data-disabled]>div]:opacity-50",
|
|
448
|
+
"[&[data-disabled]]:pointer-events-auto",
|
|
449
|
+
"[&[data-disabled]>div]:hover:text-content-presentation-global-primary-light",
|
|
450
|
+
"[&[data-disabled]>div]:hover:bg-transparent",
|
|
451
|
+
"[&[data-disabled]>div]:hover:shadow-none",
|
|
452
|
+
],
|
|
453
|
+
},
|
|
454
|
+
size: {
|
|
455
|
+
S: ["typography-body-small-regular", "h-[24px]"],
|
|
456
|
+
M: ["typography-body-medium-regular", "h-[32px]"],
|
|
457
|
+
},
|
|
458
|
+
active: {
|
|
459
|
+
true: [
|
|
460
|
+
"bg-background-presentation-action-selected",
|
|
461
|
+
"text-content-presentation-action-light-primary",
|
|
462
|
+
],
|
|
463
|
+
},
|
|
464
|
+
defaultVariants: {
|
|
465
|
+
variant: "Default",
|
|
466
|
+
size: "M",
|
|
467
|
+
active: false,
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
compoundVariants: [
|
|
471
|
+
{
|
|
472
|
+
active: true,
|
|
473
|
+
variant: "info",
|
|
474
|
+
className: ["text-content-presentation-state-negative"],
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const menuContentStyles = cva(
|
|
481
|
+
[
|
|
482
|
+
"p-1",
|
|
483
|
+
"rounded-[14px]",
|
|
484
|
+
"min-w-[240px]",
|
|
485
|
+
"outline-none",
|
|
486
|
+
"overflow-scroll",
|
|
487
|
+
// Only animate the OPEN (enter) state. An exit animation on [data-state=closed]
|
|
488
|
+
// holds the old DOM node during close, which breaks close/reposition on a
|
|
489
|
+
// second right-click (Radix issue #2572).
|
|
490
|
+
"data-[state=open]:animate-in",
|
|
491
|
+
"data-[state=open]:fade-in-0",
|
|
492
|
+
"overflow-x-hidden",
|
|
493
|
+
"scrollbar-hide",
|
|
494
|
+
"backdrop-blur-[21px]",
|
|
495
|
+
"flex gap-1 flex-col",
|
|
496
|
+
],
|
|
497
|
+
{
|
|
498
|
+
variants: {
|
|
499
|
+
variant: {
|
|
500
|
+
PresentationStyle: [
|
|
501
|
+
"bg-[rgba(61,64,69,0.72)]",
|
|
502
|
+
"shadow-[0_0_32px_2px_rgba(0,0,0,0.20),0_0_48px_2px_rgba(0,0,0,0.05)]",
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
defaultVariants: {
|
|
506
|
+
variant: "PresentationStyle",
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
const menuGroupStyles = cva(["flex", "flex-col"], {
|
|
513
|
+
variants: {
|
|
514
|
+
variant: {
|
|
515
|
+
// Visually contains its items in a bordered card.
|
|
516
|
+
Boxed: ["gap-[1px]", "rounded-[10px]", "bg-bldue-500", "overflow-hidden"],
|
|
517
|
+
// No container — semantic grouping only (Radix default behavior).
|
|
518
|
+
Plain: [],
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
defaultVariants: {
|
|
522
|
+
variant: "Boxed",
|
|
523
|
+
},
|
|
524
|
+
});
|