Skip to content

Commit ec6b0d1

Browse files
arielWixttsahi
authored andcommitted
fixing the slider logic #pr (#99)
1 parent 1fb5a13 commit ec6b0d1

File tree

5 files changed

+395
-240
lines changed

5 files changed

+395
-240
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { useState, useEffect } from 'react';
2+
3+
interface PriceRangeSelectorProps {
4+
min: number;
5+
max: number;
6+
selectedMin: number;
7+
selectedMax: number;
8+
setSelectedMin: (price: number) => void;
9+
setSelectedMax: (price: number) => void;
10+
}
11+
12+
export const PriceRangeSelector: React.FC<PriceRangeSelectorProps> = ({
13+
min,
14+
max,
15+
selectedMin,
16+
selectedMax,
17+
setSelectedMin,
18+
setSelectedMax,
19+
}) => {
20+
// Local state for immediate UI updates while dragging
21+
const [localSelectedMin, setLocalSelectedMin] = useState(selectedMin);
22+
const [localSelectedMax, setLocalSelectedMax] = useState(selectedMax);
23+
24+
// Round functions for display only
25+
const roundMinValue = (value: number): number => Math.floor(value);
26+
const roundMaxValue = (value: number): number => Math.ceil(value);
27+
28+
return (
29+
<div>
30+
<div className="space-y-4">
31+
<h4 className="text-content-primary font-medium mb-4">Price Range</h4>
32+
{/* Price Range Display */}
33+
<div className="flex items-center justify-between text-sm text-content-light">
34+
<span>${String(roundMinValue(min))}</span>
35+
<span>${String(roundMaxValue(max))}</span>
36+
</div>
37+
38+
{/* Dual Range Slider */}
39+
<div className="relative h-6">
40+
<div className="absolute top-2 left-0 right-0 h-2 bg-brand-medium rounded-full">
41+
<div
42+
className="absolute h-2 rounded-full bg-gradient-primary"
43+
style={{
44+
left: `${((localSelectedMin - min) / (max - min)) * 100}%`,
45+
width: `${
46+
((localSelectedMax - min) / (max - min)) * 100 -
47+
((localSelectedMin - min) / (max - min)) * 100
48+
}%`,
49+
}}
50+
/>
51+
</div>
52+
53+
{/* Min Range Input */}
54+
<input
55+
type="range"
56+
min={min}
57+
max={max}
58+
value={localSelectedMin}
59+
onChange={e => setLocalSelectedMin(Number(e.target.value))}
60+
onMouseUp={() => {
61+
setSelectedMin(localSelectedMin);
62+
}}
63+
onTouchEnd={() => {
64+
setSelectedMin(localSelectedMin);
65+
}}
66+
className="absolute top-0 left-0 w-full h-6 bg-transparent appearance-none cursor-pointer range-slider range-slider-min"
67+
style={{
68+
zIndex: localSelectedMin > min + (max - min) * 0.5 ? 2 : 1,
69+
}}
70+
/>
71+
72+
{/* Max Range Input */}
73+
<input
74+
type="range"
75+
min={min}
76+
max={max}
77+
value={localSelectedMax}
78+
onChange={e => setLocalSelectedMax(Number(e.target.value))}
79+
onMouseUp={() => {
80+
setSelectedMax(localSelectedMax);
81+
}}
82+
onTouchEnd={() => {
83+
setSelectedMax(localSelectedMax);
84+
}}
85+
className="absolute top-0 left-0 w-full h-6 bg-transparent appearance-none cursor-pointer range-slider range-slider-max"
86+
style={{
87+
zIndex: localSelectedMax < min + (max - min) * 0.5 ? 2 : 1,
88+
}}
89+
/>
90+
</div>
91+
92+
{/* Manual Price Input */}
93+
<div className="flex items-center gap-4">
94+
<div className="flex-1">
95+
<label className="block text-xs text-content-muted mb-1">Min</label>
96+
<input
97+
type="number"
98+
value={roundMinValue(localSelectedMin)}
99+
onChange={e => {
100+
const value = Number(e.target.value) || min;
101+
setLocalSelectedMin(value);
102+
setSelectedMin(value);
103+
}}
104+
min={min}
105+
max={max}
106+
className="w-full px-3 py-2 bg-surface-primary border border-brand-light rounded-lg text-content-primary text-sm focus:outline-none focus:ring-2 focus:ring-brand-primary"
107+
/>
108+
</div>
109+
<div className="flex-1">
110+
<label className="block text-xs text-content-muted mb-1">Max</label>
111+
<input
112+
type="number"
113+
value={roundMaxValue(localSelectedMax)}
114+
onChange={e => {
115+
const value = Number(e.target.value) || max;
116+
setLocalSelectedMax(value);
117+
setSelectedMax(value);
118+
}}
119+
min={min}
120+
max={max}
121+
className="w-full px-3 py-2 bg-surface-primary border border-brand-light rounded-lg text-content-primary text-sm focus:outline-none focus:ring-2 focus:ring-brand-primary"
122+
/>
123+
</div>
124+
</div>
125+
</div>
126+
</div>
127+
);
128+
};
129+
130+
export default PriceRangeSelector;

examples/astro-stores-demo/src/components/store/ProductFilters.tsx

Lines changed: 17 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import { getStockStatusMessage } from './product-status-enums';
33
import { ProductListFilters } from '@wix/headless-stores/react';
44
import type { ProductsListFiltersServiceConfig } from '@wix/headless-stores/services';
5+
import { PriceRangeSelector } from './PriceRangeSelector';
56

67
interface ProductFiltersProps {
78
className?: string;
@@ -85,107 +86,22 @@ export const ProductFilters: React.FC<ProductFiltersProps> = ({
8586

8687
<div className={`space-y-6 ${isExpanded ? 'block' : 'hidden lg:block'}`}>
8788
<ProductListFilters.PriceRange>
88-
{({ minPrice, maxPrice, setMinPrice, setMaxPrice }) => (
89-
<div
90-
className={`space-y-6 ${isExpanded ? 'block' : 'hidden lg:block'}`}
91-
>
92-
<div>
93-
<h4 className="text-content-primary font-medium mb-4">
94-
Price Range
95-
</h4>
96-
<div className="space-y-4">
97-
{/* Price Range Display */}
98-
<div className="flex items-center justify-between text-sm text-content-light">
99-
<span>${String(minPrice)}</span>
100-
<span>${String(maxPrice)}</span>
101-
</div>
102-
103-
{/* Dual Range Slider */}
104-
<div className="relative h-6">
105-
<div className="absolute top-2 left-0 right-0 h-2 bg-brand-medium rounded-full">
106-
<div
107-
className="absolute h-2 rounded-full bg-gradient-primary"
108-
style={{
109-
left: `${
110-
((minPrice - minPrice) / (maxPrice - minPrice)) *
111-
100
112-
}%`,
113-
width: `${
114-
((maxPrice - minPrice) / (maxPrice - minPrice)) *
115-
100
116-
}%`,
117-
}}
118-
/>
119-
</div>
120-
121-
{/* Min Range Input */}
122-
123-
<input
124-
type="range"
125-
min={minPrice}
126-
max={maxPrice}
127-
value={minPrice}
128-
onChange={e => setMinPrice(Number(e.target.value))}
129-
className="absolute top-0 left-0 w-full h-6 bg-transparent appearance-none cursor-pointer range-slider range-slider-min"
130-
style={{
131-
zIndex:
132-
minPrice > minPrice + (maxPrice - minPrice) * 0.5
133-
? 2
134-
: 1,
135-
}}
136-
/>
137-
138-
{/* Max Range Input */}
139-
<input
140-
type="range"
141-
min={minPrice}
142-
max={maxPrice}
143-
value={maxPrice}
144-
onChange={e => setMaxPrice(Number(e.target.value))}
145-
className="absolute top-0 left-0 w-full h-6 bg-transparent appearance-none cursor-pointer range-slider range-slider-max"
146-
style={{
147-
zIndex:
148-
maxPrice < minPrice + (maxPrice - minPrice) * 0.5
149-
? 2
150-
: 1,
151-
}}
152-
/>
153-
</div>
154-
155-
{/* Manual Price Input */}
156-
<div className="flex items-center gap-4">
157-
<div className="flex-1">
158-
<label className="block text-xs text-content-muted mb-1">
159-
Min
160-
</label>
161-
162-
<input
163-
type="number"
164-
value={minPrice}
165-
onChange={e => {
166-
setMinPrice(Number(e.target.value));
167-
}}
168-
className="w-full px-3 py-2 bg-surface-primary border border-brand-light rounded-lg text-content-primary text-sm focus:outline-none focus:ring-2 focus:ring-brand-primary"
169-
/>
170-
</div>
171-
<div className="flex-1">
172-
<label className="block text-xs text-content-muted mb-1">
173-
Max
174-
</label>
175-
176-
<input
177-
type="number"
178-
value={maxPrice}
179-
onChange={e => {
180-
setMaxPrice(Number(e.target.value));
181-
}}
182-
className="w-full px-3 py-2 bg-surface-primary border border-brand-light rounded-lg text-content-primary text-sm focus:outline-none focus:ring-2 focus:ring-brand-primary"
183-
/>
184-
</div>
185-
</div>
186-
</div>
187-
</div>
188-
</div>
89+
{({
90+
availableMinPrice,
91+
availableMaxPrice,
92+
selectedMinPrice,
93+
selectedMaxPrice,
94+
setSelectedMinPrice,
95+
setSelectedMaxPrice,
96+
}) => (
97+
<PriceRangeSelector
98+
min={availableMinPrice}
99+
max={availableMaxPrice}
100+
selectedMin={selectedMinPrice}
101+
selectedMax={selectedMaxPrice}
102+
setSelectedMin={setSelectedMinPrice}
103+
setSelectedMax={setSelectedMaxPrice}
104+
/>
189105
)}
190106
</ProductListFilters.PriceRange>
191107

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"scripts": {
1111
"build:all": "turbo run build",
1212
"build:headless-components": "yarn build:all --filter=\"./packages/headless-components/*\"",
13+
"clean": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && rm -rf .astro ./dist .turbo",
1314
"astro": "astro",
1415
"tsc": "tsc",
1516
"syncpack": "syncpack"

packages/headless-components/stores/src/react/ProductListFilters.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,17 @@ export interface PriceRangeProps {
195195
*/
196196
export interface PriceRangeRenderProps {
197197
/** Current minimum price filter value */
198-
minPrice: number;
198+
selectedMinPrice: number;
199199
/** Current maximum price filter value */
200-
maxPrice: number;
200+
selectedMaxPrice: number;
201+
/** Catalog minimum price */
202+
availableMinPrice: number;
203+
/** Catalog maximum price */
204+
availableMaxPrice: number;
201205
/** Function to update the minimum price filter */
202-
setMinPrice: (minPrice: number) => void;
206+
setSelectedMinPrice: (minPrice: number) => void;
203207
/** Function to update the maximum price filter */
204-
setMaxPrice: (maxPrice: number) => void;
208+
setSelectedMaxPrice: (maxPrice: number) => void;
205209
}
206210

207211
/**
@@ -242,13 +246,22 @@ export interface PriceRangeRenderProps {
242246
*/
243247
export function PriceRange(props: PriceRangeProps) {
244248
const service = useService(ProductsListFiltersServiceDefinition);
245-
const minPrice = service.minPrice.get();
246-
const maxPrice = service.maxPrice.get();
247-
const setMinPrice = service.setMinPrice;
248-
const setMaxPrice = service.setMaxPrice;
249+
const selectedMinPrice = service.selectedMinPrice.get();
250+
const availableMinPrice = service.availableMinPrice.get();
251+
const selectedMaxPrice = service.selectedMaxPrice.get();
252+
const availableMaxPrice = service.availableMaxPrice.get();
253+
const setSelectedMinPrice = service.setSelectedMinPrice;
254+
const setSelectedMaxPrice = service.setSelectedMaxPrice;
249255

250256
return typeof props.children === "function"
251-
? props.children({ minPrice, maxPrice, setMinPrice, setMaxPrice })
257+
? props.children({
258+
availableMinPrice,
259+
selectedMinPrice,
260+
selectedMaxPrice,
261+
availableMaxPrice,
262+
setSelectedMinPrice,
263+
setSelectedMaxPrice,
264+
})
252265
: props.children;
253266
}
254267

0 commit comments

Comments
 (0)