diff options
Diffstat (limited to 'apps/web/src/components/ui/context-menu.tsx')
| -rw-r--r-- | apps/web/src/components/ui/context-menu.tsx | 323 |
1 files changed, 171 insertions, 152 deletions
diff --git a/apps/web/src/components/ui/context-menu.tsx b/apps/web/src/components/ui/context-menu.tsx index a1a28fa6..4af92764 100644 --- a/apps/web/src/components/ui/context-menu.tsx +++ b/apps/web/src/components/ui/context-menu.tsx @@ -2,10 +2,10 @@ import * as React from "react"; import { ContextMenu as ContextMenuPrimitive } from "radix-ui"; -import { Check, ChevronRight, Circle } from "lucide-react"; import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "../../lib/utils"; +import { cn } from "@/utils/ui"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Tick02Icon, ArrowRightIcon, CircleIcon } from "@hugeicons/core-free-icons" const ContextMenu = ContextMenuPrimitive.Root; @@ -20,196 +20,215 @@ const ContextMenuSub = ContextMenuPrimitive.Sub; const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; const contextMenuItemVariants = cva( - "relative flex cursor-pointer select-none items-center gap-2 px-2 py-1.5 text-sm outline-hidden transition-opacity data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0", - { - variants: { - variant: { - default: "focus:opacity-65 focus:text-accent-foreground", - destructive: "text-destructive focus:text-destructive/80", - }, - }, - defaultVariants: { - variant: "default", - }, - } + "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", + { + 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", + }, + }, + defaultVariants: { + variant: "default", + }, + }, ); const ContextMenuSubTrigger = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { - inset?: boolean; - variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; - } ->(({ className, inset, children, variant = "default", ...props }, ref) => ( - <ContextMenuPrimitive.SubTrigger - ref={ref} - className={cn( - contextMenuItemVariants({ variant }), - "data-[state=open]:bg-accent data-[state=open]:opacity-65", - inset && "pl-8", - className - )} - {...props} - > - {children} - <ChevronRight className="ml-auto" /> - </ContextMenuPrimitive.SubTrigger> + React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { + inset?: boolean; + 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> )); ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName; const ContextMenuSubContent = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.SubContent>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> + React.ElementRef<typeof ContextMenuPrimitive.SubContent>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> >(({ className, ...props }, ref) => ( - <ContextMenuPrimitive.SubContent - ref={ref} - className={cn( - "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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", - className - )} - {...props} - /> + <ContextMenuPrimitive.SubContent + ref={ref} + className={cn( + "bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-lg border shadow-xl py-2.5", + className, + )} + {...props} + /> )); ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName; const ContextMenuContent = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> + React.ElementRef<typeof ContextMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> >(({ className, ...props }, ref) => ( - <ContextMenuPrimitive.Portal> - <ContextMenuPrimitive.Content - ref={ref} - className={cn( - "z-50 min-w-32 overflow-hidden rounded-md border p-1 bg-popover text-popover-foreground shadow-md", - "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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", - className - )} - {...props} - /> - </ContextMenuPrimitive.Portal> + <ContextMenuPrimitive.Portal> + <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", + className, + )} + {...props} + /> + </ContextMenuPrimitive.Portal> )); ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName; const ContextMenuItem = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.Item>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { - inset?: boolean; - variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; - } ->(({ className, inset, variant = "default", ...props }, ref) => ( - <ContextMenuPrimitive.Item - ref={ref} - className={cn( - contextMenuItemVariants({ variant }), - inset && "pl-8", - className - )} - {...props} - /> + React.ElementRef<typeof ContextMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { + inset?: boolean; + variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; + 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> )); ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName; const ContextMenuCheckboxItem = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> & { - variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; - } ->(({ className, children, checked, variant = "default", ...props }, ref) => ( - <ContextMenuPrimitive.CheckboxItem - ref={ref} - className={cn(contextMenuItemVariants({ variant }), "pl-8 pr-2", className)} - checked={checked} - {...props} - > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> - <ContextMenuPrimitive.ItemIndicator> - <Check className="h-4 w-4" /> - </ContextMenuPrimitive.ItemIndicator> - </span> - {children} - </ContextMenuPrimitive.CheckboxItem> + React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> & { + 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> )); ContextMenuCheckboxItem.displayName = - ContextMenuPrimitive.CheckboxItem.displayName; + ContextMenuPrimitive.CheckboxItem.displayName; const ContextMenuRadioItem = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.RadioItem>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> & { - variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; - } ->(({ className, children, variant = "default", ...props }, ref) => ( - <ContextMenuPrimitive.RadioItem - ref={ref} - className={cn(contextMenuItemVariants({ variant }), "pl-8 pr-2", className)} - {...props} - > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> - <ContextMenuPrimitive.ItemIndicator> - <Circle className="h-2 w-2 fill-current" /> - </ContextMenuPrimitive.ItemIndicator> - </span> - {children} - </ContextMenuPrimitive.RadioItem> + React.ElementRef<typeof ContextMenuPrimitive.RadioItem>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> & { + variant?: VariantProps<typeof contextMenuItemVariants>["variant"]; + icon?: React.ReactNode; + } +>(({ className, children, variant = "default", icon, ...props }, ref) => ( + <ContextMenuPrimitive.RadioItem + ref={ref} + className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)} + {...props} + > + <span className="absolute left-2 flex size-3.5 items-center justify-center"> + <ContextMenuPrimitive.ItemIndicator> + <HugeiconsIcon icon={CircleIcon} className="size-2 fill-current" /> + </ContextMenuPrimitive.ItemIndicator> + </span> + {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} + {children} + </ContextMenuPrimitive.RadioItem> )); ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName; const ContextMenuLabel = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.Label>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { - inset?: boolean; - } ->(({ className, inset, ...props }, ref) => ( - <ContextMenuPrimitive.Label - ref={ref} - className={cn( - "px-2 py-1.5 text-sm font-semibold", - inset && "pl-8", - className - )} - {...props} - /> + React.ElementRef<typeof ContextMenuPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { + inset?: boolean; + icon?: React.ReactNode; + } +>(({ className, inset, icon, children, ...props }, ref) => ( + <ContextMenuPrimitive.Label + ref={ref} + className={cn( + "flex items-center gap-2.5 px-4 py-1.5 text-sm font-semibold text-foreground", + inset && "pl-8", + className, + )} + {...props} + > + {icon && <span className="size-4 shrink-0 text-muted-foreground">{icon}</span>} + {children} + </ContextMenuPrimitive.Label> )); ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName; const ContextMenuSeparator = React.forwardRef< - React.ElementRef<typeof ContextMenuPrimitive.Separator>, - React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> + React.ElementRef<typeof ContextMenuPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> >(({ className, ...props }, ref) => ( - <ContextMenuPrimitive.Separator - ref={ref} - className={cn("-mx-1 my-1 h-px bg-foreground/10", className)} - {...props} - /> + <ContextMenuPrimitive.Separator + ref={ref} + className={cn("bg-border my-2 h-px", className)} + {...props} + /> )); ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName; const ContextMenuShortcut = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLSpanElement>) => { - return ( - <span - className={cn("ml-auto text-xs tracking-widest opacity-60", className)} - {...props} - /> - ); + return ( + <span + className={cn("ml-auto text-xs tracking-widest text-muted-foreground opacity-60", className)} + {...props} + /> + ); }; ContextMenuShortcut.displayName = "ContextMenuShortcut"; export { - ContextMenu, - ContextMenuTrigger, - ContextMenuContent, - ContextMenuItem, - ContextMenuCheckboxItem, - ContextMenuRadioItem, - ContextMenuLabel, - ContextMenuSeparator, - ContextMenuShortcut, - ContextMenuGroup, - ContextMenuPortal, - ContextMenuSub, - ContextMenuSubContent, - ContextMenuSubTrigger, - ContextMenuRadioGroup, + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, + ContextMenuItem, + ContextMenuCheckboxItem, + ContextMenuRadioItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuGroup, + ContextMenuPortal, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuRadioGroup, }; |
