'use client'; import { useState, useRef, FocusEvent, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { FiMenu, FiX, FiHome, FiFileText, FiFile, FiUser, FiMail, FiMapPin, FiFeather, FiTag, FiServer, FiCpu, FiList, FiChevronDown, FiChevronRight } from 'react-icons/fi'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; export type IconKey = | 'home' | 'blog' | 'file' | 'user' | 'contact' | 'location' | 'pen' | 'tags' | 'server' | 'device' | 'menu'; const ICON_MAP: Record = { home: FiHome, blog: FiFileText, file: FiFile, user: FiUser, contact: FiMail, location: FiMapPin, pen: FiFeather, tags: FiTag, server: FiServer, device: FiCpu, menu: FiList }; export interface NavLinkItem { key: string; href?: string; label: string; iconKey: IconKey; children?: NavLinkItem[]; } interface NavMenuProps { items: NavLinkItem[]; } export function NavMenu({ items }: NavMenuProps) { const [open, setOpen] = useState(false); const [activeDropdown, setActiveDropdown] = useState(null); const [expandedMobileItems, setExpandedMobileItems] = useState([]); const [mounted, setMounted] = useState(false); const closeTimer = useRef(null); const pathname = usePathname(); useEffect(() => { setMounted(true); }, []); // Lock body scroll when menu is open useEffect(() => { if (open) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [open]); // Close menu on route change useEffect(() => { setOpen(false); }, [pathname]); const toggle = () => setOpen((val) => !val); const close = () => setOpen(false); const handleBlur = (event: FocusEvent) => { if (!event.currentTarget.contains(event.relatedTarget as Node)) { setActiveDropdown(null); } }; const clearCloseTimer = () => { if (closeTimer.current) { clearTimeout(closeTimer.current); closeTimer.current = null; } }; const openDropdown = (key: string) => { clearCloseTimer(); setActiveDropdown(key); }; const scheduleCloseDropdown = () => { clearCloseTimer(); closeTimer.current = window.setTimeout(() => setActiveDropdown(null), 180); }; const toggleMobileItem = (key: string) => { setExpandedMobileItems(prev => prev.includes(key) ? prev.filter(k => k !== key) : [...prev, key] ); }; const renderDesktopChild = (item: NavLinkItem) => { const Icon = ICON_MAP[item.iconKey] ?? FiFile; return item.href ? ( {item.label} ) : null; }; const renderMobileItem = (item: NavLinkItem, depth = 0) => { const Icon = ICON_MAP[item.iconKey] ?? FiFile; const hasChildren = item.children && item.children.length > 0; const isExpanded = expandedMobileItems.includes(item.key); if (hasChildren) { return (
{item.children!.map(child => renderMobileItem(child, depth + 1))}
); } return item.href ? ( {item.label} ) : null; }; return ( <> {/* Mobile Menu Trigger */} {/* Mobile Menu Overlay - Portaled */} {mounted && createPortal(
{/* Close button area */}
{items.map(item => renderMobileItem(item))}

© {new Date().getFullYear()} All rights reserved.

, document.body )} {/* Desktop Menu */} ); }