101 lines
4.2 KiB
TypeScript
101 lines
4.2 KiB
TypeScript
import {type ReactNode} from 'react';
|
|
import {Link, useLocation} from 'react-router';
|
|
|
|
const FILTER_TABS: {id: string; label: string; to: string}[] = [
|
|
{id: 'all', label: 'All Products', to: '/collections/all'},
|
|
{id: 'face', label: 'Face', to: '/collections/face'},
|
|
{id: 'ritual', label: 'Ritual', to: '/collections/ritual'},
|
|
];
|
|
|
|
type CollectionPageViewProps = {
|
|
title: string;
|
|
/** Optional `className` for the H1 (default matches PLP: `max-w-[20ch]`). Use a wider class for long search queries. */
|
|
titleClassName?: string;
|
|
eyebrow?: string;
|
|
/** Subtitle e.g. "8 products" — omit on paginated subpages if unknown */
|
|
productCountLine?: string | null;
|
|
/** When false, hides the All / Face / Ritual collection tabs (e.g. search page). */
|
|
showFilterTabs?: boolean;
|
|
children: ReactNode;
|
|
};
|
|
|
|
function buildFilterTo(path: string, search: string) {
|
|
const sp = new URLSearchParams(search);
|
|
sp.delete('cursor');
|
|
sp.delete('direction');
|
|
const q = sp.toString();
|
|
return q ? `${path}?${q}` : path;
|
|
}
|
|
|
|
function isFilterActive(pathname: string, itemTo: string): boolean {
|
|
const path = (pathname.replace(/\/$/, '') || '/') as string;
|
|
const to = itemTo.replace(/\/$/, '');
|
|
if (to === '/collections/all') {
|
|
return path === '/collections/all' || path.endsWith('/collections/all');
|
|
}
|
|
return path === to || path.endsWith(to);
|
|
}
|
|
|
|
/**
|
|
* Figma: Products / collection (node 121:2974) — band header, filter tabs, sort, 4-up grid.
|
|
*/
|
|
export function CollectionPageView({
|
|
title,
|
|
titleClassName = "max-w-[20ch]",
|
|
eyebrow = 'Our Collection',
|
|
productCountLine,
|
|
showFilterTabs = true,
|
|
children,
|
|
}: CollectionPageViewProps) {
|
|
const location = useLocation();
|
|
|
|
return (
|
|
<div className="w-full bg-[#f8f4ef]">
|
|
<header className="w-full border-b border-[rgba(26,26,24,0.08)] bg-[#ede8e0] pt-[40px] pb-10 sm:pt-[48px] sm:pb-12">
|
|
<div className="mx-auto w-full max-w-[1710px] px-6 sm:px-10 lg:px-16 2xl:px-[206px]">
|
|
<p className="m-0 font-['Raleway',sans-serif] text-[0.75rem] font-medium uppercase not-italic leading-[1.1] tracking-[0.32em] text-[#5d0b24]">
|
|
{eyebrow}
|
|
</p>
|
|
<h1
|
|
className={`m-0 mt-3 break-words font-['Cormorant_Garamond',serif] text-[2.5rem] font-light not-italic leading-[1.08] tracking-[-0.02em] text-[#1a1a18] sm:text-[3rem] lg:text-[4.25rem] lg:leading-[1.02] ${titleClassName}`}
|
|
>
|
|
{title}
|
|
</h1>
|
|
{productCountLine ? (
|
|
<p className="m-0 mt-3 font-['Raleway',sans-serif] text-base font-medium not-italic leading-[1.4] text-[#5a5550]">
|
|
{productCountLine}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
</header>
|
|
|
|
<div className="mx-auto w-full max-w-[1400px] px-4 pb-20 pt-8 sm:px-8 sm:pt-10 lg:px-[51px] lg:pt-[42.5px]">
|
|
{showFilterTabs ? (
|
|
<div className="mb-6 flex w-full min-h-[42px] flex-col border-b border-[rgba(26,26,24,0.08)] pb-4 sm:mb-8 sm:min-h-[68px] sm:pb-px">
|
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3 sm:min-h-[41.5px]">
|
|
{FILTER_TABS.map((item) => {
|
|
const active = isFilterActive(location.pathname, item.to);
|
|
return (
|
|
<Link
|
|
key={item.to}
|
|
className={
|
|
active
|
|
? "inline-flex min-h-[42px] min-w-[6rem] items-center justify-center [background-color:#1a1a18] px-3 font-['Raleway',sans-serif] text-[0.7rem] font-medium uppercase not-italic leading-none tracking-[0.1em] !text-white no-underline sm:min-h-[41.5px] sm:px-5"
|
|
: "inline-flex min-h-[42px] min-w-[4rem] items-center justify-center border border-[rgba(26,26,24,0.1)] [background:transparent] px-3 font-['Raleway',sans-serif] text-[0.7rem] font-medium uppercase not-italic leading-none tracking-[0.1em] text-[#7a7068] no-underline transition-opacity duration-200 hover:opacity-80 sm:min-h-[41.5px] sm:px-5"
|
|
}
|
|
to={buildFilterTo(item.to, location.search)}
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
|
|
{children}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|