{
  "title": "Navigation Menu",
  "description": "A collection of links and navigation elements with shared gliding indicator, spring popup scale entrance, and viewport dimension morphing.",
  "name": "motion-navigation-menu",
  "dependencies": [
    "motion",
    "@base-ui/react",
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [],
  "type": "registry:ui",
  "files": [
    {
      "path": "components/paceui/motion/navigation-menu.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\n\nimport { NavigationMenu as NavigationMenuPrimitive } from \"@base-ui/react/navigation-menu\";\nimport { cva } from \"class-variance-authority\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface NavigationMenuHighlightContextType {\n    highlightLayoutId: string;\n    activeTriggerId: string | null;\n    openItemValue: any;\n    hasActiveTrigger: boolean;\n    isScrolling: boolean;\n    setActiveTrigger: (id: string) => void;\n    clearActiveTriggerWithDelay: () => void;\n    closeMenu: () => void;\n}\n\nconst NavigationMenuHighlightContext = React.createContext<NavigationMenuHighlightContextType | null>(null);\n\nexport const useNavigationMenu = () => React.useContext(NavigationMenuHighlightContext);\n\nexport interface NavigationMenuProps extends NavigationMenuPrimitive.Root.Props {\n    positionerProps?: NavigationMenuPositionerProps;\n    closeOnScroll?: boolean;\n}\n\nfunction NavigationMenu({\n    className,\n    children,\n    positionerProps,\n    onValueChange,\n    closeOnScroll = true,\n    value: controlledValue,\n    ...props\n}: NavigationMenuProps) {\n    const generatedLayoutId = React.useId();\n    const [uncontrolledValue, setUncontrolledValue] = React.useState<any>(null);\n    const isControlled = controlledValue !== undefined;\n    const openItemValue = isControlled ? controlledValue : uncontrolledValue;\n\n    const [activeTriggerId, setActiveTriggerId] = React.useState<string | null>(null);\n    const [isScrolling, setIsScrolling] = React.useState(false);\n    const isScrollingRef = React.useRef(false);\n    const timeoutRef = React.useRef<any>(null);\n    const scrollTimeoutRef = React.useRef<any>(null);\n\n    const closeMenu = React.useCallback(() => {\n        if (!isControlled) {\n            setUncontrolledValue(null);\n        }\n        setActiveTriggerId(null);\n        if (isControlled && onValueChange) {\n            onValueChange(null, { reason: \"none\" } as any);\n        }\n    }, [isControlled, onValueChange]);\n\n    const handleValueChange = React.useCallback(\n        (value: any, eventDetails: any) => {\n            if (isScrollingRef.current && value) {\n                return;\n            }\n            if (!isControlled) {\n                setUncontrolledValue(value);\n            }\n            onValueChange?.(value, eventDetails);\n            if (!value) {\n                if (timeoutRef.current) clearTimeout(timeoutRef.current);\n                timeoutRef.current = setTimeout(() => {\n                    setActiveTriggerId(null);\n                }, 150);\n            }\n        },\n        [isControlled, onValueChange],\n    );\n\n    const setActiveTrigger = React.useCallback((id: string) => {\n        if (isScrollingRef.current) return;\n        if (timeoutRef.current) clearTimeout(timeoutRef.current);\n        setActiveTriggerId(id);\n    }, []);\n\n    const clearActiveTriggerWithDelay = React.useCallback(() => {\n        if (timeoutRef.current) clearTimeout(timeoutRef.current);\n        timeoutRef.current = setTimeout(() => {\n            if (!openItemValue) {\n                setActiveTriggerId(null);\n            }\n        }, 150);\n    }, [openItemValue]);\n\n    React.useEffect(() => {\n        if (!closeOnScroll) return;\n\n        const handleScroll = (event: Event) => {\n            const target = event.target as Node | null;\n            if (target && target instanceof Element) {\n                if (target.closest('[data-slot=\"navigation-menu-popup\"]')) {\n                    return;\n                }\n            }\n\n            closeMenu();\n\n            isScrollingRef.current = true;\n            setIsScrolling(true);\n\n            if (scrollTimeoutRef.current) {\n                clearTimeout(scrollTimeoutRef.current);\n            }\n            scrollTimeoutRef.current = setTimeout(() => {\n                isScrollingRef.current = false;\n                setIsScrolling(false);\n            }, 150);\n        };\n\n        window.addEventListener(\"scroll\", handleScroll, { passive: true, capture: true });\n\n        return () => {\n            window.removeEventListener(\"scroll\", handleScroll, { capture: true });\n            if (scrollTimeoutRef.current) {\n                clearTimeout(scrollTimeoutRef.current);\n            }\n        };\n    }, [closeOnScroll, closeMenu]);\n\n    const hasActiveTrigger = Boolean(activeTriggerId || openItemValue);\n\n    const highlightValue = React.useMemo(\n        () => ({\n            highlightLayoutId: generatedLayoutId,\n            activeTriggerId,\n            openItemValue,\n            hasActiveTrigger,\n            isScrolling,\n            setActiveTrigger,\n            clearActiveTriggerWithDelay,\n            closeMenu,\n        }),\n        [\n            generatedLayoutId,\n            activeTriggerId,\n            openItemValue,\n            hasActiveTrigger,\n            isScrolling,\n            setActiveTrigger,\n            clearActiveTriggerWithDelay,\n            closeMenu,\n        ],\n    );\n\n    return (\n        <NavigationMenuHighlightContext.Provider value={highlightValue}>\n            <NavigationMenuPrimitive.Root\n                data-slot=\"navigation-menu\"\n                className={cn(\n                    \"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center\",\n                    isScrolling && \"pointer-events-none\",\n                    className,\n                )}\n                value={openItemValue}\n                onValueChange={handleValueChange}\n                onPointerLeave={clearActiveTriggerWithDelay}\n                {...props}>\n                {children}\n                <NavigationMenuPositioner {...positionerProps} />\n            </NavigationMenuPrimitive.Root>\n        </NavigationMenuHighlightContext.Provider>\n    );\n}\n\nexport interface NavigationMenuListProps extends NavigationMenuPrimitive.List.Props {}\n\nfunction NavigationMenuList({ className, children, ...props }: NavigationMenuListProps) {\n    return (\n        <NavigationMenuPrimitive.List\n            data-slot=\"navigation-menu-list\"\n            className={cn(\"group flex flex-1 list-none items-center justify-center gap-0\", className)}\n            {...props}>\n            {children}\n        </NavigationMenuPrimitive.List>\n    );\n}\n\nexport interface NavigationMenuItemProps extends NavigationMenuPrimitive.Item.Props {}\n\nfunction NavigationMenuItem({ className, ...props }: NavigationMenuItemProps) {\n    return (\n        <NavigationMenuPrimitive.Item\n            data-slot=\"navigation-menu-item\"\n            className={cn(\"relative\", className)}\n            {...props}\n        />\n    );\n}\n\nconst navigationMenuTriggerStyle = cva(\n    \"bg-background hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-md px-3.5 py-1.5 text-sm font-medium transition-all focus-visible:ring-[3px] focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-8.5 w-max items-center justify-center disabled:pointer-events-none outline-none\",\n);\n\nexport interface NavigationMenuTriggerProps extends NavigationMenuPrimitive.Trigger.Props {\n    hideArrow?: boolean;\n    isActive?: boolean;\n}\n\nfunction NavigationMenuTrigger({\n    className,\n    children,\n    hideArrow = false,\n    isActive = false,\n    ...props\n}: NavigationMenuTriggerProps) {\n    const highlightContext = React.useContext(NavigationMenuHighlightContext);\n    const triggerId = React.useId();\n\n    return (\n        <NavigationMenuPrimitive.Trigger\n            data-slot=\"navigation-menu-trigger\"\n            nativeButton={false}\n            {...props}\n            render={(triggerProps, state) => {\n                const isHovered = highlightContext?.activeTriggerId === triggerId;\n                const isOpen = state.open;\n                const isItemActive = isHovered || isOpen;\n                const isCurrentActive = isItemActive || (!highlightContext?.hasActiveTrigger && isActive);\n\n                const {\n                    onPointerDown: _onPointerDown,\n                    onMouseDown: _onMouseDown,\n                    onClick: _onClick,\n                    ...cleanTriggerProps\n                } = triggerProps;\n\n                return (\n                    <div\n                        {...(cleanTriggerProps as any)}\n                        ref={(triggerProps as any).ref}\n                        onPointerEnter={(e: any) => {\n                            if (highlightContext?.isScrolling) return;\n                            triggerProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveTrigger(triggerId);\n                        }}\n                        onFocus={(e: any) => {\n                            if (highlightContext?.isScrolling) return;\n                            triggerProps.onFocus?.(e);\n                            highlightContext?.setActiveTrigger(triggerId);\n                        }}\n                        onClick={(e: any) => {\n                            props.onClick?.(e);\n                        }}\n                        className={cn(\n                            \"group/navigation-menu-trigger relative inline-flex h-8.5 w-max cursor-default items-center justify-center rounded-md px-3.5 py-1.5 text-sm font-medium outline-none select-none disabled:pointer-events-none disabled:opacity-50\",\n                            highlightContext?.isScrolling && \"pointer-events-none\",\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId && (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className=\"bg-muted pointer-events-none absolute inset-0 z-0 rounded-md\"\n                                transition={{ duration: 0.18, ease: \"easeInOut\" }}\n                            />\n                        )}\n                        <span className=\"relative z-10 flex items-center justify-center\">\n                            {children}\n                            {!hideArrow && (\n                                <ChevronDownIcon\n                                    className={cn(\n                                        \"relative top-px ml-1 size-3 transition-transform duration-200 ease-out\",\n                                        state.open && \"rotate-180\",\n                                    )}\n                                    aria-hidden=\"true\"\n                                />\n                            )}\n                        </span>\n                    </div>\n                );\n            }}\n        />\n    );\n}\n\nexport interface NavigationMenuContentProps extends NavigationMenuPrimitive.Content.Props {}\n\nfunction NavigationMenuContent({ className, ...props }: NavigationMenuContentProps) {\n    return (\n        <NavigationMenuPrimitive.Content\n            data-slot=\"navigation-menu-content\"\n            className={cn(\n                \"h-full w-max max-w-[calc(100vw-32px)] p-1 outline-none\",\n                \"[transform:translateX(0)] [transition:opacity_240ms_ease,transform_300ms_cubic-bezier(0.16,1,0.3,1)]\",\n                \"data-ending-style:opacity-0 data-starting-style:opacity-0\",\n                \"data-starting-style:data-[activation-direction*=left]:[transform:translateX(-40%)] data-starting-style:data-[activation-direction*=right]:[transform:translateX(40%)]\",\n                \"data-ending-style:data-[activation-direction*=left]:[transform:translateX(40%)] data-ending-style:data-[activation-direction*=right]:[transform:translateX(-40%)]\",\n                \"group-data-[activation-direction*=left]:data-starting-style:[transform:translateX(-40%)] group-data-[activation-direction*=right]:data-starting-style:[transform:translateX(40%)]\",\n                \"group-data-[activation-direction*=left]:data-ending-style:[transform:translateX(40%)] group-data-[activation-direction*=right]:data-ending-style:[transform:translateX(-40%)]\",\n                \"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none\",\n                className,\n            )}\n            {...props}\n        />\n    );\n}\n\nexport interface NavigationMenuPositionerProps extends NavigationMenuPrimitive.Positioner.Props {\n    popupClassName?: string;\n}\n\nfunction NavigationMenuPositioner({\n    className,\n    side = \"bottom\",\n    sideOffset = 6,\n    align = \"start\",\n    alignOffset = 0,\n    popupClassName,\n    ...props\n}: NavigationMenuPositionerProps) {\n    return (\n        <NavigationMenuPrimitive.Portal>\n            <NavigationMenuPrimitive.Positioner\n                data-slot=\"navigation-menu-positioner\"\n                side={side}\n                sideOffset={sideOffset}\n                align={align}\n                alignOffset={alignOffset}\n                className={cn(\n                    \"isolate z-50 h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:absolute data-[side=bottom]:before:inset-x-0 data-[side=bottom]:before:-top-3.5 data-[side=bottom]:before:h-3.5 data-[side=bottom]:before:content-['']\",\n                    className,\n                )}\n                {...props}>\n                <NavigationMenuPrimitive.Popup\n                    data-slot=\"navigation-menu-popup\"\n                    className={cn(\n                        \"bg-popover text-popover-foreground ring-foreground/10 relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-md shadow-md ring-1 transition-[width,height,opacity,transform,translate,scale] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:-translate-y-1.5 data-ending-style:scale-96 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:-translate-y-1.5 data-starting-style:scale-96 data-starting-style:opacity-0\",\n                        popupClassName,\n                    )}>\n                    <NavigationMenuPrimitive.Viewport\n                        data-slot=\"navigation-menu-viewport\"\n                        className=\"relative h-full w-full overflow-hidden\"\n                    />\n                </NavigationMenuPrimitive.Popup>\n            </NavigationMenuPrimitive.Positioner>\n        </NavigationMenuPrimitive.Portal>\n    );\n}\n\nexport interface NavigationMenuLinkProps extends NavigationMenuPrimitive.Link.Props {}\n\nfunction NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps) {\n    return (\n        <NavigationMenuPrimitive.Link\n            data-slot=\"navigation-menu-link\"\n            className={cn(\n                \"data-[active=true]:focus:bg-muted data-[active=true]:hover:bg-muted data-[active=true]:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-1.5 rounded-sm p-1.5 text-sm transition-colors outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4\",\n                className,\n            )}\n            {...props}\n            render={(linkProps) => (\n                <motion.div {...(linkProps as any)} transition={{ duration: 0.15, ease: \"easeOut\" }} />\n            )}\n        />\n    );\n}\n\nexport interface NavigationMenuIndicatorProps extends NavigationMenuPrimitive.Icon.Props {}\n\nfunction NavigationMenuIndicator({ className, ...props }: NavigationMenuIndicatorProps) {\n    return (\n        <NavigationMenuPrimitive.Icon\n            data-slot=\"navigation-menu-indicator\"\n            className={cn(\"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden\", className)}\n            {...props}\n            render={(iconProps, state) => (\n                <AnimatePresence>\n                    {state.open && (\n                        <motion.div\n                            {...(iconProps as any)}\n                            initial={{ opacity: 0, y: -4 }}\n                            animate={{ opacity: 1, y: 0 }}\n                            exit={{ opacity: 0, y: -4 }}\n                            transition={{ duration: 0.18, ease: \"easeOut\" }}\n                            className=\"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden\">\n                            <div className=\"bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n                        </motion.div>\n                    )}\n                </AnimatePresence>\n            )}\n        />\n    );\n}\n\nexport interface NavigationMenuArrowProps extends NavigationMenuPrimitive.Arrow.Props {}\n\nfunction NavigationMenuArrow({ className, ...props }: NavigationMenuArrowProps) {\n    return (\n        <NavigationMenuPrimitive.Arrow\n            data-slot=\"navigation-menu-arrow\"\n            className={cn(\n                \"before:border-border before:bg-popover relative block h-1.5 w-3 overflow-clip transition-[left,right] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] before:absolute before:bottom-0 before:left-1/2 before:block before:h-[calc(6px*sqrt(2))] before:w-[calc(6px*sqrt(2))] before:-translate-x-1/2 before:translate-y-1/2 before:rotate-45 before:border before:content-[''] data-[side=bottom]:top-[-6px] data-[side=left]:right-[-9px] data-[side=left]:rotate-90 data-[side=right]:left-[-9px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-6px] data-[side=top]:rotate-180\",\n                className,\n            )}\n            {...props}\n        />\n    );\n}\n\nfunction NavigationMenuViewport({ className, ...props }: NavigationMenuPrimitive.Viewport.Props) {\n    return (\n        <NavigationMenuPrimitive.Viewport\n            data-slot=\"navigation-menu-viewport\"\n            className={cn(\"relative size-full overflow-hidden\", className)}\n            {...props}\n        />\n    );\n}\n\nexport {\n    NavigationMenu,\n    NavigationMenuContent,\n    NavigationMenuIndicator,\n    NavigationMenuItem,\n    NavigationMenuLink,\n    NavigationMenuList,\n    NavigationMenuTrigger,\n    navigationMenuTriggerStyle,\n    NavigationMenuPositioner,\n    NavigationMenuArrow,\n    NavigationMenuViewport,\n};\n"
    }
  ],
  "mcp": {
    "description": "A collection of links for navigating websites. Provides gliding hover/focus trigger indicators, spring popup scale reveals, and animated indicator arrows.",
    "tags": [
      "component",
      "navigation-menu",
      "navigation",
      "menu",
      "paceui",
      "animation"
    ]
  }
}