"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import Image from "next/image"; import { usePathname } from "next/navigation"; import { endLinks, singleLinks, subMenus } from "@/data/navigation"; import { useHeightVar } from "@/hooks/useHeightVar"; import { useNavToggleMenu } from "@/hooks/useNavToggleMenu"; import { useSupportsHover } from "@/hooks/useSupportsHover"; import { useToggleBodyOverflow } from "@/hooks/useToggleBodyOverflow"; import styles from "./Navbar.module.css"; import { useIsDesktop } from "@/hooks/useIsDesktop"; import { NavbarSubMenu } from "./NavbarSubMenu"; type BasicLink = { label: string; to: string; }; export default function Navbar() { const pathname = usePathname(); const navRef = useRef(null); const supportsHover = useSupportsHover(); const isDesktop = useIsDesktop(1024); const [instantSwitch, setInstantSwitch] = useState(false); const raf1Ref = useRef(null); const raf2Ref = useRef(null); const hamburgerRef = useRef(null); const navLinksRef = useRef(null); const { menuOpen, openSubmenuId, noTransition, closeInstantNoAnim, setOpenSubmenuId, toggleMenu, } = useNavToggleMenu({ supportsHover, desktopBp: 1024 }); useToggleBodyOverflow(menuOpen); useHeightVar(navRef, "--nav-h", "offset"); useEffect(() => { return () => { if (raf1Ref.current !== null) cancelAnimationFrame(raf1Ref.current); if (raf2Ref.current !== null) cancelAnimationFrame(raf2Ref.current); }; }, []); const clearInstantSwitchSoon = () => { if (raf1Ref.current !== null) cancelAnimationFrame(raf1Ref.current); if (raf2Ref.current !== null) cancelAnimationFrame(raf2Ref.current); raf1Ref.current = requestAnimationFrame(() => { raf2Ref.current = requestAnimationFrame(() => { setInstantSwitch(false); raf1Ref.current = null; raf2Ref.current = null; }); }); }; const setOpenSubmenuIdWithInstantSwitch = (id: string | null) => { if (id !== null && openSubmenuId !== null && openSubmenuId !== id) { setInstantSwitch(true); setOpenSubmenuId(id); clearInstantSwitchSoon(); return; } setInstantSwitch(false); setOpenSubmenuId(id); }; const isActivePath = (to: string) => { if (to === "/") return pathname === "/"; return pathname === to || pathname.startsWith(`${to}/`); }; const handleNavLinkClick = () => { setInstantSwitch(false); closeInstantNoAnim(); }; const renderNavLink = (link: BasicLink) => (
  • {link.label}
  • ); useEffect(() => { if (isDesktop || !menuOpen) return; const navEl = navRef.current; if (!navEl) return; const getFocusableElements = () => Array.from( navEl.querySelectorAll( 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])', ), ).filter((el) => { if (el.hasAttribute("inert")) return false; if (el.closest("[inert]")) return false; return true; }); const firstMenuFocusable = navLinksRef.current?.querySelector( 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])', ); firstMenuFocusable?.focus(); const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); toggleMenu(); requestAnimationFrame(() => { hamburgerRef.current?.focus(); }); return; } if (e.key !== "Tab") return; const currentFocusable = getFocusableElements(); const currentFirst = currentFocusable[0]; const currentLast = currentFocusable[currentFocusable.length - 1]; if (!currentFirst || !currentLast) return; if (e.shiftKey) { if (document.activeElement === currentFirst) { e.preventDefault(); currentLast.focus(); } } else { if (document.activeElement === currentLast) { e.preventDefault(); currentFirst.focus(); } } }; document.addEventListener("keydown", onKeyDown); return () => { document.removeEventListener("keydown", onKeyDown); }; }, [isDesktop, menuOpen, toggleMenu]); return ( ); }