132 lines
5.2 KiB
TypeScript
132 lines
5.2 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { useEffect, useState } from 'react';
|
|
|
|
type NavLink = { href: string; label: string };
|
|
type NavSection = { title: string; links: NavLink[] };
|
|
|
|
export default function MobileNav({
|
|
personalisedLinks,
|
|
shopBySections,
|
|
customer,
|
|
}: {
|
|
personalisedLinks: NavLink[];
|
|
shopBySections: NavSection[];
|
|
customer: { name: string | null; email: string } | null;
|
|
}) {
|
|
const [open, setOpen] = useState(false);
|
|
const [expanded, setExpanded] = useState<string | null>(null);
|
|
|
|
// Lock background scroll while the drawer is open, and always start closed
|
|
// on route change (this component stays mounted across client-side nav).
|
|
useEffect(() => {
|
|
document.body.style.overflow = open ? 'hidden' : '';
|
|
return () => {
|
|
document.body.style.overflow = '';
|
|
};
|
|
}, [open]);
|
|
|
|
const close = () => {
|
|
setOpen(false);
|
|
setExpanded(null);
|
|
};
|
|
|
|
return (
|
|
<div className="md:hidden">
|
|
<button
|
|
onClick={() => setOpen(true)}
|
|
aria-label="Open menu"
|
|
aria-expanded={open}
|
|
className="flex h-9 w-9 items-center justify-center border border-line text-ink"
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M3 6h18M3 12h18M3 18h18" />
|
|
</svg>
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="fixed inset-0 z-50 flex">
|
|
<div className="absolute inset-0 bg-black/30" onClick={close} />
|
|
<div className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-paper p-6">
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-script text-3xl leading-none text-ink">Craft2Prints</span>
|
|
<button onClick={close} aria-label="Close menu" className="flex h-9 w-9 items-center justify-center border border-line text-ink">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M6 6l12 12M18 6L6 18" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="mt-8 flex flex-col divide-y divide-line">
|
|
<div className="py-2">
|
|
<button
|
|
onClick={() => setExpanded((s) => (s === 'personalised' ? null : 'personalised'))}
|
|
aria-expanded={expanded === 'personalised'}
|
|
className="flex w-full items-center justify-between py-2 text-left tag-label"
|
|
>
|
|
Personalised
|
|
<span className={`transition-transform ${expanded === 'personalised' ? 'rotate-180' : ''}`}>▾</span>
|
|
</button>
|
|
{expanded === 'personalised' && (
|
|
<div className="flex flex-col pb-2 pl-2">
|
|
{personalisedLinks.map((link) => (
|
|
<Link key={link.href} href={link.href} onClick={close} className="py-2 text-sm text-muted hover:text-ink">
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Link href="/products" onClick={close} className="py-4 tag-label">
|
|
Products
|
|
</Link>
|
|
|
|
<div className="py-2">
|
|
<button
|
|
onClick={() => setExpanded((s) => (s === 'shopby' ? null : 'shopby'))}
|
|
aria-expanded={expanded === 'shopby'}
|
|
className="flex w-full items-center justify-between py-2 text-left tag-label"
|
|
>
|
|
Shop by
|
|
<span className={`transition-transform ${expanded === 'shopby' ? 'rotate-180' : ''}`}>▾</span>
|
|
</button>
|
|
{expanded === 'shopby' && (
|
|
<div className="flex flex-col gap-3 pb-2 pl-2">
|
|
{shopBySections
|
|
.filter((s) => s.links.length > 0)
|
|
.map((section) => (
|
|
<div key={section.title}>
|
|
<p className="text-xs font-medium uppercase tracking-wide text-muted">{section.title}</p>
|
|
<div className="flex flex-col">
|
|
{section.links.map((link) => (
|
|
<Link key={link.href} href={link.href} onClick={close} className="py-2 text-sm text-muted hover:text-ink">
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
{shopBySections.every((s) => s.links.length === 0) && (
|
|
<p className="text-sm text-muted">Coming soon.</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Link href="/gallery" onClick={close} className="py-4 tag-label">
|
|
Gallery
|
|
</Link>
|
|
|
|
<Link href={customer ? '/account' : '/account/login'} onClick={close} className="py-4 tag-label">
|
|
{customer ? (customer.name ?? 'My account') : 'Login'}
|
|
</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|