Files
basiclean-webapp/components/Navbar/NavbarSubMenu.tsx
T
2026-05-03 10:39:15 +02:00

108 lines
2.7 KiB
TypeScript

"use client";
import type { FC, MouseEvent } from "react";
import { useRef } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useHeightVar } from "@/hooks/useHeightVar";
import { useIsDesktop } from "@/hooks/useIsDesktop";
import styles from "./NavbarSubMenu.module.css";
type SubMenuItem = {
label: string;
to: string;
};
type Props = {
id: string;
open: boolean;
noTransition: boolean;
instantSwitch: boolean;
setOpenSubmenuId: (id: string | null) => void;
closeInstantNoAnim?: () => void;
label: string;
items: SubMenuItem[];
};
export const NavbarSubMenu: FC<Props> = ({
id,
open,
noTransition,
instantSwitch,
setOpenSubmenuId,
closeInstantNoAnim,
label,
items,
}) => {
const pathname = usePathname();
const subMenuRef = useRef<HTMLUListElement | null>(null);
const isDesktop = useIsDesktop();
const openMenu = () => setOpenSubmenuId(id);
const closeMenu = () => setOpenSubmenuId(null);
const toggleMenu = () => setOpenSubmenuId(open ? null : id);
useHeightVar(subMenuRef, "--submenu-h", "scroll", open);
const handleSubmenuClick = (e: MouseEvent<HTMLUListElement>) => {
const target = e.target as HTMLElement;
if (target.closest("a")) {
closeInstantNoAnim?.();
}
};
return (
<li
className={styles["nav-item-with-sub"]}
onMouseEnter={isDesktop ? openMenu : undefined}
onMouseLeave={isDesktop ? closeMenu : undefined}
>
<button
type="button"
className={styles["nav-sub-toggle"]}
onClick={toggleMenu}
aria-expanded={open}
aria-controls={`submenu-${id}`}
>
{label}
</button>
<ul
ref={subMenuRef}
id={`submenu-${id}`}
hidden={!open}
className={[
styles["nav-submenu"],
open ? styles.open : "",
noTransition || instantSwitch ? styles.noTransition : "",
].join(" ")}
onClick={handleSubmenuClick}
>
<li className={styles["nav-submenu-inner"]}>
<ul className={styles["nav-submenu-list"]}>
{items.map((item) => {
const isActive =
item.to === "/"
? pathname === "/"
: pathname === item.to || pathname.startsWith(`${item.to}/`);
return (
<li key={item.to}>
<Link
href={item.to}
aria-current={isActive ? "page" : undefined}
onClick={closeInstantNoAnim}
>
{item.label}
</Link>
</li>
);
})}
</ul>
</li>
</ul>
</li>
);
};