@akinon/pz-similar-products 1.92.0-rc.20 → 1.92.0-rc.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +311 -21
- package/package.json +1 -1
- package/src/hooks/use-similar-products.ts +27 -12
- package/src/types/index.ts +36 -0
- package/src/utils/image-conversion.ts +44 -0
- package/src/utils/index.ts +6 -1
- package/src/views/filters.tsx +722 -628
- package/src/views/header-image-search-feature.tsx +16 -4
- package/src/views/image-search.tsx +136 -84
- package/src/views/main.tsx +12 -3
- package/src/views/product-image-search-feature.tsx +12 -3
- package/src/views/results.tsx +19 -7
- package/src/views/search-modal.tsx +42 -7
package/src/views/filters.tsx
CHANGED
|
@@ -79,6 +79,38 @@ export function SimilarProductsFilterSidebar({
|
|
|
79
79
|
}: FilterSidebarProps) {
|
|
80
80
|
const { t } = useLocalization();
|
|
81
81
|
|
|
82
|
+
if (settings?.customRenderers?.render?.filterSidebar?.renderSidebar) {
|
|
83
|
+
return settings.customRenderers.render.filterSidebar.renderSidebar({
|
|
84
|
+
isFilterMenuOpen,
|
|
85
|
+
setIsFilterMenuOpen,
|
|
86
|
+
searchResults,
|
|
87
|
+
isLoading,
|
|
88
|
+
handleFacetChange,
|
|
89
|
+
removeFacetFilter,
|
|
90
|
+
currentImageUrl,
|
|
91
|
+
isCropping,
|
|
92
|
+
imageRef,
|
|
93
|
+
fileInputRef,
|
|
94
|
+
crop,
|
|
95
|
+
setCrop,
|
|
96
|
+
completedCrop,
|
|
97
|
+
setCompletedCrop,
|
|
98
|
+
handleCropComplete,
|
|
99
|
+
toggleCropMode,
|
|
100
|
+
processCompletedCrop,
|
|
101
|
+
processManualCrop,
|
|
102
|
+
resetCrop,
|
|
103
|
+
product,
|
|
104
|
+
hasUploadedImage,
|
|
105
|
+
handleFileUpload,
|
|
106
|
+
handleResetToOriginal,
|
|
107
|
+
fileError,
|
|
108
|
+
showResetButton,
|
|
109
|
+
settings,
|
|
110
|
+
className
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
82
114
|
const sizeKey = settings?.commonProductAttributes?.find(
|
|
83
115
|
(item: any) => item.translationKey === 'size'
|
|
84
116
|
)?.key;
|
|
@@ -135,724 +167,786 @@ export function SimilarProductsFilterSidebar({
|
|
|
135
167
|
);
|
|
136
168
|
|
|
137
169
|
return (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const mobileHeaderClassName = twMerge(
|
|
158
|
-
'flex justify-between mb-6 md:hidden',
|
|
159
|
-
settings?.customStyles?.filterSidebarMobileHeader
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return (
|
|
163
|
-
<>
|
|
164
|
-
<div className={mobileHeaderClassName}>
|
|
165
|
-
<h3 className="text-2xl font-bold">
|
|
166
|
-
{t('common.product.filters')}
|
|
167
|
-
</h3>
|
|
168
|
-
<Button
|
|
169
|
-
appearance="ghost"
|
|
170
|
-
size="sm"
|
|
171
|
-
onClick={() => setIsFilterMenuOpen(false)}
|
|
172
|
-
className="hover:bg-gray-200 rounded-full p-0 w-6 h-6"
|
|
173
|
-
>
|
|
174
|
-
<Icon name="close" size={16} />
|
|
175
|
-
</Button>
|
|
176
|
-
</div>
|
|
177
|
-
<div className="flex justify-between items-center mb-6 md:hidden">
|
|
178
|
-
<span className="text-sm">
|
|
179
|
-
{searchResults?.pagination?.total_count || 0} items
|
|
180
|
-
</span>
|
|
181
|
-
</div>
|
|
182
|
-
</>
|
|
183
|
-
);
|
|
184
|
-
})()}
|
|
185
|
-
|
|
186
|
-
{(() => {
|
|
187
|
-
if (
|
|
188
|
-
settings?.customRenderers?.render?.filterSidebar?.renderImageSection
|
|
189
|
-
) {
|
|
190
|
-
return settings.customRenderers.render.filterSidebar.renderImageSection(
|
|
191
|
-
{
|
|
192
|
-
currentImageUrl,
|
|
193
|
-
isCropping,
|
|
194
|
-
onToggleCrop: handleToggleCropMode,
|
|
195
|
-
onFileUpload: handleFileChangeWithCropReset,
|
|
196
|
-
onResetToOriginal: handleResetToOriginal,
|
|
197
|
-
fileError,
|
|
198
|
-
isLoading
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const imageSectionClassName = twMerge(
|
|
204
|
-
'relative mb-2 md:mb-6 mt-4',
|
|
205
|
-
settings?.customStyles?.imageSection
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const imageContainerClassName = twMerge(
|
|
209
|
-
'relative bg-white overflow-hidden flex items-center justify-center transition-all duration-300 ease-in-out',
|
|
210
|
-
isCropping ? 'border-2 border-dashed border-gray-300' : '',
|
|
211
|
-
settings?.customStyles?.imageContainer
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const cropButtonClassName = twMerge(
|
|
215
|
-
`absolute z-10 bottom-3 left-3 p-2 rounded-full bg-white shadow-md w-10 h-10 ${
|
|
216
|
-
isCropping ? 'text-red-500' : 'text-gray-700'
|
|
217
|
-
} ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`,
|
|
218
|
-
settings?.customStyles?.cropButton
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
const renderCropButton = () => {
|
|
222
|
-
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
223
|
-
return null;
|
|
224
|
-
|
|
170
|
+
<>
|
|
171
|
+
{isFilterMenuOpen && (
|
|
172
|
+
<div
|
|
173
|
+
className={twMerge(
|
|
174
|
+
'fixed inset-0 bg-black bg-opacity-50 z-40 md:hidden',
|
|
175
|
+
settings?.customStyles?.filterSidebarMobileOverlay
|
|
176
|
+
)}
|
|
177
|
+
onClick={() => setIsFilterMenuOpen(false)}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
|
|
181
|
+
<div
|
|
182
|
+
className={sidebarClassName}
|
|
183
|
+
style={{
|
|
184
|
+
maxHeight: 'calc(100vh - 120px)'
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
{(() => {
|
|
225
188
|
if (
|
|
226
|
-
settings?.customRenderers?.render?.filterSidebar?.
|
|
189
|
+
settings?.customRenderers?.render?.filterSidebar?.renderMobileHeader
|
|
227
190
|
) {
|
|
228
|
-
return settings.customRenderers.render.filterSidebar.
|
|
191
|
+
return settings.customRenderers.render.filterSidebar.renderMobileHeader(
|
|
229
192
|
{
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
193
|
+
title: t('common.product.filters'),
|
|
194
|
+
itemCount: searchResults?.pagination?.total_count || 0,
|
|
195
|
+
onClose: () => setIsFilterMenuOpen(false)
|
|
233
196
|
}
|
|
234
197
|
);
|
|
235
198
|
}
|
|
236
199
|
|
|
200
|
+
const mobileHeaderClassName = twMerge(
|
|
201
|
+
'flex justify-between mb-6 md:hidden',
|
|
202
|
+
settings?.customStyles?.filterSidebarMobileHeader
|
|
203
|
+
);
|
|
204
|
+
|
|
237
205
|
return (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{isLoading ? (
|
|
246
|
-
<LoaderSpinner className="w-5 h-5" />
|
|
247
|
-
) : isCropping ? (
|
|
248
|
-
<svg
|
|
249
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
250
|
-
width="20"
|
|
251
|
-
height="20"
|
|
252
|
-
viewBox="0 0 24 24"
|
|
253
|
-
fill="none"
|
|
254
|
-
stroke="currentColor"
|
|
255
|
-
strokeWidth="2"
|
|
256
|
-
strokeLinecap="round"
|
|
257
|
-
strokeLinejoin="round"
|
|
206
|
+
<>
|
|
207
|
+
<div className={mobileHeaderClassName}>
|
|
208
|
+
<h3
|
|
209
|
+
className={twMerge(
|
|
210
|
+
'text-2xl font-bold',
|
|
211
|
+
settings?.customStyles?.filterSidebarMobileTitle
|
|
212
|
+
)}
|
|
258
213
|
>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
stroke="currentColor"
|
|
270
|
-
strokeWidth="2"
|
|
271
|
-
strokeLinecap="round"
|
|
272
|
-
strokeLinejoin="round"
|
|
214
|
+
{t('common.product.filters')}
|
|
215
|
+
</h3>
|
|
216
|
+
<Button
|
|
217
|
+
appearance="ghost"
|
|
218
|
+
size="sm"
|
|
219
|
+
onClick={() => setIsFilterMenuOpen(false)}
|
|
220
|
+
className={twMerge(
|
|
221
|
+
'hover:bg-gray-200 rounded-full p-0 w-6 h-6',
|
|
222
|
+
settings?.customStyles?.filterSidebarMobileCloseButton
|
|
223
|
+
)}
|
|
273
224
|
>
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
225
|
+
<Icon name="close" size={16} />
|
|
226
|
+
</Button>
|
|
227
|
+
</div>
|
|
228
|
+
<div
|
|
229
|
+
className={twMerge(
|
|
230
|
+
'flex justify-between items-center mb-6 md:hidden',
|
|
231
|
+
settings?.customStyles?.filterSidebarMobileCounter
|
|
232
|
+
)}
|
|
233
|
+
>
|
|
234
|
+
<span
|
|
235
|
+
className={twMerge(
|
|
236
|
+
'text-sm',
|
|
237
|
+
settings?.customStyles?.filterSidebarMobileCounterText
|
|
238
|
+
)}
|
|
239
|
+
>
|
|
240
|
+
{searchResults?.pagination?.total_count || 0} items
|
|
241
|
+
</span>
|
|
242
|
+
</div>
|
|
243
|
+
</>
|
|
279
244
|
);
|
|
280
|
-
}
|
|
245
|
+
})()}
|
|
281
246
|
|
|
282
|
-
|
|
283
|
-
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
284
|
-
return null;
|
|
247
|
+
{(() => {
|
|
285
248
|
if (
|
|
286
|
-
|
|
287
|
-
!completedCrop ||
|
|
288
|
-
completedCrop.width <= 10 ||
|
|
289
|
-
completedCrop.height <= 10
|
|
290
|
-
)
|
|
291
|
-
return null;
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
settings?.customRenderers?.render?.filterSidebar?.renderTickButton
|
|
249
|
+
settings?.customRenderers?.render?.filterSidebar?.renderImageSection
|
|
295
250
|
) {
|
|
296
|
-
return settings.customRenderers.render.filterSidebar.
|
|
251
|
+
return settings.customRenderers.render.filterSidebar.renderImageSection(
|
|
297
252
|
{
|
|
298
|
-
|
|
299
|
-
|
|
253
|
+
currentImageUrl,
|
|
254
|
+
isCropping,
|
|
255
|
+
onToggleCrop: handleToggleCropMode,
|
|
256
|
+
onFileUpload: handleFileChangeWithCropReset,
|
|
257
|
+
onResetToOriginal: handleResetToOriginal,
|
|
258
|
+
fileError,
|
|
259
|
+
isLoading
|
|
300
260
|
}
|
|
301
261
|
);
|
|
302
262
|
}
|
|
303
263
|
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}`,
|
|
308
|
-
settings?.customStyles?.tickButton
|
|
264
|
+
const imageSectionClassName = twMerge(
|
|
265
|
+
'relative mb-2 md:mb-6 mt-4',
|
|
266
|
+
settings?.customStyles?.imageSection
|
|
309
267
|
);
|
|
310
268
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
onClick={() => {
|
|
316
|
-
if (!isLoading) {
|
|
317
|
-
handleSafeProcessCrop(completedCrop);
|
|
318
|
-
}
|
|
319
|
-
}}
|
|
320
|
-
disabled={isLoading}
|
|
321
|
-
className={tickButtonClassName}
|
|
322
|
-
>
|
|
323
|
-
{isLoading ? (
|
|
324
|
-
<LoaderSpinner className="w-5 h-5" />
|
|
325
|
-
) : (
|
|
326
|
-
<svg
|
|
327
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
328
|
-
width="20"
|
|
329
|
-
height="20"
|
|
330
|
-
viewBox="0 0 24 24"
|
|
331
|
-
fill="none"
|
|
332
|
-
stroke="currentColor"
|
|
333
|
-
strokeWidth="2"
|
|
334
|
-
strokeLinecap="round"
|
|
335
|
-
strokeLinejoin="round"
|
|
336
|
-
>
|
|
337
|
-
<polyline points="20,6 9,17 4,12"></polyline>
|
|
338
|
-
</svg>
|
|
339
|
-
)}
|
|
340
|
-
</Button>
|
|
269
|
+
const imageContainerClassName = twMerge(
|
|
270
|
+
'relative bg-white overflow-hidden flex items-center justify-center transition-all duration-300 ease-in-out',
|
|
271
|
+
isCropping ? 'border-2 border-dashed border-gray-300' : '',
|
|
272
|
+
settings?.customStyles?.imageContainer
|
|
341
273
|
);
|
|
342
|
-
};
|
|
343
274
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
275
|
+
const cropButtonClassName = twMerge(
|
|
276
|
+
`absolute z-10 bottom-3 left-3 p-2 rounded-full bg-white shadow-md w-10 h-10 ${
|
|
277
|
+
isCropping ? 'text-red-500' : 'text-gray-700'
|
|
278
|
+
} ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`,
|
|
279
|
+
settings?.customStyles?.cropButton
|
|
280
|
+
);
|
|
349
281
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
282
|
+
const renderCropButton = () => {
|
|
283
|
+
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
284
|
+
return null;
|
|
285
|
+
|
|
286
|
+
if (
|
|
287
|
+
settings?.customRenderers?.render?.filterSidebar?.renderCropButton
|
|
288
|
+
) {
|
|
289
|
+
return settings.customRenderers.render.filterSidebar.renderCropButton(
|
|
290
|
+
{
|
|
291
|
+
isCropping,
|
|
292
|
+
onClick: handleToggleCropMode,
|
|
293
|
+
disabled: isLoading
|
|
362
294
|
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
363
297
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
298
|
+
return (
|
|
299
|
+
<Button
|
|
300
|
+
appearance="ghost"
|
|
301
|
+
size="sm"
|
|
302
|
+
onClick={handleToggleCropMode}
|
|
303
|
+
disabled={isLoading}
|
|
304
|
+
className={cropButtonClassName}
|
|
305
|
+
>
|
|
306
|
+
{isLoading ? (
|
|
307
|
+
<LoaderSpinner className="w-5 h-5" />
|
|
308
|
+
) : isCropping ? (
|
|
309
|
+
<svg
|
|
310
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
311
|
+
width="20"
|
|
312
|
+
height="20"
|
|
313
|
+
viewBox="0 0 24 24"
|
|
314
|
+
fill="none"
|
|
315
|
+
stroke="currentColor"
|
|
316
|
+
strokeWidth="2"
|
|
317
|
+
strokeLinecap="round"
|
|
318
|
+
strokeLinejoin="round"
|
|
319
|
+
>
|
|
320
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
321
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
322
|
+
</svg>
|
|
323
|
+
) : (
|
|
324
|
+
<svg
|
|
325
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
326
|
+
width="20"
|
|
327
|
+
height="20"
|
|
328
|
+
viewBox="0 0 24 24"
|
|
329
|
+
fill="none"
|
|
330
|
+
stroke="currentColor"
|
|
331
|
+
strokeWidth="2"
|
|
332
|
+
strokeLinecap="round"
|
|
333
|
+
strokeLinejoin="round"
|
|
334
|
+
>
|
|
335
|
+
<path d="M6 2v14a2 2 0 0 0 2 2h14"></path>
|
|
336
|
+
<path d="M18 22V8a2 2 0 0 0-2-2H2"></path>
|
|
337
|
+
</svg>
|
|
338
|
+
)}
|
|
339
|
+
</Button>
|
|
340
|
+
);
|
|
341
|
+
};
|
|
368
342
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}}
|
|
382
|
-
onComplete={handleCropComplete}
|
|
383
|
-
onDragStart={() => {}}
|
|
384
|
-
onDragEnd={() => {}}
|
|
385
|
-
ruleOfThirds={false}
|
|
386
|
-
aspect={settings?.cropAspectRatio}
|
|
387
|
-
className="slider-crop"
|
|
388
|
-
disabled={isLoading}
|
|
389
|
-
keepSelection={true}
|
|
390
|
-
>
|
|
391
|
-
<img
|
|
392
|
-
ref={imageRef}
|
|
393
|
-
src={currentImageUrl || ''}
|
|
394
|
-
alt={product?.name || 'Product image'}
|
|
395
|
-
className="max-w-full max-h-[200px] md:max-h-[280px]"
|
|
396
|
-
style={{ transform: `scale(1) rotate(0deg)` }}
|
|
397
|
-
/>
|
|
398
|
-
</ReactCrop>
|
|
399
|
-
) : (
|
|
400
|
-
<div className="relative w-full h-full flex items-center justify-center">
|
|
401
|
-
<img
|
|
402
|
-
ref={imageRef}
|
|
403
|
-
src={currentImageUrl || ''}
|
|
404
|
-
alt={product?.name || 'Product image'}
|
|
405
|
-
className="max-w-full max-h-[200px] md:max-h-[280px] object-contain"
|
|
406
|
-
/>
|
|
407
|
-
{!isCropping && completedCrop && (
|
|
408
|
-
<div className="hidden md:block absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-in-out">
|
|
409
|
-
<div
|
|
410
|
-
className="absolute transition-all duration-300 ease-in-out"
|
|
411
|
-
style={{
|
|
412
|
-
width: `${completedCrop.width}px`,
|
|
413
|
-
height: `${completedCrop.height}px`,
|
|
414
|
-
left: `${completedCrop.x}px`,
|
|
415
|
-
top: `${completedCrop.y}px`,
|
|
416
|
-
boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',
|
|
417
|
-
border: '2px solid white'
|
|
418
|
-
}}
|
|
419
|
-
></div>
|
|
420
|
-
</div>
|
|
421
|
-
)}
|
|
422
|
-
</div>
|
|
423
|
-
)}
|
|
424
|
-
</div>
|
|
425
|
-
);
|
|
426
|
-
})()}
|
|
427
|
-
</div>
|
|
343
|
+
const renderTickButton = () => {
|
|
344
|
+
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
345
|
+
return null;
|
|
346
|
+
if (
|
|
347
|
+
!isCropping ||
|
|
348
|
+
!completedCrop ||
|
|
349
|
+
completedCrop.width <= 10 ||
|
|
350
|
+
completedCrop.height <= 10
|
|
351
|
+
)
|
|
352
|
+
return null;
|
|
428
353
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
type="file"
|
|
440
|
-
accept="image/*"
|
|
441
|
-
ref={fileInputRef}
|
|
442
|
-
onChange={handleFileChangeWithCropReset}
|
|
443
|
-
className="hidden"
|
|
444
|
-
/>
|
|
445
|
-
{(() => {
|
|
446
|
-
if (
|
|
447
|
-
settings?.customRenderers?.render?.filterSidebar
|
|
448
|
-
?.renderUploadButton
|
|
449
|
-
) {
|
|
450
|
-
return settings.customRenderers.render.filterSidebar.renderUploadButton(
|
|
451
|
-
{
|
|
452
|
-
onClick: handleNewImageClick,
|
|
453
|
-
disabled: isLoading
|
|
454
|
-
}
|
|
455
|
-
);
|
|
456
|
-
}
|
|
354
|
+
if (
|
|
355
|
+
settings?.customRenderers?.render?.filterSidebar?.renderTickButton
|
|
356
|
+
) {
|
|
357
|
+
return settings.customRenderers.render.filterSidebar.renderTickButton(
|
|
358
|
+
{
|
|
359
|
+
onClick: () => handleSafeProcessCrop(completedCrop),
|
|
360
|
+
disabled: isLoading
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|
|
457
364
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
365
|
+
const tickButtonClassName = twMerge(
|
|
366
|
+
`absolute z-10 bottom-3 right-3 p-2 rounded-full bg-white shadow-md w-10 h-10 text-green-600 ${
|
|
367
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
368
|
+
}`,
|
|
369
|
+
settings?.customStyles?.tickButton
|
|
370
|
+
);
|
|
464
371
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
372
|
+
return (
|
|
373
|
+
<Button
|
|
374
|
+
appearance="ghost"
|
|
375
|
+
size="sm"
|
|
376
|
+
onClick={() => {
|
|
377
|
+
if (!isLoading) {
|
|
378
|
+
handleSafeProcessCrop(completedCrop);
|
|
379
|
+
}
|
|
380
|
+
}}
|
|
381
|
+
disabled={isLoading}
|
|
382
|
+
className={tickButtonClassName}
|
|
383
|
+
>
|
|
384
|
+
{isLoading ? (
|
|
385
|
+
<LoaderSpinner className="w-5 h-5" />
|
|
386
|
+
) : (
|
|
387
|
+
<svg
|
|
388
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
389
|
+
width="20"
|
|
390
|
+
height="20"
|
|
391
|
+
viewBox="0 0 24 24"
|
|
392
|
+
fill="none"
|
|
393
|
+
stroke="currentColor"
|
|
394
|
+
strokeWidth="2"
|
|
395
|
+
strokeLinecap="round"
|
|
396
|
+
strokeLinejoin="round"
|
|
397
|
+
>
|
|
398
|
+
<polyline points="20,6 9,17 4,12"></polyline>
|
|
399
|
+
</svg>
|
|
493
400
|
)}
|
|
401
|
+
</Button>
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
return (
|
|
406
|
+
<div className={imageSectionClassName}>
|
|
407
|
+
<div className={imageContainerClassName}>
|
|
408
|
+
{renderCropButton()}
|
|
409
|
+
{renderTickButton()}
|
|
494
410
|
|
|
495
411
|
{(() => {
|
|
496
412
|
if (
|
|
497
413
|
settings?.customRenderers?.render?.filterSidebar
|
|
498
|
-
?.
|
|
414
|
+
?.renderImageContainer
|
|
499
415
|
) {
|
|
500
|
-
return settings.customRenderers.render.filterSidebar.
|
|
416
|
+
return settings.customRenderers.render.filterSidebar.renderImageContainer(
|
|
501
417
|
{
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
showResetButton && (hasUploadedImage || completedCrop)
|
|
418
|
+
imageUrl: currentImageUrl || '',
|
|
419
|
+
productName: product?.name || 'Product image',
|
|
420
|
+
isCropping
|
|
506
421
|
}
|
|
507
422
|
);
|
|
508
423
|
}
|
|
509
424
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const resetButtonClassName = twMerge(
|
|
514
|
-
`flex items-center gap-2 text-xs md:text-sm justify-center bg-blue-100 hover:bg-blue-200 border-blue-200 text-blue-600 ${
|
|
515
|
-
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
516
|
-
}`,
|
|
517
|
-
settings?.customStyles?.resetButton
|
|
425
|
+
const imageWrapperClassName = twMerge(
|
|
426
|
+
'w-full h-full flex items-center justify-center',
|
|
427
|
+
settings?.customStyles?.imageWrapper
|
|
518
428
|
);
|
|
519
429
|
|
|
520
430
|
return (
|
|
521
|
-
<
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
431
|
+
<div className={imageWrapperClassName}>
|
|
432
|
+
{isCropping ? (
|
|
433
|
+
<ReactCrop
|
|
434
|
+
crop={crop}
|
|
435
|
+
onChange={(newCrop) => {
|
|
436
|
+
if (!isLoading) {
|
|
437
|
+
setCrop(newCrop);
|
|
438
|
+
if (newCrop?.width > 10 && newCrop?.height > 10) {
|
|
439
|
+
setCompletedCrop(newCrop);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}}
|
|
443
|
+
onComplete={handleCropComplete}
|
|
444
|
+
onDragStart={() => {}}
|
|
445
|
+
onDragEnd={() => {}}
|
|
446
|
+
ruleOfThirds={false}
|
|
447
|
+
aspect={settings?.cropAspectRatio}
|
|
448
|
+
className={twMerge(
|
|
449
|
+
'slider-crop',
|
|
450
|
+
settings?.customStyles?.cropComponent
|
|
451
|
+
)}
|
|
452
|
+
disabled={isLoading}
|
|
453
|
+
keepSelection={true}
|
|
454
|
+
>
|
|
455
|
+
<img
|
|
456
|
+
ref={imageRef}
|
|
457
|
+
src={currentImageUrl || ''}
|
|
458
|
+
alt={product?.name || 'Product image'}
|
|
459
|
+
className={twMerge(
|
|
460
|
+
'max-w-full max-h-[200px] md:max-h-[280px]',
|
|
461
|
+
settings?.customStyles?.cropImage,
|
|
462
|
+
settings?.customStyles?.cropImageActive
|
|
463
|
+
)}
|
|
464
|
+
style={{ transform: `scale(1) rotate(0deg)` }}
|
|
465
|
+
/>
|
|
466
|
+
</ReactCrop>
|
|
467
|
+
) : (
|
|
468
|
+
<div
|
|
469
|
+
className={twMerge(
|
|
470
|
+
'relative w-full h-full flex items-center justify-center',
|
|
471
|
+
settings?.customStyles?.cropImageContainer
|
|
472
|
+
)}
|
|
473
|
+
>
|
|
474
|
+
<img
|
|
475
|
+
ref={imageRef}
|
|
476
|
+
src={currentImageUrl || ''}
|
|
477
|
+
alt={product?.name || 'Product image'}
|
|
478
|
+
className={twMerge(
|
|
479
|
+
'max-w-full max-h-[200px] md:max-h-[280px] object-contain',
|
|
480
|
+
settings?.customStyles?.cropImage,
|
|
481
|
+
settings?.customStyles?.cropImageNonCropping
|
|
482
|
+
)}
|
|
483
|
+
/>
|
|
484
|
+
{!isCropping && completedCrop && (
|
|
485
|
+
<div
|
|
486
|
+
className={twMerge(
|
|
487
|
+
'hidden md:block absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-in-out',
|
|
488
|
+
settings?.customStyles?.cropOverlay,
|
|
489
|
+
settings?.customStyles?.cropOverlayBackground
|
|
490
|
+
)}
|
|
491
|
+
>
|
|
492
|
+
<div
|
|
493
|
+
className={twMerge(
|
|
494
|
+
'absolute transition-all duration-300 ease-in-out',
|
|
495
|
+
settings?.customStyles?.cropSelection,
|
|
496
|
+
settings?.customStyles?.cropSelectionHighlight
|
|
497
|
+
)}
|
|
498
|
+
style={{
|
|
499
|
+
width: `${completedCrop.width}px`,
|
|
500
|
+
height: `${completedCrop.height}px`,
|
|
501
|
+
left: `${completedCrop.x}px`,
|
|
502
|
+
top: `${completedCrop.y}px`,
|
|
503
|
+
boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',
|
|
504
|
+
border: '2px solid white'
|
|
505
|
+
}}
|
|
506
|
+
></div>
|
|
507
|
+
</div>
|
|
508
|
+
)}
|
|
509
|
+
</div>
|
|
510
|
+
)}
|
|
511
|
+
</div>
|
|
547
512
|
);
|
|
548
513
|
})()}
|
|
549
514
|
</div>
|
|
550
|
-
)}
|
|
551
515
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
516
|
+
{!isCropping && (
|
|
517
|
+
<div
|
|
518
|
+
className={twMerge(
|
|
519
|
+
'flex flex-col md:flex-row justify-center mt-3 gap-2',
|
|
520
|
+
settings?.customStyles?.cropControls
|
|
521
|
+
)}
|
|
522
|
+
>
|
|
523
|
+
{settings?.enableFileUpload !== false && (
|
|
524
|
+
<>
|
|
525
|
+
<input
|
|
526
|
+
type="file"
|
|
527
|
+
accept="image/*"
|
|
528
|
+
ref={fileInputRef}
|
|
529
|
+
onChange={handleFileChangeWithCropReset}
|
|
530
|
+
className="hidden"
|
|
531
|
+
/>
|
|
532
|
+
{(() => {
|
|
533
|
+
if (
|
|
534
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
535
|
+
?.renderUploadButton
|
|
536
|
+
) {
|
|
537
|
+
return settings.customRenderers.render.filterSidebar.renderUploadButton(
|
|
538
|
+
{
|
|
539
|
+
onClick: handleNewImageClick,
|
|
540
|
+
disabled: isLoading
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const uploadButtonClassName = twMerge(
|
|
546
|
+
`flex items-center gap-2 text-xs md:text-sm justify-center bg-gray-100 hover:bg-gray-200 border-gray-200 ${
|
|
547
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
548
|
+
}`,
|
|
549
|
+
settings?.customStyles?.uploadButton
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
return (
|
|
553
|
+
<Button
|
|
554
|
+
appearance="outlined"
|
|
555
|
+
size="sm"
|
|
556
|
+
onClick={handleNewImageClick}
|
|
557
|
+
disabled={isLoading}
|
|
558
|
+
className={uploadButtonClassName}
|
|
559
|
+
>
|
|
560
|
+
<svg
|
|
561
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
562
|
+
width="20"
|
|
563
|
+
height="20"
|
|
564
|
+
viewBox="0 0 24 24"
|
|
565
|
+
fill="none"
|
|
566
|
+
stroke="currentColor"
|
|
567
|
+
strokeWidth="2"
|
|
568
|
+
strokeLinecap="round"
|
|
569
|
+
strokeLinejoin="round"
|
|
570
|
+
className="text-gray-500"
|
|
571
|
+
>
|
|
572
|
+
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
|
|
573
|
+
<circle cx="12" cy="13" r="4"></circle>
|
|
574
|
+
</svg>
|
|
575
|
+
{t('common.product.new_image')}
|
|
576
|
+
</Button>
|
|
577
|
+
);
|
|
578
|
+
})()}
|
|
579
|
+
</>
|
|
580
|
+
)}
|
|
581
|
+
|
|
582
|
+
{(() => {
|
|
583
|
+
if (
|
|
584
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
585
|
+
?.renderResetButton
|
|
586
|
+
) {
|
|
587
|
+
return settings.customRenderers.render.filterSidebar.renderResetButton(
|
|
588
|
+
{
|
|
589
|
+
onClick: handleResetToOriginal,
|
|
590
|
+
disabled: isLoading,
|
|
591
|
+
showButton:
|
|
592
|
+
showResetButton &&
|
|
593
|
+
(hasUploadedImage || completedCrop)
|
|
594
|
+
}
|
|
595
|
+
);
|
|
562
596
|
}
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
597
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
598
|
+
if (
|
|
599
|
+
!showResetButton ||
|
|
600
|
+
(!hasUploadedImage && !completedCrop)
|
|
601
|
+
)
|
|
602
|
+
return null;
|
|
570
603
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
);
|
|
578
|
-
})()}
|
|
579
|
-
</div>
|
|
580
|
-
);
|
|
581
|
-
})()}
|
|
582
|
-
|
|
583
|
-
{/* Filters */}
|
|
584
|
-
<div className="space-y-2 md:space-y-4">
|
|
585
|
-
{searchResults?.facets
|
|
586
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
587
|
-
?.map((facet) => {
|
|
588
|
-
if (
|
|
589
|
-
settings?.customRenderers?.render?.filterSidebar
|
|
590
|
-
?.renderFilterGroup
|
|
591
|
-
) {
|
|
592
|
-
return settings.customRenderers.render.filterSidebar.renderFilterGroup(
|
|
593
|
-
{
|
|
594
|
-
facet,
|
|
595
|
-
onFacetChange: handleFacetChange,
|
|
596
|
-
isLoading
|
|
597
|
-
}
|
|
598
|
-
);
|
|
599
|
-
}
|
|
604
|
+
const resetButtonClassName = twMerge(
|
|
605
|
+
`flex items-center gap-2 text-xs md:text-sm justify-center bg-blue-100 hover:bg-blue-200 border-blue-200 text-blue-600 ${
|
|
606
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
607
|
+
}`,
|
|
608
|
+
settings?.customStyles?.resetButton
|
|
609
|
+
);
|
|
600
610
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
611
|
+
return (
|
|
612
|
+
<Button
|
|
613
|
+
appearance="outlined"
|
|
614
|
+
size="sm"
|
|
615
|
+
onClick={handleResetToOriginal}
|
|
616
|
+
disabled={isLoading}
|
|
617
|
+
className={resetButtonClassName}
|
|
618
|
+
>
|
|
619
|
+
<svg
|
|
620
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
621
|
+
width="20"
|
|
622
|
+
height="20"
|
|
623
|
+
viewBox="0 0 24 24"
|
|
624
|
+
fill="none"
|
|
625
|
+
stroke="currentColor"
|
|
626
|
+
strokeWidth="2"
|
|
627
|
+
strokeLinecap="round"
|
|
628
|
+
strokeLinejoin="round"
|
|
629
|
+
className="text-blue-600"
|
|
630
|
+
>
|
|
631
|
+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
|
|
632
|
+
<path d="M21 3v5h-5"></path>
|
|
633
|
+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path>
|
|
634
|
+
<path d="M3 21v-5h5"></path>
|
|
635
|
+
</svg>
|
|
636
|
+
{t('common.product.reset_to_original')}
|
|
637
|
+
</Button>
|
|
638
|
+
);
|
|
639
|
+
})()}
|
|
640
|
+
</div>
|
|
641
|
+
)}
|
|
606
642
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
643
|
+
{fileError &&
|
|
644
|
+
!isCropping &&
|
|
645
|
+
(() => {
|
|
646
|
+
if (
|
|
647
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
648
|
+
?.renderErrorMessage
|
|
649
|
+
) {
|
|
650
|
+
return settings.customRenderers.render.filterSidebar.renderErrorMessage(
|
|
651
|
+
{
|
|
652
|
+
error: fileError
|
|
653
|
+
}
|
|
654
|
+
);
|
|
655
|
+
}
|
|
614
656
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
657
|
+
const errorClassName = twMerge(
|
|
658
|
+
'mt-2 px-3 py-2 bg-red-50 border border-red-100 rounded-md',
|
|
659
|
+
settings?.customStyles?.errorMessage
|
|
660
|
+
);
|
|
619
661
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
662
|
+
return (
|
|
663
|
+
<div className={errorClassName}>
|
|
664
|
+
<p className="text-xs text-red-600 font-medium">
|
|
665
|
+
{fileError}
|
|
666
|
+
</p>
|
|
667
|
+
</div>
|
|
668
|
+
);
|
|
669
|
+
})()}
|
|
670
|
+
</div>
|
|
671
|
+
);
|
|
672
|
+
})()}
|
|
627
673
|
|
|
628
|
-
|
|
674
|
+
{/* Filters */}
|
|
675
|
+
<div className="space-y-2 md:space-y-4">
|
|
676
|
+
{searchResults?.facets
|
|
677
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
678
|
+
?.map((facet) => {
|
|
629
679
|
if (
|
|
630
680
|
settings?.customRenderers?.render?.filterSidebar
|
|
631
|
-
?.
|
|
681
|
+
?.renderFilterGroup
|
|
632
682
|
) {
|
|
633
|
-
return settings.customRenderers.render.filterSidebar.
|
|
683
|
+
return settings.customRenderers.render.filterSidebar.renderFilterGroup(
|
|
634
684
|
{
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
685
|
+
facet,
|
|
686
|
+
onFacetChange: handleFacetChange,
|
|
687
|
+
isLoading
|
|
638
688
|
}
|
|
639
689
|
);
|
|
640
690
|
}
|
|
641
|
-
return null;
|
|
642
|
-
};
|
|
643
691
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
isLoading,
|
|
655
|
-
isSelected: choice.is_selected
|
|
656
|
-
}
|
|
692
|
+
const Component = getComponentByWidgetType(
|
|
693
|
+
facet.widget_type,
|
|
694
|
+
facet.key
|
|
695
|
+
);
|
|
696
|
+
const choices = facet.data.choices || [];
|
|
697
|
+
|
|
698
|
+
if (!Component) {
|
|
699
|
+
console.warn(
|
|
700
|
+
'Component not found for widget type:',
|
|
701
|
+
facet.widget_type
|
|
657
702
|
);
|
|
703
|
+
return null;
|
|
658
704
|
}
|
|
659
705
|
|
|
660
|
-
const
|
|
661
|
-
'filter-
|
|
662
|
-
settings?.customStyles?.
|
|
706
|
+
const filterGroupClassName = twMerge(
|
|
707
|
+
'filter-group',
|
|
708
|
+
settings?.customStyles?.filterGroup
|
|
663
709
|
);
|
|
664
710
|
|
|
665
|
-
const
|
|
666
|
-
'
|
|
667
|
-
|
|
711
|
+
const filterGroupContentClassName = twMerge(
|
|
712
|
+
clsx('flex gap-4', {
|
|
713
|
+
'flex-wrap flex-row': facet.key === sizeKey,
|
|
714
|
+
'flex-col': facet.key !== sizeKey
|
|
715
|
+
}),
|
|
716
|
+
settings?.customStyles?.filterGroupContent
|
|
668
717
|
);
|
|
669
718
|
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
719
|
+
const renderFilterGroupTitle = () => {
|
|
720
|
+
if (
|
|
721
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
722
|
+
?.renderFilterGroupTitle
|
|
723
|
+
) {
|
|
724
|
+
return settings.customRenderers.render.filterSidebar.renderFilterGroupTitle(
|
|
725
|
+
{
|
|
726
|
+
title: facet.name,
|
|
727
|
+
isCollapsed: choices.some((choice) => choice.is_selected),
|
|
728
|
+
onToggle: () => {}
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
};
|
|
674
734
|
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
735
|
+
const renderFilterItem = (choice: any, index: number) => {
|
|
736
|
+
if (
|
|
737
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
738
|
+
?.renderFilterItem
|
|
739
|
+
) {
|
|
740
|
+
return settings.customRenderers.render.filterSidebar.renderFilterItem(
|
|
741
|
+
{
|
|
742
|
+
choice,
|
|
743
|
+
facetKey: facet.key,
|
|
744
|
+
onFacetChange: handleFacetChange,
|
|
745
|
+
isLoading,
|
|
746
|
+
isSelected: choice.is_selected
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const filterItemClassName = twMerge(
|
|
752
|
+
'filter-item',
|
|
753
|
+
settings?.customStyles?.filterItem
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
const filterItemInputClassName = twMerge(
|
|
757
|
+
'filter-item-input',
|
|
758
|
+
settings?.customStyles?.filterItemInput
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
const filterItemLabelClassName = twMerge(
|
|
762
|
+
'filter-item-label',
|
|
763
|
+
settings?.customStyles?.filterItemLabel
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
const filterItemCountClassName = twMerge(
|
|
767
|
+
'filter-item-count',
|
|
768
|
+
settings?.customStyles?.filterItemCount
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
return (
|
|
772
|
+
<div key={choice.value} className={filterItemClassName}>
|
|
773
|
+
<Component
|
|
774
|
+
key={choice.label}
|
|
775
|
+
value={choice.value}
|
|
776
|
+
label={choice.label}
|
|
777
|
+
name={facet.key}
|
|
778
|
+
onChange={() => {
|
|
779
|
+
if (!isLoading) {
|
|
780
|
+
handleFacetChange(facet.key, choice.value);
|
|
781
|
+
}
|
|
782
|
+
}}
|
|
783
|
+
onClick={() => {
|
|
784
|
+
if (!isLoading && facet.key === sizeKey) {
|
|
785
|
+
handleFacetChange(facet.key, choice.value);
|
|
786
|
+
}
|
|
787
|
+
}}
|
|
788
|
+
checked={choice.is_selected}
|
|
789
|
+
data-testid={`${choice.label.trim()}`}
|
|
790
|
+
disabled={isLoading}
|
|
791
|
+
className={filterItemInputClassName}
|
|
792
|
+
>
|
|
793
|
+
<span className={filterItemLabelClassName}>
|
|
794
|
+
{choice.label}
|
|
795
|
+
</span>{' '}
|
|
796
|
+
(
|
|
797
|
+
<span
|
|
798
|
+
data-testid={`filter-count-${facet.name.toLowerCase()}-${index}`}
|
|
799
|
+
className={filterItemCountClassName}
|
|
800
|
+
>
|
|
801
|
+
{choice.quantity}
|
|
802
|
+
</span>
|
|
803
|
+
)
|
|
804
|
+
</Component>
|
|
805
|
+
</div>
|
|
806
|
+
);
|
|
807
|
+
};
|
|
679
808
|
|
|
680
809
|
return (
|
|
681
|
-
<div key={
|
|
682
|
-
<
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
name={facet.key}
|
|
687
|
-
onChange={() => {
|
|
688
|
-
if (!isLoading) {
|
|
689
|
-
handleFacetChange(facet.key, choice.value);
|
|
690
|
-
}
|
|
691
|
-
}}
|
|
692
|
-
onClick={() => {
|
|
693
|
-
if (!isLoading && facet.key === sizeKey) {
|
|
694
|
-
handleFacetChange(facet.key, choice.value);
|
|
695
|
-
}
|
|
696
|
-
}}
|
|
697
|
-
checked={choice.is_selected}
|
|
698
|
-
data-testid={`${choice.label.trim()}`}
|
|
699
|
-
disabled={isLoading}
|
|
700
|
-
className={filterItemInputClassName}
|
|
810
|
+
<div key={facet.key} className={filterGroupClassName}>
|
|
811
|
+
<Accordion
|
|
812
|
+
title={renderFilterGroupTitle() || facet.name}
|
|
813
|
+
isCollapse={choices.some((choice) => choice.is_selected)}
|
|
814
|
+
dataTestId={`filter-${facet.name}`}
|
|
701
815
|
>
|
|
702
|
-
<
|
|
703
|
-
{
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
{choice.quantity}
|
|
711
|
-
</span>
|
|
712
|
-
)
|
|
713
|
-
</Component>
|
|
816
|
+
<div className={filterGroupContentClassName}>
|
|
817
|
+
{choices
|
|
818
|
+
.slice(0, 10)
|
|
819
|
+
.map((choice, index) =>
|
|
820
|
+
renderFilterItem(choice, index)
|
|
821
|
+
)}
|
|
822
|
+
</div>
|
|
823
|
+
</Accordion>
|
|
714
824
|
</div>
|
|
715
825
|
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
<div className={filterGroupContentClassName}>
|
|
726
|
-
{choices
|
|
727
|
-
.slice(0, 10)
|
|
728
|
-
.map((choice, index) => renderFilterItem(choice, index))}
|
|
729
|
-
</div>
|
|
730
|
-
</Accordion>
|
|
731
|
-
</div>
|
|
826
|
+
})}
|
|
827
|
+
</div>
|
|
828
|
+
|
|
829
|
+
{/* Mobile Active Filters and Clear Button */}
|
|
830
|
+
{(() => {
|
|
831
|
+
const hasActiveFilters = searchResults?.facets
|
|
832
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
833
|
+
?.some((facet) =>
|
|
834
|
+
facet.data.choices?.some((choice) => choice.is_selected)
|
|
732
835
|
);
|
|
733
|
-
})}
|
|
734
|
-
</div>
|
|
735
836
|
|
|
736
|
-
|
|
737
|
-
{(() => {
|
|
738
|
-
const hasActiveFilters = searchResults?.facets
|
|
739
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
740
|
-
?.some((facet) =>
|
|
741
|
-
facet.data.choices?.some((choice) => choice.is_selected)
|
|
742
|
-
);
|
|
837
|
+
if (!hasActiveFilters) return null;
|
|
743
838
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
.flatMap(
|
|
749
|
-
(facet) =>
|
|
750
|
-
facet.data.choices
|
|
751
|
-
?.filter((choice) => choice.is_selected)
|
|
752
|
-
.map((choice) => ({
|
|
753
|
-
key: facet.key,
|
|
754
|
-
value: choice.value.toString(),
|
|
755
|
-
label: choice.label
|
|
756
|
-
})) || []
|
|
757
|
-
);
|
|
758
|
-
|
|
759
|
-
const handleClearAll = () => {
|
|
760
|
-
if (!isLoading) {
|
|
761
|
-
const facetsToRemove = [];
|
|
762
|
-
searchResults?.facets
|
|
763
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
764
|
-
?.forEach((facet) => {
|
|
839
|
+
const activeFilters = searchResults.facets
|
|
840
|
+
.filter((facet) => facet.key !== 'category_ids')
|
|
841
|
+
.flatMap(
|
|
842
|
+
(facet) =>
|
|
765
843
|
facet.data.choices
|
|
766
844
|
?.filter((choice) => choice.is_selected)
|
|
767
|
-
.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
845
|
+
.map((choice) => ({
|
|
846
|
+
key: facet.key,
|
|
847
|
+
value: choice.value.toString(),
|
|
848
|
+
label: choice.label
|
|
849
|
+
})) || []
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
const handleClearAll = () => {
|
|
853
|
+
if (!isLoading) {
|
|
854
|
+
const facetsToRemove = [];
|
|
855
|
+
searchResults?.facets
|
|
856
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
857
|
+
?.forEach((facet) => {
|
|
858
|
+
facet.data.choices
|
|
859
|
+
?.filter((choice) => choice.is_selected)
|
|
860
|
+
.forEach((choice) => {
|
|
861
|
+
facetsToRemove.push({
|
|
862
|
+
facetKey: facet.key,
|
|
863
|
+
choiceValue: choice.value
|
|
864
|
+
});
|
|
771
865
|
});
|
|
772
|
-
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
facetsToRemove.forEach(({ facetKey, choiceValue }) => {
|
|
869
|
+
removeFacetFilter(facetKey, choiceValue);
|
|
773
870
|
});
|
|
871
|
+
}
|
|
872
|
+
};
|
|
774
873
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
874
|
+
if (
|
|
875
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
876
|
+
?.renderMobileActiveFilters
|
|
877
|
+
) {
|
|
878
|
+
return (
|
|
879
|
+
<div className="md:hidden mt-6">
|
|
880
|
+
{settings.customRenderers.render.filterSidebar.renderMobileActiveFilters(
|
|
881
|
+
{
|
|
882
|
+
filters: activeFilters,
|
|
883
|
+
onRemove: handleFacetChange,
|
|
884
|
+
onClearAll: handleClearAll,
|
|
885
|
+
isLoading
|
|
886
|
+
}
|
|
887
|
+
)}
|
|
888
|
+
</div>
|
|
889
|
+
);
|
|
778
890
|
}
|
|
779
|
-
};
|
|
780
891
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
?.
|
|
784
|
-
) {
|
|
785
|
-
return (
|
|
786
|
-
<div className="md:hidden mt-6">
|
|
787
|
-
{settings.customRenderers.render.filterSidebar.renderMobileActiveFilters(
|
|
788
|
-
{
|
|
789
|
-
filters: activeFilters,
|
|
790
|
-
onRemove: handleFacetChange,
|
|
791
|
-
onClearAll: handleClearAll,
|
|
792
|
-
isLoading
|
|
793
|
-
}
|
|
794
|
-
)}
|
|
795
|
-
</div>
|
|
892
|
+
const mobileActiveFiltersClassName = twMerge(
|
|
893
|
+
'md:hidden mt-6',
|
|
894
|
+
settings?.customStyles?.mobileActiveFilters
|
|
796
895
|
);
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
</h4>
|
|
820
|
-
<div className="flex flex-wrap gap-2">
|
|
821
|
-
{activeFilters.map((filter) => (
|
|
822
|
-
<div
|
|
823
|
-
key={`${filter.key}-${filter.value}`}
|
|
824
|
-
className={mobileActiveFilterTagClassName}
|
|
825
|
-
>
|
|
826
|
-
<span>{filter.label}</span>
|
|
827
|
-
<Button
|
|
828
|
-
appearance="ghost"
|
|
829
|
-
size="sm"
|
|
830
|
-
onClick={() => {
|
|
831
|
-
if (!isLoading) {
|
|
832
|
-
handleFacetChange(filter.key, filter.value);
|
|
833
|
-
}
|
|
834
|
-
}}
|
|
835
|
-
disabled={isLoading}
|
|
836
|
-
className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
|
|
896
|
+
|
|
897
|
+
const mobileActiveFilterTagClassName = twMerge(
|
|
898
|
+
'flex items-center gap-1 px-2 py-1 bg-gray-100 rounded-full text-xs',
|
|
899
|
+
settings?.customStyles?.mobileActiveFilterTag
|
|
900
|
+
);
|
|
901
|
+
|
|
902
|
+
const mobileClearAllButtonClassName = twMerge(
|
|
903
|
+
'w-full',
|
|
904
|
+
settings?.customStyles?.mobileClearAllButton
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
return (
|
|
908
|
+
<div className={mobileActiveFiltersClassName}>
|
|
909
|
+
<div className="mb-4">
|
|
910
|
+
<h4 className="text-sm font-medium mb-2">
|
|
911
|
+
{t('common.product.active_filters')}
|
|
912
|
+
</h4>
|
|
913
|
+
<div className="flex flex-wrap gap-2">
|
|
914
|
+
{activeFilters.map((filter) => (
|
|
915
|
+
<div
|
|
916
|
+
key={`${filter.key}-${filter.value}`}
|
|
917
|
+
className={mobileActiveFilterTagClassName}
|
|
837
918
|
>
|
|
838
|
-
<
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
919
|
+
<span>{filter.label}</span>
|
|
920
|
+
<Button
|
|
921
|
+
appearance="ghost"
|
|
922
|
+
size="sm"
|
|
923
|
+
onClick={() => {
|
|
924
|
+
if (!isLoading) {
|
|
925
|
+
handleFacetChange(filter.key, filter.value);
|
|
926
|
+
}
|
|
927
|
+
}}
|
|
928
|
+
disabled={isLoading}
|
|
929
|
+
className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
|
|
930
|
+
>
|
|
931
|
+
<Icon name="close" size={10} />
|
|
932
|
+
</Button>
|
|
933
|
+
</div>
|
|
934
|
+
))}
|
|
935
|
+
</div>
|
|
842
936
|
</div>
|
|
843
|
-
</div>
|
|
844
937
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
938
|
+
<Button
|
|
939
|
+
appearance="outlined"
|
|
940
|
+
className={mobileClearAllButtonClassName}
|
|
941
|
+
onClick={handleClearAll}
|
|
942
|
+
disabled={isLoading}
|
|
943
|
+
>
|
|
944
|
+
{t('common.product.clear_all_filters')}
|
|
945
|
+
</Button>
|
|
946
|
+
</div>
|
|
947
|
+
);
|
|
948
|
+
})()}
|
|
949
|
+
</div>
|
|
950
|
+
</>
|
|
857
951
|
);
|
|
858
952
|
}
|