Files
website/src/components/home/ProductsMegaMenu.tsx
T

173 lines
5.6 KiB
TypeScript

"use client";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { MEGA_MENU_TABS } from "@/data/home";
export type MegaColumn = {
title: string;
href: string;
links: readonly { label: string; href: string }[];
};
export type MegaTabData = {
id: string;
nameFa: string;
nameEn: string;
href: string;
image: string;
/** Second-level categories as columns, each with third-level children */
columns?: readonly MegaColumn[];
/** Flat fallback when columns are not provided */
links?: readonly { label: string; href: string }[];
};
type ProductsMegaMenuProps = {
open: boolean;
onClose: () => void;
tabs?: MegaTabData[];
};
function columnsForTab(tab: MegaTabData): MegaColumn[] {
if (tab.columns?.length) return [...tab.columns];
if (tab.links?.length) {
return [
{
title: tab.nameFa,
href: tab.href,
links: tab.links,
},
];
}
return [];
}
export default function ProductsMegaMenu({
open,
onClose,
tabs,
}: ProductsMegaMenuProps) {
const list = (tabs?.length ? tabs : MEGA_MENU_TABS) as MegaTabData[];
const [activeId, setActiveId] = useState(list[0]?.id ?? "coat");
const active = list.find((t) => t.id === activeId) ?? list[0];
if (!open || !active) return null;
const columns = columnsForTab(active);
return (
<div
id="products-mega-menu"
role="region"
aria-label="منوی محصولات"
className="pointer-events-auto absolute inset-x-0 top-full z-40 border-t border-ink/5 bg-white shadow-[0_16px_40px_rgba(0,0,0,0.1)]"
onMouseLeave={onClose}
>
<div className="mx-auto max-w-[1709px] px-4 py-5 sm:px-6 lg:px-10 lg:py-6">
<div
role="tablist"
aria-label="دسته‌بندی محصولات"
className="flex flex-wrap items-end justify-start gap-x-1 gap-y-1 border-b border-ink/10"
>
{list.map((tab) => {
const selected = tab.id === active.id;
return (
<button
key={tab.id}
type="button"
role="tab"
aria-selected={selected}
id={`mega-tab-${tab.id}`}
aria-controls={`mega-panel-${tab.id}`}
onMouseEnter={() => setActiveId(tab.id)}
onFocus={() => setActiveId(tab.id)}
onClick={() => setActiveId(tab.id)}
className={`-mb-px border-b-2 px-3 py-2.5 text-center transition-colors sm:px-4 ${
selected
? "border-ink"
: "border-transparent hover:opacity-90"
}`}
>
<span
className={`block font-doran text-sm font-medium sm:text-[15px] ${
selected ? "text-ink" : "text-ink/80"
}`}
>
{tab.nameFa}
</span>
<span
dir="ltr"
className={`mt-0.5 block font-montserrat text-[9px] font-light uppercase tracking-[0.22em] sm:text-[10px] ${
selected ? "text-ink/70" : "text-ink/40"
}`}
>
{tab.nameEn}
</span>
</button>
);
})}
</div>
<div
role="tabpanel"
id={`mega-panel-${active.id}`}
aria-labelledby={`mega-tab-${active.id}`}
className="mt-5 flex items-start justify-start gap-6 sm:gap-8 lg:gap-10"
>
<div className="min-w-0 flex-1 pt-1">
<div className="grid grid-cols-2 gap-x-6 gap-y-7 sm:grid-cols-3 sm:gap-x-8 lg:grid-cols-4 xl:grid-cols-5 xl:gap-x-10">
{columns.map((col) => (
<div key={col.href + col.title} className="min-w-0">
<Link
href={col.href}
onClick={onClose}
className="block text-sm font-medium text-ink transition-opacity hover:opacity-70 sm:text-[15px]"
>
{col.title}
</Link>
{col.links.length > 0 ? (
<ul className="mt-2.5 flex flex-col gap-1.5 text-[13px] font-light text-ink/70 sm:text-sm">
{col.links.map((link) => (
<li key={link.href + link.label}>
<Link
href={link.href}
onClick={onClose}
className="transition-colors hover:text-ink"
>
{link.label}
</Link>
</li>
))}
</ul>
) : null}
</div>
))}
</div>
</div>
<div className="relative hidden h-[160px] w-[120px] shrink-0 overflow-hidden bg-[#f3f3f3] sm:block sm:h-[200px] sm:w-[150px] lg:h-[220px] lg:w-[160px]">
{list.map((tab) => (
<div
key={tab.id}
className={`absolute inset-0 transition-opacity duration-[400ms] ease-out ${
tab.id === active.id ? "opacity-100" : "opacity-0"
}`}
aria-hidden={tab.id !== active.id}
>
<Image
src={tab.image}
alt=""
fill
sizes="160px"
className="object-cover"
/>
</div>
))}
</div>
</div>
</div>
</div>
);
}