{"version":3,"file":"NavigationMenu.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n    /** Visible label for the entry. */\n    label: ReactNode;\n    /** When set, the entry renders as an anchor. */\n    href?: string;\n    /** Invoked when the entry is activated (click / Enter). */\n    onSelect?: () => void;\n    /** Nested entries — presence turns the item into a submenu trigger. */\n    children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n    /** Top-level navigation entries. */\n    items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n *   or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n *     items={[\n *         { label: \"Home\", href: \"/\" },\n *         {\n *             label: \"Products\",\n *             children: [\n *                 { label: \"Analytics\", href: \"/analytics\" },\n *                 { label: \"Billing\", onSelect: () => openBilling() },\n *             ],\n *         },\n *     ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n    const [clickedIndex, setClickedIndex] = useState<number>(-1);\n    const [hovered, setHovered] = useState<number>(-1);\n    const baseId = useId();\n    const rootRef = useRef<HTMLElement>(null);\n\n    const close = useCallback((): void => {\n        setClickedIndex(-1);\n        setHovered(-1);\n    }, []);\n\n    const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n    useEffect(() => {\n        if (openIndex === -1) return;\n        const onKey = (event: KeyboardEvent): void => {\n            if (event.key === \"Escape\") close();\n        };\n        const onDown = (event: MouseEvent): void => {\n            if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n        };\n        window.addEventListener(\"keydown\", onKey);\n        window.addEventListener(\"mousedown\", onDown);\n        return () => {\n            window.removeEventListener(\"keydown\", onKey);\n            window.removeEventListener(\"mousedown\", onDown);\n        };\n    }, [openIndex, close]);\n\n    const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n        const handleClick = (): void => {\n            item.onSelect?.();\n            close();\n        };\n        if (item.href) {\n            return (\n                <a\n                    href={item.href}\n                    role={role === \"menuitem\" ? \"menuitem\" : undefined}\n                    className={styles.link}\n                    onClick={handleClick}\n                >\n                    {item.label}\n                </a>\n            );\n        }\n        return (\n            <button\n                type=\"button\"\n                role={role === \"menuitem\" ? \"menuitem\" : undefined}\n                className={styles.link}\n                onClick={handleClick}\n            >\n                {item.label}\n            </button>\n        );\n    };\n\n    return (\n        <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n            <ul className={styles.list}>\n                {items.map((item, index) => {\n                    const hasChildren = !!item.children && item.children.length > 0;\n                    if (!hasChildren) {\n                        return (\n                            <li key={index} className={styles.item}>\n                                {renderLeaf(item, \"none\")}\n                            </li>\n                        );\n                    }\n                    const open = openIndex === index;\n                    const panelId = `${baseId}-panel-${index}`;\n                    return (\n                        <li\n                            key={index}\n                            className={styles.item}\n                            onMouseEnter={() => setHovered(index)}\n                            onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n                        >\n                            <button\n                                type=\"button\"\n                                className={styles.trigger}\n                                aria-haspopup=\"menu\"\n                                aria-expanded={open}\n                                aria-controls={panelId}\n                                onClick={() =>\n                                    setClickedIndex((prev) => (prev === index ? -1 : index))\n                                }\n                                onFocus={() => setHovered(index)}\n                            >\n                                {item.label}\n                            </button>\n                            {open && (\n                                <ul id={panelId} role=\"menu\" className={styles.panel}>\n                                    {item.children!.map((child, childIndex) => (\n                                        <li key={childIndex} role=\"none\" className={styles.subItem}>\n                                            {renderLeaf(child, \"menuitem\")}\n                                        </li>\n                                    ))}\n                                </ul>\n                            )}\n                        </li>\n                    );\n                })}\n            </ul>\n        </nav>\n    );\n}\n"],"mappings":"qIAgDA,SAAgB,EAAe,CAAE,QAAO,YAAW,GAAG,GAA8B,CAChF,GAAM,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAiB,EAAE,EACrD,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAiB,EAAE,EAC3C,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,GAAA,EAAU,EAAA,OAAA,CAAoB,IAAI,EAElC,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAgB,EAAE,EAClB,EAAW,EAAE,CACjB,EAAG,CAAC,CAAC,EAEC,EAAY,IAAiB,GAAoB,EAAf,GAExC,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAc,GAAI,OACtB,IAAM,EAAS,GAA+B,CACtC,EAAM,MAAQ,UAAU,EAAM,CACtC,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAW,CAAK,CAAC,EAErB,IAAM,GAAc,EAA0B,IAAyC,CACnF,IAAM,MAA0B,CAC5B,EAAK,WAAW,EAChB,EAAM,CACV,EAaA,OAZI,EAAK,MAED,EAAA,EAAA,IAAA,CAAC,IAAD,CACI,KAAM,EAAK,KACX,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,EAER,SAAA,EAAK,KACP,CAAA,GAIP,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,EAER,SAAA,EAAK,KACF,CAAA,CAEhB,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,EAC1D,UAAA,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,KACjB,SAAA,EAAM,KAAK,EAAM,IAAU,CAExB,GAAI,EADkB,EAAK,UAAY,EAAK,SAAS,OAAS,GAE1D,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAgB,UAAW,EAAA,QAAO,KAC7B,SAAA,EAAW,EAAM,MAAM,CACxB,EAFK,CAEL,EAGZ,IAAM,EAAO,IAAc,EACrB,EAAU,GAAG,EAAO,SAAS,IACnC,OACI,EAAA,EAAA,KAAA,CAAC,KAAD,CAEI,UAAW,EAAA,QAAO,KAClB,iBAAoB,EAAW,CAAK,EACpC,iBAAoB,EAAY,GAAU,IAAS,EAAQ,GAAK,CAAK,EAJzE,SAAA,EAMI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,gBAAc,OACd,gBAAe,EACf,gBAAe,EACf,YACI,EAAiB,GAAU,IAAS,EAAQ,GAAK,CAAM,EAE3D,YAAe,EAAW,CAAK,EAE9B,SAAA,EAAK,KACF,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,KAAK,OAAO,UAAW,EAAA,QAAO,MAC1C,SAAA,EAAK,SAAU,KAAK,EAAO,KACxB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAqB,KAAK,OAAO,UAAW,EAAA,QAAO,QAC9C,SAAA,EAAW,EAAO,UAAU,CAC7B,EAFK,CAEL,CACP,CACD,CAAA,CAER,CA3BK,EAAA,CA2BL,CAEZ,CAAC,CACD,CAAA,CACH,CAAA,CAEb"}