@mgonza02/goal-image-gallery 1.0.0
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/IMPLEMENTATION_SUMMARY.md +259 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +428 -0
- package/QUICK_REFERENCE.md +235 -0
- package/README.md +479 -0
- package/lib/GoalImageGallery.js +244 -0
- package/lib/components/ImageGalleryEmptyState.js +87 -0
- package/lib/components/ImageGalleryGrid.js +108 -0
- package/lib/components/ImageGalleryItem.js +175 -0
- package/lib/components/ImageGalleryModal.js +149 -0
- package/lib/components/index.js +5 -0
- package/lib/examples.js +503 -0
- package/lib/hooks/use-image-gallery.js +252 -0
- package/lib/index.d.ts +187 -0
- package/lib/index.js +148 -0
- package/lib/styles/styled-components.js +173 -0
- package/lib/utils/image-utils.js +129 -0
- package/package.json +93 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# GoalImageGallery - Quick Reference
|
|
2
|
+
|
|
3
|
+
## Basic Usage
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import GoalImageGallery from './components/common/goal-image-gallery';
|
|
7
|
+
|
|
8
|
+
// Read-only gallery
|
|
9
|
+
<GoalImageGallery imageCodes={['img1', 'img2']} canEdit={false} />
|
|
10
|
+
|
|
11
|
+
// Editable gallery
|
|
12
|
+
<GoalImageGallery
|
|
13
|
+
imageCodes={images}
|
|
14
|
+
canEdit={true}
|
|
15
|
+
ownerEntity={{ id: 'entity-123' }}
|
|
16
|
+
imageHandlerApi={handleUpload}
|
|
17
|
+
afterUpload={refreshData}
|
|
18
|
+
multiple={true}
|
|
19
|
+
/>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Props Reference
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
|------|------|---------|-------------|
|
|
26
|
+
| `imageCodes` | `string \| array` | - | Image codes to display |
|
|
27
|
+
| `canEdit` | `boolean` | `false` | Enable editing |
|
|
28
|
+
| `ownerEntity` | `object` | - | Owner entity data |
|
|
29
|
+
| `imageHandlerApi` | `function` | - | Upload handler function |
|
|
30
|
+
| `afterUpload` | `function` | - | Success callback |
|
|
31
|
+
| `multiple` | `boolean` | `false` | Allow multiple images |
|
|
32
|
+
| `permission` | `string` | - | Required permission |
|
|
33
|
+
| `emptyMessage` | `string` | `'No hay imΓ‘genes para mostrar'` | Empty state message |
|
|
34
|
+
| `showImageInfo` | `boolean` | `false` | Show image info |
|
|
35
|
+
| `allowDownload` | `boolean` | `true` | Enable downloads |
|
|
36
|
+
|
|
37
|
+
## Upload Methods
|
|
38
|
+
|
|
39
|
+
### 1. File Upload
|
|
40
|
+
```javascript
|
|
41
|
+
// Users can:
|
|
42
|
+
// - Click "Add Image" button
|
|
43
|
+
// - Drag & drop files
|
|
44
|
+
// - Use file dialog
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Clipboard Paste
|
|
48
|
+
```javascript
|
|
49
|
+
// Users can:
|
|
50
|
+
// - Copy image (Ctrl+C)
|
|
51
|
+
// - Paste in gallery (Ctrl+V)
|
|
52
|
+
// - Automatically processed
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. URL Loading
|
|
56
|
+
```javascript
|
|
57
|
+
// Users can:
|
|
58
|
+
// - Enter image URL
|
|
59
|
+
// - Load remote images
|
|
60
|
+
// - Validate URL format
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API Handler Example
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
const handleImageUpload = async (imageData) => {
|
|
67
|
+
try {
|
|
68
|
+
const response = await api.uploadImage({
|
|
69
|
+
...imageData,
|
|
70
|
+
companyId: currentCompany,
|
|
71
|
+
entityId: ownerEntity.id
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
data: response.data,
|
|
77
|
+
message: 'Upload successful'
|
|
78
|
+
};
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
message: error.message
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Keyboard Shortcuts
|
|
89
|
+
|
|
90
|
+
| Key | Action |
|
|
91
|
+
|-----|--------|
|
|
92
|
+
| `β` / `β` | Navigate images |
|
|
93
|
+
| `+` / `=` | Zoom in |
|
|
94
|
+
| `-` | Zoom out |
|
|
95
|
+
| `Escape` | Close viewer |
|
|
96
|
+
| `Ctrl+V` | Paste image |
|
|
97
|
+
|
|
98
|
+
## Common Patterns
|
|
99
|
+
|
|
100
|
+
### Product Gallery
|
|
101
|
+
```javascript
|
|
102
|
+
<GoalImageGallery
|
|
103
|
+
imageCodes={product.images}
|
|
104
|
+
canEdit={hasPermission('EDIT_PRODUCTS')}
|
|
105
|
+
ownerEntity={{ id: product.id, type: 'product' }}
|
|
106
|
+
imageHandlerApi={productApi.uploadImages}
|
|
107
|
+
afterUpload={refreshProduct}
|
|
108
|
+
multiple={true}
|
|
109
|
+
showImageInfo={true}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### User Avatar
|
|
114
|
+
```javascript
|
|
115
|
+
<GoalImageGallery
|
|
116
|
+
imageCodes={[user.avatar]}
|
|
117
|
+
canEdit={isOwner}
|
|
118
|
+
ownerEntity={{ id: user.id, type: 'user' }}
|
|
119
|
+
imageHandlerApi={userApi.uploadAvatar}
|
|
120
|
+
afterUpload={refreshUser}
|
|
121
|
+
multiple={false}
|
|
122
|
+
emptyMessage="No avatar set"
|
|
123
|
+
/>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Document Attachments
|
|
127
|
+
```javascript
|
|
128
|
+
<GoalImageGallery
|
|
129
|
+
imageCodes={document.attachments}
|
|
130
|
+
canEdit={canEditDocs}
|
|
131
|
+
ownerEntity={{ id: document.id, type: 'document' }}
|
|
132
|
+
imageHandlerApi={docApi.uploadAttachments}
|
|
133
|
+
afterUpload={refreshDocument}
|
|
134
|
+
multiple={true}
|
|
135
|
+
permission="EDIT_DOCUMENTS"
|
|
136
|
+
allowDownload={true}
|
|
137
|
+
/>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Error Handling
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const handleUpload = async (imageData) => {
|
|
144
|
+
try {
|
|
145
|
+
// Validate file
|
|
146
|
+
if (imageData.size > 5 * 1024 * 1024) {
|
|
147
|
+
throw new Error('File too large');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Upload
|
|
151
|
+
const response = await api.upload(imageData);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
data: response.data
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: error.message
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Customization
|
|
167
|
+
|
|
168
|
+
### Custom Empty Message
|
|
169
|
+
```javascript
|
|
170
|
+
<GoalImageGallery
|
|
171
|
+
imageCodes={[]}
|
|
172
|
+
emptyMessage="Start your gallery by adding images"
|
|
173
|
+
canEdit={true}
|
|
174
|
+
// ... other props
|
|
175
|
+
/>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Permission-Based Editing
|
|
179
|
+
```javascript
|
|
180
|
+
<GoalImageGallery
|
|
181
|
+
imageCodes={images}
|
|
182
|
+
canEdit={hasPermission('EDIT_IMAGES')}
|
|
183
|
+
permission="EDIT_IMAGES"
|
|
184
|
+
// ... other props
|
|
185
|
+
/>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Custom Upload Folder
|
|
189
|
+
```javascript
|
|
190
|
+
const handleUpload = async (imageData) => {
|
|
191
|
+
return await api.uploadImage({
|
|
192
|
+
...imageData,
|
|
193
|
+
folder: 'custom-folder',
|
|
194
|
+
application: 'MY_APP'
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Browser Support
|
|
200
|
+
|
|
201
|
+
- β
Chrome/Edge (full support)
|
|
202
|
+
- β
Firefox (full support)
|
|
203
|
+
- β
Safari (full support)
|
|
204
|
+
- β οΈ IE11 (limited, no clipboard)
|
|
205
|
+
- β
Mobile browsers (optimized)
|
|
206
|
+
|
|
207
|
+
## Performance Tips
|
|
208
|
+
|
|
209
|
+
1. Use thumbnail versions for gallery
|
|
210
|
+
2. Implement pagination for large sets
|
|
211
|
+
3. Compress images before upload
|
|
212
|
+
4. Use lazy loading for images
|
|
213
|
+
5. Cleanup object URLs properly
|
|
214
|
+
|
|
215
|
+
## Troubleshooting
|
|
216
|
+
|
|
217
|
+
**Images not loading?**
|
|
218
|
+
- Check image URLs
|
|
219
|
+
- Verify permissions
|
|
220
|
+
- Check network connectivity
|
|
221
|
+
|
|
222
|
+
**Upload not working?**
|
|
223
|
+
- Verify API handler
|
|
224
|
+
- Check file size limits
|
|
225
|
+
- Validate file formats
|
|
226
|
+
|
|
227
|
+
**Clipboard paste not working?**
|
|
228
|
+
- Ensure HTTPS
|
|
229
|
+
- Check browser support
|
|
230
|
+
- Verify permissions
|
|
231
|
+
|
|
232
|
+
**Poor performance?**
|
|
233
|
+
- Reduce image sizes
|
|
234
|
+
- Use pagination
|
|
235
|
+
- Implement virtual scrolling
|
package/README.md
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# GoalImageGallery Component Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `GoalImageGallery` is a modern, feature-rich image gallery component for the Goal application. It provides a comprehensive solution for displaying, managing, and uploading images with advanced UI/UX features and accessibility support.
|
|
6
|
+
|
|
7
|
+
## Key Features
|
|
8
|
+
|
|
9
|
+
### πΌοΈ **Multi-Method Image Upload**
|
|
10
|
+
- **File Upload**: Traditional file selection with drag-and-drop support
|
|
11
|
+
- **Clipboard Paste**: Paste images directly from clipboard using Ctrl+V
|
|
12
|
+
- **URL Loading**: Load images from remote URLs
|
|
13
|
+
|
|
14
|
+
### π¨ **Modern UI/UX**
|
|
15
|
+
- Responsive grid layout that adapts to screen size
|
|
16
|
+
- Smooth hover effects and transitions
|
|
17
|
+
- Material Design 3 styling
|
|
18
|
+
- Dark/light theme support
|
|
19
|
+
- Loading states and skeleton animations
|
|
20
|
+
|
|
21
|
+
### π **Advanced Image Viewer**
|
|
22
|
+
- Full-screen modal viewer
|
|
23
|
+
- Zoom controls (0.5x to 3x)
|
|
24
|
+
- Image navigation (previous/next)
|
|
25
|
+
- Keyboard navigation support
|
|
26
|
+
- Download functionality
|
|
27
|
+
|
|
28
|
+
### βΏ **Accessibility**
|
|
29
|
+
- ARIA labels and roles
|
|
30
|
+
- Keyboard navigation
|
|
31
|
+
- Screen reader support
|
|
32
|
+
- High contrast mode compatibility
|
|
33
|
+
- Focus management
|
|
34
|
+
|
|
35
|
+
### π± **Mobile Responsive**
|
|
36
|
+
- Touch-friendly interface
|
|
37
|
+
- Optimized for mobile devices
|
|
38
|
+
- Swipe gestures support
|
|
39
|
+
- Responsive breakpoints
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
import GoalImageGallery from './components/common/goal-image-gallery';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Basic Usage
|
|
48
|
+
|
|
49
|
+
### Simple Gallery
|
|
50
|
+
```javascript
|
|
51
|
+
<GoalImageGallery
|
|
52
|
+
imageCodes={['img1', 'img2', 'img3']}
|
|
53
|
+
canEdit={false}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Editable Gallery
|
|
58
|
+
```javascript
|
|
59
|
+
<GoalImageGallery
|
|
60
|
+
imageCodes={imageCodes}
|
|
61
|
+
canEdit={true}
|
|
62
|
+
ownerEntity={{ id: 'product-123' }}
|
|
63
|
+
imageHandlerApi={handleImageUpload}
|
|
64
|
+
afterUpload={refreshImages}
|
|
65
|
+
multiple={true}
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Advanced Configuration
|
|
70
|
+
```javascript
|
|
71
|
+
<GoalImageGallery
|
|
72
|
+
imageCodes={imageCodes}
|
|
73
|
+
canEdit={true}
|
|
74
|
+
ownerEntity={{ id: 'product-123', type: 'product' }}
|
|
75
|
+
imageHandlerApi={handleImageUpload}
|
|
76
|
+
afterUpload={refreshImages}
|
|
77
|
+
multiple={true}
|
|
78
|
+
permission="EDIT_IMAGES"
|
|
79
|
+
emptyMessage="No product images available"
|
|
80
|
+
showImageInfo={true}
|
|
81
|
+
allowDownload={true}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Props
|
|
86
|
+
|
|
87
|
+
| Prop | Type | Default | Description |
|
|
88
|
+
|------|------|---------|-------------|
|
|
89
|
+
| `canEdit` | `boolean` | `false` | Enable editing capabilities |
|
|
90
|
+
| `ownerEntity` | `object` | - | Entity that owns the images |
|
|
91
|
+
| `imageHandlerApi` | `function` | - | API function for image operations |
|
|
92
|
+
| `afterUpload` | `function` | - | Callback after successful upload |
|
|
93
|
+
| `imageCodes` | `string \| array` | - | Image codes to display |
|
|
94
|
+
| `multiple` | `boolean` | `false` | Allow multiple image selection |
|
|
95
|
+
| `permission` | `string` | - | Required permission for editing |
|
|
96
|
+
| `emptyMessage` | `string` | `'No hay imΓ‘genes para mostrar'` | Message shown when no images |
|
|
97
|
+
| `showImageInfo` | `boolean` | `false` | Show image information overlay |
|
|
98
|
+
| `allowDownload` | `boolean` | `true` | Enable download functionality |
|
|
99
|
+
|
|
100
|
+
## Upload Methods
|
|
101
|
+
|
|
102
|
+
### 1. File Upload
|
|
103
|
+
Users can upload images by:
|
|
104
|
+
- Clicking the "Add Image" button
|
|
105
|
+
- Dragging and dropping files onto the gallery
|
|
106
|
+
- Using the file selection dialog
|
|
107
|
+
|
|
108
|
+
**Supported formats**: JPEG, PNG, GIF, WebP
|
|
109
|
+
**Maximum file size**: 25MB (configurable)
|
|
110
|
+
|
|
111
|
+
### 2. Clipboard Paste
|
|
112
|
+
Users can paste images directly from the clipboard:
|
|
113
|
+
- Copy an image from any source (screenshot, browser, etc.)
|
|
114
|
+
- Press `Ctrl+V` anywhere in the gallery
|
|
115
|
+
- The image will be automatically processed and uploaded
|
|
116
|
+
|
|
117
|
+
**Note**: Clipboard functionality requires modern browser support
|
|
118
|
+
|
|
119
|
+
### 3. URL Loading
|
|
120
|
+
Users can load images from URLs:
|
|
121
|
+
- Enter a direct image URL
|
|
122
|
+
- The component validates the URL and loads the image
|
|
123
|
+
- Supports all standard image formats
|
|
124
|
+
|
|
125
|
+
## Keyboard Shortcuts
|
|
126
|
+
|
|
127
|
+
### Image Viewer
|
|
128
|
+
- `β` / `β`: Navigate between images
|
|
129
|
+
- `+` / `=`: Zoom in
|
|
130
|
+
- `-`: Zoom out
|
|
131
|
+
- `Escape`: Close image viewer
|
|
132
|
+
|
|
133
|
+
### Gallery Navigation
|
|
134
|
+
- `Tab`: Navigate between interactive elements
|
|
135
|
+
- `Enter` / `Space`: Activate buttons
|
|
136
|
+
- `Escape`: Close dialogs
|
|
137
|
+
|
|
138
|
+
## API Integration
|
|
139
|
+
|
|
140
|
+
The component requires an `imageHandlerApi` function that handles image operations:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const handleImageUpload = async (imageData) => {
|
|
144
|
+
try {
|
|
145
|
+
const response = await api.uploadImages({
|
|
146
|
+
...imageData,
|
|
147
|
+
companyId: currentCompany,
|
|
148
|
+
entityId: ownerEntity.id
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
data: response.data,
|
|
154
|
+
message: 'Images uploaded successfully'
|
|
155
|
+
};
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
message: error.message
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Styling and Theming
|
|
166
|
+
|
|
167
|
+
The component uses Material-UI's theming system and can be customized:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
const theme = createTheme({
|
|
171
|
+
components: {
|
|
172
|
+
MuiCard: {
|
|
173
|
+
styleOverrides: {
|
|
174
|
+
root: {
|
|
175
|
+
// Custom card styles
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Performance Considerations
|
|
184
|
+
|
|
185
|
+
- **Image Optimization**: Uses thumbnail versions for gallery view
|
|
186
|
+
- **Lazy Loading**: Images are loaded on demand
|
|
187
|
+
- **Memory Management**: Proper cleanup of object URLs
|
|
188
|
+
- **Debounced Operations**: Prevents excessive API calls
|
|
189
|
+
|
|
190
|
+
## Error Handling
|
|
191
|
+
|
|
192
|
+
The component provides comprehensive error handling:
|
|
193
|
+
|
|
194
|
+
- **Upload Errors**: File size, format, or network issues
|
|
195
|
+
- **Loading Errors**: Broken image URLs or network problems
|
|
196
|
+
- **Permission Errors**: Insufficient user permissions
|
|
197
|
+
- **Browser Compatibility**: Graceful degradation for older browsers
|
|
198
|
+
|
|
199
|
+
## Browser Support
|
|
200
|
+
|
|
201
|
+
- **Modern Browsers**: Full feature support
|
|
202
|
+
- **Internet Explorer 11**: Limited support (no clipboard paste)
|
|
203
|
+
- **Mobile Browsers**: Optimized experience
|
|
204
|
+
- **Accessibility Tools**: Full compatibility
|
|
205
|
+
|
|
206
|
+
## Examples
|
|
207
|
+
|
|
208
|
+
### Product Gallery
|
|
209
|
+
```javascript
|
|
210
|
+
<GoalImageGallery
|
|
211
|
+
imageCodes={product.images}
|
|
212
|
+
canEdit={hasPermission('EDIT_PRODUCTS')}
|
|
213
|
+
ownerEntity={{ id: product.id, type: 'product' }}
|
|
214
|
+
imageHandlerApi={productImageApi.upload}
|
|
215
|
+
afterUpload={refreshProduct}
|
|
216
|
+
multiple={true}
|
|
217
|
+
emptyMessage="No product images available"
|
|
218
|
+
showImageInfo={true}
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### User Avatar
|
|
223
|
+
```javascript
|
|
224
|
+
<GoalImageGallery
|
|
225
|
+
imageCodes={user.avatar}
|
|
226
|
+
canEdit={isOwner}
|
|
227
|
+
ownerEntity={{ id: user.id, type: 'user' }}
|
|
228
|
+
imageHandlerApi={userImageApi.upload}
|
|
229
|
+
afterUpload={refreshUser}
|
|
230
|
+
multiple={false}
|
|
231
|
+
emptyMessage="No avatar set"
|
|
232
|
+
showImageInfo={false}
|
|
233
|
+
/>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Document Gallery
|
|
237
|
+
```javascript
|
|
238
|
+
<GoalImageGallery
|
|
239
|
+
imageCodes={document.attachments}
|
|
240
|
+
canEdit={canEditDocument}
|
|
241
|
+
ownerEntity={{ id: document.id, type: 'document' }}
|
|
242
|
+
imageHandlerApi={documentImageApi.upload}
|
|
243
|
+
afterUpload={refreshDocument}
|
|
244
|
+
multiple={true}
|
|
245
|
+
permission="EDIT_DOCUMENTS"
|
|
246
|
+
allowDownload={true}
|
|
247
|
+
/>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Best Practices
|
|
251
|
+
|
|
252
|
+
1. **Performance**: Use thumbnail versions for gallery display
|
|
253
|
+
2. **Accessibility**: Always provide meaningful alt text
|
|
254
|
+
3. **Error Handling**: Implement comprehensive error handling
|
|
255
|
+
4. **Permissions**: Check user permissions before enabling editing
|
|
256
|
+
5. **Validation**: Validate file types and sizes on both client and server
|
|
257
|
+
6. **Responsive Design**: Test on various screen sizes
|
|
258
|
+
7. **Loading States**: Provide visual feedback during operations
|
|
259
|
+
|
|
260
|
+
## Troubleshooting
|
|
261
|
+
|
|
262
|
+
### Common Issues
|
|
263
|
+
|
|
264
|
+
**Issue**: Images not loading
|
|
265
|
+
- **Solution**: Check image URLs and permissions
|
|
266
|
+
|
|
267
|
+
**Issue**: Upload not working
|
|
268
|
+
- **Solution**: Verify `imageHandlerApi` is properly configured
|
|
269
|
+
|
|
270
|
+
**Issue**: Clipboard paste not working
|
|
271
|
+
- **Solution**: Ensure HTTPS and modern browser support
|
|
272
|
+
|
|
273
|
+
**Issue**: Poor performance with many images
|
|
274
|
+
- **Solution**: Implement pagination or virtual scrolling
|
|
275
|
+
|
|
276
|
+
### Debug Mode
|
|
277
|
+
|
|
278
|
+
Enable debug mode for development:
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
<GoalImageGallery
|
|
282
|
+
// ... other props
|
|
283
|
+
debug={process.env.NODE_ENV === 'development'}
|
|
284
|
+
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Migration Guide
|
|
288
|
+
|
|
289
|
+
### From CommonImageGallery
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
// Old
|
|
293
|
+
<CommonImageGallery
|
|
294
|
+
photos={photos}
|
|
295
|
+
canEdit={canEdit}
|
|
296
|
+
onSelectImage={handleSelect}
|
|
297
|
+
/>
|
|
298
|
+
|
|
299
|
+
// New
|
|
300
|
+
<GoalImageGallery
|
|
301
|
+
imageCodes={imageCodes}
|
|
302
|
+
canEdit={canEdit}
|
|
303
|
+
ownerEntity={ownerEntity}
|
|
304
|
+
imageHandlerApi={imageApi}
|
|
305
|
+
afterUpload={handleUpload}
|
|
306
|
+
/>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### From Basic Image Display
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
// Old
|
|
313
|
+
{images.map(img => (
|
|
314
|
+
<img key={img.id} src={img.url} alt={img.title} />
|
|
315
|
+
))}
|
|
316
|
+
|
|
317
|
+
// New
|
|
318
|
+
<GoalImageGallery
|
|
319
|
+
imageCodes={images.map(img => img.code)}
|
|
320
|
+
canEdit={false}
|
|
321
|
+
/>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Contributing
|
|
325
|
+
|
|
326
|
+
When contributing to this component:
|
|
327
|
+
|
|
328
|
+
1. Follow the existing code style
|
|
329
|
+
2. Add tests for new features
|
|
330
|
+
3. Update documentation
|
|
331
|
+
4. Test accessibility features
|
|
332
|
+
5. Verify mobile responsiveness
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
This component is part of the Goal application and follows the project's licensing terms.
|
|
337
|
+
|
|
338
|
+
## ποΈ **Modular Architecture**
|
|
339
|
+
|
|
340
|
+
The component has been fully modularized for better maintainability and reusability:
|
|
341
|
+
|
|
342
|
+
### Component Structure
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
goal-image-gallery/
|
|
346
|
+
βββ index.js # Main exports
|
|
347
|
+
βββ GoalImageGallery.js # Main component
|
|
348
|
+
βββ components/ # Modular sub-components
|
|
349
|
+
β βββ ImageGalleryGrid.js
|
|
350
|
+
β βββ ImageGalleryModal.js
|
|
351
|
+
β βββ ImageGalleryItem.js
|
|
352
|
+
β βββ ImageGalleryEmptyState.js
|
|
353
|
+
βββ hooks/ # Custom hooks
|
|
354
|
+
β βββ use-image-gallery.js
|
|
355
|
+
βββ styles/ # Styled components
|
|
356
|
+
β βββ styled-components.js
|
|
357
|
+
βββ utils/ # Utility functions
|
|
358
|
+
β βββ image-utils.js
|
|
359
|
+
βββ docs/ # Documentation
|
|
360
|
+
βββ README.md
|
|
361
|
+
βββ QUICK_REFERENCE.md
|
|
362
|
+
βββ MIGRATION.md
|
|
363
|
+
βββ examples.js
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Available Exports
|
|
367
|
+
|
|
368
|
+
#### Main Component
|
|
369
|
+
```javascript
|
|
370
|
+
import { GoalImageGallery } from '@goal/components/common/goal-image-gallery';
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### Sub-components
|
|
374
|
+
```javascript
|
|
375
|
+
import {
|
|
376
|
+
ImageGalleryGrid,
|
|
377
|
+
ImageGalleryModal,
|
|
378
|
+
ImageGalleryItem,
|
|
379
|
+
ImageGalleryEmptyState
|
|
380
|
+
} from '@goal/components/common/goal-image-gallery';
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### Custom Hooks
|
|
384
|
+
```javascript
|
|
385
|
+
import {
|
|
386
|
+
useImageGallery,
|
|
387
|
+
useImageZoom,
|
|
388
|
+
useClipboard,
|
|
389
|
+
useKeyboardNavigation
|
|
390
|
+
} from '@goal/components/common/goal-image-gallery';
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### Styled Components
|
|
394
|
+
```javascript
|
|
395
|
+
import {
|
|
396
|
+
StyledImageContainer,
|
|
397
|
+
ImageOverlay,
|
|
398
|
+
StyledModalContainer
|
|
399
|
+
} from '@goal/components/common/goal-image-gallery';
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Utility Functions
|
|
403
|
+
```javascript
|
|
404
|
+
import {
|
|
405
|
+
handleImageError,
|
|
406
|
+
downloadImage,
|
|
407
|
+
processImageCodes,
|
|
408
|
+
getGridColumns,
|
|
409
|
+
isValidImageFile,
|
|
410
|
+
formatFileSize
|
|
411
|
+
} from '@goal/components/common/goal-image-gallery';
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Using Individual Components
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
import {
|
|
418
|
+
ImageGalleryGrid,
|
|
419
|
+
ImageGalleryModal,
|
|
420
|
+
useImageGallery
|
|
421
|
+
} from '@goal/components/common/goal-image-gallery';
|
|
422
|
+
|
|
423
|
+
function CustomGallery() {
|
|
424
|
+
const { imageList, openImage, handleOpenImage } = useImageGallery({
|
|
425
|
+
imageCodes: ['image1', 'image2'],
|
|
426
|
+
multiple: true
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<>
|
|
431
|
+
<ImageGalleryGrid
|
|
432
|
+
imageList={imageList}
|
|
433
|
+
onImageClick={handleOpenImage}
|
|
434
|
+
emptyMessage="No images"
|
|
435
|
+
/>
|
|
436
|
+
<ImageGalleryModal
|
|
437
|
+
open={openImage !== null}
|
|
438
|
+
imageUrl={openImage}
|
|
439
|
+
imageList={imageList}
|
|
440
|
+
onClose={() => handleOpenImage(null)}
|
|
441
|
+
/>
|
|
442
|
+
</>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## π¦ **Independent Package**
|
|
448
|
+
|
|
449
|
+
The component is now ready for export as an independent package:
|
|
450
|
+
|
|
451
|
+
```json
|
|
452
|
+
{
|
|
453
|
+
"name": "@goal/image-gallery",
|
|
454
|
+
"version": "1.0.0",
|
|
455
|
+
"description": "Enhanced, modular image gallery component for React applications",
|
|
456
|
+
"main": "index.js",
|
|
457
|
+
"module": "index.js",
|
|
458
|
+
"types": "index.d.ts"
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### TypeScript Support
|
|
463
|
+
|
|
464
|
+
Full TypeScript definitions are included:
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
interface GoalImageGalleryProps {
|
|
468
|
+
canEdit?: boolean;
|
|
469
|
+
ownerEntity?: object;
|
|
470
|
+
imageHandlerApi?: (data: any) => Promise<any>;
|
|
471
|
+
afterUpload?: (result: any) => void;
|
|
472
|
+
imageCodes?: string | string[];
|
|
473
|
+
multiple?: boolean;
|
|
474
|
+
permission?: string;
|
|
475
|
+
emptyMessage?: string;
|
|
476
|
+
showImageInfo?: boolean;
|
|
477
|
+
allowDownload?: boolean;
|
|
478
|
+
}
|
|
479
|
+
```
|