"use client"; import dynamic from "next/dynamic"; import Image from "next/image"; import { type ReactNode, useEffect, useRef, useState } from "react"; /** * The animated body loads lazily (`motion/react` is ~43 KB gzip and only needed * while a dropdown is open), so public pages pay nothing until the first * interaction. */ const NavDropdownPanel = dynamic( () => import("@/components/nav-dropdown-panel"), { ssr: false }, ); interface NavDropdownProps { label: string; icon?: string; /** Rendered as a link (default) or a menu item (`as="menu"`). */ children: ReactNode; } export function NavDropdown({ label, icon, children }: NavDropdownProps) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") setOpen(false); } if (open) document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, [open]); useEffect(() => { function onOutsideClick(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) { setOpen(false); } } if (open) { document.addEventListener("mousedown", onOutsideClick); } return () => document.removeEventListener("mousedown", onOutsideClick); }, [open]); return (
{/* Closed state is plain HTML; the animation loads on first open. */} {children}
); }