AbiirSkincare/Hydrogen/web/app/components/CollectionPageView.tsx

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>
);
}