209 lines
7.3 KiB
TypeScript
209 lines
7.3 KiB
TypeScript
"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";
|
|
|
|
interface OffcanvasMenuProps {
|
|
isOffcanvasOpen: boolean;
|
|
isActive: boolean;
|
|
handleClick: () => void;
|
|
navigationData?: any[];
|
|
servicesLoading?: boolean;
|
|
servicesError?: string | null;
|
|
}
|
|
|
|
const OffcanvasMenu = ({
|
|
isOffcanvasOpen,
|
|
isActive,
|
|
handleClick,
|
|
navigationData = [],
|
|
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="/images/logo-light.png" 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">
|
|
{navigationData.length === 0 ? (
|
|
<div className="text-center py-5">
|
|
<p className="text-muted">No data available</p>
|
|
</div>
|
|
) : (
|
|
<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 || !item.submenu || item.submenu.length === 0) ? (
|
|
<li>
|
|
<span className="text-muted">No data available</span>
|
|
</li>
|
|
) : item.submenu ? (
|
|
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>
|
|
))
|
|
) : null}
|
|
</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>+359 896 13 80 30</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;
|