@akinon/pz-similar-products 1.92.0-rc.19 β†’ 1.92.0-rc.21

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @akinon/pz-similar-products
2
2
 
3
+ ## 1.92.0-rc.21
4
+
5
+ ### Minor Changes
6
+
7
+ - d99a6a7: ZERO-3457: Fixed the settings prop and made sure everything is customizable.
8
+
9
+ ## 1.92.0-rc.20
10
+
11
+ ### Minor Changes
12
+
13
+ - 8b1d24e: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
14
+
3
15
  ## 1.92.0-rc.19
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -44,11 +44,12 @@
44
44
  - [Modern Pagination with Progress](#markdown-header-modern-pagination-with-progress)
45
45
  - [Advanced Modal with Custom Header & Controls](#markdown-header-advanced-modal-with-custom-header-controls)
46
46
  - [Complete Brand Theme Integration](#markdown-header-complete-brand-theme-integration)
47
+ - [Advanced Component Customization](#markdown-header-advanced-component-customization)
47
48
 
48
49
  ### πŸ“– Styling Reference
49
50
 
50
- - [Available Style Targets (40+ options)](#markdown-header-available-style-targets-40-options)
51
- - [Available Render Functions (25+ options)](#markdown-header-available-render-functions-25-options)
51
+ - [Available Style Targets (50+ options)](#markdown-header-available-style-targets-50-options)
52
+ - [Available Render Functions (30+ options)](#markdown-header-available-render-functions-30-options)
52
53
 
53
54
  ### βœ… Best Practices
54
55
 
@@ -65,7 +66,7 @@
65
66
  - **βœ‚οΈ Image Cropping**: Built-in cropping functionality with manual confirmation for precise searches
66
67
  - **πŸŽ›οΈ Advanced Filtering**: Dynamic facet-based filtering system
67
68
  - **πŸ“„ Multiple Pagination Modes**: Traditional pagination, load more button, and infinite scroll
68
- - **🎨 Granular Customization**: 40+ style targets and 25+ render points
69
+ - **🎨 Granular Customization**: 50+ style targets and 30+ render points
69
70
  - **πŸ“± Mobile Responsive**: Optimized for all device sizes
70
71
  - **⚑ Performance Optimized**: Lazy loading and efficient rendering
71
72
  - **πŸ”§ TypeScript**: Full TypeScript support with detailed type definitions
@@ -174,49 +175,147 @@ interface SimilarProductsSettings {
174
175
  loadMoreThreshold?: number; // Pixels from bottom for auto load (default: 100)
175
176
  maxPagesLoadMore?: number; // Maximum pages to load in load-more mode
176
177
 
177
- // 40+ granular style targets
178
+ // 50+ granular style targets
178
179
  customStyles?: {
179
180
  // Main components
180
181
  modal?: string;
182
+ modalContent?: string; // NEW: Grid container
181
183
  filterSidebar?: string;
182
184
  resultsGrid?: string;
185
+ resultsContainer?: string; // NEW: Results wrapper
186
+ imageSearchModal?: string;
183
187
 
184
- // Deep nested elements
188
+ // Active filters
189
+ activeFiltersContainer?: string;
190
+ activeFiltersWrapper?: string; // NEW: Filters wrapper
191
+ activeFilterTag?: string;
192
+ activeFilterTagButton?: string;
193
+
194
+ // Controls section
195
+ controlsContainer?: string;
196
+ controlsInner?: string; // NEW: Controls inner wrapper
197
+ controlsLeft?: string; // NEW: Left controls (count, filter)
198
+ controlsRight?: string; // NEW: Right controls (sort)
199
+ itemCount?: string;
200
+ sortDropdown?: string;
201
+ filterToggleButton?: string;
202
+
203
+ // Filter sidebar mobile
204
+ filterSidebarMobileHeader?: string;
205
+ filterSidebarMobileTitle?: string; // NEW: Mobile title
206
+ filterSidebarMobileCloseButton?: string; // NEW: Mobile close button
207
+ filterSidebarMobileCounter?: string; // NEW: Mobile counter wrapper
208
+ filterSidebarMobileCounterText?: string; // NEW: Mobile counter text
209
+ filterGroup?: string;
210
+ filterGroupTitle?: string;
211
+ filterGroupContent?: string;
212
+ filterItem?: string;
213
+ filterItemInput?: string;
214
+ filterItemLabel?: string;
215
+ filterItemCount?: string;
216
+
217
+ // Image section
218
+ imageSection?: string;
219
+ imageContainer?: string;
220
+ imageWrapper?: string;
221
+ cropButton?: string;
222
+ tickButton?: string;
223
+ uploadButton?: string;
224
+ resetButton?: string;
225
+ errorMessage?: string;
226
+ cropControls?: string;
227
+
228
+ // Products grid
229
+ gridContainer?: string;
185
230
  productItem?: string;
231
+ productImageWrapper?: string;
186
232
  productImage?: string;
233
+ productInfo?: string;
187
234
  productTitle?: string;
188
235
  productPrice?: string;
236
+ productPriceContainer?: string; // NEW: Price wrapper
237
+ productOldPrice?: string;
238
+
239
+ // Pagination & load more
240
+ pagination?: string;
241
+ paginationContainer?: string;
242
+ paginationInfo?: string;
189
243
  paginationButton?: string;
244
+ paginationButtonActive?: string;
245
+ paginationButtonDisabled?: string;
246
+ paginationPrevious?: string;
247
+ paginationNext?: string;
190
248
  loadMoreButton?: string;
191
249
  loadMoreContainer?: string;
192
- activeFilterTag?: string;
193
- filterGroup?: string;
194
- imageSection?: string;
250
+
251
+ // Loading & empty states
195
252
  loadingSpinner?: string;
196
- // ... see examples for complete list
253
+ loadingOverlay?: string;
254
+ emptyState?: string;
255
+ emptyStateInner?: string; // NEW: Empty state inner wrapper
256
+ emptyStateIcon?: string;
257
+ emptyStateText?: string;
258
+
259
+ // Image search modal
260
+ imageSearchContent?: string; // NEW: Search modal grid
261
+ imageSearchUploadSection?: string; // NEW: Upload section
262
+ imageSearchUploadTitle?: string; // NEW: Upload title
263
+ imageSearchUploadArea?: string; // NEW: Drag & drop area
264
+ imageSearchUploadButton?: string; // NEW: Upload button
265
+ imageSearchTipsSection?: string; // NEW: Tips section
266
+ imageSearchTipsTitle?: string; // NEW: Tips title
267
+ imageSearchTipsList?: string; // NEW: Tips list
268
+ imageSearchTipsItem?: string; // NEW: Individual tip
269
+ imageSearchModalOverlay?: string; // NEW: Image search modal overlay
270
+
271
+ // Mobile active filters
272
+ mobileActiveFilters?: string;
273
+ mobileActiveFilterTag?: string;
274
+ mobileClearAllButton?: string;
197
275
  };
198
276
 
199
277
  // 25+ render functions for granular control
200
278
  customRenderers?: {
279
+ // Component-level renderers (full control)
280
+ Modal?: React.ComponentType<SimilarProductsModalProps>;
281
+ FilterSidebar?: React.ComponentType<FilterSidebarProps>;
282
+ ResultsGrid?: React.ComponentType<ResultsGridProps>;
283
+ ImageSearchModal?: React.ComponentType<any>;
284
+
285
+ // Granular render functions (30+ options)
201
286
  render?: {
202
287
  modal?: {
288
+ renderModal?: (props) => React.ReactNode; // Full modal override
203
289
  renderHeader?: (props) => React.ReactNode;
204
290
  renderActiveFilters?: (props) => React.ReactNode;
205
291
  renderControls?: (props) => React.ReactNode;
206
292
  renderItemCount?: (props) => React.ReactNode;
293
+ renderSortDropdown?: (props) => React.ReactNode;
294
+ renderFilterToggleButton?: (props) => React.ReactNode;
295
+ // ... see examples for complete list
296
+ };
297
+ filterSidebar?: {
298
+ renderSidebar?: (props) => React.ReactNode; // Full sidebar override
299
+ renderImageSection?: (props) => React.ReactNode;
300
+ renderFilterGroup?: (props) => React.ReactNode;
301
+ renderCropButton?: (props) => React.ReactNode;
302
+ renderTickButton?: (props) => React.ReactNode;
303
+ renderUploadButton?: (props) => React.ReactNode;
304
+ renderResetButton?: (props) => React.ReactNode;
207
305
  // ... see examples for complete list
208
306
  };
209
307
  resultsGrid?: {
308
+ renderGrid?: (props) => React.ReactNode; // Full grid override
210
309
  renderProductItem?: (props) => React.ReactNode;
211
310
  renderPagination?: (props) => React.ReactNode;
212
311
  renderGridContainer?: (props) => React.ReactNode;
312
+ renderLoadMore?: (props) => React.ReactNode;
313
+ renderEmptyState?: (props) => React.ReactNode;
213
314
  // ... see examples for complete list
214
315
  };
215
- filterSidebar?: {
216
- renderImageSection?: (props) => React.ReactNode;
217
- renderFilterGroup?: (props) => React.ReactNode;
218
- renderCropButton?: (props) => React.ReactNode;
219
- // ... see examples for complete list
316
+ imageSearchModal?: {
317
+ renderModal?: (props) => React.ReactNode; // Full image search modal override
318
+ renderUploadArea?: (props) => React.ReactNode;
220
319
  };
221
320
  };
222
321
  };
@@ -817,7 +916,8 @@ export function SiteHeader() {
817
916
  settings: {
818
917
  maxFileSize: 5,
819
918
  customStyles: {
820
- imageSearchModal: 'max-w-lg rounded-xl'
919
+ imageSearchModal: 'max-w-lg rounded-xl',
920
+ imageSearchModalOverlay: 'bg-black/70 backdrop-blur-sm' // Custom overlay
821
921
  }
822
922
  }
823
923
  }}
@@ -1226,9 +1326,160 @@ const hybridSettings = {
1226
1326
  };
1227
1327
  ```
1228
1328
 
1329
+ ### Advanced Component Customization
1330
+
1331
+ The plugin now supports complete component overrides and granular element styling for maximum flexibility:
1332
+
1333
+ #### Full Component Override Example
1334
+
1335
+ ```tsx
1336
+ // Custom Image Search Modal Component
1337
+ const CustomImageSearchModal = ({
1338
+ isOpen,
1339
+ setIsOpen,
1340
+ fileInputRef,
1341
+ handleImageFileChange,
1342
+ settings
1343
+ }) => {
1344
+ return (
1345
+ <Modal open={isOpen} setOpen={setIsOpen} className="custom-search-modal">
1346
+ <div className="custom-grid p-8">
1347
+ <div className="upload-section">
1348
+ <h3 className="text-2xl font-bold mb-4">Upload Your Image</h3>
1349
+ <div className="drag-drop-area border-2 border-dashed border-purple-300 rounded-xl p-12">
1350
+ <input
1351
+ type="file"
1352
+ ref={fileInputRef}
1353
+ onChange={handleImageFileChange}
1354
+ accept="image/*"
1355
+ className="hidden"
1356
+ />
1357
+ <button
1358
+ onClick={() => fileInputRef.current?.click()}
1359
+ className="w-full py-6 px-8 bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-xl"
1360
+ >
1361
+ Choose Image
1362
+ </button>
1363
+ </div>
1364
+ </div>
1365
+ </div>
1366
+ </Modal>
1367
+ );
1368
+ };
1369
+
1370
+ // Using component override
1371
+ const advancedComponentSettings = {
1372
+ customRenderers: {
1373
+ // Complete component replacement
1374
+ ImageSearchModal: CustomImageSearchModal,
1375
+
1376
+ // Or use render functions for granular control
1377
+ render: {
1378
+ modal: {
1379
+ renderModal: (props) => (
1380
+ <CustomModal {...props} className="advanced-modal" />
1381
+ )
1382
+ },
1383
+ filterSidebar: {
1384
+ renderSidebar: (props) => (
1385
+ <CustomFilterSidebar {...props} className="advanced-sidebar" />
1386
+ )
1387
+ },
1388
+ resultsGrid: {
1389
+ renderGrid: (props) => (
1390
+ <CustomResultsGrid {...props} className="advanced-grid" />
1391
+ )
1392
+ }
1393
+ }
1394
+ }
1395
+ };
1396
+ ```
1397
+
1398
+ #### New Granular Style Targets
1399
+
1400
+ ```tsx
1401
+ const advancedStylingSettings = {
1402
+ customStyles: {
1403
+ // NEW: Modal structure
1404
+ modalContent: 'md:grid-cols-3 gap-8',
1405
+ resultsContainer: 'p-8 md:p-12',
1406
+
1407
+ // NEW: Control sections
1408
+ controlsInner: 'border-none bg-gray-50 rounded-lg p-6',
1409
+ controlsLeft: 'gap-6',
1410
+ controlsRight: 'relative z-20',
1411
+ activeFiltersWrapper: 'mb-6',
1412
+
1413
+ // NEW: Mobile filter sidebar
1414
+ filterSidebarMobileTitle: 'text-3xl font-bold text-purple-600',
1415
+ filterSidebarMobileCloseButton: 'w-10 h-10 bg-red-100 hover:bg-red-200',
1416
+ filterSidebarMobileCounter: 'bg-blue-50 rounded-lg p-4',
1417
+ filterSidebarMobileCounterText: 'text-blue-800 font-semibold',
1418
+
1419
+ // NEW: Product grid enhancements
1420
+ productPriceContainer: 'flex flex-col space-y-2',
1421
+ emptyStateInner: 'py-24',
1422
+
1423
+ // NEW: Image search modal sections
1424
+ imageSearchContent: 'gap-12 md:grid-cols-1',
1425
+ imageSearchUploadSection:
1426
+ 'bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl p-8',
1427
+ imageSearchUploadTitle: 'text-2xl font-bold text-gray-800',
1428
+ imageSearchUploadArea:
1429
+ 'border-4 border-dashed border-purple-300 hover:border-purple-500 transition-colors',
1430
+ imageSearchUploadButton:
1431
+ 'bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700',
1432
+ imageSearchTipsSection: 'bg-yellow-50 border-l-4 border-yellow-400',
1433
+ imageSearchTipsTitle: 'text-yellow-800 font-bold',
1434
+ imageSearchTipsList: 'text-yellow-700',
1435
+ imageSearchTipsItem: 'hover:text-yellow-900 transition-colors',
1436
+ imageSearchModalOverlay: 'bg-black/60 backdrop-blur-md' // NEW: Custom overlay
1437
+ }
1438
+ };
1439
+ ```
1440
+
1441
+ #### Combined Approach Example
1442
+
1443
+ ```tsx
1444
+ // Mix component overrides with granular styling
1445
+ const hybridAdvancedSettings = {
1446
+ customRenderers: {
1447
+ // Override only specific components
1448
+ ImageSearchModal: CustomImageSearchModal,
1449
+
1450
+ render: {
1451
+ modal: {
1452
+ renderHeader: ({ title, onClose }) => (
1453
+ <div className="flex items-center justify-between bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6 rounded-t-xl">
1454
+ <h2 className="text-2xl font-bold">{title}</h2>
1455
+ <button
1456
+ onClick={onClose}
1457
+ className="w-10 h-10 bg-white/20 hover:bg-white/30 rounded-full flex items-center justify-center transition-colors"
1458
+ >
1459
+ βœ•
1460
+ </button>
1461
+ </div>
1462
+ )
1463
+ }
1464
+ }
1465
+ },
1466
+
1467
+ customStyles: {
1468
+ // Fine-tune specific elements
1469
+ modalContent: 'md:grid-cols-4 gap-6',
1470
+ resultsContainer: 'bg-gray-50 rounded-xl p-8',
1471
+ productItem:
1472
+ 'bg-white rounded-lg shadow-sm hover:shadow-xl transition-all duration-300',
1473
+ productPriceContainer:
1474
+ 'bg-gradient-to-r from-green-50 to-blue-50 rounded-lg p-3',
1475
+ controlsInner: 'bg-white rounded-lg shadow-sm border p-6'
1476
+ }
1477
+ };
1478
+ ```
1479
+
1229
1480
  ## Complete Styling Reference
1230
1481
 
1231
- ### Available Style Targets (40+ options)
1482
+ ### Available Style Targets (50+ options)
1232
1483
 
1233
1484
  **Modal & Layout:**
1234
1485
 
@@ -1258,26 +1509,41 @@ const hybridSettings = {
1258
1509
 
1259
1510
  - `loadingSpinner`, `loadingOverlay`, `emptyState`, `emptyStateIcon`, `emptyStateText`
1260
1511
 
1261
- ### Available Render Functions (25+ options)
1512
+ ### Available Render Functions (30+ options)
1513
+
1514
+ **Component-Level Renderers (Full Control):**
1515
+
1516
+ - `Modal` - Complete modal component override
1517
+ - `FilterSidebar` - Complete sidebar component override
1518
+ - `ResultsGrid` - Complete grid component override
1519
+ - `ImageSearchModal` - Complete image search modal override
1262
1520
 
1263
1521
  **Modal Renderers:**
1264
1522
 
1265
- - `renderModal`, `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
1523
+ - `renderModal` - Full modal override
1524
+ - `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
1266
1525
  - `renderControls`, `renderSortDropdown`, `renderItemCount`, `renderFilterToggleButton`, `renderEmptyState`
1267
1526
 
1268
1527
  **Filter Sidebar Renderers:**
1269
1528
 
1270
- - `renderSidebar`, `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
1529
+ - `renderSidebar` - Full sidebar override
1530
+ - `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
1271
1531
  - `renderCropButton`, `renderTickButton`, `renderUploadButton`, `renderResetButton`, `renderErrorMessage`
1272
1532
  - `renderFilterGroup`, `renderFilterGroupTitle`, `renderFilterItem`, `renderFilterItemInput`, `renderFilterItemLabel`, `renderFilterItemCount`
1273
1533
  - `renderMobileActiveFilters`
1274
1534
 
1275
1535
  **Results Grid Renderers:**
1276
1536
 
1277
- - `renderGrid`, `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
1537
+ - `renderGrid` - Full grid override
1538
+ - `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
1278
1539
  - `renderPagination`, `renderPaginationButton`, `renderPaginationInfo`, `renderPaginationPrevious`, `renderPaginationNext`, `renderLoadMore`, `renderLoadMoreButton`
1279
1540
  - `renderLoadingState`, `renderLoadingOverlay`, `renderEmptyState`
1280
1541
 
1542
+ **Image Search Modal Renderers:**
1543
+
1544
+ - `renderModal` - Full image search modal override
1545
+ - `renderUploadArea` - Upload section customization
1546
+
1281
1547
  ## Best Practices
1282
1548
 
1283
1549
  ### Performance Optimization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-similar-products",
3
- "version": "1.92.0-rc.19",
3
+ "version": "1.92.0-rc.21",
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "peerDependencies": {
@@ -58,7 +58,7 @@ export function useSimilarProducts(product: Product) {
58
58
  pagination: {
59
59
  current_page: 1,
60
60
  num_pages: 1,
61
- page_size: 1,
61
+ page_size: 20,
62
62
  total_count: 0
63
63
  }
64
64
  }),
@@ -91,14 +91,6 @@ export function useSimilarProducts(product: Product) {
91
91
  searchParams.page = String(page);
92
92
  }
93
93
 
94
- // Test iΓ§in farklΔ± parametre isimlerini deniyoruz
95
- searchParams.page_size = '1';
96
- searchParams.pageSize = '1';
97
- searchParams.limit = '1';
98
- searchParams.per_page = '1';
99
- searchParams.size = '1';
100
- console.log('πŸ” API Γ§ağrΔ±sΔ± searchParams:', searchParams);
101
-
102
94
  if (facets) {
103
95
  facets.forEach((facet) => {
104
96
  if (String(facet.key) === 'category_ids') return;
@@ -633,7 +625,21 @@ export function useSimilarProducts(product: Product) {
633
625
  if (!file) return;
634
626
 
635
627
  try {
636
- const validation = await validateImageFileWithTranslation(file);
628
+ let processedFile = file;
629
+
630
+ if (file.type === 'image/webp') {
631
+ try {
632
+ const { convertWebPToJPEG } = await import(
633
+ '../utils/image-conversion'
634
+ );
635
+ processedFile = await convertWebPToJPEG(file);
636
+ } catch (error) {
637
+ console.error('WebP conversion failed:', error);
638
+ processedFile = file;
639
+ }
640
+ }
641
+
642
+ const validation = await validateImageFileWithTranslation(processedFile);
637
643
  if (!validation.isValid) {
638
644
  setFileError(validation.error!);
639
645
  return;
@@ -657,7 +663,7 @@ export function useSimilarProducts(product: Product) {
657
663
 
658
664
  reader.onerror = () =>
659
665
  setFileError(t('common.similar_products.errors.file_read_error'));
660
- reader.readAsDataURL(file);
666
+ reader.readAsDataURL(processedFile);
661
667
  } catch (error) {
662
668
  setFileError(t('common.similar_products.errors.processing_error'));
663
669
  }
@@ -815,7 +821,7 @@ export function useSimilarProducts(product: Product) {
815
821
  result.facets,
816
822
  resultsWithResetSorter.facets
817
823
  ),
818
- sorters: resultsWithResetSorter.sorters // Reset edilmiş sorter'ları kullan
824
+ sorters: resultsWithResetSorter.sorters
819
825
  };
820
826
  updateResultsAndKey(finalResult);
821
827
  }
@@ -316,20 +316,29 @@ export interface SimilarProductsSettings {
316
316
  maxPagesLoadMore?: number;
317
317
  customStyles?: {
318
318
  modal?: string;
319
+ modalContent?: string;
319
320
  filterSidebar?: string;
320
321
  resultsGrid?: string;
321
322
  imageSearchModal?: string;
322
323
 
323
324
  activeFiltersContainer?: string;
325
+ activeFiltersWrapper?: string;
324
326
  activeFilterTag?: string;
325
327
  activeFilterTagButton?: string;
326
328
 
327
329
  controlsContainer?: string;
330
+ controlsInner?: string;
331
+ controlsLeft?: string;
332
+ controlsRight?: string;
328
333
  itemCount?: string;
329
334
  sortDropdown?: string;
330
335
  filterToggleButton?: string;
331
336
 
332
337
  filterSidebarMobileHeader?: string;
338
+ filterSidebarMobileTitle?: string;
339
+ filterSidebarMobileCloseButton?: string;
340
+ filterSidebarMobileCounter?: string;
341
+ filterSidebarMobileCounterText?: string;
333
342
  filterGroup?: string;
334
343
  filterGroupTitle?: string;
335
344
  filterGroupContent?: string;
@@ -356,6 +365,7 @@ export interface SimilarProductsSettings {
356
365
  productInfo?: string;
357
366
  productTitle?: string;
358
367
  productPrice?: string;
368
+ productPriceContainer?: string;
359
369
  productOldPrice?: string;
360
370
 
361
371
  pagination?: string;
@@ -372,12 +382,25 @@ export interface SimilarProductsSettings {
372
382
  loadingSpinner?: string;
373
383
  loadingOverlay?: string;
374
384
  emptyState?: string;
385
+ emptyStateInner?: string;
375
386
  emptyStateIcon?: string;
376
387
  emptyStateText?: string;
377
388
 
389
+ imageSearchContent?: string;
390
+ imageSearchUploadSection?: string;
391
+ imageSearchUploadTitle?: string;
392
+ imageSearchUploadArea?: string;
393
+ imageSearchUploadButton?: string;
394
+ imageSearchTipsSection?: string;
395
+ imageSearchTipsTitle?: string;
396
+ imageSearchTipsList?: string;
397
+ imageSearchTipsItem?: string;
398
+ imageSearchModalOverlay?: string;
399
+
378
400
  mobileActiveFilters?: string;
379
401
  mobileActiveFilterTag?: string;
380
402
  mobileClearAllButton?: string;
403
+ filterSidebarMobileOverlay?: string;
381
404
  };
382
405
  customRenderers?: {
383
406
  Modal?: React.ComponentType<SimilarProductsModalProps>;
@@ -0,0 +1,44 @@
1
+ export const convertWebPToJPEG = (file: File): Promise<File> => {
2
+ return new Promise((resolve, reject) => {
3
+ const canvas = document.createElement('canvas');
4
+ const ctx = canvas.getContext('2d');
5
+ const img = new Image();
6
+
7
+ img.onload = () => {
8
+ canvas.width = img.naturalWidth;
9
+ canvas.height = img.naturalHeight;
10
+
11
+ if (!ctx) {
12
+ reject(new Error('Canvas context not available'));
13
+ return;
14
+ }
15
+
16
+ ctx.drawImage(img, 0, 0);
17
+
18
+ canvas.toBlob(
19
+ (blob) => {
20
+ if (!blob) {
21
+ reject(new Error('Failed to convert image'));
22
+ return;
23
+ }
24
+
25
+ const convertedFile = new File(
26
+ [blob],
27
+ file.name.replace(/\.webp$/i, '.jpg'),
28
+ {
29
+ type: 'image/jpeg',
30
+ lastModified: Date.now()
31
+ }
32
+ );
33
+
34
+ resolve(convertedFile);
35
+ },
36
+ 'image/jpeg',
37
+ 0.9
38
+ );
39
+ };
40
+
41
+ img.onerror = () => reject(new Error('Failed to load image'));
42
+ img.src = URL.createObjectURL(file);
43
+ });
44
+ };
@@ -21,7 +21,12 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
21
21
  render: {}
22
22
  },
23
23
  theme: {},
24
- cssVariables: {}
24
+ cssVariables: {},
25
+ paginationType: 'pagination',
26
+ loadMoreText: '',
27
+ loadMoreStyle: 'button',
28
+ loadMoreThreshold: 10,
29
+ maxPagesLoadMore: 20
25
30
  };
26
31
 
27
32
  export function mergeSettings(