{
  "title": "Context Menu",
  "description": "Displays a menu located at the pointer position, triggered by a right-click or long-press, with spring entrance transitions, submenus, checkboxes, and radio items.",
  "name": "motion-context-menu",
  "dependencies": [
    "motion",
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [],
  "type": "registry:ui",
  "files": [
    {
      "path": "components/paceui/motion/context-menu.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\n\nimport { ContextMenu as ContextMenuPrimitive } from \"@base-ui/react/context-menu\";\nimport { CheckIcon, ChevronRightIcon } from \"lucide-react\";\nimport { AnimatePresence, type Transition, motion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface ContextMenuHighlightContextType {\n    highlightLayoutId: string;\n    activeItemId: string | null;\n    setActiveItem: (id: string) => void;\n    clearActiveItemWithDelay: () => void;\n}\n\nconst ContextMenuHighlightContext = React.createContext<ContextMenuHighlightContextType | null>(null);\n\nexport interface ContextMenuProps extends ContextMenuPrimitive.Root.Props {}\n\nfunction ContextMenu({ ...props }: ContextMenuProps) {\n    return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\n}\n\nfunction ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {\n    return <ContextMenuPrimitive.Portal data-slot=\"context-menu-portal\" {...props} />;\n}\n\nfunction ContextMenuTrigger({ className, ...props }: ContextMenuPrimitive.Trigger.Props) {\n    return (\n        <ContextMenuPrimitive.Trigger\n            data-slot=\"context-menu-trigger\"\n            className={cn(\"select-none\", className)}\n            {...props}\n        />\n    );\n}\n\nexport interface ContextMenuContentProps\n    extends\n        ContextMenuPrimitive.Popup.Props,\n        Pick<ContextMenuPrimitive.Positioner.Props, \"align\" | \"alignOffset\" | \"side\" | \"sideOffset\"> {\n    transition?: Transition;\n}\n\nfunction ContextMenuContent({\n    align = \"start\",\n    alignOffset = 4,\n    side = \"right\",\n    sideOffset = 0,\n    transition = { type: \"spring\", stiffness: 380, damping: 26, mass: 0.8 },\n    className,\n    children,\n    ...props\n}: ContextMenuContentProps) {\n    const parentHighlightContext = React.useContext(ContextMenuHighlightContext);\n\n    const generatedLayoutId = React.useId();\n    const highlightLayoutId = parentHighlightContext?.highlightLayoutId ?? generatedLayoutId;\n\n    const [localActiveItemId, setLocalActiveItemId] = React.useState<string | null>(null);\n    const timeoutRef = React.useRef<any>(null);\n\n    const setActiveItem =\n        parentHighlightContext?.setActiveItem ??\n        React.useCallback((id: string) => {\n            if (timeoutRef.current) clearTimeout(timeoutRef.current);\n            setLocalActiveItemId(id);\n        }, []);\n\n    const clearActiveItemWithDelay =\n        parentHighlightContext?.clearActiveItemWithDelay ??\n        React.useCallback(() => {\n            if (timeoutRef.current) clearTimeout(timeoutRef.current);\n            timeoutRef.current = setTimeout(() => {\n                setLocalActiveItemId(null);\n            }, 180);\n        }, []);\n\n    const activeItemId = parentHighlightContext ? parentHighlightContext.activeItemId : localActiveItemId;\n\n    const highlightValue: ContextMenuHighlightContextType = parentHighlightContext ?? {\n        highlightLayoutId,\n        activeItemId,\n        setActiveItem,\n        clearActiveItemWithDelay,\n    };\n\n    return (\n        <ContextMenuPrimitive.Portal>\n            <ContextMenuPrimitive.Positioner\n                className=\"isolate z-50 outline-none\"\n                align={align}\n                alignOffset={alignOffset}\n                side={side}\n                sideOffset={sideOffset}>\n                <ContextMenuPrimitive.Popup\n                    data-slot=\"context-menu-content\"\n                    {...props}\n                    render={(popupProps, state) => (\n                        <AnimatePresence initial={false}>\n                            {state.open && (\n                                <motion.div\n                                    {...popupProps}\n                                    key=\"context-menu-content\"\n                                    initial={{\n                                        opacity: 0,\n                                        scale: 0.94,\n                                        y: side === \"bottom\" ? -8 : side === \"top\" ? 8 : 0,\n                                        x: side === \"right\" ? -8 : side === \"left\" ? 8 : 0,\n                                    }}\n                                    animate={{ opacity: 1, scale: 1, y: 0, x: 0 }}\n                                    exit={{\n                                        opacity: 0,\n                                        scale: 0.94,\n                                        y: side === \"bottom\" ? -6 : side === \"top\" ? 6 : 0,\n                                        x: side === \"right\" ? -6 : side === \"left\" ? 6 : 0,\n                                    }}\n                                    transition={transition}\n                                    className={cn(\n                                        \"ring-foreground/10 bg-popover text-popover-foreground z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md ring-1 outline-none\",\n                                        className,\n                                    )}>\n                                    <ContextMenuHighlightContext.Provider value={highlightValue}>\n                                        <div onPointerLeave={clearActiveItemWithDelay} className=\"contents\">\n                                            {children}\n                                        </div>\n                                    </ContextMenuHighlightContext.Provider>\n                                </motion.div>\n                            )}\n                        </AnimatePresence>\n                    )}\n                />\n            </ContextMenuPrimitive.Positioner>\n        </ContextMenuPrimitive.Portal>\n    );\n}\n\nfunction ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {\n    return <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />;\n}\n\nfunction ContextMenuLabel({\n    className,\n    inset,\n    ...props\n}: ContextMenuPrimitive.GroupLabel.Props & {\n    inset?: boolean;\n}) {\n    return (\n        <ContextMenuPrimitive.Group>\n            <ContextMenuPrimitive.GroupLabel\n                data-slot=\"context-menu-label\"\n                data-inset={inset}\n                className={cn(\"text-muted-foreground px-2 py-1.5 text-xs font-medium data-inset:pl-8\", className)}\n                {...props}\n            />\n        </ContextMenuPrimitive.Group>\n    );\n}\n\nexport interface ContextMenuItemProps extends ContextMenuPrimitive.Item.Props {\n    inset?: boolean;\n    variant?: \"default\" | \"destructive\";\n}\n\nfunction ContextMenuItem({ className, inset, variant = \"default\", children, ...props }: ContextMenuItemProps) {\n    const highlightContext = React.useContext(ContextMenuHighlightContext);\n    const itemId = React.useId();\n\n    return (\n        <ContextMenuPrimitive.Item\n            data-slot=\"context-menu-item\"\n            data-inset={inset}\n            data-variant={variant}\n            {...props}\n            render={(itemProps, state) => {\n                const isCurrentActive = highlightContext?.activeItemId === itemId;\n\n                return (\n                    <motion.div\n                        {...itemProps}\n                        onPointerEnter={(e: any) => {\n                            itemProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        onFocus={(e: any) => {\n                            itemProps.onFocus?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        whileTap={{ scale: 0.98 }}\n                        transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n                        className={cn(\n                            \"group/context-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                            (state.highlighted || isCurrentActive) &&\n                                (variant === \"destructive\" ? \"text-destructive\" : \"text-accent-foreground\"),\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId && (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className={cn(\n                                    \"pointer-events-none absolute inset-0 z-0 rounded-sm\",\n                                    variant === \"destructive\"\n                                        ? \"bg-destructive/10 dark:bg-destructive/20\"\n                                        : \"bg-accent\",\n                                )}\n                                transition={{ type: \"spring\", stiffness: 450, damping: 32 }}\n                            />\n                        )}\n                        <span className=\"relative z-10 flex w-full items-center gap-2\">{children}</span>\n                    </motion.div>\n                );\n            }}\n        />\n    );\n}\n\nfunction ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {\n    return <ContextMenuPrimitive.SubmenuRoot data-slot=\"context-menu-sub\" {...props} />;\n}\n\nexport interface ContextMenuSubTriggerProps extends ContextMenuPrimitive.SubmenuTrigger.Props {\n    inset?: boolean;\n}\n\nfunction ContextMenuSubTrigger({ className, inset, children, ...props }: ContextMenuSubTriggerProps) {\n    const highlightContext = React.useContext(ContextMenuHighlightContext);\n    const itemId = React.useId();\n\n    return (\n        <ContextMenuPrimitive.SubmenuTrigger\n            data-slot=\"context-menu-sub-trigger\"\n            data-inset={inset}\n            {...props}\n            render={(triggerProps, state) => {\n                const isCurrentActive = highlightContext?.activeItemId === itemId;\n\n                return (\n                    <motion.div\n                        {...triggerProps}\n                        onPointerEnter={(e: any) => {\n                            triggerProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        onFocus={(e: any) => {\n                            triggerProps.onFocus?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        whileTap={{ scale: 0.98 }}\n                        transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n                        className={cn(\n                            \"group/context-menu-sub-trigger relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                            (state.highlighted || state.open || isCurrentActive) && \"text-accent-foreground\",\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId ? (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className=\"bg-accent pointer-events-none absolute inset-0 z-0 rounded-sm\"\n                                transition={{ type: \"spring\", stiffness: 450, damping: 32 }}\n                            />\n                        ) : state.open ? (\n                            <div className=\"bg-accent/60 pointer-events-none absolute inset-0 z-0 rounded-sm\" />\n                        ) : null}\n                        <span className=\"relative z-10 flex w-full items-center\">{children}</span>\n                        <ChevronRightIcon className=\"relative z-10 ml-auto\" />\n                    </motion.div>\n                );\n            }}\n        />\n    );\n}\n\nfunction ContextMenuSubContent({\n    align = \"start\",\n    alignOffset = -3,\n    side = \"right\",\n    sideOffset = 4,\n    className,\n    ...props\n}: React.ComponentProps<typeof ContextMenuContent>) {\n    return (\n        <ContextMenuContent\n            data-slot=\"context-menu-sub-content\"\n            className={cn(\"w-auto min-w-[140px] shadow-lg\", className)}\n            align={align}\n            alignOffset={alignOffset}\n            side={side}\n            sideOffset={sideOffset}\n            {...props}\n        />\n    );\n}\n\nexport interface ContextMenuCheckboxItemProps extends ContextMenuPrimitive.CheckboxItem.Props {}\n\nfunction ContextMenuCheckboxItem({ className, children, checked, ...props }: ContextMenuCheckboxItemProps) {\n    const highlightContext = React.useContext(ContextMenuHighlightContext);\n    const itemId = React.useId();\n\n    return (\n        <ContextMenuPrimitive.CheckboxItem\n            data-slot=\"context-menu-checkbox-item\"\n            className={cn(\n                \"group/context-menu-checkbox-item relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                className,\n            )}\n            checked={checked}\n            {...props}\n            render={(itemProps, state) => {\n                const isCurrentActive = highlightContext?.activeItemId === itemId;\n\n                return (\n                    <motion.div\n                        {...itemProps}\n                        onPointerEnter={(e: any) => {\n                            itemProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        onFocus={(e: any) => {\n                            itemProps.onFocus?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        whileTap={{ scale: 0.98 }}\n                        transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n                        className={cn(\n                            \"group/context-menu-checkbox-item relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                            (state.highlighted || isCurrentActive) && \"text-accent-foreground\",\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId && (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className=\"bg-accent pointer-events-none absolute inset-0 z-0 rounded-sm\"\n                                transition={{ type: \"spring\", stiffness: 450, damping: 32 }}\n                            />\n                        )}\n                        <span className=\"relative z-10 flex items-center gap-2\">{children}</span>\n                        <span\n                            className=\"pointer-events-none absolute right-2 z-10 flex items-center justify-center\"\n                            data-slot=\"context-menu-checkbox-item-indicator\">\n                            <ContextMenuPrimitive.CheckboxItemIndicator>\n                                <motion.span\n                                    initial={{ scale: 0, opacity: 0 }}\n                                    animate={{ scale: 1, opacity: 1 }}\n                                    exit={{ scale: 0, opacity: 0 }}\n                                    transition={{ type: \"spring\", stiffness: 400, damping: 22 }}\n                                    className=\"flex items-center justify-center\">\n                                    <CheckIcon />\n                                </motion.span>\n                            </ContextMenuPrimitive.CheckboxItemIndicator>\n                        </span>\n                    </motion.div>\n                );\n            }}\n        />\n    );\n}\n\nfunction ContextMenuRadioGroup({ ...props }: ContextMenuPrimitive.RadioGroup.Props) {\n    return <ContextMenuPrimitive.RadioGroup data-slot=\"context-menu-radio-group\" {...props} />;\n}\n\nexport interface ContextMenuRadioItemProps extends ContextMenuPrimitive.RadioItem.Props {}\n\nfunction ContextMenuRadioItem({ className, children, ...props }: ContextMenuRadioItemProps) {\n    const highlightContext = React.useContext(ContextMenuHighlightContext);\n    const itemId = React.useId();\n\n    return (\n        <ContextMenuPrimitive.RadioItem\n            data-slot=\"context-menu-radio-item\"\n            className={cn(\n                \"group/context-menu-radio-item relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                className,\n            )}\n            {...props}\n            render={(itemProps, state) => {\n                const isCurrentActive = highlightContext?.activeItemId === itemId;\n\n                return (\n                    <motion.div\n                        {...itemProps}\n                        onPointerEnter={(e: any) => {\n                            itemProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        onFocus={(e: any) => {\n                            itemProps.onFocus?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        whileTap={{ scale: 0.98 }}\n                        transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n                        className={cn(\n                            \"group/context-menu-radio-item relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                            (state.highlighted || isCurrentActive) && \"text-accent-foreground\",\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId && (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className=\"bg-accent pointer-events-none absolute inset-0 z-0 rounded-sm\"\n                                transition={{ type: \"spring\", stiffness: 450, damping: 32 }}\n                            />\n                        )}\n                        <span className=\"relative z-10 flex items-center gap-2\">{children}</span>\n                        <span\n                            className=\"pointer-events-none absolute right-2 z-10 flex items-center justify-center\"\n                            data-slot=\"context-menu-radio-item-indicator\">\n                            <ContextMenuPrimitive.RadioItemIndicator>\n                                <motion.span\n                                    initial={{ scale: 0, opacity: 0 }}\n                                    animate={{ scale: 1, opacity: 1 }}\n                                    exit={{ scale: 0, opacity: 0 }}\n                                    transition={{ type: \"spring\", stiffness: 400, damping: 22 }}\n                                    className=\"flex items-center justify-center\">\n                                    <CheckIcon />\n                                </motion.span>\n                            </ContextMenuPrimitive.RadioItemIndicator>\n                        </span>\n                    </motion.div>\n                );\n            }}\n        />\n    );\n}\n\nfunction ContextMenuSeparator({ className, ...props }: ContextMenuPrimitive.Separator.Props) {\n    return (\n        <ContextMenuPrimitive.Separator\n            data-slot=\"context-menu-separator\"\n            className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n            {...props}\n        />\n    );\n}\n\nfunction ContextMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n    return (\n        <span\n            data-slot=\"context-menu-shortcut\"\n            className={cn(\n                \"text-muted-foreground group-focus/context-menu-item:text-accent-foreground ml-auto text-xs tracking-widest\",\n                className,\n            )}\n            {...props}\n        />\n    );\n}\n\nexport {\n    ContextMenu,\n    ContextMenuTrigger,\n    ContextMenuContent,\n    ContextMenuItem,\n    ContextMenuCheckboxItem,\n    ContextMenuRadioItem,\n    ContextMenuLabel,\n    ContextMenuSeparator,\n    ContextMenuShortcut,\n    ContextMenuGroup,\n    ContextMenuPortal,\n    ContextMenuSub,\n    ContextMenuSubContent,\n    ContextMenuSubTrigger,\n    ContextMenuRadioGroup,\n};\n"
    }
  ],
  "mcp": {
    "description": "Displays a menu located at the pointer position, triggered by a right-click or long-press. Provides smooth spring scale popup, submenus, checkbox and radio transitions.",
    "tags": [
      "component",
      "context-menu",
      "menu",
      "paceui",
      "animation"
    ]
  }
}