update
This commit is contained in:
204
frontEnd/components/shared/layout/header/OffcanvasMenu.tsx
Normal file
204
frontEnd/components/shared/layout/header/OffcanvasMenu.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { OffcanvasData } from "@/public/data/offcanvas-data";
|
||||
import logoLight from "@/public/images/logo-light.png";
|
||||
|
||||
interface OffcanvasMenuProps {
|
||||
isOffcanvasOpen: boolean;
|
||||
isActive: boolean;
|
||||
handleClick: () => void;
|
||||
navigationData?: any[];
|
||||
servicesLoading?: boolean;
|
||||
servicesError?: string | null;
|
||||
}
|
||||
|
||||
const OffcanvasMenu = ({
|
||||
isOffcanvasOpen,
|
||||
isActive,
|
||||
handleClick,
|
||||
navigationData = OffcanvasData,
|
||||
servicesLoading = false,
|
||||
servicesError = null
|
||||
}: OffcanvasMenuProps) => {
|
||||
const [openDropdown, setOpenDropdown] = useState(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const handleDropdownToggle = (index: any) => {
|
||||
setOpenDropdown((prev) => (prev === index ? null : index));
|
||||
};
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const parentItems = document.querySelectorAll(
|
||||
".navbar__item--has-children"
|
||||
);
|
||||
|
||||
parentItems.forEach((parentItem) => {
|
||||
const childItems = parentItem.querySelectorAll(".active-current-sub");
|
||||
|
||||
if (childItems.length > 0) {
|
||||
parentItem.classList.add("active-current-parent");
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="offcanvas-nav">
|
||||
<div
|
||||
className={
|
||||
"offcanvas-menu" + (isOffcanvasOpen ? " show-offcanvas-menu" : " ")
|
||||
}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<nav
|
||||
className={
|
||||
"offcanvas-menu__wrapper" + (isActive ? " " : " nav-fade-active")
|
||||
}
|
||||
data-lenis-prevent
|
||||
>
|
||||
<div className="offcanvas-menu__header nav-fade">
|
||||
<div className="logo">
|
||||
<Link href="/" className="logo-img">
|
||||
<Image src={logoLight} priority alt="Image" title="Logo" width={160} height={60} />
|
||||
</Link>
|
||||
</div>
|
||||
<button
|
||||
aria-label="close offcanvas menu"
|
||||
className="close-offcanvas-menu"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="offcanvas-menu__list">
|
||||
<div className="navbar__menu">
|
||||
<ul>
|
||||
{navigationData.map((item, index) =>
|
||||
item.submenu ? (
|
||||
<li
|
||||
className="navbar__item navbar__item--has-children nav-fade"
|
||||
key={index}
|
||||
>
|
||||
<button
|
||||
aria-label="dropdown menu"
|
||||
className={
|
||||
"navbar__dropdown-label" +
|
||||
(openDropdown === index
|
||||
? " navbar__item-active"
|
||||
: " ")
|
||||
}
|
||||
onClick={() => handleDropdownToggle(index)}
|
||||
>
|
||||
{item.title}
|
||||
{item.title === "Services" && servicesLoading && (
|
||||
<span className="loading-indicator">⏳</span>
|
||||
)}
|
||||
</button>
|
||||
<AnimateHeight
|
||||
duration={400}
|
||||
height={openDropdown === index ? "auto" : 0}
|
||||
>
|
||||
<ul className="navbar__sub-menu">
|
||||
{item.title === "Services" && servicesLoading ? (
|
||||
<li>
|
||||
<span className="text-muted">Loading services...</span>
|
||||
</li>
|
||||
) : item.title === "Services" && servicesError ? (
|
||||
<li>
|
||||
<span className="text-danger">Failed to load services</span>
|
||||
</li>
|
||||
) : (
|
||||
item.submenu.map((subItem: any, subIndex: number) => (
|
||||
<li key={subIndex}>
|
||||
<Link
|
||||
href={subItem.path || "#"}
|
||||
className={
|
||||
mounted && pathname === subItem.path
|
||||
? " active-current-sub"
|
||||
: " "
|
||||
}
|
||||
>
|
||||
{subItem.title}
|
||||
</Link>
|
||||
</li>
|
||||
))
|
||||
)}
|
||||
</ul>
|
||||
</AnimateHeight>
|
||||
</li>
|
||||
) : (
|
||||
<li className="navbar__item nav-fade" key={index}>
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
mounted && pathname === item.path ? " active-current-link" : " "
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div className="offcanvas-menu__enterprise-info nav-fade">
|
||||
<div className="enterprise-contact">
|
||||
<h4>Get in Touch</h4>
|
||||
<p>Ready to transform your business?</p>
|
||||
<div className="contact-methods">
|
||||
<a href="tel:+359896138030" className="contact-item">
|
||||
<i className="fa-solid fa-phone"></i>
|
||||
<span>+359896138030</span>
|
||||
</a>
|
||||
<a href="mailto:info@gnxsoft.com" className="contact-item">
|
||||
<i className="fa-solid fa-envelope"></i>
|
||||
<span>info@gnxsoft.com</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="enterprise-social nav-fade">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="Connect with us on LinkedIn"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="View our code on GitHub"
|
||||
>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="anime">
|
||||
<span className="nav-fade"></span>
|
||||
<span className="nav-fade"></span>
|
||||
<span className="nav-fade"></span>
|
||||
<span className="nav-fade"></span>
|
||||
<span className="nav-fade"></span>
|
||||
<span className="nav-fade"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OffcanvasMenu;
|
||||
Reference in New Issue
Block a user