{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "navigation-menu",
  "title": "Navigation Menu",
  "description": "Navigation Menu from the Get IT Done design system.",
  "dependencies": [
    "@radix-ui/react-navigation-menu@^1.4.1",
    "class-variance-authority@^0.7.0",
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/navigation-menu.tsx",
      "content": "'use client';\n\nimport * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu';\nimport { cva } from 'class-variance-authority';\nimport { ChevronDown } from 'lucide-react';\n\nimport { ComponentPropsWithoutRef, ElementRef, forwardRef, MutableRefObject, ReactNode, useCallback, useEffect, useLayoutEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { menuItemFocusVisible } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\n\ninterface NavigationMenuProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> {\n  // Minimum distance in px between the open panel and the viewport edges. The panel is shifted sideways to keep it inside.\n  collisionPadding?: number;\n}\n\nconst NavigationMenu = forwardRef<ElementRef<typeof NavigationMenuPrimitive.Root>, NavigationMenuProps>(\n  ({ className, children, collisionPadding, ...props }, ref) => (\n    <NavigationMenuPrimitive.Root\n      ref={ref}\n      className={cn('relative z-sticky flex max-w-max flex-1 items-center justify-center', className)}\n      {...props}\n    >\n      {children}\n      <NavigationMenuViewport collisionPadding={collisionPadding} />\n    </NavigationMenuPrimitive.Root>\n  ),\n);\nNavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;\n\nconst NavigationMenuList = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.List>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.List\n    ref={ref}\n    className={cn('group flex flex-1 list-none items-center justify-center space-x-1', className)}\n    {...props}\n  />\n));\nNavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;\n\nconst NavigationMenuItem = NavigationMenuPrimitive.Item;\n\nconst navigationMenuTriggerStyle = cva(\n  'group inline-flex h-control-md w-max items-center justify-center rounded-control bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-muted hover:text-foreground focus:bg-muted focus:text-foreground focus-visible:outline-none focus-visible:ring-focus focus-visible:ring-offset-focus focus-visible:ring-offset-background focus-visible:ring-ring-default disabled:pointer-events-none disabled:opacity-disabled data-[active]:bg-muted data-[state=open]:bg-muted',\n);\n\ninterface NavigationMenuTriggerProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> {\n  // Leading icon before the label. In a top bar end slot with `data-topbar-action` it is all that stays below sm (the label is the tooltip).\n  icon?: ReactNode;\n}\n\nconst NavigationMenuTrigger = forwardRef<ElementRef<typeof NavigationMenuPrimitive.Trigger>, NavigationMenuTriggerProps>(({ className, children, type = 'button', icon, title, ...props }, ref) => (\n  <NavigationMenuPrimitive.Trigger\n    ref={ref}\n    type={type}\n    // In a top bar end slot (`data-topbar-action`) the label is hidden below sm: its text is the tooltip.\n    title={title ?? ((props as Record<string, unknown>)['data-topbar-action'] !== undefined && typeof children === 'string' ? children : undefined)}\n    className={cn(navigationMenuTriggerStyle(), 'group', className)}\n    {...props}\n  >\n    {icon && (\n      <span aria-hidden=\"true\" className=\"mr-2 inline-flex h-icon-md w-icon-md shrink-0 items-center justify-center group-data-[topbar-action]:max-sm:mr-0\">\n        {icon}\n      </span>\n    )}\n    {children}{' '}\n    <ChevronDown\n      data-chevron=\"\"\n      className=\"relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180\"\n      aria-hidden=\"true\"\n    />\n  </NavigationMenuPrimitive.Trigger>\n));\nNavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;\n\nconst NavigationMenuContent = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Content>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Content\n    ref={ref}\n    className={cn(\n      'left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ',\n      className,\n    )}\n    {...props}\n  />\n));\nNavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;\n\nconst NavigationMenuLink = NavigationMenuPrimitive.Link;\n\ninterface NavigationMenuViewportProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> {\n  collisionPadding?: number;\n}\n\nconst DEFAULT_COLLISION_PADDING = 16;\n// Gap in px between the root and the panel (the mt-[6px] of the viewport). The indicator arrow fills it.\nconst PANEL_OFFSET = 6;\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\n// The panel renders in a portal on the dropdown layer, so the page chrome stacking context (header, topbar) can no longer clip or under-cover it (a cookie banner, z-banner, stays below). The portal anchor is `fixed` and placed from the root rect: below the root, shifted sideways to stay inside the viewport, and capped to the height that is left below the root. Radix keeps the content in the viewport element, so its focus and keyboard handling is unchanged.\nconst NavigationMenuViewport = forwardRef<ElementRef<typeof NavigationMenuPrimitive.Viewport>, NavigationMenuViewportProps>(\n  ({ className, collisionPadding = DEFAULT_COLLISION_PADDING, ...props }, ref) => {\n    // State instead of refs: the panel only exists while a menu is open, so the effects must re-run when it mounts.\n    const [origin, setOrigin] = useState<HTMLSpanElement | null>(null);\n    const [anchor, setAnchor] = useState<HTMLDivElement | null>(null);\n    const [viewport, setViewport] = useState<HTMLDivElement | null>(null);\n    const [mounted, setMounted] = useState(false);\n    useEffect(() => setMounted(true), []);\n    const root = origin?.parentElement ?? null;\n    // Stable callback ref: an inline function would detach and attach on every render and loop through the state setter.\n    const setViewportRef = useCallback(\n      (node: HTMLDivElement | null) => {\n        setViewport(node);\n        if (typeof ref === 'function') ref(node);\n        else if (ref) (ref as MutableRefObject<HTMLDivElement | null>).current = node;\n      },\n      [ref],\n    );\n\n    const place = useCallback(() => {\n      if (!anchor || !root) return;\n      const rootRect = root.getBoundingClientRect();\n      const availableWidth = document.documentElement.clientWidth;\n      const availableHeight = document.documentElement.clientHeight;\n      const width = viewport?.offsetWidth ?? 0;\n      let shift = 0;\n      if (width > 0) {\n        // Radix anchors the panel at the left edge of the root. Shift it left when it would run past the right viewport edge, and right when it would run past the left edge.\n        const overflow = rootRect.left + width - (availableWidth - collisionPadding);\n        if (overflow > 0) shift = -overflow;\n        if (rootRect.left + shift < collisionPadding) shift = collisionPadding - rootRect.left;\n      }\n      anchor.style.left = `${rootRect.left + shift}px`;\n      anchor.style.top = `${rootRect.bottom}px`;\n      anchor.style.setProperty('--navigation-menu-available-height', `${Math.max(availableHeight - rootRect.bottom - PANEL_OFFSET - collisionPadding, 0)}px`);\n    }, [anchor, root, viewport, collisionPadding]);\n\n    // Runs on first open (the viewport element appears), on content size changes (observer), on window resize and on scroll of any ancestor.\n    useIsomorphicLayoutEffect(() => {\n      place();\n      if (!anchor || !root) return undefined;\n      window.addEventListener('resize', place);\n      window.addEventListener('scroll', place, true);\n      const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(place);\n      observer?.observe(root);\n      if (viewport) observer?.observe(viewport);\n      return () => {\n        window.removeEventListener('resize', place);\n        window.removeEventListener('scroll', place, true);\n        observer?.disconnect();\n      };\n    }, [place, anchor, root, viewport]);\n\n    const panel = (\n      <div ref={setAnchor} data-slot=\"navigation-menu-viewport-anchor\" className={cn('fixed z-dropdown flex justify-center')}>\n        <NavigationMenuPrimitive.Viewport\n          className={cn(\n            'origin-top-center relative mt-[6px] h-[var(--radix-navigation-menu-viewport-height)] max-h-[var(--navigation-menu-available-height)] w-full max-w-[calc(100vw-theme(spacing.8))] overflow-y-auto overflow-x-hidden overscroll-contain rounded-popover border border-solid border-hairline bg-popover text-popover-foreground shadow-popover duration-150 [scroll-padding-block:theme(spacing.2)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-[0.98] md:w-[var(--radix-navigation-menu-viewport-width)]',\n            className,\n          )}\n          ref={setViewportRef}\n          {...props}\n        />\n      </div>\n    );\n    return (\n      <>\n        <span ref={setOrigin} hidden aria-hidden=\"true\" data-slot=\"navigation-menu-viewport-origin\" />\n        {mounted && createPortal(panel, document.body)}\n      </>\n    );\n  },\n);\nNavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName;\n\nconst NavigationMenuIndicator = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Indicator>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Indicator\n    ref={ref}\n    className={cn(\n      'top-full z-raised flex h-[6px] items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in',\n      className,\n    )}\n    {...props}\n  >\n    <div className=\"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md\" />\n  </NavigationMenuPrimitive.Indicator>\n));\nNavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName;\n\nconst MenuListItem = forwardRef<ElementRef<'a'>, ComponentPropsWithoutRef<'a'>>(\n  ({ className, title, children, ...props }, ref) => {\n    return (\n      <li>\n        <NavigationMenuLink asChild>\n          <a\n            ref={ref}\n            className={cn(\n              'block',\n              'select-none',\n              'space-y-1',\n              'rounded-item',\n              'p-3',\n              'leading-4',\n              'no-underline',\n              'outline-none',\n              'transition-colors',\n              'hover:bg-muted',\n              'focus:bg-muted',\n              ...menuItemFocusVisible,\n              className,\n            )}\n            {...props}\n          >\n            <div className=\"text-sm font-medium leading-none\">{title}</div>\n            <p className=\"line-clamp-2 text-sm leading-4 text-text-subtle\">{children}</p>\n          </a>\n        </NavigationMenuLink>\n      </li>\n    );\n  },\n);\nMenuListItem.displayName = 'MenuListItem';\n\nexport {\n  MenuListItem,\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuIndicator,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  type NavigationMenuTriggerProps,\n  NavigationMenuViewport,\n  navigationMenuTriggerStyle,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}