"use client"; import { AnimatePresence, motion } from "motion/react"; import Image from "next/image"; import { type ReactNode, useCallback, useEffect, useRef, useState, } from "react"; import { mobileMenuVariants } from "@/lib/motion"; interface MobileNavProps { children: ReactNode; menuLabel?: string; closeLabel?: string; brandLabel?: string; } export function MobileNav({ children, menuLabel = "Open menu", closeLabel = "Close menu", brandLabel = "", }: MobileNavProps) { const [open, setOpen] = useState(false); const detailsRef = useRef(null); const menuRef = useRef(null); const MENU_ID = "mobile-nav-menu"; const handleEscape = useCallback( (e: KeyboardEvent) => { if (e.key === "Escape" && open) { setOpen(false); detailsRef.current?.querySelector("button")?.focus(); } }, [open], ); useEffect(() => { document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); }, [handleEscape]); useEffect(() => { if (!open) return; const menu = menuRef.current; if (!menu) return; const focusableSelector = 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'; function handleTab(e: KeyboardEvent) { if (e.key !== "Tab" || !menu) return; const focusable = Array.from( menu.querySelectorAll(focusableSelector), ); if (focusable.length === 0) return; const first = focusable[0]; const last = focusable[focusable.length - 1]; if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } } menu.addEventListener("keydown", handleTab); return () => menu.removeEventListener("keydown", handleTab); }, [open]); return (
{open && (
{brandLabel} {closeLabel}
{children}
)}
); }