Initial commit: Craft2Prints with Stages 1-3
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user