@akinon/pz-similar-products 1.92.0-rc.16

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/README.md ADDED
@@ -0,0 +1,1372 @@
1
+ # Similar Products
2
+
3
+ 🔍 **AI-powered visual search plugin for e-commerce platforms with comprehensive customization options.**
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ ### 📋 Main Sections
10
+
11
+ - [Features](#markdown-header-features)
12
+ - [Installation](#markdown-header-installation)
13
+ - [Quick Start](#markdown-header-quick-start)
14
+ - [Available Components](#markdown-header-available-components)
15
+ - [Configuration](#markdown-header-configuration)
16
+ - [Customization Strategies](#markdown-header-customization-strategies)
17
+ - [Professional Examples](#markdown-header-professional-examples)
18
+ - [Usage Examples](#markdown-header-usage-examples)
19
+ - [Complete Styling Reference](#markdown-header-complete-styling-reference)
20
+ - [Best Practices](#markdown-header-best-practices)
21
+ - [Summary](#markdown-header-summary)
22
+
23
+ ### 🔧 Quick Start Guide
24
+
25
+ - [Plugin Module Integration](#markdown-header-plugin-module-integration)
26
+
27
+ ### 📦 Available Components
28
+
29
+ - [ProductImageSearchFeature](#markdown-header-productimasearchfeature)
30
+ - [HeaderImageSearchFeature](#markdown-header-headerimasearchfeature)
31
+ - [SimilarProductsPlugin](#markdown-header-similarproductsplugin)
32
+
33
+ ### ⚙️ Configuration
34
+
35
+ - [SimilarProductsSettings Interface](#markdown-header-similarproductssettings-interface)
36
+
37
+ ### 🎨 Professional Examples
38
+
39
+ - [E-commerce Store Integration](#markdown-header-e-commerce-store-integration)
40
+ - [Brand Customization](#markdown-header-brand-customization)
41
+ - [Custom Product Cards with Badges](#markdown-header-custom-product-cards-with-badges)
42
+ - [Advanced Filter Sidebar with Dynamic Icons](#markdown-header-advanced-filter-sidebar-with-dynamic-icons)
43
+ - [Header Integration](#markdown-header-header-integration)
44
+ - [Modern Pagination with Progress](#markdown-header-modern-pagination-with-progress)
45
+ - [Advanced Modal with Custom Header & Controls](#markdown-header-advanced-modal-with-custom-header-controls)
46
+ - [Complete Brand Theme Integration](#markdown-header-complete-brand-theme-integration)
47
+
48
+ ### 📖 Styling Reference
49
+
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)
52
+
53
+ ### ✅ Best Practices
54
+
55
+ - [Performance Optimization](#markdown-header-performance-optimization)
56
+ - [Accessibility](#markdown-header-accessibility)
57
+ - [Essential Props](#markdown-header-essential-props)
58
+ - [Common Settings](#markdown-header-common-settings)
59
+
60
+ ---
61
+
62
+ ## Features
63
+
64
+ - **🖼️ Visual Search**: AI-powered image-based product discovery
65
+ - **✂️ Image Cropping**: Built-in cropping functionality with manual confirmation for precise searches
66
+ - **🎛️ Advanced Filtering**: Dynamic facet-based filtering system
67
+ - **📄 Multiple Pagination Modes**: Traditional pagination, load more button, and infinite scroll
68
+ - **🎨 Granular Customization**: 40+ style targets and 25+ render points
69
+ - **📱 Mobile Responsive**: Optimized for all device sizes
70
+ - **⚡ Performance Optimized**: Lazy loading and efficient rendering
71
+ - **🔧 TypeScript**: Full TypeScript support with detailed type definitions
72
+
73
+ ## Installation
74
+
75
+ ```bash
76
+ npx @akinon/projectzero@latest --plugins
77
+ ```
78
+
79
+ Select `pz-similar-products` from the plugin list during installation.
80
+
81
+ ## Quick Start
82
+
83
+ ### Plugin Module Integration
84
+
85
+ The recommended way to integrate the plugin is using ProjectZero's PluginModule system:
86
+
87
+ ```tsx
88
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
89
+
90
+ // Product detail page integration
91
+ <PluginModule
92
+ component={Component.ProductImageSearchFeature}
93
+ props={{
94
+ product,
95
+ activeIndex,
96
+ showResetButton: true,
97
+ settings: {
98
+ maxFileSize: 5,
99
+ enableCropping: true,
100
+ customStyles: {
101
+ modal: 'max-w-6xl rounded-xl shadow-2xl',
102
+ productItem: 'rounded-lg shadow-sm hover:shadow-lg'
103
+ }
104
+ }
105
+ }}
106
+ />
107
+
108
+ // Header search integration
109
+ <PluginModule
110
+ component={Component.HeaderImageSearchFeature}
111
+ props={{
112
+ isEnabled: true,
113
+ settings: {
114
+ maxFileSize: 5,
115
+ customStyles: {
116
+ imageSearchModal: 'max-w-lg rounded-xl'
117
+ }
118
+ }
119
+ }}
120
+ />
121
+ ```
122
+
123
+ ## Available Components
124
+
125
+ ### ProductImageSearchFeature
126
+
127
+ Ready-to-use component for product detail pages with visual search functionality.
128
+
129
+ **Props:**
130
+
131
+ - `product`: Product object (required)
132
+ - `activeIndex`: Current image index in product gallery
133
+ - `showResetButton`: Show reset to original image button
134
+ - `settings`: Customization settings object
135
+
136
+ ### HeaderImageSearchFeature
137
+
138
+ Header search integration component for global image upload functionality.
139
+
140
+ **Props:**
141
+
142
+ - `isEnabled`: Enable/disable the feature
143
+ - `settings`: Customization settings object
144
+
145
+ ### SimilarProductsPlugin
146
+
147
+ Core modal component for advanced integration scenarios.
148
+
149
+ **Props:**
150
+
151
+ - `product`: Product object (required)
152
+ - `isOpen`: Modal open state
153
+ - `onClose`: Close handler function
154
+ - `settings`: Customization settings object
155
+
156
+ ## Configuration
157
+
158
+ ### SimilarProductsSettings Interface
159
+
160
+ ```typescript
161
+ interface SimilarProductsSettings {
162
+ // Core functionality
163
+ maxFileSize?: number; // MB (default: 5)
164
+ imageFormats?: string[]; // ['jpg', 'jpeg', 'png', 'webp']
165
+ cropAspectRatio?: number; // Fixed aspect ratio
166
+ resultsPerPage?: number; // Products per page (default: 20)
167
+ enableCropping?: boolean; // Enable image cropping (default: true)
168
+ enableFileUpload?: boolean; // Enable file upload (default: true)
169
+
170
+ // Pagination settings
171
+ paginationType?: 'pagination' | 'load-more' | 'infinite-scroll'; // Pagination mode (default: 'pagination')
172
+ loadMoreText?: string; // Load more button text (default: 'Load More')
173
+ loadMoreStyle?: 'button' | 'auto'; // Load more behavior (default: 'button')
174
+ loadMoreThreshold?: number; // Pixels from bottom for auto load (default: 100)
175
+ maxPagesLoadMore?: number; // Maximum pages to load in load-more mode
176
+
177
+ // 40+ granular style targets
178
+ customStyles?: {
179
+ // Main components
180
+ modal?: string;
181
+ filterSidebar?: string;
182
+ resultsGrid?: string;
183
+
184
+ // Deep nested elements
185
+ productItem?: string;
186
+ productImage?: string;
187
+ productTitle?: string;
188
+ productPrice?: string;
189
+ paginationButton?: string;
190
+ loadMoreButton?: string;
191
+ loadMoreContainer?: string;
192
+ activeFilterTag?: string;
193
+ filterGroup?: string;
194
+ imageSection?: string;
195
+ loadingSpinner?: string;
196
+ // ... see examples for complete list
197
+ };
198
+
199
+ // 25+ render functions for granular control
200
+ customRenderers?: {
201
+ render?: {
202
+ modal?: {
203
+ renderHeader?: (props) => React.ReactNode;
204
+ renderActiveFilters?: (props) => React.ReactNode;
205
+ renderControls?: (props) => React.ReactNode;
206
+ renderItemCount?: (props) => React.ReactNode;
207
+ // ... see examples for complete list
208
+ };
209
+ resultsGrid?: {
210
+ renderProductItem?: (props) => React.ReactNode;
211
+ renderPagination?: (props) => React.ReactNode;
212
+ renderGridContainer?: (props) => React.ReactNode;
213
+ // ... see examples for complete list
214
+ };
215
+ filterSidebar?: {
216
+ renderImageSection?: (props) => React.ReactNode;
217
+ renderFilterGroup?: (props) => React.ReactNode;
218
+ renderCropButton?: (props) => React.ReactNode;
219
+ // ... see examples for complete list
220
+ };
221
+ };
222
+ };
223
+
224
+ // Theme configuration
225
+ theme?: {
226
+ colors?: Record<string, string>;
227
+ spacing?: Record<string, string>;
228
+ borderRadius?: Record<string, string>;
229
+ };
230
+ }
231
+ ```
232
+
233
+ ## Image Cropping with Manual Confirmation
234
+
235
+ The plugin features an advanced image cropping system with manual confirmation for precise control over search queries.
236
+
237
+ ### Cropping Workflow
238
+
239
+ 1. **Activate Crop Mode**: Click the crop button (✂️) to enter cropping mode
240
+ 2. **Select Area**: Drag to select the desired area of the image
241
+ 3. **Confirm Selection**: Click the green tick button (✓) to process the crop
242
+ 4. **Cancel/Reset**: Click the X button to cancel current selection or return to previous successful crop
243
+
244
+ ### Smart Crop Memory
245
+
246
+ The system intelligently manages crop states:
247
+
248
+ - **Successful Crops**: Remembered and can be restored if new crop is cancelled
249
+ - **Pending Crops**: Automatically discarded when crop mode is cancelled
250
+ - **Visual Feedback**: Crop overlay remains visible when not in crop mode
251
+
252
+ ### Customization Options
253
+
254
+ ```tsx
255
+ const cropSettings = {
256
+ enableCropping: true, // Enable/disable cropping feature
257
+ cropAspectRatio: 1, // Fixed aspect ratio (optional)
258
+
259
+ customStyles: {
260
+ cropButton: 'bg-white shadow-lg hover:shadow-xl transition-shadow',
261
+ tickButton: 'bg-green-50 text-green-600 hover:bg-green-100', // New tick button styling
262
+ imageContainer: 'border-2 border-dashed border-blue-300'
263
+ },
264
+
265
+ customRenderers: {
266
+ render: {
267
+ filterSidebar: {
268
+ renderCropButton: ({ isCropping, onClick, disabled }) => (
269
+ <button
270
+ onClick={onClick}
271
+ disabled={disabled}
272
+ className={`custom-crop-btn ${isCropping ? 'active' : ''}`}
273
+ >
274
+ {isCropping ? '✕ Cancel' : '✂️ Crop'}
275
+ </button>
276
+ ),
277
+ renderTickButton: ({ onClick, disabled }) => (
278
+ <button
279
+ onClick={onClick}
280
+ disabled={disabled}
281
+ className="custom-tick-btn bg-green-500 text-white rounded-full"
282
+ >
283
+ ✓ Apply Crop
284
+ </button>
285
+ )
286
+ }
287
+ }
288
+ }
289
+ };
290
+ ```
291
+
292
+ ### Loading States
293
+
294
+ Both crop and confirmation buttons show loading spinners during processing:
295
+
296
+ ```tsx
297
+ const loadingSettings = {
298
+ customStyles: {
299
+ cropButton: 'transition-opacity duration-200',
300
+ tickButton: 'transition-opacity duration-200 disabled:opacity-50'
301
+ }
302
+ };
303
+ ```
304
+
305
+ ## Load More & Pagination Modes
306
+
307
+ The plugin supports three pagination modes for different user experiences:
308
+
309
+ ### Pagination Types
310
+
311
+ 1. **Traditional Pagination** (default): Classic page numbers with previous/next buttons
312
+ 2. **Load More Button**: Shows "Load More" button to append additional results
313
+ 3. **Infinite Scroll**: Automatically loads more content as user scrolls
314
+
315
+ ### Load More Configuration
316
+
317
+ ```tsx
318
+ const loadMoreSettings = {
319
+ paginationType: 'load-more', // Enable load more mode
320
+ loadMoreText: 'Show More Products', // Custom button text
321
+ loadMoreStyle: 'button', // 'button' or 'auto' (for infinite scroll)
322
+ loadMoreThreshold: 100, // Pixels from bottom for auto-trigger
323
+ maxPagesLoadMore: 5, // Limit max pages in load-more mode
324
+
325
+ customStyles: {
326
+ loadMoreButton:
327
+ 'px-8 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors',
328
+ loadMoreContainer: 'flex justify-center items-center mt-8'
329
+ }
330
+ };
331
+ ```
332
+
333
+ ### Custom Load More Renderers
334
+
335
+ Full control over load more functionality:
336
+
337
+ ```tsx
338
+ const customLoadMoreSettings = {
339
+ paginationType: 'load-more',
340
+
341
+ customRenderers: {
342
+ render: {
343
+ resultsGrid: {
344
+ renderLoadMore: ({
345
+ onLoadMore,
346
+ hasMore,
347
+ isLoading,
348
+ currentPage,
349
+ totalPages
350
+ }) => (
351
+ <div className="flex flex-col items-center space-y-4 mt-12">
352
+ {/* Progress indicator */}
353
+ <div className="text-center">
354
+ <div className="text-sm text-gray-600 mb-2">
355
+ Page {currentPage} of {totalPages}
356
+ </div>
357
+ <div className="w-64 bg-gray-200 rounded-full h-2">
358
+ <div
359
+ className="bg-blue-600 h-2 rounded-full transition-all duration-300"
360
+ style={{
361
+ width: `${(currentPage / totalPages) * 100}%`
362
+ }}
363
+ ></div>
364
+ </div>
365
+ </div>
366
+
367
+ {/* Load more button */}
368
+ {hasMore && (
369
+ <button
370
+ onClick={onLoadMore}
371
+ disabled={isLoading}
372
+ className="flex items-center space-x-2 px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 disabled:opacity-50 transition-all"
373
+ >
374
+ {isLoading ? (
375
+ <>
376
+ <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
377
+ <span>Loading...</span>
378
+ </>
379
+ ) : (
380
+ <>
381
+ <span>Load More Products</span>
382
+ <svg
383
+ className="w-4 h-4"
384
+ fill="none"
385
+ stroke="currentColor"
386
+ viewBox="0 0 24 24"
387
+ >
388
+ <path
389
+ strokeLinecap="round"
390
+ strokeLinejoin="round"
391
+ strokeWidth={2}
392
+ d="M19 9l-7 7-7-7"
393
+ />
394
+ </svg>
395
+ </>
396
+ )}
397
+ </button>
398
+ )}
399
+
400
+ {/* Completion message */}
401
+ {!hasMore && (
402
+ <div className="text-center text-gray-500">
403
+ <p>All products loaded</p>
404
+ </div>
405
+ )}
406
+ </div>
407
+ ),
408
+
409
+ renderLoadMoreButton: ({ onClick, disabled, isLoading, text }) => (
410
+ <button
411
+ onClick={onClick}
412
+ disabled={disabled}
413
+ className="px-6 py-3 bg-black text-white rounded-md hover:bg-gray-800 disabled:opacity-50 transition-colors"
414
+ >
415
+ {isLoading ? (
416
+ <div className="flex items-center space-x-2">
417
+ <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
418
+ <span>Loading...</span>
419
+ </div>
420
+ ) : (
421
+ text
422
+ )}
423
+ </button>
424
+ )
425
+ }
426
+ }
427
+ }
428
+ };
429
+ ```
430
+
431
+ ### Infinite Scroll Example
432
+
433
+ Automatic loading with intersection observer:
434
+
435
+ ```tsx
436
+ const infiniteScrollSettings = {
437
+ paginationType: 'infinite-scroll',
438
+ loadMoreThreshold: 200, // Start loading 200px before bottom
439
+ maxPagesLoadMore: 10, // Prevent endless loading
440
+
441
+ customStyles: {
442
+ loadMoreContainer: 'flex justify-center py-8',
443
+ loadingSpinner: 'h-8 w-8 text-blue-600'
444
+ }
445
+ };
446
+ ```
447
+
448
+ ## Customization Strategies
449
+
450
+ ### 1. Quick Styling with CSS Classes
451
+
452
+ For rapid customization, apply CSS classes to specific elements:
453
+
454
+ ```tsx
455
+ const settings = {
456
+ customStyles: {
457
+ modal: 'max-w-6xl rounded-2xl shadow-2xl border-4 border-blue-500',
458
+ productItem: 'hover:scale-105 transition-all duration-300 shadow-lg',
459
+ filterSidebar: 'bg-gradient-to-b from-gray-50 to-white',
460
+ paginationButton: 'rounded-full px-4 py-2 bg-blue-600 text-white',
461
+ activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full'
462
+ }
463
+ };
464
+ ```
465
+
466
+ ### 2. Advanced Customization with Render Functions
467
+
468
+ For complete control over specific interface elements:
469
+
470
+ ```tsx
471
+ const settings = {
472
+ customRenderers: {
473
+ render: {
474
+ modal: {
475
+ renderHeader: ({ title, onClose }) => (
476
+ <div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6">
477
+ <div className="flex justify-between items-center">
478
+ <h2 className="text-2xl font-bold">🔍 Visual Search</h2>
479
+ <button
480
+ onClick={onClose}
481
+ className="text-white hover:text-blue-200"
482
+ >
483
+
484
+ </button>
485
+ </div>
486
+ </div>
487
+ )
488
+ },
489
+ resultsGrid: {
490
+ renderProductItem: ({ product, index }) => (
491
+ <div className="bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow">
492
+ <div className="aspect-square overflow-hidden rounded-t-xl">
493
+ <img
494
+ src={product.productimage_set?.[0]?.image}
495
+ alt={product.name}
496
+ className="w-full h-full object-cover hover:scale-110 transition-transform"
497
+ />
498
+ </div>
499
+ <div className="p-4">
500
+ <h3 className="font-semibold text-gray-900 mb-2">
501
+ {product.name}
502
+ </h3>
503
+ <p className="text-xl font-bold text-green-600">
504
+ {product.price} ₺
505
+ </p>
506
+ </div>
507
+ </div>
508
+ )
509
+ }
510
+ }
511
+ }
512
+ };
513
+ ```
514
+
515
+ ## Professional Examples
516
+
517
+ ### E-commerce Store Integration
518
+
519
+ Complete production-ready implementation:
520
+
521
+ ```tsx
522
+ const storeSettings = {
523
+ // Performance & functionality
524
+ maxFileSize: 5,
525
+ resultsPerPage: 24,
526
+ enableCropping: true,
527
+
528
+ // Professional styling
529
+ customStyles: {
530
+ modal: 'max-w-7xl rounded-xl shadow-2xl',
531
+ productItem: 'rounded-lg shadow-sm hover:shadow-lg transition-shadow',
532
+ paginationButton: 'px-4 py-2 rounded-lg border hover:bg-gray-50',
533
+ activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full'
534
+ },
535
+
536
+ // Custom branding
537
+ customRenderers: {
538
+ render: {
539
+ modal: {
540
+ renderHeader: ({ title, onClose }) => (
541
+ <div className="bg-blue-600 text-white p-6 rounded-t-xl">
542
+ <div className="flex justify-between items-center">
543
+ <h2 className="text-2xl font-bold">Find Similar Products</h2>
544
+ <button
545
+ onClick={onClose}
546
+ className="text-white hover:text-blue-200"
547
+ >
548
+
549
+ </button>
550
+ </div>
551
+ </div>
552
+ )
553
+ }
554
+ }
555
+ }
556
+ };
557
+
558
+ <PluginModule
559
+ component={Component.ProductImageSearchFeature}
560
+ props={{
561
+ product,
562
+ activeIndex,
563
+ showResetButton: true,
564
+ settings: storeSettings
565
+ }}
566
+ />;
567
+ ```
568
+
569
+ ### Brand Customization
570
+
571
+ Customize colors, spacing and styling to match your brand:
572
+
573
+ ```tsx
574
+ const brandSettings = {
575
+ theme: {
576
+ colors: {
577
+ primary: '#FF6B35', // Your brand color
578
+ secondary: '#004E64',
579
+ background: '#F8F9FA'
580
+ }
581
+ },
582
+
583
+ customStyles: {
584
+ modal: 'border-4 border-orange-500 rounded-2xl',
585
+ productItem: 'hover:scale-105 transition-transform',
586
+ activeFilterTag: 'bg-orange-100 text-orange-800 px-3 py-1 rounded-full'
587
+ }
588
+ };
589
+ ```
590
+
591
+ ### Custom Product Cards with Badges
592
+
593
+ Create custom product displays with badges, animations and enhanced styling:
594
+
595
+ ```tsx
596
+ const customProductSettings = {
597
+ // Granular styling for product elements
598
+ customStyles: {
599
+ productItem:
600
+ 'group relative bg-white rounded-xl shadow-sm hover:shadow-xl transition-all overflow-hidden',
601
+ productImageWrapper: 'relative aspect-square overflow-hidden',
602
+ productImage:
603
+ 'w-full h-full object-cover group-hover:scale-105 transition-transform duration-300',
604
+ productInfo: 'p-4',
605
+ productTitle: 'font-semibold text-gray-900 mb-2 line-clamp-2',
606
+ productPrice: 'text-lg font-bold text-green-600',
607
+ productOldPrice: 'text-sm text-gray-500 line-through ml-2'
608
+ },
609
+
610
+ // Custom render function for complete control
611
+ customRenderers: {
612
+ render: {
613
+ resultsGrid: {
614
+ renderProductItem: ({ product, index }) => (
615
+ <div className="group relative bg-white rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden">
616
+ {/* Custom badges */}
617
+ {index < 3 && (
618
+ <div className="absolute top-2 left-2 z-10 bg-red-500 text-white text-xs px-2 py-1 rounded-full font-bold">
619
+ TOP {index + 1}
620
+ </div>
621
+ )}
622
+ {product.discount && (
623
+ <div className="absolute top-2 right-2 z-10 bg-orange-500 text-white text-xs px-2 py-1 rounded-full">
624
+ -{product.discount}%
625
+ </div>
626
+ )}
627
+
628
+ {/* Image with hover effect */}
629
+ <div className="aspect-square overflow-hidden">
630
+ <img
631
+ src={product.productimage_set?.[0]?.image}
632
+ alt={product.name}
633
+ className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
634
+ />
635
+ </div>
636
+
637
+ {/* Enhanced product info */}
638
+ <div className="p-4">
639
+ <h3 className="font-semibold text-gray-900 mb-2">
640
+ {product.name}
641
+ </h3>
642
+ <div className="flex items-center justify-between">
643
+ <div className="flex items-center gap-2">
644
+ <span className="text-xl font-bold text-green-600">
645
+ {product.price} ₺
646
+ </span>
647
+ {product.old_price && (
648
+ <span className="text-sm text-gray-500 line-through">
649
+ {product.old_price} ₺
650
+ </span>
651
+ )}
652
+ </div>
653
+ <button className="bg-blue-600 text-white px-3 py-1 rounded-full text-sm hover:bg-blue-700 transition-colors">
654
+ Add to Cart
655
+ </button>
656
+ </div>
657
+ </div>
658
+ </div>
659
+ )
660
+ }
661
+ }
662
+ }
663
+ };
664
+ ```
665
+
666
+ ### Advanced Filter Sidebar with Dynamic Icons
667
+
668
+ Customize filters with dynamic icons, enhanced mobile header, and professional styling:
669
+
670
+ ```tsx
671
+ const advancedFilterSettings = {
672
+ customStyles: {
673
+ filterSidebarMobileHeader:
674
+ 'bg-gradient-to-r from-purple-600 to-blue-600 text-white p-6 -m-6 mb-6',
675
+ filterGroup: 'mb-6 border-b border-gray-200 pb-4',
676
+ filterGroupTitle:
677
+ 'text-lg font-semibold text-gray-900 mb-3 flex items-center',
678
+ filterItem:
679
+ 'flex items-center justify-between py-2 hover:bg-gray-50 px-2 rounded transition-colors',
680
+ filterItemLabel: 'text-sm text-gray-700 flex-1',
681
+ filterItemCount: 'text-xs bg-gray-100 px-2 py-1 rounded-full ml-2',
682
+ imageSection: 'border-2 border-dashed border-purple-300 rounded-lg p-4',
683
+ cropButton: 'bg-purple-600 text-white shadow-lg hover:bg-purple-700',
684
+ uploadButton:
685
+ 'bg-gradient-to-r from-blue-500 to-purple-600 text-white border-0 hover:from-blue-600 hover:to-purple-700',
686
+ resetButton:
687
+ 'bg-gradient-to-r from-green-500 to-blue-500 text-white border-0'
688
+ },
689
+
690
+ customRenderers: {
691
+ render: {
692
+ filterSidebar: {
693
+ // Enhanced mobile header with icon and item count
694
+ renderMobileHeader: ({ title, itemCount, onClose }) => (
695
+ <div className="bg-gradient-to-r from-purple-600 to-blue-600 text-white p-6 -m-6 mb-6 rounded-lg">
696
+ <div className="flex justify-between items-center">
697
+ <div>
698
+ <h3 className="text-xl font-bold flex items-center">
699
+ <svg
700
+ className="w-6 h-6 mr-2"
701
+ fill="currentColor"
702
+ viewBox="0 0 20 20"
703
+ >
704
+ <path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V4z" />
705
+ </svg>
706
+ {title}
707
+ </h3>
708
+ <p className="text-purple-100 text-sm">
709
+ {itemCount} products found
710
+ </p>
711
+ </div>
712
+ <button
713
+ onClick={onClose}
714
+ className="text-white hover:text-purple-200"
715
+ >
716
+ <svg
717
+ className="w-6 h-6"
718
+ fill="none"
719
+ stroke="currentColor"
720
+ viewBox="0 0 24 24"
721
+ >
722
+ <path
723
+ strokeLinecap="round"
724
+ strokeLinejoin="round"
725
+ strokeWidth={2}
726
+ d="M6 18L18 6M6 6l12 12"
727
+ />
728
+ </svg>
729
+ </button>
730
+ </div>
731
+ </div>
732
+ ),
733
+
734
+ // Dynamic icons based on filter type
735
+ renderFilterGroup: ({ facet, onFacetChange, isLoading }) => (
736
+ <div className="mb-6 bg-white rounded-lg border border-gray-200 p-4">
737
+ <h4 className="font-bold text-gray-900 mb-3 flex items-center">
738
+ {/* Dynamic icons based on facet type */}
739
+ {facet.key === 'color' && (
740
+ <div className="w-4 h-4 bg-gradient-to-r from-red-500 to-blue-500 rounded-full mr-2"></div>
741
+ )}
742
+ {facet.key === 'size' && (
743
+ <svg
744
+ className="w-4 h-4 mr-2"
745
+ fill="currentColor"
746
+ viewBox="0 0 20 20"
747
+ >
748
+ <path d="M4 3a2 2 0 000 4h12a2 2 0 000-4H4zM4 9a2 2 0 000 4h12a2 2 0 000-4H4zM4 15a2 2 0 000 4h12a2 2 0 000-4H4z" />
749
+ </svg>
750
+ )}
751
+ {facet.key === 'brand' && (
752
+ <svg
753
+ className="w-4 h-4 mr-2"
754
+ fill="currentColor"
755
+ viewBox="0 0 20 20"
756
+ >
757
+ <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
758
+ </svg>
759
+ )}
760
+ {facet.name}
761
+ </h4>
762
+ <div className="space-y-2">
763
+ {facet.data.choices?.slice(0, 8).map((choice) => (
764
+ <label
765
+ key={choice.value}
766
+ className="flex items-center justify-between cursor-pointer p-2 hover:bg-gray-50 rounded transition-colors"
767
+ >
768
+ <div className="flex items-center flex-1">
769
+ <input
770
+ type="checkbox"
771
+ checked={choice.is_selected}
772
+ onChange={() => onFacetChange(facet.key, choice.value)}
773
+ disabled={isLoading}
774
+ className="mr-3 rounded border-gray-300 text-purple-600 focus:ring-purple-500"
775
+ />
776
+ <span className="text-sm font-medium">{choice.label}</span>
777
+ </div>
778
+ <span className="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded-full font-medium">
779
+ {choice.quantity}
780
+ </span>
781
+ </label>
782
+ ))}
783
+ </div>
784
+ </div>
785
+ )
786
+ }
787
+ }
788
+ }
789
+ };
790
+ ```
791
+
792
+ ### Header Integration
793
+
794
+ Add visual search to your site header:
795
+
796
+ ```tsx
797
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
798
+
799
+ export function SiteHeader() {
800
+ return (
801
+ <header className="flex items-center justify-between p-4">
802
+ <div className="logo">Your Store</div>
803
+
804
+ <div className="search-area flex items-center gap-4">
805
+ {/* Regular search */}
806
+ <input
807
+ type="text"
808
+ placeholder="Search products..."
809
+ className="border rounded-lg px-4 py-2"
810
+ />
811
+
812
+ {/* Visual search */}
813
+ <PluginModule
814
+ component={Component.HeaderImageSearchFeature}
815
+ props={{
816
+ isEnabled: true,
817
+ settings: {
818
+ maxFileSize: 5,
819
+ customStyles: {
820
+ imageSearchModal: 'max-w-lg rounded-xl'
821
+ }
822
+ }
823
+ }}
824
+ />
825
+ </div>
826
+ </header>
827
+ );
828
+ }
829
+ ```
830
+
831
+ ### Modern Pagination with Progress
832
+
833
+ Create advanced pagination with progress bars and smart page navigation:
834
+
835
+ ```tsx
836
+ const modernPaginationSettings = {
837
+ customStyles: {
838
+ paginationContainer: 'flex flex-col items-center space-y-4 mt-8',
839
+ paginationInfo: 'text-sm text-gray-600 font-medium',
840
+ paginationButton:
841
+ 'px-3 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50 transition-colors',
842
+ paginationButtonActive:
843
+ 'bg-blue-600 text-white border-blue-600 hover:bg-blue-700',
844
+ paginationPrevious:
845
+ 'px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors',
846
+ paginationNext:
847
+ 'px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors'
848
+ },
849
+
850
+ customRenderers: {
851
+ render: {
852
+ resultsGrid: {
853
+ renderPagination: ({ pagination, onPageChange, isLoading }) => (
854
+ <div className="flex flex-col items-center space-y-6 mt-12">
855
+ {/* Progress bar */}
856
+ <div className="text-center">
857
+ <div className="text-sm text-gray-600 mb-2">
858
+ Page {pagination.current_page} of {pagination.num_pages}
859
+ </div>
860
+ <div className="w-full bg-gray-200 rounded-full h-2 max-w-xs">
861
+ <div
862
+ className="bg-blue-600 h-2 rounded-full transition-all duration-300"
863
+ style={{
864
+ width: `${
865
+ (pagination.current_page / pagination.num_pages) * 100
866
+ }%`
867
+ }}
868
+ ></div>
869
+ </div>
870
+ </div>
871
+
872
+ {/* Smart pagination buttons */}
873
+ <div className="flex items-center space-x-2">
874
+ <button
875
+ onClick={() => onPageChange(pagination.current_page - 1)}
876
+ disabled={pagination.current_page === 1 || isLoading}
877
+ className="flex items-center px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 disabled:opacity-50 transition-colors"
878
+ >
879
+ <svg
880
+ className="w-4 h-4 mr-2"
881
+ fill="none"
882
+ stroke="currentColor"
883
+ viewBox="0 0 24 24"
884
+ >
885
+ <path
886
+ strokeLinecap="round"
887
+ strokeLinejoin="round"
888
+ strokeWidth={2}
889
+ d="M15 19l-7-7 7-7"
890
+ />
891
+ </svg>
892
+ Previous
893
+ </button>
894
+
895
+ {Array.from(
896
+ { length: Math.min(5, pagination.num_pages) },
897
+ (_, i) => {
898
+ const page = i + Math.max(1, pagination.current_page - 2);
899
+ const isActive = page === pagination.current_page;
900
+
901
+ return (
902
+ <button
903
+ key={page}
904
+ onClick={() => onPageChange(page)}
905
+ disabled={isLoading}
906
+ className={`px-4 py-2 rounded-lg transition-colors ${
907
+ isActive
908
+ ? 'bg-blue-600 text-white'
909
+ : 'bg-gray-100 hover:bg-gray-200 text-gray-700'
910
+ }`}
911
+ >
912
+ {page}
913
+ </button>
914
+ );
915
+ }
916
+ )}
917
+
918
+ <button
919
+ onClick={() => onPageChange(pagination.current_page + 1)}
920
+ disabled={
921
+ pagination.current_page === pagination.num_pages || isLoading
922
+ }
923
+ className="flex items-center px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 disabled:opacity-50 transition-colors"
924
+ >
925
+ Next
926
+ <svg
927
+ className="w-4 h-4 ml-2"
928
+ fill="none"
929
+ stroke="currentColor"
930
+ viewBox="0 0 24 24"
931
+ >
932
+ <path
933
+ strokeLinecap="round"
934
+ strokeLinejoin="round"
935
+ strokeWidth={2}
936
+ d="M9 5l7 7-7 7"
937
+ />
938
+ </svg>
939
+ </button>
940
+ </div>
941
+ </div>
942
+ )
943
+ }
944
+ }
945
+ }
946
+ };
947
+ ```
948
+
949
+ ### Advanced Modal with Custom Header & Controls
950
+
951
+ Create a fully branded modal experience:
952
+
953
+ ```tsx
954
+ const advancedModalSettings = {
955
+ customStyles: {
956
+ modal: 'max-w-7xl rounded-2xl shadow-2xl border-4 border-blue-500',
957
+ activeFiltersContainer:
958
+ 'bg-gradient-to-r from-blue-50 to-purple-50 border-l-4 border-blue-500 p-4',
959
+ activeFilterTag:
960
+ 'bg-white border border-blue-200 text-blue-800 px-3 py-1 rounded-full shadow-sm',
961
+ controlsContainer: 'bg-gray-50 border-y border-gray-200',
962
+ itemCount:
963
+ 'text-sm font-medium text-green-600 bg-green-100 px-3 py-1 rounded-full',
964
+ sortDropdown:
965
+ 'bg-white border-2 border-gray-300 rounded-lg px-4 py-2 focus:border-blue-500 focus:ring-2 focus:ring-blue-200'
966
+ },
967
+
968
+ customRenderers: {
969
+ render: {
970
+ modal: {
971
+ renderHeader: ({ title, onClose }) => (
972
+ <div className="bg-gradient-to-r from-blue-600 via-purple-600 to-blue-800 text-white p-6">
973
+ <div className="flex justify-between items-center">
974
+ <div className="flex items-center">
975
+ <div className="w-10 h-10 bg-white bg-opacity-20 rounded-full flex items-center justify-center mr-4">
976
+ <span className="text-2xl">🔍</span>
977
+ </div>
978
+ <div>
979
+ <h2 className="text-2xl font-bold mb-1">
980
+ 🎯 AI Visual Search
981
+ </h2>
982
+ <p className="text-blue-100">
983
+ Discover your perfect match with advanced AI
984
+ </p>
985
+ </div>
986
+ </div>
987
+ <button
988
+ onClick={onClose}
989
+ className="text-white hover:text-blue-200 bg-white bg-opacity-20 rounded-full p-2 transition-colors"
990
+ >
991
+
992
+ </button>
993
+ </div>
994
+ </div>
995
+ ),
996
+ renderItemCount: ({ count, isLoading }) => (
997
+ <div className="flex items-center bg-green-100 text-green-800 px-3 py-2 rounded-lg">
998
+ <div
999
+ className={`w-2 h-2 rounded-full mr-2 ${
1000
+ isLoading ? 'bg-yellow-500 animate-pulse' : 'bg-green-500'
1001
+ }`}
1002
+ ></div>
1003
+ <span className="text-sm font-medium">
1004
+ {count} {count === 1 ? 'product' : 'products'} found
1005
+ </span>
1006
+ </div>
1007
+ ),
1008
+ renderActiveFilterTag: ({ filter, onRemove, isLoading }) => (
1009
+ <div className="inline-flex items-center bg-gradient-to-r from-blue-100 to-purple-100 border border-blue-300 text-blue-800 px-3 py-2 rounded-full text-sm font-medium shadow-sm mr-2 mb-2">
1010
+ <span className="mr-2">{filter.label}</span>
1011
+ <button
1012
+ onClick={onRemove}
1013
+ disabled={isLoading}
1014
+ className="text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-1 transition-colors disabled:opacity-50"
1015
+ >
1016
+
1017
+ </button>
1018
+ </div>
1019
+ )
1020
+ }
1021
+ }
1022
+ }
1023
+ };
1024
+ ```
1025
+
1026
+ ### Complete Brand Theme Integration
1027
+
1028
+ Full brand customization with theme variables, CSS styling, and custom components:
1029
+
1030
+ ```tsx
1031
+ const brandThemeSettings = {
1032
+ // CSS Variables and Theme
1033
+ theme: {
1034
+ colors: {
1035
+ primary: '#FF6B35', // Brand orange
1036
+ secondary: '#004E64', // Brand navy
1037
+ accent: '#FFB700', // Brand yellow
1038
+ background: '#F8F9FA',
1039
+ text: '#212529'
1040
+ },
1041
+ spacing: {
1042
+ xs: '0.25rem',
1043
+ sm: '0.5rem',
1044
+ md: '1rem',
1045
+ lg: '1.5rem',
1046
+ xl: '2rem'
1047
+ },
1048
+ borderRadius: {
1049
+ sm: '0.25rem',
1050
+ md: '0.5rem',
1051
+ lg: '1rem'
1052
+ }
1053
+ },
1054
+
1055
+ // Comprehensive brand styling
1056
+ customStyles: {
1057
+ modal: 'max-w-7xl rounded-2xl shadow-2xl border-4 border-orange-500',
1058
+ activeFiltersContainer: 'bg-orange-50 border-l-4 border-orange-500',
1059
+ activeFilterTag: 'bg-orange-100 text-orange-800 border border-orange-300',
1060
+ controlsContainer: 'bg-navy-900 text-white',
1061
+ itemCount: 'bg-yellow-100 text-yellow-800 font-bold',
1062
+ sortDropdown:
1063
+ 'border-orange-300 focus:border-orange-500 focus:ring-orange-200',
1064
+ filterGroup: 'bg-white border-l-4 border-orange-500 shadow-lg',
1065
+ filterGroupTitle: 'text-navy-900 font-bold text-lg',
1066
+ productItem:
1067
+ 'border-2 border-orange-200 hover:border-orange-500 hover:shadow-lg',
1068
+ productPrice: 'text-2xl font-bold text-orange-600',
1069
+ pagination: 'space-x-2',
1070
+ paginationButton: 'border-orange-300 text-orange-600 hover:bg-orange-50',
1071
+ paginationButtonActive: 'bg-orange-600 text-white border-orange-600'
1072
+ },
1073
+
1074
+ // Brand-specific custom components
1075
+ customRenderers: {
1076
+ render: {
1077
+ modal: {
1078
+ renderHeader: ({ title, onClose }) => (
1079
+ <div className="bg-gradient-to-r from-orange-500 to-red-600 text-white p-6">
1080
+ <div className="flex justify-between items-center">
1081
+ <div className="flex items-center">
1082
+ <div className="w-12 h-12 bg-white bg-opacity-20 rounded-full flex items-center justify-center mr-4">
1083
+ <span className="text-2xl">🔥</span>
1084
+ </div>
1085
+ <div>
1086
+ <h2 className="text-3xl font-bold mb-1">Visual Search</h2>
1087
+ <p className="text-orange-100">Powered by AI • Brand Store</p>
1088
+ </div>
1089
+ </div>
1090
+ <button
1091
+ onClick={onClose}
1092
+ className="text-white hover:text-orange-200 bg-white bg-opacity-20 rounded-full p-3 transition-colors"
1093
+ >
1094
+
1095
+ </button>
1096
+ </div>
1097
+ </div>
1098
+ )
1099
+ },
1100
+ resultsGrid: {
1101
+ renderProductItem: ({ product, index }) => (
1102
+ <div className="bg-white rounded-xl border-2 border-orange-200 hover:border-orange-500 hover:shadow-xl transition-all duration-300 overflow-hidden">
1103
+ {/* Brand-specific badges */}
1104
+ {product.is_featured && (
1105
+ <div className="absolute top-3 left-3 z-10 bg-gradient-to-r from-orange-500 to-red-500 text-white text-xs px-3 py-1 rounded-full font-bold shadow-lg">
1106
+ 🔥 FEATURED
1107
+ </div>
1108
+ )}
1109
+
1110
+ <div className="aspect-square overflow-hidden bg-gray-100">
1111
+ <img
1112
+ src={product.productimage_set?.[0]?.image}
1113
+ alt={product.name}
1114
+ className="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
1115
+ />
1116
+ </div>
1117
+
1118
+ <div className="p-4 border-t-2 border-orange-100">
1119
+ <h3 className="font-bold text-navy-900 mb-2 line-clamp-2">
1120
+ {product.name}
1121
+ </h3>
1122
+ <div className="flex items-center justify-between">
1123
+ <div>
1124
+ <span className="text-2xl font-bold text-orange-600">
1125
+ {product.price} ₺
1126
+ </span>
1127
+ {product.old_price && (
1128
+ <span className="text-sm text-gray-500 line-through ml-2">
1129
+ {product.old_price} ₺
1130
+ </span>
1131
+ )}
1132
+ </div>
1133
+ <button className="bg-gradient-to-r from-orange-500 to-red-500 text-white px-4 py-2 rounded-lg font-bold hover:from-orange-600 hover:to-red-600 transition-all shadow-lg">
1134
+ ADD TO CART
1135
+ </button>
1136
+ </div>
1137
+ </div>
1138
+ </div>
1139
+ )
1140
+ }
1141
+ }
1142
+ }
1143
+ };
1144
+ ```
1145
+
1146
+ ## Usage Examples
1147
+
1148
+ Here's how to use these customization settings in your components:
1149
+
1150
+ ```tsx
1151
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
1152
+
1153
+ // Example 1: E-commerce store with custom product cards
1154
+ <PluginModule
1155
+ component={Component.ProductImageSearchFeature}
1156
+ props={{
1157
+ product,
1158
+ activeIndex,
1159
+ showResetButton: true,
1160
+ settings: customProductSettings
1161
+ }}
1162
+ />
1163
+
1164
+ // Example 2: Advanced filtering with dynamic icons
1165
+ <PluginModule
1166
+ component={Component.ProductImageSearchFeature}
1167
+ props={{
1168
+ product,
1169
+ activeIndex,
1170
+ settings: advancedFilterSettings
1171
+ }}
1172
+ />
1173
+
1174
+ // Example 3: Modern pagination experience
1175
+ <PluginModule
1176
+ component={Component.ProductImageSearchFeature}
1177
+ props={{
1178
+ product,
1179
+ activeIndex,
1180
+ settings: modernPaginationSettings
1181
+ }}
1182
+ />
1183
+
1184
+ // Example 4: Advanced modal with custom controls
1185
+ <PluginModule
1186
+ component={Component.ProductImageSearchFeature}
1187
+ props={{
1188
+ product,
1189
+ activeIndex,
1190
+ settings: advancedModalSettings
1191
+ }}
1192
+ />
1193
+
1194
+ // Example 5: Complete brand integration
1195
+ <PluginModule
1196
+ component={Component.ProductImageSearchFeature}
1197
+ props={{
1198
+ product,
1199
+ activeIndex,
1200
+ settings: brandThemeSettings
1201
+ }}
1202
+ />
1203
+
1204
+ // Mix and match approaches
1205
+ const hybridSettings = {
1206
+ // Use brand colors
1207
+ theme: brandThemeSettings.theme,
1208
+
1209
+ // Use modern pagination
1210
+ customRenderers: {
1211
+ render: {
1212
+ resultsGrid: {
1213
+ renderPagination: modernPaginationSettings.customRenderers.render.resultsGrid.renderPagination
1214
+ },
1215
+ modal: {
1216
+ renderHeader: brandThemeSettings.customRenderers.render.modal.renderHeader
1217
+ }
1218
+ }
1219
+ },
1220
+
1221
+ // Use custom product cards styling
1222
+ customStyles: {
1223
+ ...customProductSettings.customStyles,
1224
+ modal: brandThemeSettings.customStyles.modal
1225
+ }
1226
+ };
1227
+ ```
1228
+
1229
+ ## Complete Styling Reference
1230
+
1231
+ ### Available Style Targets (40+ options)
1232
+
1233
+ **Modal & Layout:**
1234
+
1235
+ - `modal`, `activeFiltersContainer`, `controlsContainer`, `imageSearchModal`
1236
+
1237
+ **Filter Sidebar:**
1238
+
1239
+ - `filterSidebar`, `filterSidebarMobileHeader`, `filterGroup`, `filterGroupTitle`, `filterGroupContent`
1240
+ - `filterItem`, `filterItemInput`, `filterItemLabel`, `filterItemCount`
1241
+ - `imageSection`, `imageContainer`, `imageWrapper`, `cropButton`, `tickButton`, `uploadButton`, `resetButton`, `errorMessage`, `cropControls`
1242
+ - `mobileActiveFilters`, `mobileActiveFilterTag`, `mobileClearAllButton`
1243
+
1244
+ **Products & Results:**
1245
+
1246
+ - `resultsGrid`, `resultsContainer`, `gridContainer`
1247
+ - `productItem`, `productImageWrapper`, `productImage`, `productInfo`, `productTitle`, `productPrice`, `productOldPrice`
1248
+
1249
+ **Pagination:**
1250
+
1251
+ - `pagination`, `paginationContainer`, `paginationInfo`, `paginationButton`, `paginationButtonActive`, `paginationButtonDisabled`, `paginationPrevious`, `paginationNext`, `loadMoreButton`, `loadMoreContainer`
1252
+
1253
+ **Controls & Active Filters:**
1254
+
1255
+ - `itemCount`, `sortDropdown`, `filterToggleButton`, `activeFilterTag`, `activeFilterTagButton`
1256
+
1257
+ **Loading & States:**
1258
+
1259
+ - `loadingSpinner`, `loadingOverlay`, `emptyState`, `emptyStateIcon`, `emptyStateText`
1260
+
1261
+ ### Available Render Functions (25+ options)
1262
+
1263
+ **Modal Renderers:**
1264
+
1265
+ - `renderModal`, `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
1266
+ - `renderControls`, `renderSortDropdown`, `renderItemCount`, `renderFilterToggleButton`, `renderEmptyState`
1267
+
1268
+ **Filter Sidebar Renderers:**
1269
+
1270
+ - `renderSidebar`, `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
1271
+ - `renderCropButton`, `renderTickButton`, `renderUploadButton`, `renderResetButton`, `renderErrorMessage`
1272
+ - `renderFilterGroup`, `renderFilterGroupTitle`, `renderFilterItem`, `renderFilterItemInput`, `renderFilterItemLabel`, `renderFilterItemCount`
1273
+ - `renderMobileActiveFilters`
1274
+
1275
+ **Results Grid Renderers:**
1276
+
1277
+ - `renderGrid`, `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
1278
+ - `renderPagination`, `renderPaginationButton`, `renderPaginationInfo`, `renderPaginationPrevious`, `renderPaginationNext`, `renderLoadMore`, `renderLoadMoreButton`
1279
+ - `renderLoadingState`, `renderLoadingOverlay`, `renderEmptyState`
1280
+
1281
+ ## Best Practices
1282
+
1283
+ ### Performance Optimization
1284
+
1285
+ ```tsx
1286
+ const optimizedSettings = {
1287
+ maxFileSize: 5, // Reasonable limit
1288
+ resultsPerPage: 20, // Optimal for performance
1289
+ enableCropping: true, // Better search accuracy
1290
+
1291
+ customStyles: {
1292
+ loadingSpinner: 'text-blue-600', // Brand consistency
1293
+ productItem: 'hover:shadow-lg transition-shadow' // Smooth interactions
1294
+ }
1295
+ };
1296
+ ```
1297
+
1298
+ ### Accessibility
1299
+
1300
+ ```tsx
1301
+ const accessibleSettings = {
1302
+ customRenderers: {
1303
+ render: {
1304
+ modal: {
1305
+ renderHeader: ({ title, onClose }) => (
1306
+ <div className="bg-blue-600 text-white p-6">
1307
+ <h2 className="text-xl font-bold" id="modal-title">
1308
+ {title}
1309
+ </h2>
1310
+ <button
1311
+ onClick={onClose}
1312
+ aria-label="Close modal"
1313
+ className="text-white hover:text-blue-200"
1314
+ >
1315
+
1316
+ </button>
1317
+ </div>
1318
+ )
1319
+ }
1320
+ }
1321
+ }
1322
+ };
1323
+ ```
1324
+
1325
+ ### Essential Props
1326
+
1327
+ ```tsx
1328
+ // Basic integration
1329
+ <PluginModule
1330
+ component={Component.ProductImageSearchFeature}
1331
+ props={{
1332
+ product, // Required: Product object
1333
+ activeIndex, // Current image index
1334
+ showResetButton: true, // Show reset functionality
1335
+ settings: {
1336
+ /* Your customization */
1337
+ }
1338
+ }}
1339
+ />
1340
+ ```
1341
+
1342
+ ### Common Settings
1343
+
1344
+ ```tsx
1345
+ const commonSettings = {
1346
+ maxFileSize: 5, // 5MB file limit
1347
+ resultsPerPage: 20, // 20 products per page
1348
+ enableCropping: true, // Enable image cropping
1349
+ enableFileUpload: true, // Enable file uploads
1350
+
1351
+ customStyles: {
1352
+ modal: 'max-w-6xl rounded-xl shadow-2xl',
1353
+ productItem: 'rounded-lg shadow-sm hover:shadow-lg'
1354
+ }
1355
+ };
1356
+ ```
1357
+
1358
+ ## Summary
1359
+
1360
+ ### 🚀 **Getting Started in 3 Steps:**
1361
+
1362
+ 1. **Install**: `npx @akinon/projectzero@latest --plugins`
1363
+ 2. **Integrate**: Add `<PluginModule component={Component.ProductImageSearchFeature} />`
1364
+ 3. **Customize**: Use any of the examples above or create your own
1365
+
1366
+ ### 🎨 **Customization Approaches:**
1367
+
1368
+ - **Quick Styling**: Use `customStyles` for rapid CSS class customization
1369
+ - **Advanced Control**: Use `customRenderers` for complete component replacement
1370
+ - **Hybrid Approach**: Mix and match both methods for maximum flexibility
1371
+
1372
+ **Made by the ProjectZero team**