Template
Add collection filters and sort, out-of-stock options, gallery drag
- Collections: filters and sort via the Storefront API — the products connection now takes `filters`/`after` and returns its facets, with cursor-based load more on the collection page - Extract ProductFilters (renamed from SearchFilters) and a shared ProductToolbar so search and collections use the same chrome - PDP: mark option values with no in-stock variant using a diagonal strike, computed against the other selected options - PDP: pointer drag-scrolling for the mobile image carousel, since a scroll container doesn't drag with a mouse - Header/cart: circular icon buttons, tighter spacing, and icon sizes set by class (Button's base CSS clamps un-classed svgs to size-4) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jWbNNJLksC1QG8z8845FX
This commit is contained in:
co-authored by
Claude Opus 5
parent
cc901b9ce8
commit
2ef5639a2e
@@ -3,10 +3,10 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import ProductCard from './product-card';
|
||||
import SearchFilters from './search-filters';
|
||||
import ProductFilters from './product-filters';
|
||||
import ProductToolbar from './product-toolbar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Loader } from '@/app/components/ui/loader';
|
||||
import { RiEqualizerLine, RiArrowDownSLine, RiCheckLine } from '@remixicon/react';
|
||||
import {
|
||||
searchProducts,
|
||||
type SearchFilter,
|
||||
@@ -42,7 +42,6 @@ const SearchResults: React.FC = () => {
|
||||
const [hasNextPage, setHasNextPage] = useState(false);
|
||||
|
||||
const [sortIndex, setSortIndex] = useState(0);
|
||||
const [sortOpen, setSortOpen] = useState(false);
|
||||
const [filtersOpen, setFiltersOpen] = useState(false);
|
||||
const [activeFilters, setActiveFilters] = useState<string[]>([]);
|
||||
|
||||
@@ -126,58 +125,15 @@ const SearchResults: React.FC = () => {
|
||||
Search
|
||||
</h1>
|
||||
|
||||
{/* Toolbar */}
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<Button
|
||||
onClick={() => setFiltersOpen(true)}
|
||||
variant="ghost"
|
||||
className="gap-x-2 px-0 font-normal hover:bg-transparent"
|
||||
>
|
||||
<RiEqualizerLine size={18} />
|
||||
Filters
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center gap-x-4">
|
||||
<span className="text-sm text-muted-foreground tabular-nums">
|
||||
{totalCount} {totalCount === 1 ? 'Item' : 'Items'}
|
||||
</span>
|
||||
|
||||
<div className="relative">
|
||||
<Button
|
||||
onClick={() => setSortOpen((prev) => !prev)}
|
||||
variant="ghost"
|
||||
aria-expanded={sortOpen}
|
||||
className="gap-x-1 px-0 font-normal hover:bg-transparent"
|
||||
>
|
||||
Sort
|
||||
<RiArrowDownSLine size={16} />
|
||||
</Button>
|
||||
|
||||
{sortOpen && (
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 z-40"
|
||||
onClick={() => setSortOpen(false)}
|
||||
/>
|
||||
<div className="absolute right-0 top-full z-50 mt-1 w-56 rounded-md border border-border bg-background py-1 shadow-md">
|
||||
{SORT_OPTIONS.map((option, index) => (
|
||||
<button
|
||||
key={option.label}
|
||||
onClick={() => {
|
||||
setSortIndex(index);
|
||||
setSortOpen(false);
|
||||
}}
|
||||
className="flex w-full items-center justify-between px-4 py-2 text-left text-sm text-foreground hover:bg-accent"
|
||||
>
|
||||
{option.label}
|
||||
{index === sortIndex && <RiCheckLine size={16} />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<ProductToolbar
|
||||
totalCount={totalCount}
|
||||
onOpenFilters={() => setFiltersOpen(true)}
|
||||
sortOptions={SORT_OPTIONS}
|
||||
sortIndex={sortIndex}
|
||||
onSortChange={setSortIndex}
|
||||
activeFilterCount={activeFilters.length}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Results */}
|
||||
@@ -227,7 +183,7 @@ const SearchResults: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SearchFilters
|
||||
<ProductFilters
|
||||
open={filtersOpen}
|
||||
onOpenChange={setFiltersOpen}
|
||||
filters={filters}
|
||||
|
||||
Reference in New Issue
Block a user