@akinon/pz-similar-products 1.92.0-rc.21 → 1.92.0-rc.23

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,13 @@
1
1
  # @akinon/pz-similar-products
2
2
 
3
+ ## 1.92.0-rc.23
4
+
5
+ ## 1.92.0-rc.22
6
+
7
+ ### Minor Changes
8
+
9
+ - 143be2b: ZERO-3457: Crop styles are customizable and logic improved for rendering similar products modal
10
+
3
11
  ## 1.92.0-rc.21
4
12
 
5
13
  ### Minor Changes
package/README.md CHANGED
@@ -272,6 +272,18 @@ interface SimilarProductsSettings {
272
272
  mobileActiveFilters?: string;
273
273
  mobileActiveFilterTag?: string;
274
274
  mobileClearAllButton?: string;
275
+
276
+ // New crop-related custom styles
277
+ cropComponent?: string;
278
+ cropImage?: string;
279
+ cropImageActive?: string;
280
+ cropImageNonCropping?: string;
281
+ cropImageContainer?: string;
282
+ cropOverlay?: string;
283
+ cropSelection?: string;
284
+ cropSelectionBorder?: string;
285
+ cropOverlayBackground?: string;
286
+ cropSelectionHighlight?: string;
275
287
  };
276
288
 
277
289
  // 25+ render functions for granular control
@@ -557,7 +569,18 @@ const settings = {
557
569
  productItem: 'hover:scale-105 transition-all duration-300 shadow-lg',
558
570
  filterSidebar: 'bg-gradient-to-b from-gray-50 to-white',
559
571
  paginationButton: 'rounded-full px-4 py-2 bg-blue-600 text-white',
560
- activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full'
572
+ activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full',
573
+
574
+ // Advanced crop styling
575
+ cropComponent: 'border-2 border-dashed border-blue-300 rounded-lg',
576
+ cropImage: 'rounded-lg shadow-md',
577
+ cropImageActive: 'brightness-110 contrast-110',
578
+ cropImageNonCropping: 'hover:scale-105 transition-transform',
579
+ cropImageContainer: 'bg-gray-50 rounded-lg p-2',
580
+ cropOverlay: 'backdrop-blur-sm',
581
+ cropOverlayBackground: 'bg-black/60',
582
+ cropSelection: 'border-4 border-blue-500',
583
+ cropSelectionHighlight: 'shadow-2xl shadow-blue-500/50'
561
584
  }
562
585
  };
563
586
  ```
@@ -1490,6 +1513,7 @@ const hybridAdvancedSettings = {
1490
1513
  - `filterSidebar`, `filterSidebarMobileHeader`, `filterGroup`, `filterGroupTitle`, `filterGroupContent`
1491
1514
  - `filterItem`, `filterItemInput`, `filterItemLabel`, `filterItemCount`
1492
1515
  - `imageSection`, `imageContainer`, `imageWrapper`, `cropButton`, `tickButton`, `uploadButton`, `resetButton`, `errorMessage`, `cropControls`
1516
+ - `cropComponent`, `cropImage`, `cropImageActive`, `cropImageNonCropping`, `cropImageContainer`, `cropOverlay`, `cropSelection`, `cropSelectionBorder`, `cropOverlayBackground`, `cropSelectionHighlight`
1493
1517
  - `mobileActiveFilters`, `mobileActiveFilterTag`, `mobileClearAllButton`
1494
1518
 
1495
1519
  **Products & Results:**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-similar-products",
3
- "version": "1.92.0-rc.21",
3
+ "version": "1.92.0-rc.23",
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "peerDependencies": {
@@ -874,6 +874,14 @@ export function useSimilarProducts(product: Product) {
874
874
  setFileError('');
875
875
  }, []);
876
876
 
877
+ const clearResults = useCallback(() => {
878
+ setSearchResults(null);
879
+ setResultsKey(0);
880
+ setLastProductIds([]);
881
+ setAllLoadedProducts([]);
882
+ setLoadedPages(new Set([1]));
883
+ }, []);
884
+
877
885
  const clearFileInput = useCallback(
878
886
  (fileInputRef: React.RefObject<HTMLInputElement>) => {
879
887
  if (fileInputRef.current) {
@@ -940,6 +948,7 @@ export function useSimilarProducts(product: Product) {
940
948
  loadedPages: Array.from(loadedPages),
941
949
  allLoadedProducts,
942
950
  clearError,
951
+ clearResults,
943
952
  clearFileInput
944
953
  };
945
954
  }
@@ -357,6 +357,19 @@ export interface SimilarProductsSettings {
357
357
  errorMessage?: string;
358
358
  cropControls?: string;
359
359
 
360
+ // Detailed crop styling
361
+ cropComponent?: string;
362
+ cropImage?: string;
363
+ cropImageActive?: string;
364
+ cropOverlay?: string;
365
+ cropSelection?: string;
366
+ cropSelectionBorder?: string;
367
+ cropImageNonCropping?: string;
368
+ cropImageContainer?: string;
369
+ cropImageWrapper?: string;
370
+ cropOverlayBackground?: string;
371
+ cropSelectionHighlight?: string;
372
+
360
373
  resultsContainer?: string;
361
374
  gridContainer?: string;
362
375
  productItem?: string;
@@ -445,7 +445,10 @@ export function SimilarProductsFilterSidebar({
445
445
  onDragEnd={() => {}}
446
446
  ruleOfThirds={false}
447
447
  aspect={settings?.cropAspectRatio}
448
- className="slider-crop"
448
+ className={twMerge(
449
+ 'slider-crop',
450
+ settings?.customStyles?.cropComponent
451
+ )}
449
452
  disabled={isLoading}
450
453
  keepSelection={true}
451
454
  >
@@ -453,22 +456,45 @@ export function SimilarProductsFilterSidebar({
453
456
  ref={imageRef}
454
457
  src={currentImageUrl || ''}
455
458
  alt={product?.name || 'Product image'}
456
- className="max-w-full max-h-[200px] md:max-h-[280px]"
459
+ className={twMerge(
460
+ 'max-w-full max-h-[200px] md:max-h-[280px]',
461
+ settings?.customStyles?.cropImage,
462
+ settings?.customStyles?.cropImageActive
463
+ )}
457
464
  style={{ transform: `scale(1) rotate(0deg)` }}
458
465
  />
459
466
  </ReactCrop>
460
467
  ) : (
461
- <div className="relative w-full h-full flex items-center justify-center">
468
+ <div
469
+ className={twMerge(
470
+ 'relative w-full h-full flex items-center justify-center',
471
+ settings?.customStyles?.cropImageContainer
472
+ )}
473
+ >
462
474
  <img
463
475
  ref={imageRef}
464
476
  src={currentImageUrl || ''}
465
477
  alt={product?.name || 'Product image'}
466
- className="max-w-full max-h-[200px] md:max-h-[280px] object-contain"
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
+ )}
467
483
  />
468
484
  {!isCropping && completedCrop && (
469
- <div className="hidden md:block absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-in-out">
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
+ >
470
492
  <div
471
- className="absolute transition-all duration-300 ease-in-out"
493
+ className={twMerge(
494
+ 'absolute transition-all duration-300 ease-in-out',
495
+ settings?.customStyles?.cropSelection,
496
+ settings?.customStyles?.cropSelectionHighlight
497
+ )}
472
498
  style={{
473
499
  width: `${completedCrop.width}px`,
474
500
  height: `${completedCrop.height}px`,
@@ -56,7 +56,8 @@ export function SimilarProductsPlugin({
56
56
  loadedPages,
57
57
  fetchSimilarProductsByImageUrl,
58
58
  fetchSimilarProductsByImageCrop,
59
- clearError
59
+ clearError,
60
+ clearResults
60
61
  } = useSimilarProducts(product);
61
62
 
62
63
  const {
@@ -108,6 +109,7 @@ export function SimilarProductsPlugin({
108
109
  if (!isOpen) {
109
110
  setHasInitialSearchDone(false);
110
111
  clearError();
112
+ clearResults();
111
113
  }
112
114
  }, [
113
115
  isOpen,
@@ -115,7 +117,8 @@ export function SimilarProductsPlugin({
115
117
  activeIndex,
116
118
  hasUploadedImage,
117
119
  hasInitialSearchDone,
118
- clearError
120
+ clearError,
121
+ clearResults
119
122
  ]);
120
123
 
121
124
  useEffect(() => {
@@ -142,6 +145,11 @@ export function SimilarProductsPlugin({
142
145
  }
143
146
  };
144
147
 
148
+ const handleModalClose = () => {
149
+ resetCrop();
150
+ onClose();
151
+ };
152
+
145
153
  const ModalComponent =
146
154
  settings.customRenderers?.Modal || SimilarProductsModal;
147
155
  const ImageSearchModalComponent =
@@ -151,7 +159,7 @@ export function SimilarProductsPlugin({
151
159
  <>
152
160
  <ModalComponent
153
161
  isOpen={isOpen}
154
- onClose={onClose}
162
+ onClose={handleModalClose}
155
163
  searchResults={searchResults}
156
164
  resultsKey={resultsKey}
157
165
  isLoading={isLoading}
@@ -526,10 +526,14 @@ export function SimilarProductsResultsGrid({
526
526
  );
527
527
  };
528
528
 
529
- if (isLoading && (!searchResults || !searchResults.products)) {
529
+ if (isLoading && (!searchResults || !searchResults.products?.length)) {
530
530
  return renderLoadingState();
531
531
  }
532
532
 
533
+ if (!isLoading && searchResults && searchResults.products?.length === 0) {
534
+ return renderEmptyState();
535
+ }
536
+
533
537
  const renderLoadingOverlay = () => {
534
538
  if (settings?.customRenderers?.render?.resultsGrid?.renderLoadingOverlay) {
535
539
  return settings.customRenderers.render.resultsGrid.renderLoadingOverlay();
@@ -585,16 +589,14 @@ export function SimilarProductsResultsGrid({
585
589
  <div className={gridClassName}>
586
590
  {isLoading &&
587
591
  searchResults &&
588
- searchResults.products &&
592
+ searchResults.products?.length > 0 &&
589
593
  renderLoadingOverlay()}
590
594
 
591
- {searchResults && searchResults.products?.length > 0 ? (
595
+ {searchResults && searchResults.products?.length > 0 && (
592
596
  <div className={resultsContainerClassName}>
593
597
  {renderGridContainer()}
594
598
  {renderPagination()}
595
599
  </div>
596
- ) : (
597
- renderEmptyState()
598
600
  )}
599
601
  </div>
600
602
  );
@@ -326,6 +326,10 @@ export function SimilarProductsModal({
326
326
  };
327
327
 
328
328
  const renderSortDropdown = () => {
329
+ if (!searchResults?.products?.length || !searchResults?.sorters?.length) {
330
+ return null;
331
+ }
332
+
329
333
  if (settings?.customRenderers?.render?.modal?.renderSortDropdown) {
330
334
  return settings.customRenderers.render.modal.renderSortDropdown({
331
335
  sorters: searchResults?.sorters || [],