style(header): Implement responsive mobile menu and improve header layout

This commit is contained in:
2026-02-06 11:35:24 +07:00
parent dc68fe17ab
commit 26037e39b7
6 changed files with 964 additions and 49 deletions

View File

@@ -0,0 +1,112 @@
"use client";
import React, { useState, useEffect } from "react";
import Link from "next/link";
interface MenuItem {
label: string;
href: string;
submenu?: MenuItem[];
}
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
menuItems: MenuItem[];
}
const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose, menuItems }) => {
const [expandedItems, setExpandedItems] = useState<{ [key: string]: boolean }>({});
// Disable body scroll when menu is open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
const toggleSubmenu = (path: string) => {
setExpandedItems((prev) => ({
...prev,
[path]: !prev[path],
}));
};
const handleLinkClick = () => {
onClose();
};
// Recursive component for rendering menu items with nested submenus
const renderMenuItem = (item: MenuItem, index: number, parentPath: string = "", level: number = 0) => {
const currentPath = parentPath ? `${parentPath}-${index}` : `${index}`;
const hasSubmenu = item.submenu && item.submenu.length > 0;
const isExpanded = expandedItems[currentPath];
return (
<li key={currentPath} className={`mobile-menu-item mobile-menu-level-${level}`}>
<div className="mobile-menu-item-wrapper">
{hasSubmenu ? (
<>
<span className="mobile-menu-link">{item.label}</span>
<button
className="mobile-menu-toggle"
onClick={() => toggleSubmenu(currentPath)}
aria-label={`Toggle ${item.label} submenu`}
>
<i className={`fas ${isExpanded ? "fa-minus" : "fa-plus"}`}></i>
</button>
</>
) : (
<Link href={item.href} className="mobile-menu-link" onClick={handleLinkClick}>
{item.label}
</Link>
)}
</div>
{/* Submenu with accordion animation - supports nested submenus */}
{hasSubmenu && (
<ul
className={`mobile-submenu mobile-submenu-level-${level + 1} ${isExpanded ? "mobile-submenu-open" : ""}`}
>
{item.submenu!.map((subItem, subIndex) =>
renderMenuItem(subItem, subIndex, currentPath, level + 1),
)}
</ul>
)}
</li>
);
};
return (
<>
{/* Mobile Menu Slide Panel */}
<div className={`mobile-slide-menu ${isOpen ? "mobile-menu-open" : ""}`}>
<div className="mobile-menu-header">
<div className="mobile-menu-logo">
<Link href="/" onClick={handleLinkClick}>
<img src="/assets/img/logo/black-logo.svg" alt="logo" />
</Link>
</div>
<button className="mobile-menu-close" onClick={onClose} aria-label="Close menu">
<i className="fas fa-times"></i>
</button>
</div>
<nav className="mobile-menu-nav">
<ul className="mobile-menu-list">{menuItems.map((item, index) => renderMenuItem(item, index))}</ul>
</nav>
</div>
{/* Overlay */}
<div className={`mobile-menu-overlay ${isOpen ? "mobile-overlay-open" : ""}`} onClick={onClose}></div>
</>
);
};
export default MobileMenu;