Skip to content

Commit e013a32

Browse files
committed
Enhance search filters with comprehensive modal and global styles
- Replace CollectionsModal with more robust FiltersModal - Add dynamic filtering functionality for collections and product attributes - Introduce scrollbar-hide utility class in global CSS - Expand menuFilters with additional product brands and types - Update search layout to use new FiltersModal component
1 parent be88639 commit e013a32

File tree

6 files changed

+235
-116
lines changed

6 files changed

+235
-116
lines changed

app/globals.css

+10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
}
2525
}
2626

27+
@layer utilities {
28+
.scrollbar-hide {
29+
-ms-overflow-style: none; /* IE and Edge */
30+
scrollbar-width: none; /* Firefox */
31+
}
32+
.scrollbar-hide::-webkit-scrollbar {
33+
display: none; /* Chrome, Safari and Opera */
34+
}
35+
}
36+
2737
.swiper-container-borders {
2838
position: relative;
2939
}

app/search/layout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Footer from 'components/layout/footer';
2-
import CollectionsModal from 'components/layout/search/collections-modal';
32
import FilterDropdown from 'components/layout/search/filter-dropdown';
3+
import FiltersModal from 'components/layout/search/filters-modal';
44
import { sorting } from 'lib/constants';
55
import { getCollections, getPage } from 'lib/shopify';
66
import ChildrenWrapper from './children-wrapper';
@@ -22,7 +22,7 @@ export default async function SearchLayout({ children }: { children: React.React
2222
<div className="fixed left-0 right-0 top-[80px] z-20 bg-white">
2323
<div className="border-y-2 border-452-blue-light p-4">
2424
<div className="container flex items-center justify-between">
25-
<CollectionsModal collections={collections} />
25+
<FiltersModal collections={collections} />
2626
<FilterDropdown list={sorting} title="Sort by" />
2727
</div>
2828
</div>

components/layout/search/collections-modal.tsx

-113
This file was deleted.
+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
'use client';
2+
3+
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react';
4+
import { AdjustmentsHorizontalIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
5+
import clsx from 'clsx';
6+
import CloseCart from 'components/cart/close-cart';
7+
import AnimatedUnderlineLink from 'components/ctas/underline';
8+
import { menuFilters } from 'lib/constants';
9+
import { Collection } from 'lib/shopify/types';
10+
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
11+
import { Fragment, useState } from 'react';
12+
13+
const skeleton = 'mb-3 h-4 w-5/6 animate-pulse rounded';
14+
const activeAndTitles = 'bg-neutral-800 dark:bg-neutral-300';
15+
const items = 'bg-neutral-400 dark:bg-neutral-700';
16+
17+
export default function CollectionsModal({ collections }: { collections: Collection[] }) {
18+
const [isOpen, setIsOpen] = useState(false);
19+
const [collectionsCollapsed, setCollectionsCollapsed] = useState(false);
20+
const [filtersCollapsed, setFiltersCollapsed] = useState(false);
21+
const pathname = usePathname();
22+
const router = useRouter();
23+
const searchParams = useSearchParams();
24+
const openModal = () => setIsOpen(true);
25+
const closeModal = () => setIsOpen(false);
26+
27+
const handleFilterChange = (filterType: string, value: string) => {
28+
const params = new URLSearchParams(searchParams);
29+
const currentFilters = params.get(filterType)?.split(',') || [];
30+
31+
if (currentFilters.includes(value)) {
32+
const newFilters = currentFilters.filter((filter) => filter !== value);
33+
if (newFilters.length > 0) {
34+
params.set(filterType, newFilters.join(','));
35+
} else {
36+
params.delete(filterType);
37+
}
38+
} else {
39+
params.set(filterType, [...currentFilters, value].join(','));
40+
}
41+
42+
router.push(`${pathname}?${params.toString()}`);
43+
};
44+
45+
const isFilterSelected = (filterType: string, value: string) => {
46+
const currentFilters = searchParams.get(filterType)?.split(',') || [];
47+
return currentFilters.includes(value);
48+
};
49+
50+
return (
51+
<>
52+
<button
53+
aria-label="Abrir filtros"
54+
onClick={openModal}
55+
className="flex items-center gap-2 text-452-blue-light hover:opacity-80"
56+
>
57+
<AdjustmentsHorizontalIcon className="h-6 w-6" />
58+
<span className="font-oswald text-lg">Filtros</span>
59+
</button>
60+
61+
<Transition show={isOpen}>
62+
<Dialog onClose={closeModal} className="relative z-50">
63+
<TransitionChild
64+
as={Fragment}
65+
enter="transition-all ease-in-out duration-300"
66+
enterFrom="opacity-0 backdrop-blur-none"
67+
enterTo="opacity-100 backdrop-blur-[.5px]"
68+
leave="transition-all ease-in-out duration-200"
69+
leaveFrom="opacity-100 backdrop-blur-[.5px]"
70+
leaveTo="opacity-0 backdrop-blur-none"
71+
>
72+
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
73+
</TransitionChild>
74+
<TransitionChild
75+
as={Fragment}
76+
enter="transition-all ease-in-out duration-300"
77+
enterFrom="translate-x-[-100%]"
78+
enterTo="translate-x-0"
79+
leave="transition-all ease-in-out duration-200"
80+
leaveFrom="translate-x-0"
81+
leaveTo="translate-x-[-100%]"
82+
>
83+
<DialogPanel className="fixed bottom-0 left-0 top-0 flex h-full w-full flex-col border-r border-452-blue-light bg-white p-6 backdrop-blur-3xl md:w-[390px]">
84+
<div className="flex items-center justify-between">
85+
<p className="text-lg font-semibold text-452-blue-light">Filtros</p>
86+
<button aria-label="Cerrar filtros" onClick={closeModal}>
87+
<CloseCart />
88+
</button>
89+
</div>
90+
<div className="scrollbar-hide mt-4 flex flex-col gap-4 overflow-y-auto">
91+
<div>
92+
<button
93+
onClick={() => setCollectionsCollapsed(!collectionsCollapsed)}
94+
className="flex w-full items-center justify-between border-b border-452-blue-light py-2"
95+
>
96+
<h3 className="font-semibold text-452-blue-light">Colecciones</h3>
97+
<ChevronUpIcon
98+
className={clsx(
99+
'h-5 w-5 text-452-blue-light transition-transform duration-200',
100+
collectionsCollapsed ? 'rotate-180' : ''
101+
)}
102+
/>
103+
</button>
104+
<div
105+
className={clsx(
106+
'mt-2 space-y-2 overflow-hidden transition-all duration-200',
107+
collectionsCollapsed ? 'mt-0 h-0' : 'h-auto'
108+
)}
109+
>
110+
{collections.map((collection) => {
111+
const isActive = pathname === collection.path;
112+
return (
113+
<div key={collection.handle} className="px-2">
114+
{isActive ? (
115+
<div className="relative inline-block">
116+
<span className="text-sm text-452-blue-light">
117+
{collection.title === 'All' ? 'Todo' : collection.title}
118+
<div className="absolute bottom-0 left-0 h-[2px] w-full bg-452-blue-light" />
119+
</span>
120+
</div>
121+
) : (
122+
<AnimatedUnderlineLink
123+
href={collection.path}
124+
className="text-sm text-452-blue-light"
125+
initiallyUnderlined={false}
126+
>
127+
{collection.title === 'All' ? 'Todo' : collection.title}
128+
</AnimatedUnderlineLink>
129+
)}
130+
</div>
131+
);
132+
})}
133+
</div>
134+
</div>
135+
136+
<div>
137+
<button
138+
onClick={() => setFiltersCollapsed(!filtersCollapsed)}
139+
className="flex w-full items-center justify-between border-b border-452-blue-light py-2"
140+
>
141+
<h3 className="font-semibold text-452-blue-light">Filtros</h3>
142+
<ChevronUpIcon
143+
className={clsx(
144+
'h-5 w-5 text-452-blue-light transition-transform duration-200',
145+
filtersCollapsed ? 'rotate-180' : ''
146+
)}
147+
/>
148+
</button>
149+
<div
150+
className={clsx(
151+
'mt-2 space-y-4 overflow-hidden transition-all duration-200',
152+
filtersCollapsed ? 'mt-0 h-0' : 'h-auto'
153+
)}
154+
>
155+
{Object.entries(menuFilters).map(([category, filters]) => (
156+
<div key={category} className="space-y-2">
157+
<h4 className="font-medium capitalize text-452-blue-light">{category}</h4>
158+
{Object.entries(filters).map(([filterType, values]) => (
159+
<div key={filterType} className="ml-2 space-y-1">
160+
<h5 className="text-sm font-medium capitalize text-452-blue-light">
161+
{filterType}
162+
</h5>
163+
{values.map((value) => (
164+
<label key={value} className="flex items-center gap-2 px-2">
165+
<input
166+
type="checkbox"
167+
checked={isFilterSelected(filterType, value)}
168+
onChange={() => handleFilterChange(filterType, value)}
169+
className="text-452-blue-light focus:ring-452-blue-light"
170+
/>
171+
<span className="text-sm text-452-blue-light">{value}</span>
172+
</label>
173+
))}
174+
</div>
175+
))}
176+
</div>
177+
))}
178+
</div>
179+
</div>
180+
</div>
181+
</DialogPanel>
182+
</TransitionChild>
183+
</Dialog>
184+
</Transition>
185+
</>
186+
);
187+
}

lib/constants.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,43 @@ export const TAGS = {
2727
};
2828

2929
export const menuFilters = {
30+
patinetas: {
31+
marca: [
32+
'Catrina',
33+
'Beat',
34+
'Glitch',
35+
'Zenit',
36+
'Suburbios',
37+
'Santa Cruz',
38+
'Neighborhood',
39+
'Copal',
40+
'Deza',
41+
'Tricolor',
42+
'Toy Machine',
43+
'Baker',
44+
'Deathwish',
45+
'Flip'
46+
],
47+
tipo: ['Cruiser', 'Mini cruiser', 'Longboard', 'Penny', 'Clásica']
48+
},
3049
tablas: {
3150
medida: ['7.25"', '7.5"', '7.75"', '8.0"', '8.125"', '8.25"', '8.5"', '8.75"'],
32-
marca: ['Alakin', 'Catrina', 'Zenit', 'Jaguar']
51+
marca: [
52+
'Catrina',
53+
'Beat',
54+
'Glitch',
55+
'Zenit',
56+
'Suburbios',
57+
'Santa Cruz',
58+
'Neighborhood',
59+
'Copal',
60+
'Deza',
61+
'Tricolor',
62+
'Toy Machine',
63+
'Baker',
64+
'Deathwish',
65+
'Flip'
66+
]
3367
},
3468
trucks: {
3569
medida: ['8.0"', '8.25"', '8.5"'],

styles/globals.css

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)