{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "navigation-menu",
  "title": "Navigation Menu",
  "author": "AlgorizeTech",
  "description": "A horizontal navigation bar with dropdown panels and a sliding active indicator. Use it for primary site navigation with grouped links.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/NavigationMenu.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { ChevronDown } from \"lucide-react\";\nimport React, {\n  createContext,\n  forwardRef,\n  useContext,\n  useRef,\n  useState,\n} from \"react\";\nimport { getThemeStyles } from \"./navigation-menu-theme\";\n\n// ==========================================\n// THEME & CONFIGURATION\n// ==========================================\nexport type NavColor = \"indigo\" | \"rose\" | \"emerald\" | \"zinc\" | (string & {});\nexport type NavVariant = \"default\" | \"minimal\" | \"glass\";\n\n// ==========================================\n// CONTEXT\n// ==========================================\ninterface NavContextType {\n  activeValue: string | null;\n  setActiveValue: (val: string | null) => void;\n  hoveredValue: string | null;\n  setHoveredValue: (val: string | null) => void;\n  indicatorStyle: React.CSSProperties;\n  setIndicatorStyle: React.Dispatch<React.SetStateAction<React.CSSProperties>>;\n  theme: Record<string, string>;\n  variant: NavVariant;\n  listRef: React.RefObject<HTMLUListElement | null>;\n}\n\nconst NavContext = createContext<NavContextType | null>(null);\nconst useNavContext = () => {\n  const ctx = useContext(NavContext);\n  if (!ctx)\n    throw new Error(\"Navigation components must be used within NavigationMenu\");\n  return ctx;\n};\n\nconst NavItemContext = createContext<{ value: string } | null>(null);\nconst useNavItemContext = () => {\n  const ctx = useContext(NavItemContext);\n  if (!ctx)\n    throw new Error(\n      \"NavigationMenuItem components must be used within NavigationMenuItem\",\n    );\n  return ctx;\n};\n\n// ==========================================\n// COMPONENTS\n// ==========================================\n\nexport interface NavigationMenuProps extends React.HTMLAttributes<HTMLElement> {\n  color?: NavColor;\n  variant?: NavVariant;\n}\n\nexport const NavigationMenu = forwardRef<HTMLElement, NavigationMenuProps>(\n  (\n    { children, color = \"primary\", variant = \"default\", className, ...props },\n    ref,\n  ) => {\n    const [activeValue, setActiveValue] = useState<string | null>(null);\n    const [hoveredValue, setHoveredValue] = useState<string | null>(null);\n    const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({\n      opacity: 0,\n    });\n    const listRef = useRef<HTMLUListElement>(null);\n    const { theme, style } = getThemeStyles(color as string);\n\n    // Close dropdowns when mouse leaves the entire nav area\n    const handleMouseLeave = () => {\n      setActiveValue(null);\n      setHoveredValue(null);\n      setIndicatorStyle((prev) => ({ ...prev, opacity: 0 }));\n    };\n\n    return (\n      <NavContext.Provider\n        value={{\n          activeValue,\n          setActiveValue,\n          hoveredValue,\n          setHoveredValue,\n          indicatorStyle,\n          setIndicatorStyle,\n          theme,\n          variant,\n          listRef,\n        }}\n      >\n        <nav\n          ref={ref}\n          onMouseLeave={handleMouseLeave}\n          className={cn(\n            \"relative flex w-full items-center justify-center z-50\",\n            className,\n          )}\n          style={style}\n          {...props}\n        >\n          {children}\n        </nav>\n      </NavContext.Provider>\n    );\n  },\n);\nNavigationMenu.displayName = \"NavigationMenu\";\n\nexport const NavigationMenuList = forwardRef<\n  HTMLUListElement,\n  React.HTMLAttributes<HTMLUListElement>\n>(({ children, className, ...props }, ref) => {\n  const { listRef, variant } = useNavContext();\n\n  const variantStyles = {\n    default:\n      \"bg-background border border-border shadow-sm\",\n    glass:\n      \"bg-background/60 backdrop-blur-xl border border-foreground/10 shadow-lg\",\n    minimal: \"bg-transparent border-none\",\n  };\n\n  return (\n    <div\n      className={cn(\n        \"relative flex justify-center max-w-full w-auto rounded-full p-1 sm:p-1.5\",\n        variantStyles[variant],\n      )}\n    >\n      <ul\n        ref={(node) => {\n          if (typeof ref === \"function\") ref(node);\n          else if (ref) ref.current = node;\n          listRef.current = node;\n        }}\n        className={cn(\n          \"group flex flex-nowrap flex-1 list-none items-center justify-center gap-0.5 sm:gap-1 max-w-full\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n      </ul>\n    </div>\n  );\n});\nNavigationMenuList.displayName = \"NavigationMenuList\";\n\nexport const NavigationMenuItem = forwardRef<\n  HTMLLIElement,\n  React.HTMLAttributes<HTMLLIElement> & { value: string }\n>(({ children, className, value, ...props }, ref) => {\n  return (\n    <NavItemContext.Provider value={{ value }}>\n      {/* ── THE FIX: Removed 'relative' from li so dropdowns center to the entire <nav>! ── */}\n      <li ref={ref} className={cn(\"list-none\", className)} {...props}>\n        {children}\n      </li>\n    </NavItemContext.Provider>\n  );\n});\nNavigationMenuItem.displayName = \"NavigationMenuItem\";\n\nexport const NavigationMenuTrigger = forwardRef<\n  HTMLButtonElement,\n  React.ButtonHTMLAttributes<HTMLButtonElement>\n>(({ children, className, ...props }, ref) => {\n  const {\n    activeValue,\n    setActiveValue,\n    setHoveredValue,\n    setIndicatorStyle,\n    listRef,\n  } = useNavContext();\n  const { value } = useNavItemContext();\n  const triggerRef = useRef<HTMLButtonElement>(null);\n  const isActive = activeValue === value;\n\n  const updateIndicator = () => {\n    if (triggerRef.current && listRef.current) {\n      const triggerRect = triggerRef.current.getBoundingClientRect();\n      const listRect = listRef.current.getBoundingClientRect();\n      setIndicatorStyle({\n        left: triggerRect.left - listRect.left,\n        width: triggerRect.width,\n        opacity: 1,\n      });\n    }\n  };\n\n  // ── TOUCH-SAFE HOVER (Only triggers for real mouse cursors) ──────────\n  const handlePointerEnter = (e: React.PointerEvent<HTMLButtonElement>) => {\n    if (e.pointerType === \"mouse\") {\n      setActiveValue(value);\n      setHoveredValue(value);\n      updateIndicator();\n    }\n  };\n\n  // ── TOUCH/CLICK TOGGLE SUPPORT ───────────────────────────────────────\n  const handleClick = () => {\n    if (isActive) {\n      setActiveValue(null);\n      setHoveredValue(null);\n      setIndicatorStyle((prev) => ({ ...prev, opacity: 0 }));\n    } else {\n      setActiveValue(value);\n      setHoveredValue(value);\n      updateIndicator();\n    }\n  };\n\n  return (\n    <button\n      ref={(node) => {\n        if (typeof ref === \"function\") ref(node);\n        else if (ref) ref.current = node;\n        triggerRef.current = node;\n      }}\n      onPointerEnter={handlePointerEnter}\n      onFocus={() => {\n        setActiveValue(value);\n        setHoveredValue(value);\n        updateIndicator();\n      }}\n      onClick={handleClick}\n      className={cn(\n        \"relative z-10 flex items-center justify-center gap-0.5 sm:gap-1.5 rounded-full px-2 sm:px-4 py-1 sm:py-2 text-[11px] sm:text-sm font-semibold transition-colors outline-none cursor-pointer whitespace-nowrap\",\n        isActive\n          ? \"text-foreground\"\n          : \"text-muted-foreground\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronDown\n        className={cn(\n          \"relative top-[1px] w-3 h-3 sm:w-3.5 sm:h-3.5 transition-transform duration-300 shrink-0\",\n          isActive && \"rotate-180\",\n        )}\n        aria-hidden=\"true\"\n      />\n    </button>\n  );\n});\nNavigationMenuTrigger.displayName = \"NavigationMenuTrigger\";\n\nexport const NavigationMenuContent = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ children, className, ...props }, ref) => {\n  const { activeValue, variant } = useNavContext();\n  const { value } = useNavItemContext();\n  const isActive = activeValue === value;\n\n  // A plain popover is the same white as most pages, so the panel reads as\n  // part of the page. It wears a whisper of the accent (relative OKLCH, as\n  // Folder does), a tinted edge and a layered drop shadow to lift it off.\n  const tintedPanel =\n    \"text-popover-foreground border bg-[oklch(from_var(--nav-accent)_0.978_calc(c*0.1)_h)] border-[oklch(from_var(--nav-accent)_0.9_calc(c*0.22)_h)] dark:bg-[oklch(from_var(--nav-accent)_0.21_calc(c*0.1)_h)] dark:border-foreground/10\";\n  const variantStyles = {\n    default: cn(\n      tintedPanel,\n      \"shadow-[0_2px_4px_rgb(0_0_0/0.04),0_24px_48px_-16px_rgb(0_0_0/0.22)]\",\n    ),\n    glass:\n      \"bg-popover/80 text-popover-foreground backdrop-blur-2xl border border-foreground/10 shadow-[0_2px_4px_rgb(0_0_0/0.05),0_28px_56px_-16px_rgb(0_0_0/0.28)]\",\n    minimal: cn(\n      tintedPanel,\n      \"shadow-[0_1px_2px_rgb(0_0_0/0.04),0_16px_32px_-16px_rgb(0_0_0/0.16)]\",\n    ),\n  };\n\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        \"absolute left-1/2 top-full -translate-x-1/2 pt-1.5 sm:pt-3 w-max max-w-[calc(100vw-1rem)] origin-top transition-all duration-300 ease-out z-[100]\",\n        isActive\n          ? \"opacity-100 scale-100 translate-y-0 pointer-events-auto\"\n          : \"opacity-0 scale-95 -translate-y-2 pointer-events-none\",\n        className,\n      )}\n      {...props}\n    >\n      <div\n        className={cn(\n          \"w-full h-full rounded-2xl overflow-hidden transition-colors\",\n          variantStyles[variant],\n        )}\n      >\n        <div className=\"p-2 sm:p-4 md:p-6 w-full h-full max-h-[70vh] sm:max-h-[75vh] overflow-y-auto\">\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n});\nNavigationMenuContent.displayName = \"NavigationMenuContent\";\n\nexport const NavigationMenuLink = forwardRef<\n  HTMLAnchorElement,\n  React.AnchorHTMLAttributes<HTMLAnchorElement> & { active?: boolean }\n>(({ children, className, active, ...props }, ref) => {\n  const navCtx = useContext(NavContext);\n  const theme = navCtx?.theme;\n\n  return (\n    <a\n      ref={ref}\n      data-active={active ? \"\" : undefined}\n      aria-current={active ? \"page\" : undefined}\n      className={cn(\n        // Hover is a faint wash of the accent — grey muted went muddy on the\n        // tinted panel.\n        \"block select-none space-y-1 rounded-xl p-2.5 sm:p-3 leading-none no-underline outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--nav-accent)_9%,transparent)] focus-visible:bg-[color-mix(in_oklab,var(--nav-accent)_9%,transparent)] cursor-pointer\",\n        className,\n        active &&\n          (theme?.bgSoft\n            ? cn(theme.bgSoft, theme?.text || \"text-primary\", \"font-semibold\")\n            : \"bg-primary/10 text-primary font-semibold\"),\n      )}\n      {...props}\n    >\n      {children}\n    </a>\n  );\n});\nNavigationMenuLink.displayName = \"NavigationMenuLink\";\n\nexport const NavigationMenuIndicator = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { indicatorStyle, theme } = useNavContext();\n\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        \"absolute top-0 bottom-0 z-0 transition-all duration-300 ease-out rounded-full pointer-events-none\",\n        theme.bgSoft,\n        className,\n      )}\n      style={indicatorStyle}\n      {...props}\n    />\n  );\n});\nNavigationMenuIndicator.displayName = \"NavigationMenuIndicator\";\n",
      "type": "registry:component",
      "target": "components/custom/blocks/NavigationMenu.tsx"
    },
    {
      "path": "src/components/custom/blocks/navigation-menu-theme.ts",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nconst colorThemes: Record<string, Record<string, string>> = {\n  primary: {\n    bg: \"bg-primary text-primary-foreground\",\n    text: \"text-primary\",\n    bgSoft: \"bg-primary/10\",\n    border: \"border-primary/30\",\n  },\n  indigo: {\n    bg: \"bg-indigo-600 text-white\",\n    text: \"text-indigo-600 dark:text-indigo-400\",\n    bgSoft: \"bg-indigo-50 dark:bg-indigo-500/10\",\n    border: \"border-indigo-200 dark:border-indigo-800\",\n  },\n  rose: {\n    bg: \"bg-rose-600 text-white\",\n    text: \"text-rose-600 dark:text-rose-400\",\n    bgSoft: \"bg-rose-50 dark:bg-rose-500/10\",\n    border: \"border-rose-200 dark:border-rose-800\",\n  },\n  emerald: {\n    bg: \"bg-emerald-600 text-white\",\n    text: \"text-emerald-600 dark:text-emerald-400\",\n    bgSoft: \"bg-emerald-50 dark:bg-emerald-500/10\",\n    border: \"border-emerald-200 dark:border-emerald-800\",\n  },\n  zinc: {\n    bg: \"bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900\",\n    text: \"text-zinc-900 dark:text-zinc-100\",\n    bgSoft: \"bg-zinc-100 dark:bg-zinc-800/50\",\n    border: \"border-zinc-200 dark:border-zinc-800\",\n  },\n};\n\nconst hexToRgb = (hex: string) => {\n  let cleanHex = hex.replace(/^#/, \"\");\n  if (cleanHex.length === 3)\n    cleanHex = cleanHex\n      .split(\"\")\n      .map((x) => x + x)\n      .join(\"\");\n  const result = /^([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(cleanHex);\n  return result\n    ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`\n    : \"99, 102, 241\";\n};\n\n// One CSS colour per theme key, exposed as `--nav-accent` so surfaces can be\n// tinted from it with relative OKLCH / color-mix whatever the colour source.\nconst accentColors: Record<string, string> = {\n  primary: \"var(--primary)\",\n  indigo: \"#4f46e5\",\n  rose: \"#e11d48\",\n  emerald: \"#059669\",\n  zinc: \"#52525b\",\n};\n\nexport const getThemeStyles = (color: string) => {\n  if (color && color.startsWith(\"#\")) {\n    const rgb = hexToRgb(color);\n    return {\n      theme: {\n        bg: \"bg-[var(--nav-color)] text-white\",\n        text: \"text-[var(--nav-color)]\",\n        bgSoft: \"bg-[rgba(var(--nav-color-rgb),0.1)]\",\n        border: \"border-[rgba(var(--nav-color-rgb),0.2)]\",\n      },\n      style: {\n        \"--nav-color\": color,\n        \"--nav-color-rgb\": rgb,\n        \"--nav-accent\": color,\n      } as React.CSSProperties,\n    };\n  }\n  return {\n    theme: colorThemes[color] || colorThemes.primary,\n    style: {\n      \"--nav-accent\": accentColors[color] ?? accentColors.primary,\n    } as React.CSSProperties,\n  };\n};\n",
      "type": "registry:component",
      "target": "components/custom/blocks/navigation-menu-theme.ts"
    }
  ],
  "docs": "Named exports: NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, getThemeStyles (from navigation-menu-theme.ts, which installs alongside). Compose the parts; the root tracks active and hovered state and animates the indicator between triggers. Variants: default | minimal | glass.",
  "categories": [
    "navigation"
  ],
  "type": "registry:component"
}
