diff options
Diffstat (limited to 'apps/web/src/components/ui/context-menu.tsx')
| -rw-r--r-- | apps/web/src/components/ui/context-menu.tsx | 179 |
1 files changed, 118 insertions, 61 deletions
diff --git a/apps/web/src/components/ui/context-menu.tsx b/apps/web/src/components/ui/context-menu.tsx index 4af92764..a49d8a88 100644 --- a/apps/web/src/components/ui/context-menu.tsx +++ b/apps/web/src/components/ui/context-menu.tsx @@ -5,7 +5,11 @@ import { ContextMenu as ContextMenuPrimitive } from "radix-ui"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/utils/ui"; import { HugeiconsIcon } from "@hugeicons/react"; -import { Tick02Icon, ArrowRightIcon, CircleIcon } from "@hugeicons/core-free-icons" +import { + Tick02Icon, + ArrowRightIcon, + CircleIcon, +} from "@hugeicons/core-free-icons"; const ContextMenu = ContextMenuPrimitive.Root; @@ -20,12 +24,14 @@ const ContextMenuSub = ContextMenuPrimitive.Sub; const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; const contextMenuItemVariants = cva( - "relative flex cursor-pointer select-none items-center gap-2.5 px-4 py-1.5 last:pb-1 text-base outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0", + "relative flex cursor-pointer select-none items-center gap-2.5 px-4 py-1.5 text-sm outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0", { variants: { variant: { - default: "focus:bg-accent/35 focus:text-accent-foreground [&_svg]:text-muted-foreground", - destructive: "text-destructive focus:bg-destructive/5 focus:text-destructive [&_svg]:text-destructive", + default: + "focus:bg-accent focus:text-accent-foreground [&_svg]:text-muted-foreground", + destructive: + "text-destructive focus:bg-destructive/5 focus:text-destructive [&_svg]:text-destructive", }, }, defaultVariants: { @@ -41,22 +47,32 @@ const ContextMenuSubTrigger = React.forwardRef< variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; icon?: React.ReactNode; } ->(({ className, inset, children, variant = "default", icon, ...props }, ref) => ( - <ContextMenuPrimitive.SubTrigger - ref={ref} - className={cn( - contextMenuItemVariants({ variant }), - "data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", - inset && "pl-8", - className, - )} - {...props} - > - {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} - {children} - <HugeiconsIcon icon={ArrowRightIcon} className="ml-auto text-muted-foreground/80" /> - </ContextMenuPrimitive.SubTrigger> -)); +>( + ( + { className, inset, children, variant = "default", icon, ...props }, + ref, + ) => ( + <ContextMenuPrimitive.SubTrigger + ref={ref} + className={cn( + contextMenuItemVariants({ variant }), + "data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", + inset && "pl-8", + className, + )} + {...props} + > + {icon && ( + <span className="size-4 shrink-0 text-muted-foreground">{icon}</span> + )} + {children} + <HugeiconsIcon + icon={ArrowRightIcon} + className="ml-auto text-muted-foreground/80" + /> + </ContextMenuPrimitive.SubTrigger> + ), +); ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName; const ContextMenuSubContent = React.forwardRef< @@ -76,13 +92,15 @@ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName; const ContextMenuContent = React.forwardRef< React.ElementRef<typeof ContextMenuPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> ->(({ className, ...props }, ref) => ( - <ContextMenuPrimitive.Portal> + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & { + container?: HTMLElement | null; + } +>(({ className, container, ...props }, ref) => ( + <ContextMenuPrimitive.Portal container={container ?? undefined}> <ContextMenuPrimitive.Content ref={ref} className={cn( - "bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-lg border shadow-xl py-2.5", + "bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-lg border shadow-xl py-1.5", className, )} {...props} @@ -99,25 +117,46 @@ const ContextMenuItem = React.forwardRef< icon?: React.ReactNode; textRight?: string; } ->(({ className, inset, variant = "default", icon, children, textRight, ...props }, ref) => ( - <ContextMenuPrimitive.Item - ref={ref} - className={cn( - contextMenuItemVariants({ variant }), - inset && "pl-8", +>( + ( + { className, - )} - {...props} - > - {icon && <span className="[&_svg]:size-4 [&_svg]:shrink-0">{icon}</span>} - {children} - {textRight && ( - <span className="ml-auto text-[0.60rem] tracking-widest text-muted-foreground/80 mb-0.5"> - {textRight} - </span> - )} - </ContextMenuPrimitive.Item> -)); + inset, + variant = "default", + icon, + children, + textRight, + ...props + }, + ref, + ) => { + const shouldInsetContent = inset || Boolean(icon); + + return ( + <ContextMenuPrimitive.Item + ref={ref} + className={cn( + contextMenuItemVariants({ variant }), + shouldInsetContent && "pl-8", + className, + )} + {...props} + > + {icon && ( + <span className="absolute left-3 flex size-3.5 items-center justify-center text-muted-foreground [&_svg]:size-4 [&_svg]:shrink-0"> + {icon} + </span> + )} + {children} + {textRight && ( + <span className="ml-auto text-[0.60rem] tracking-widest text-muted-foreground/80 mb-0.5"> + {textRight} + </span> + )} + </ContextMenuPrimitive.Item> + ); + }, +); ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName; const ContextMenuCheckboxItem = React.forwardRef< @@ -126,22 +165,33 @@ const ContextMenuCheckboxItem = React.forwardRef< variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; icon?: React.ReactNode; } ->(({ className, children, checked, variant = "default", icon, ...props }, ref) => ( - <ContextMenuPrimitive.CheckboxItem - ref={ref} - className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)} - checked={checked} - {...props} - > - <span className="absolute left-2 flex size-3.5 items-center justify-center"> - <ContextMenuPrimitive.ItemIndicator> - <HugeiconsIcon icon={Tick02Icon} className="size-4" /> - </ContextMenuPrimitive.ItemIndicator> - </span> - {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} - {children} - </ContextMenuPrimitive.CheckboxItem> -)); +>( + ( + { className, children, checked, variant = "default", icon, ...props }, + ref, + ) => ( + <ContextMenuPrimitive.CheckboxItem + ref={ref} + className={cn( + contextMenuItemVariants({ variant }), + "pr-2 pl-8", + className, + )} + checked={checked} + {...props} + > + <span className="absolute left-3 flex size-3.5 items-center justify-center"> + <ContextMenuPrimitive.ItemIndicator> + <HugeiconsIcon icon={Tick02Icon} className="size-4" /> + </ContextMenuPrimitive.ItemIndicator> + </span> + {icon && ( + <span className="size-4 shrink-0 text-muted-foreground">{icon}</span> + )} + {children} + </ContextMenuPrimitive.CheckboxItem> + ), +); ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName; @@ -162,7 +212,9 @@ const ContextMenuRadioItem = React.forwardRef< <HugeiconsIcon icon={CircleIcon} className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> - {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} + {icon && ( + <span className="size-4 shrink-0 text-muted-foreground">{icon}</span> + )} {children} </ContextMenuPrimitive.RadioItem> )); @@ -184,7 +236,9 @@ const ContextMenuLabel = React.forwardRef< )} {...props} > - {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} + {icon && ( + <span className="size-4 shrink-0 text-muted-foreground">{icon}</span> + )} {children} </ContextMenuPrimitive.Label> )); @@ -208,7 +262,10 @@ const ContextMenuShortcut = ({ }: React.HTMLAttributes<HTMLSpanElement>) => { return ( <span - className={cn("ml-auto text-xs tracking-widest text-muted-foreground opacity-60", className)} + className={cn( + "ml-auto text-xs tracking-widest text-muted-foreground opacity-60", + className, + )} {...props} /> ); |
