@yogiswara/honcho-editor-ui 3.3.4 โ 3.4.1
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/dist/components/editor/GalleryAlbum/AlbumImageGallery.d.ts +14 -7
- package/dist/components/editor/GalleryAlbum/AlbumImageGallery.js +207 -5
- package/dist/components/editor/GalleryAlbum/ImageItemComponents.d.ts +25 -0
- package/dist/components/editor/GalleryAlbum/ImageItemComponents.js +179 -0
- package/dist/components/editor/GalleryAlbum/colorsGallery.d.ts +9 -0
- package/dist/components/editor/GalleryAlbum/colorsGallery.js +9 -0
- package/dist/components/editor/GalleryAlbum/svg/Tick.d.ts +2 -0
- package/dist/components/editor/GalleryAlbum/svg/Tick.js +6 -0
- package/dist/components/editor/HBulkAccordionColorAdjustment.js +1 -2
- package/dist/components/editor/HBulkAccordionColorAdjustmentColors.js +1 -1
- package/dist/components/editor/HBulkPreset.d.ts +3 -7
- package/dist/components/editor/HBulkPreset.js +21 -22
- package/dist/components/editor/HBulkPresetMobile.d.ts +2 -2
- package/dist/components/editor/HBulkPresetMobile.js +2 -2
- package/dist/components/editor/HImageEditorBulkMobile.d.ts +2 -2
- package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +0 -3
- package/dist/hooks/demo/HonchoEditorBulkDemo.js +770 -411
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +0 -3
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +882 -354
- package/dist/hooks/demo/index.d.ts +0 -2
- package/dist/hooks/demo/index.js +3 -2
- package/dist/hooks/editor/type.d.ts +15 -13
- package/dist/hooks/editor/useHonchoEditorBulk.d.ts +47 -5
- package/dist/hooks/editor/useHonchoEditorBulk.js +252 -133
- package/dist/hooks/useAdjustmentHistory.js +12 -12
- package/dist/hooks/useAdjustmentHistoryBatch.d.ts +33 -31
- package/dist/hooks/useAdjustmentHistoryBatch.js +703 -170
- package/dist/hooks/usePreset.js +12 -12
- package/dist/index.d.ts +5 -7
- package/dist/index.js +5 -4
- package/dist/services/type.d.ts +14 -0
- package/dist/utils/adjustment.d.ts +1 -1
- package/dist/utils/adjustment.js +15 -14
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +11 -0
- package/package.json +4 -2
|
@@ -1,411 +1,770 @@
|
|
|
1
|
-
|
|
2
|
-
import React, { useState, useMemo, useRef, useEffect } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
// import React, { useState, useMemo, useRef, useEffect } from 'react';
|
|
3
|
+
// import {
|
|
4
|
+
// Box,
|
|
5
|
+
// Container,
|
|
6
|
+
// Typography,
|
|
7
|
+
// Button,
|
|
8
|
+
// Grid,
|
|
9
|
+
// Card,
|
|
10
|
+
// CardMedia,
|
|
11
|
+
// CardContent,
|
|
12
|
+
// CardActions,
|
|
13
|
+
// Checkbox,
|
|
14
|
+
// Chip,
|
|
15
|
+
// Alert,
|
|
16
|
+
// CircularProgress,
|
|
17
|
+
// ButtonGroup,
|
|
18
|
+
// Paper,
|
|
19
|
+
// Divider,
|
|
20
|
+
// FormControl,
|
|
21
|
+
// InputLabel,
|
|
22
|
+
// Select,
|
|
23
|
+
// MenuItem,
|
|
24
|
+
// Stack
|
|
25
|
+
// } from '@mui/material';
|
|
26
|
+
// import { PhotoData, useHonchoEditorBulk } from '../editor/useHonchoEditorBulk';
|
|
27
|
+
// import { Controller, AdjustmentState, Preset } from '../editor/type';
|
|
28
|
+
// import { Gallery, ResponseGalleryPaging, CreateEditorTaskRequest, GetHistoryResponse, GetGalleryUpdateTimestampResponse } from '../editor/type';
|
|
29
|
+
// import { useEditorHeadless } from "../../lib/hooks/useEditorHeadless";
|
|
30
|
+
// import { useImageProcessor } from "../../lib/hooks/useImageProcessor";
|
|
31
|
+
// // Mock data for demonstration
|
|
32
|
+
// const createMockGallery = (id: string, adjustments?: Partial<AdjustmentState>): Gallery => ({
|
|
33
|
+
// id,
|
|
34
|
+
// uid: 'demo-user',
|
|
35
|
+
// event_id: 'demo-event',
|
|
36
|
+
// download: {
|
|
37
|
+
// key: `${id}-download`,
|
|
38
|
+
// path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
|
|
39
|
+
// size: 1024000,
|
|
40
|
+
// width: 800,
|
|
41
|
+
// height: 600,
|
|
42
|
+
// },
|
|
43
|
+
// download_edited: {
|
|
44
|
+
// key: `${id}-download-edited`,
|
|
45
|
+
// path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
|
|
46
|
+
// size: 1024000,
|
|
47
|
+
// width: 800,
|
|
48
|
+
// height: 600,
|
|
49
|
+
// },
|
|
50
|
+
// thumbnail: {
|
|
51
|
+
// key: `${id}-thumb`,
|
|
52
|
+
// path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
|
|
53
|
+
// size: 50000,
|
|
54
|
+
// width: 300,
|
|
55
|
+
// height: 200,
|
|
56
|
+
// },
|
|
57
|
+
// thumbnail_edited: {
|
|
58
|
+
// key: `${id}-thumb-edited`,
|
|
59
|
+
// path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/thumbnail.jpeg`,
|
|
60
|
+
// size: 50000,
|
|
61
|
+
// width: 300,
|
|
62
|
+
// height: 200,
|
|
63
|
+
// },
|
|
64
|
+
// is_original: true,
|
|
65
|
+
// available: true,
|
|
66
|
+
// show_gallery: true,
|
|
67
|
+
// editor_config: {
|
|
68
|
+
// color_adjustment: {
|
|
69
|
+
// temperature: adjustments?.tempScore || 0,
|
|
70
|
+
// tint: adjustments?.tintScore || 0,
|
|
71
|
+
// vibrance: adjustments?.vibranceScore || 0,
|
|
72
|
+
// saturation: adjustments?.saturationScore || 0,
|
|
73
|
+
// exposure: adjustments?.exposureScore || 0,
|
|
74
|
+
// highlights: adjustments?.highlightsScore || 0,
|
|
75
|
+
// shadows: adjustments?.shadowsScore || 0,
|
|
76
|
+
// whites: adjustments?.whitesScore || 0,
|
|
77
|
+
// blacks: adjustments?.blacksScore || 0,
|
|
78
|
+
// contrast: adjustments?.contrastScore || 0,
|
|
79
|
+
// clarity: adjustments?.clarityScore || 0,
|
|
80
|
+
// sharpness: adjustments?.sharpnessScore || 0,
|
|
81
|
+
// },
|
|
82
|
+
// transformation_adjustment: [],
|
|
83
|
+
// watermarks: [],
|
|
84
|
+
// },
|
|
85
|
+
// log: {
|
|
86
|
+
// created_at: new Date().toISOString(),
|
|
87
|
+
// updated_at: new Date().toISOString(),
|
|
88
|
+
// },
|
|
89
|
+
// });
|
|
90
|
+
// // Mock Controller implementation
|
|
91
|
+
// const createMockController = (): Controller => {
|
|
92
|
+
// console.log('[Controller] ๐ญ createMockController() called - Creating new mock controller instance for bulk editor');
|
|
93
|
+
// // Generate mock images from 1 to 1277 using picsum.dev static URLs
|
|
94
|
+
// const mockImages: Gallery[] = [];
|
|
95
|
+
// for (let i = 1; i <= 1000; i++) {
|
|
96
|
+
// const id = i.toString();
|
|
97
|
+
// // let adjustments: Partial<AdjustmentState> | undefined;
|
|
98
|
+
// mockImages.push(createMockGallery(id, undefined));
|
|
99
|
+
// }
|
|
100
|
+
// return {
|
|
101
|
+
// onGetImage: async (uid: string, imageId: string) => {
|
|
102
|
+
// console.log(`[Controller] ๐ท onGetImage called: uid=${uid}, imageId=${imageId}`);
|
|
103
|
+
// await new Promise(resolve => setTimeout(resolve, 500));
|
|
104
|
+
// const image = mockImages.find(img => img.id === imageId);
|
|
105
|
+
// if (!image) {
|
|
106
|
+
// console.error(`[Controller] โ Image ${imageId} not found`);
|
|
107
|
+
// throw new Error(`Image ${imageId} not found`);
|
|
108
|
+
// }
|
|
109
|
+
// console.log(`[Controller] ๐ท onGetImage returning image:`, image.id);
|
|
110
|
+
// return image;
|
|
111
|
+
// },
|
|
112
|
+
// getImageList: async (uid: string, eventId: string, page: number) => {
|
|
113
|
+
// console.log(`[Controller] ๐ getImageList called: uid=${uid}, eventId=${eventId}, page=${page}`);
|
|
114
|
+
// await new Promise(resolve => setTimeout(resolve, 800));
|
|
115
|
+
// const pageSize = 1000; // Increased page size for better UX with more images
|
|
116
|
+
// const startIndex = (page - 1) * pageSize;
|
|
117
|
+
// const endIndex = startIndex + pageSize;
|
|
118
|
+
// const pageImages = mockImages.slice(startIndex, endIndex);
|
|
119
|
+
// const result = {
|
|
120
|
+
// gallery: pageImages,
|
|
121
|
+
// limit: pageSize,
|
|
122
|
+
// current_page: page,
|
|
123
|
+
// prev_page: page > 1 ? page - 1 : 0,
|
|
124
|
+
// next_page: endIndex < mockImages.length ? page + 1 : 0,
|
|
125
|
+
// sum_of_image: pageImages.length,
|
|
126
|
+
// } as ResponseGalleryPaging;
|
|
127
|
+
// console.log(`[Controller] ๐ getImageList returning ${pageImages.length} images for page ${page} (total available: ${mockImages.length})`, result);
|
|
128
|
+
// return result;
|
|
129
|
+
// },
|
|
130
|
+
// syncConfig: async (uid: string) => {
|
|
131
|
+
// console.log(`[Controller] ๐ syncConfig called: uid=${uid}`);
|
|
132
|
+
// await new Promise(resolve => setTimeout(resolve, 200));
|
|
133
|
+
// console.log(`[Controller] ๐ syncConfig completed for uid=${uid}`);
|
|
134
|
+
// },
|
|
135
|
+
// handleBack: (uid: string, imageId: string) => {
|
|
136
|
+
// console.log(`[Controller] โฌ
๏ธ handleBack called: uid=${uid}, imageId=${imageId}`);
|
|
137
|
+
// console.log(`[Controller] โฌ
๏ธ Back to: ${imageId}`);
|
|
138
|
+
// },
|
|
139
|
+
// getPresets: async (uid: string) => {
|
|
140
|
+
// console.log(`[Controller] ๐จ getPresets called: uid=${uid}`);
|
|
141
|
+
// await new Promise(resolve => setTimeout(resolve, 300));
|
|
142
|
+
// const presets = [
|
|
143
|
+
// { id: '1', name: 'Warm Sunset', is_default: false, temperature: 15, tint: 5, saturation: 8, vibrance: 12, exposure: 2, contrast: 5, highlights: -10, shadows: 8, whites: 3, blacks: -5, clarity: 4, sharpness: 6 },
|
|
144
|
+
// { id: '2', name: 'Cool Morning', is_default: false, temperature: -12, tint: -3, saturation: -2, vibrance: 5, exposure: 1, contrast: 3, highlights: -5, shadows: 12, whites: 8, blacks: -8, clarity: 6, sharpness: 4 },
|
|
145
|
+
// { id: '3', name: 'High Contrast', is_default: false, temperature: 0, tint: 0, saturation: 5, vibrance: 8, exposure: 0, contrast: 20, highlights: -15, shadows: 15, whites: 10, blacks: -10, clarity: 15, sharpness: 8 },
|
|
146
|
+
// ] as Preset[];
|
|
147
|
+
// console.log(`[Controller] ๐จ getPresets returning ${presets.length} presets`, presets.map(p => ({ id: p.id, name: p.name })));
|
|
148
|
+
// return presets;
|
|
149
|
+
// },
|
|
150
|
+
// createPreset: async (uid: string, name: string, settings: AdjustmentState) => {
|
|
151
|
+
// console.log(`[Controller] โ createPreset called: uid=${uid}, name=${name}`, settings);
|
|
152
|
+
// await new Promise(resolve => setTimeout(resolve, 500));
|
|
153
|
+
// console.log(`[Controller] โ createPreset completed: ${name}`);
|
|
154
|
+
// },
|
|
155
|
+
// deletePreset: async (uid: string, presetId: string) => {
|
|
156
|
+
// console.log(`[Controller] ๐๏ธ deletePreset called: uid=${uid}, presetId=${presetId}`);
|
|
157
|
+
// await new Promise(resolve => setTimeout(resolve, 300));
|
|
158
|
+
// console.log(`[Controller] ๐๏ธ deletePreset completed: ${presetId}`);
|
|
159
|
+
// },
|
|
160
|
+
// updatePreset: async (uid: string, data: Preset) => {
|
|
161
|
+
// console.log(`[Controller] ๐ updatePreset called: uid=${uid}`, data);
|
|
162
|
+
// await new Promise(resolve => setTimeout(resolve, 300));
|
|
163
|
+
// console.log(`[Controller] ๐ updatePreset completed:`, data.name);
|
|
164
|
+
// },
|
|
165
|
+
// createEditorConfig: async (uid: string, payload: CreateEditorTaskRequest) => {
|
|
166
|
+
// console.log(`[Controller] โ๏ธ createEditorConfig called: uid=${uid}`, payload);
|
|
167
|
+
// await new Promise(resolve => setTimeout(resolve, 300));
|
|
168
|
+
// console.log(`[Controller] โ๏ธ createEditorConfig completed for gallery_id=${payload.gallery_id}, task_id=${payload.task_id}`);
|
|
169
|
+
// },
|
|
170
|
+
// getEditorHistory: async (uid: string, imageId: string): Promise<GetHistoryResponse> => {
|
|
171
|
+
// console.log(`[Controller] ๐ getEditorHistory called: uid=${uid}, imageId=${imageId}`);
|
|
172
|
+
// await new Promise(resolve => setTimeout(resolve, 200));
|
|
173
|
+
// const result = {
|
|
174
|
+
// current_task_id: "",
|
|
175
|
+
// history: []
|
|
176
|
+
// };
|
|
177
|
+
// console.log(`[Controller] ๐ getEditorHistory returning empty history for demo:`, result);
|
|
178
|
+
// return result;
|
|
179
|
+
// },
|
|
180
|
+
// getGalleryUpdateTimestamp: async (uid: string, eventId: string): Promise<GetGalleryUpdateTimestampResponse> => {
|
|
181
|
+
// console.log(`[Controller] โฐ getGalleryUpdateTimestamp called: uid=${uid}, eventId=${eventId}`);
|
|
182
|
+
// await new Promise(resolve => setTimeout(resolve, 100));
|
|
183
|
+
// const result = {
|
|
184
|
+
// gallery: []
|
|
185
|
+
// };
|
|
186
|
+
// console.log(`[Controller] โฐ getGalleryUpdateTimestamp returning:`, result);
|
|
187
|
+
// return result;
|
|
188
|
+
// },
|
|
189
|
+
// setHistoryIndex: async (uid: string, imageId: string, taskId: string) => {
|
|
190
|
+
// console.log(`[Controller] ๐ setHistoryIndex called: uid=${uid}, imageId=${imageId}, taskId=${taskId}`);
|
|
191
|
+
// await new Promise(resolve => setTimeout(resolve, 100));
|
|
192
|
+
// console.log(`[Controller] ๐ setHistoryIndex completed - Set history index for image ${imageId} to task ${taskId}`);
|
|
193
|
+
// },
|
|
194
|
+
// };
|
|
195
|
+
// };
|
|
196
|
+
// const AdjustmentControls: React.FC<{
|
|
197
|
+
// label: string;
|
|
198
|
+
// onDecreaseMax: () => void;
|
|
199
|
+
// onDecrease: () => void;
|
|
200
|
+
// onIncrease: () => void;
|
|
201
|
+
// onIncreaseMax: () => void;
|
|
202
|
+
// disabled?: boolean;
|
|
203
|
+
// }> = ({ label, onDecreaseMax, onDecrease, onIncrease, onIncreaseMax, disabled = false }) => (
|
|
204
|
+
// <Box mb={2}>
|
|
205
|
+
// <Typography variant="body2" gutterBottom>{label}</Typography>
|
|
206
|
+
// <ButtonGroup size="small" variant="outlined">
|
|
207
|
+
// <Button onClick={onDecreaseMax} disabled={disabled}>--</Button>
|
|
208
|
+
// <Button onClick={onDecrease} disabled={disabled}>-</Button>
|
|
209
|
+
// <Button onClick={onIncrease} disabled={disabled}>+</Button>
|
|
210
|
+
// <Button onClick={onIncreaseMax} disabled={disabled}>++</Button>
|
|
211
|
+
// </ButtonGroup>
|
|
212
|
+
// </Box>
|
|
213
|
+
// );
|
|
214
|
+
// const ImageCard = React.memo<{
|
|
215
|
+
// image: PhotoData;
|
|
216
|
+
// onToggleSelection: (key: string) => void;
|
|
217
|
+
// }>(({ image, onToggleSelection }) => {
|
|
218
|
+
// const [isVisible, setIsVisible] = useState(false);
|
|
219
|
+
// const [shouldLoadImage, setShouldLoadImage] = useState(false);
|
|
220
|
+
// const [isInView, setIsInView] = useState(false);
|
|
221
|
+
// const cardRef = useRef<HTMLDivElement>(null);
|
|
222
|
+
// const abortControllerRef = useRef<AbortController | null>(null);
|
|
223
|
+
// useEffect(() => {
|
|
224
|
+
// const visibilityObserver = new IntersectionObserver(
|
|
225
|
+
// ([entry]) => {
|
|
226
|
+
// if (entry.isIntersecting) {
|
|
227
|
+
// setIsVisible(true);
|
|
228
|
+
// setIsInView(true);
|
|
229
|
+
// // Delay image loading slightly to ensure smooth scrolling
|
|
230
|
+
// setTimeout(() => setShouldLoadImage(true), 100);
|
|
231
|
+
// // Don't disconnect here as we want to track when it goes out of view
|
|
232
|
+
// } else {
|
|
233
|
+
// setIsInView(false);
|
|
234
|
+
// // Cancel processing when going out of view
|
|
235
|
+
// if (abortControllerRef.current) {
|
|
236
|
+
// console.debug(`[ImageCard] Cancelling processing for image ${image.key} - out of view`);
|
|
237
|
+
// abortControllerRef.current.abort();
|
|
238
|
+
// }
|
|
239
|
+
// }
|
|
240
|
+
// },
|
|
241
|
+
// {
|
|
242
|
+
// threshold: 0.1, // Trigger when 10% of the element is visible
|
|
243
|
+
// rootMargin: '100px', // Start loading 100px before the element comes into view
|
|
244
|
+
// }
|
|
245
|
+
// );
|
|
246
|
+
// if (cardRef.current) {
|
|
247
|
+
// visibilityObserver.observe(cardRef.current);
|
|
248
|
+
// }
|
|
249
|
+
// return () => {
|
|
250
|
+
// visibilityObserver.disconnect();
|
|
251
|
+
// // Cancel any ongoing processing when component unmounts
|
|
252
|
+
// if (abortControllerRef.current) {
|
|
253
|
+
// abortControllerRef.current.abort();
|
|
254
|
+
// }
|
|
255
|
+
// };
|
|
256
|
+
// }, [image.key]);
|
|
257
|
+
// // Track adjustments changes for debugging
|
|
258
|
+
// useEffect(() => {
|
|
259
|
+
// console.log("[ImageCard] Adjustments changed for image", image.key, ":", image.adjustments);
|
|
260
|
+
// }, [image.adjustments, image.key]);
|
|
261
|
+
// // Create a key that changes when adjustments change to force abort controller recreation
|
|
262
|
+
// const adjustmentsKey = useMemo(() => {
|
|
263
|
+
// return JSON.stringify(image.adjustments);
|
|
264
|
+
// }, [image.adjustments]);
|
|
265
|
+
// // Store the current abort signal in a ref to prevent it from changing on every render
|
|
266
|
+
// const currentAbortSignalRef = useRef<AbortSignal | undefined>();
|
|
267
|
+
// const isInitializedRef = useRef(false);
|
|
268
|
+
// // Much more conservative abort controller management - only recreate when absolutely necessary
|
|
269
|
+
// useEffect(() => {
|
|
270
|
+
// if (shouldLoadImage && isInView) {
|
|
271
|
+
// // Only create abort controller if we don't have one OR if this is the first time
|
|
272
|
+
// if (!abortControllerRef.current || !isInitializedRef.current) {
|
|
273
|
+
// console.debug(`[ImageCard] Creating initial abort controller for image ${image.key}`);
|
|
274
|
+
// if (abortControllerRef.current) {
|
|
275
|
+
// abortControllerRef.current.abort();
|
|
276
|
+
// }
|
|
277
|
+
// abortControllerRef.current = new AbortController();
|
|
278
|
+
// currentAbortSignalRef.current = abortControllerRef.current.signal;
|
|
279
|
+
// isInitializedRef.current = true;
|
|
280
|
+
// }
|
|
281
|
+
// // For adjustment changes, don't recreate the controller - let useImageProcessor handle it
|
|
282
|
+
// } else {
|
|
283
|
+
// // Only clean up when not visible or not loading
|
|
284
|
+
// if (abortControllerRef.current && isInitializedRef.current) {
|
|
285
|
+
// console.debug(`[ImageCard] Cleaning up abort controller for image ${image.key} - not visible or not loading`);
|
|
286
|
+
// abortControllerRef.current.abort();
|
|
287
|
+
// abortControllerRef.current = null;
|
|
288
|
+
// currentAbortSignalRef.current = undefined;
|
|
289
|
+
// isInitializedRef.current = false;
|
|
290
|
+
// }
|
|
291
|
+
// }
|
|
292
|
+
// return () => {
|
|
293
|
+
// if (abortControllerRef.current) {
|
|
294
|
+
// abortControllerRef.current.abort();
|
|
295
|
+
// abortControllerRef.current = null;
|
|
296
|
+
// currentAbortSignalRef.current = undefined;
|
|
297
|
+
// isInitializedRef.current = false;
|
|
298
|
+
// }
|
|
299
|
+
// };
|
|
300
|
+
// }, [shouldLoadImage, isInView, image.key]); // Remove adjustmentsKey from dependencies
|
|
301
|
+
// // Separate effect to handle adjustment changes without recreating abort controller
|
|
302
|
+
// useEffect(() => {
|
|
303
|
+
// if (shouldLoadImage && isInView && isInitializedRef.current) {
|
|
304
|
+
// console.debug(`[ImageCard] Adjustments changed for image ${image.key} - letting useImageProcessor handle the change`);
|
|
305
|
+
// }
|
|
306
|
+
// }, [adjustmentsKey, shouldLoadImage, isInView, image.key]);
|
|
307
|
+
// // Memoize the useImageProcessor call to prevent unnecessary hook calls
|
|
308
|
+
// const imageProcessorParams = useMemo(() => ({
|
|
309
|
+
// photoId: image.key,
|
|
310
|
+
// photoSrc: image.src,
|
|
311
|
+
// adjustments: image.adjustments,
|
|
312
|
+
// enableEditor: shouldLoadImage && isInView, // Only enable when we should load the image AND it's in view
|
|
313
|
+
// abortSignal: currentAbortSignalRef.current, // Use stable abort signal
|
|
314
|
+
// frame: null,
|
|
315
|
+
// }), [image.key, image.src, image.adjustments, shouldLoadImage, isInView]);
|
|
316
|
+
// // Only call useImageProcessor when the card is visible AND we should load the image AND it's in view
|
|
317
|
+
// const { processedImageSrc, isProcessingComplete, cancelProcessing, isProcessing } = useImageProcessor(imageProcessorParams);
|
|
318
|
+
// // Cancel processing when card goes out of view
|
|
319
|
+
// useEffect(() => {
|
|
320
|
+
// if (!isInView && isProcessing) {
|
|
321
|
+
// console.debug(`[ImageCard] Cancelling processing for image ${image.key} - no longer in view`);
|
|
322
|
+
// cancelProcessing();
|
|
323
|
+
// }
|
|
324
|
+
// }, [isInView, isProcessing, cancelProcessing, image.key]);
|
|
325
|
+
// // Memoize the toggle selection handler to prevent unnecessary re-renders
|
|
326
|
+
// const handleToggleSelection = useMemo(() => {
|
|
327
|
+
// return () => onToggleSelection(image.key);
|
|
328
|
+
// }, [onToggleSelection, image.key]);
|
|
329
|
+
// const handleCheckboxClick = useMemo(() => {
|
|
330
|
+
// return (e: React.MouseEvent) => {
|
|
331
|
+
// e.stopPropagation();
|
|
332
|
+
// onToggleSelection(image.key);
|
|
333
|
+
// };
|
|
334
|
+
// }, [onToggleSelection, image.key]);
|
|
335
|
+
// // Create placeholder content for non-visible cards
|
|
336
|
+
// const renderPlaceholder = () => (
|
|
337
|
+
// <Box
|
|
338
|
+
// sx={{
|
|
339
|
+
// height: 200,
|
|
340
|
+
// backgroundColor: 'grey.100',
|
|
341
|
+
// display: 'flex',
|
|
342
|
+
// alignItems: 'center',
|
|
343
|
+
// justifyContent: 'center',
|
|
344
|
+
// flexDirection: 'column'
|
|
345
|
+
// }}
|
|
346
|
+
// >
|
|
347
|
+
// <CircularProgress size={24} sx={{ mb: 1 }} />
|
|
348
|
+
// <Typography variant="caption" color="text.secondary">
|
|
349
|
+
// Loading...
|
|
350
|
+
// </Typography>
|
|
351
|
+
// </Box>
|
|
352
|
+
// );
|
|
353
|
+
// // Create processing indicator
|
|
354
|
+
// const renderProcessingOverlay = () => {
|
|
355
|
+
// return isProcessing && shouldLoadImage && isInView ? (
|
|
356
|
+
// <Box
|
|
357
|
+
// sx={{
|
|
358
|
+
// position: 'absolute',
|
|
359
|
+
// top: 8,
|
|
360
|
+
// right: 8,
|
|
361
|
+
// backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
362
|
+
// color: 'white',
|
|
363
|
+
// borderRadius: 1,
|
|
364
|
+
// px: 1,
|
|
365
|
+
// py: 0.5,
|
|
366
|
+
// display: 'flex',
|
|
367
|
+
// alignItems: 'center',
|
|
368
|
+
// gap: 0.5,
|
|
369
|
+
// zIndex: 1,
|
|
370
|
+
// maxWidth: '120px'
|
|
371
|
+
// }}
|
|
372
|
+
// >
|
|
373
|
+
// <CircularProgress size={12} color="inherit" />
|
|
374
|
+
// <Typography variant="caption">Processing</Typography>
|
|
375
|
+
// </Box>
|
|
376
|
+
// ) : null;
|
|
377
|
+
// };
|
|
378
|
+
// return (
|
|
379
|
+
// <Card
|
|
380
|
+
// ref={cardRef}
|
|
381
|
+
// sx={{
|
|
382
|
+
// border: image.isSelected ? 2 : 1,
|
|
383
|
+
// borderColor: image.isSelected ? 'primary.main' : 'divider',
|
|
384
|
+
// cursor: 'pointer',
|
|
385
|
+
// transition: 'all 0.2s',
|
|
386
|
+
// position: 'relative',
|
|
387
|
+
// opacity: 1,
|
|
388
|
+
// '&:hover': {
|
|
389
|
+
// transform: 'translateY(-2px)',
|
|
390
|
+
// boxShadow: 2,
|
|
391
|
+
// }
|
|
392
|
+
// }}
|
|
393
|
+
// onClick={handleToggleSelection}
|
|
394
|
+
// >
|
|
395
|
+
// {shouldLoadImage ? (
|
|
396
|
+
// <Box sx={{ position: 'relative', width: '100%', height: '200px' }}>
|
|
397
|
+
// <img
|
|
398
|
+
// id="image-card-media"
|
|
399
|
+
// src={processedImageSrc}
|
|
400
|
+
// alt=""
|
|
401
|
+
// style={{
|
|
402
|
+
// width: '100%',
|
|
403
|
+
// height: '200px',
|
|
404
|
+
// objectFit: 'cover',
|
|
405
|
+
// opacity: 1,
|
|
406
|
+
// display: 'block'
|
|
407
|
+
// }}
|
|
408
|
+
// loading="lazy"
|
|
409
|
+
// />
|
|
410
|
+
// {renderProcessingOverlay()}
|
|
411
|
+
// </Box>
|
|
412
|
+
// ) : (
|
|
413
|
+
// renderPlaceholder()
|
|
414
|
+
// )}
|
|
415
|
+
// <CardContent sx={{ pb: 1 }}>
|
|
416
|
+
// <Box display="flex" alignItems="center" justifyContent="space-between">
|
|
417
|
+
// <Typography variant="body2" color="text.secondary">
|
|
418
|
+
// Image {image.key}
|
|
419
|
+
// {!shouldLoadImage && (
|
|
420
|
+
// <Typography
|
|
421
|
+
// component="span"
|
|
422
|
+
// variant="caption"
|
|
423
|
+
// sx={{ ml: 1 }}
|
|
424
|
+
// >
|
|
425
|
+
// (waiting...)
|
|
426
|
+
// </Typography>
|
|
427
|
+
// )}
|
|
428
|
+
// {!isInView && shouldLoadImage && (
|
|
429
|
+
// <Typography
|
|
430
|
+
// component="span"
|
|
431
|
+
// variant="caption"
|
|
432
|
+
// sx={{ ml: 1, color: 'warning.main' }}
|
|
433
|
+
// >
|
|
434
|
+
// (paused)
|
|
435
|
+
// </Typography>
|
|
436
|
+
// )}
|
|
437
|
+
// </Typography>
|
|
438
|
+
// <Checkbox
|
|
439
|
+
// checked={image.isSelected}
|
|
440
|
+
// size="small"
|
|
441
|
+
// onClick={handleCheckboxClick}
|
|
442
|
+
// />
|
|
443
|
+
// </Box>
|
|
444
|
+
// </CardContent>
|
|
445
|
+
// </Card>
|
|
446
|
+
// );
|
|
447
|
+
// }, (prevProps, nextProps) => {
|
|
448
|
+
// // Custom comparison function for React.memo - be more strict to prevent unnecessary re-renders
|
|
449
|
+
// const prevAdj = prevProps.image.adjustments || {};
|
|
450
|
+
// const nextAdj = nextProps.image.adjustments || {};
|
|
451
|
+
// // Only re-render if key properties actually change
|
|
452
|
+
// const shouldUpdate = (
|
|
453
|
+
// prevProps.image.key !== nextProps.image.key ||
|
|
454
|
+
// prevProps.image.isSelected !== nextProps.image.isSelected ||
|
|
455
|
+
// prevProps.image.src !== nextProps.image.src ||
|
|
456
|
+
// // Compare individual adjustment properties to avoid JSON.stringify issues
|
|
457
|
+
// prevAdj.temperature !== nextAdj.temperature ||
|
|
458
|
+
// prevAdj.tint !== nextAdj.tint ||
|
|
459
|
+
// prevAdj.exposure !== nextAdj.exposure ||
|
|
460
|
+
// prevAdj.contrast !== nextAdj.contrast ||
|
|
461
|
+
// prevAdj.clarity !== nextAdj.clarity ||
|
|
462
|
+
// prevAdj.vibrance !== nextAdj.vibrance ||
|
|
463
|
+
// prevAdj.saturation !== nextAdj.saturation ||
|
|
464
|
+
// prevAdj.highlights !== nextAdj.highlights ||
|
|
465
|
+
// prevAdj.shadows !== nextAdj.shadows ||
|
|
466
|
+
// prevAdj.whites !== nextAdj.whites ||
|
|
467
|
+
// prevAdj.blacks !== nextAdj.blacks ||
|
|
468
|
+
// prevAdj.sharpness !== nextAdj.sharpness
|
|
469
|
+
// );
|
|
470
|
+
// // Return false to re-render, true to skip re-render
|
|
471
|
+
// return !shouldUpdate;
|
|
472
|
+
// });
|
|
473
|
+
// export const HonchoEditorBulkDemo: React.FC = () => {
|
|
474
|
+
// const [controller] = useState(() => createMockController());
|
|
475
|
+
// const [showAdjustments, setShowAdjustments] = useState(false);
|
|
476
|
+
// const {
|
|
477
|
+
// imageData,
|
|
478
|
+
// isLoading,
|
|
479
|
+
// error,
|
|
480
|
+
// selectedIds,
|
|
481
|
+
// hasMore,
|
|
482
|
+
// selectedBulkPreset,
|
|
483
|
+
// presets, // Add presets
|
|
484
|
+
// activePreset, // Add activePreset
|
|
485
|
+
// handleToggleImageSelection,
|
|
486
|
+
// handleLoadMore,
|
|
487
|
+
// handleRefresh,
|
|
488
|
+
// handleSelectBulkPreset,
|
|
489
|
+
// handleBackCallbackBulk,
|
|
490
|
+
// // Temperature adjustments
|
|
491
|
+
// handleBulkTempDecreaseMax,
|
|
492
|
+
// handleBulkTempDecrease,
|
|
493
|
+
// handleBulkTempIncrease,
|
|
494
|
+
// handleBulkTempIncreaseMax,
|
|
495
|
+
// // Tint adjustments
|
|
496
|
+
// handleBulkTintDecreaseMax,
|
|
497
|
+
// handleBulkTintDecrease,
|
|
498
|
+
// handleBulkTintIncrease,
|
|
499
|
+
// handleBulkTintIncreaseMax,
|
|
500
|
+
// // Exposure adjustments
|
|
501
|
+
// handleBulkExposureDecreaseMax,
|
|
502
|
+
// handleBulkExposureDecrease,
|
|
503
|
+
// handleBulkExposureIncrease,
|
|
504
|
+
// handleBulkExposureIncreaseMax,
|
|
505
|
+
// // Contrast adjustments
|
|
506
|
+
// handleBulkContrastDecreaseMax,
|
|
507
|
+
// handleBulkContrastDecrease,
|
|
508
|
+
// handleBulkContrastIncrease,
|
|
509
|
+
// handleBulkContrastIncreaseMax,
|
|
510
|
+
// // Clarity adjustments
|
|
511
|
+
// handleBulkClarityDecreaseMax,
|
|
512
|
+
// handleBulkClarityDecrease,
|
|
513
|
+
// handleBulkClarityIncrease,
|
|
514
|
+
// handleBulkClarityIncreaseMax,
|
|
515
|
+
// // History actions
|
|
516
|
+
// handleUndo,
|
|
517
|
+
// handleRedo,
|
|
518
|
+
// handleReset,
|
|
519
|
+
// historyInfo,
|
|
520
|
+
// } = useHonchoEditorBulk(controller, 'demo-event', 'demo-user');
|
|
521
|
+
// const selectedCount = selectedIds.length;
|
|
522
|
+
// const totalCount = imageData.length;
|
|
523
|
+
// return (
|
|
524
|
+
// <Container maxWidth="xl" sx={{ py: 4 }}>
|
|
525
|
+
// <Typography variant="h3" gutterBottom align="center">
|
|
526
|
+
// Honcho Editor Bulk Demo
|
|
527
|
+
// </Typography>
|
|
528
|
+
// <Typography variant="subtitle1" align="center" color="text.secondary" gutterBottom>
|
|
529
|
+
// This demo shows the useHonchoEditorBulk hook with 1,277 mock images from picsum.dev
|
|
530
|
+
// </Typography>
|
|
531
|
+
// {error && (
|
|
532
|
+
// <Alert severity="error" sx={{ mb: 3 }}>
|
|
533
|
+
// {error}
|
|
534
|
+
// </Alert>
|
|
535
|
+
// )}
|
|
536
|
+
// <Paper sx={{ p: 3, mb: 3 }}>
|
|
537
|
+
// <Stack direction="row" spacing={2} alignItems="center" flexWrap="wrap">
|
|
538
|
+
// <Button
|
|
539
|
+
// variant="contained"
|
|
540
|
+
// onClick={handleRefresh}
|
|
541
|
+
// disabled={isLoading}
|
|
542
|
+
// >
|
|
543
|
+
// {isLoading ? <CircularProgress size={20} /> : 'Refresh Images'}
|
|
544
|
+
// </Button>
|
|
545
|
+
// <Button
|
|
546
|
+
// variant="outlined"
|
|
547
|
+
// onClick={() => setShowAdjustments(!showAdjustments)}
|
|
548
|
+
// >
|
|
549
|
+
// {showAdjustments ? 'Hide' : 'Show'} Adjustments
|
|
550
|
+
// </Button>
|
|
551
|
+
// <Button
|
|
552
|
+
// variant="outlined"
|
|
553
|
+
// onClick={() => {
|
|
554
|
+
// // Select all images on current page
|
|
555
|
+
// imageData.forEach(image => {
|
|
556
|
+
// if (!image.isSelected) {
|
|
557
|
+
// handleToggleImageSelection(image.key);
|
|
558
|
+
// }
|
|
559
|
+
// });
|
|
560
|
+
// }}
|
|
561
|
+
// disabled={isLoading || imageData.length === 0}
|
|
562
|
+
// >
|
|
563
|
+
// Select All
|
|
564
|
+
// </Button>
|
|
565
|
+
// <Button
|
|
566
|
+
// variant="outlined"
|
|
567
|
+
// onClick={() => {
|
|
568
|
+
// // Deselect all images
|
|
569
|
+
// imageData.forEach(image => {
|
|
570
|
+
// if (image.isSelected) {
|
|
571
|
+
// handleToggleImageSelection(image.key);
|
|
572
|
+
// }
|
|
573
|
+
// });
|
|
574
|
+
// }}
|
|
575
|
+
// disabled={isLoading || selectedCount === 0}
|
|
576
|
+
// >
|
|
577
|
+
// Clear Selection
|
|
578
|
+
// </Button>
|
|
579
|
+
// <Button
|
|
580
|
+
// variant="outlined"
|
|
581
|
+
// onClick={() => {
|
|
582
|
+
// console.log('Debug Info:', {
|
|
583
|
+
// selectedIds,
|
|
584
|
+
// selectedCount,
|
|
585
|
+
// showAdjustments,
|
|
586
|
+
// presets: presets.map(p => ({ id: p.id, name: p.name })),
|
|
587
|
+
// selectedBulkPreset,
|
|
588
|
+
// activePreset: activePreset ? { id: activePreset.id, name: activePreset.name } : null,
|
|
589
|
+
// imageData: imageData.map(img => ({ key: img.key, isSelected: img.isSelected }))
|
|
590
|
+
// });
|
|
591
|
+
// }}
|
|
592
|
+
// >
|
|
593
|
+
// Debug Selection
|
|
594
|
+
// </Button>
|
|
595
|
+
// <Button
|
|
596
|
+
// variant="outlined"
|
|
597
|
+
// onClick={handleBackCallbackBulk}
|
|
598
|
+
// >
|
|
599
|
+
// Back
|
|
600
|
+
// </Button>
|
|
601
|
+
// <Chip
|
|
602
|
+
// label={`${selectedCount} of ${totalCount} selected`}
|
|
603
|
+
// color={selectedCount > 0 ? "primary" : "default"}
|
|
604
|
+
// />
|
|
605
|
+
// {hasMore && (
|
|
606
|
+
// <Button
|
|
607
|
+
// variant="outlined"
|
|
608
|
+
// onClick={handleLoadMore}
|
|
609
|
+
// disabled={isLoading}
|
|
610
|
+
// >
|
|
611
|
+
// Load More
|
|
612
|
+
// </Button>
|
|
613
|
+
// )}
|
|
614
|
+
// <FormControl size="small" sx={{ minWidth: 150 }}>
|
|
615
|
+
// <InputLabel>Bulk Preset</InputLabel>
|
|
616
|
+
// <Select
|
|
617
|
+
// value={selectedBulkPreset}
|
|
618
|
+
// onChange={handleSelectBulkPreset}
|
|
619
|
+
// label="Bulk Preset"
|
|
620
|
+
// displayEmpty
|
|
621
|
+
// >
|
|
622
|
+
// <MenuItem value="">
|
|
623
|
+
// <em>No Preset</em>
|
|
624
|
+
// </MenuItem>
|
|
625
|
+
// {presets.map((preset) => (
|
|
626
|
+
// <MenuItem key={preset.id} value={preset.id}>
|
|
627
|
+
// {preset.name}
|
|
628
|
+
// {activePreset?.id === preset.id && ' โ'}
|
|
629
|
+
// </MenuItem>
|
|
630
|
+
// ))}
|
|
631
|
+
// </Select>
|
|
632
|
+
// </FormControl>
|
|
633
|
+
// <Typography variant="body2" sx={{ ml: 2, opacity: 0.7 }}>
|
|
634
|
+
// Active: {activePreset ? activePreset.name : 'None'} | Presets: {presets.length}
|
|
635
|
+
// </Typography>
|
|
636
|
+
// </Stack>
|
|
637
|
+
// </Paper>
|
|
638
|
+
// {showAdjustments && (
|
|
639
|
+
// <Paper sx={{ p: 3, mb: 3 }}>
|
|
640
|
+
// <Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 2 }}>
|
|
641
|
+
// <Typography variant="h6">
|
|
642
|
+
// Bulk Adjustments ({selectedCount} images selected)
|
|
643
|
+
// </Typography>
|
|
644
|
+
// <Stack direction="row" spacing={1}>
|
|
645
|
+
// <Button
|
|
646
|
+
// variant="outlined"
|
|
647
|
+
// size="small"
|
|
648
|
+
// onClick={handleUndo}
|
|
649
|
+
// disabled={!historyInfo.canUndo}
|
|
650
|
+
// sx={{ minWidth: 80 }}
|
|
651
|
+
// >
|
|
652
|
+
// Undo
|
|
653
|
+
// </Button>
|
|
654
|
+
// <Button
|
|
655
|
+
// variant="outlined"
|
|
656
|
+
// size="small"
|
|
657
|
+
// onClick={handleRedo}
|
|
658
|
+
// disabled={!historyInfo.canRedo}
|
|
659
|
+
// sx={{ minWidth: 80 }}
|
|
660
|
+
// >
|
|
661
|
+
// Redo
|
|
662
|
+
// </Button>
|
|
663
|
+
// <Button
|
|
664
|
+
// variant="outlined"
|
|
665
|
+
// size="small"
|
|
666
|
+
// onClick={() => handleReset()}
|
|
667
|
+
// disabled={selectedCount === 0}
|
|
668
|
+
// color="warning"
|
|
669
|
+
// sx={{ minWidth: 80 }}
|
|
670
|
+
// >
|
|
671
|
+
// Reset
|
|
672
|
+
// </Button>
|
|
673
|
+
// </Stack>
|
|
674
|
+
// </Stack>
|
|
675
|
+
// {selectedCount === 0 && (
|
|
676
|
+
// <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
677
|
+
// Select one or more images below to enable bulk adjustments
|
|
678
|
+
// </Typography>
|
|
679
|
+
// )}
|
|
680
|
+
// <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
681
|
+
// History: {historyInfo.currentIndex + 1}/{historyInfo.totalStates} states |
|
|
682
|
+
// Can Undo: {historyInfo.canUndo ? 'Yes' : 'No'} |
|
|
683
|
+
// Can Redo: {historyInfo.canRedo ? 'Yes' : 'No'}
|
|
684
|
+
// </Typography>
|
|
685
|
+
// <Grid container spacing={3}>
|
|
686
|
+
// <Grid item xs={12} md={3}>
|
|
687
|
+
// <Typography variant="subtitle2" gutterBottom>Color</Typography>
|
|
688
|
+
// <AdjustmentControls
|
|
689
|
+
// label="Temperature"
|
|
690
|
+
// onDecreaseMax={handleBulkTempDecreaseMax}
|
|
691
|
+
// onDecrease={handleBulkTempDecrease}
|
|
692
|
+
// onIncrease={handleBulkTempIncrease}
|
|
693
|
+
// onIncreaseMax={handleBulkTempIncreaseMax}
|
|
694
|
+
// disabled={selectedCount === 0}
|
|
695
|
+
// />
|
|
696
|
+
// <AdjustmentControls
|
|
697
|
+
// label="Tint"
|
|
698
|
+
// onDecreaseMax={handleBulkTintDecreaseMax}
|
|
699
|
+
// onDecrease={handleBulkTintDecrease}
|
|
700
|
+
// onIncrease={handleBulkTintIncrease}
|
|
701
|
+
// onIncreaseMax={handleBulkTintIncreaseMax}
|
|
702
|
+
// disabled={selectedCount === 0}
|
|
703
|
+
// />
|
|
704
|
+
// </Grid>
|
|
705
|
+
// <Grid item xs={12} md={3}>
|
|
706
|
+
// <Typography variant="subtitle2" gutterBottom>Light</Typography>
|
|
707
|
+
// <AdjustmentControls
|
|
708
|
+
// label="Exposure"
|
|
709
|
+
// onDecreaseMax={handleBulkExposureDecreaseMax}
|
|
710
|
+
// onDecrease={handleBulkExposureDecrease}
|
|
711
|
+
// onIncrease={handleBulkExposureIncrease}
|
|
712
|
+
// onIncreaseMax={handleBulkExposureIncreaseMax}
|
|
713
|
+
// disabled={selectedCount === 0}
|
|
714
|
+
// />
|
|
715
|
+
// <AdjustmentControls
|
|
716
|
+
// label="Contrast"
|
|
717
|
+
// onDecreaseMax={handleBulkContrastDecreaseMax}
|
|
718
|
+
// onDecrease={handleBulkContrastDecrease}
|
|
719
|
+
// onIncrease={handleBulkContrastIncrease}
|
|
720
|
+
// onIncreaseMax={handleBulkContrastIncreaseMax}
|
|
721
|
+
// disabled={selectedCount === 0}
|
|
722
|
+
// />
|
|
723
|
+
// </Grid>
|
|
724
|
+
// <Grid item xs={12} md={3}>
|
|
725
|
+
// <Typography variant="subtitle2" gutterBottom>Details</Typography>
|
|
726
|
+
// <AdjustmentControls
|
|
727
|
+
// label="Clarity"
|
|
728
|
+
// onDecreaseMax={handleBulkClarityDecreaseMax}
|
|
729
|
+
// onDecrease={handleBulkClarityDecrease}
|
|
730
|
+
// onIncrease={handleBulkClarityIncrease}
|
|
731
|
+
// onIncreaseMax={handleBulkClarityIncreaseMax}
|
|
732
|
+
// disabled={selectedCount === 0}
|
|
733
|
+
// />
|
|
734
|
+
// </Grid>
|
|
735
|
+
// </Grid>
|
|
736
|
+
// </Paper>
|
|
737
|
+
// )}
|
|
738
|
+
// <Grid container spacing={2}>
|
|
739
|
+
// {imageData.map((image) => (
|
|
740
|
+
// <Grid item xs={12} sm={6} md={4} lg={3} key={image.key}>
|
|
741
|
+
// <ImageCard
|
|
742
|
+
// image={image}
|
|
743
|
+
// onToggleSelection={handleToggleImageSelection}
|
|
744
|
+
// />
|
|
745
|
+
// </Grid>
|
|
746
|
+
// ))}
|
|
747
|
+
// </Grid>
|
|
748
|
+
// {isLoading && imageData.length === 0 && (
|
|
749
|
+
// <Box display="flex" justifyContent="center" py={4}>
|
|
750
|
+
// <CircularProgress />
|
|
751
|
+
// </Box>
|
|
752
|
+
// )}
|
|
753
|
+
// <Box mt={4}>
|
|
754
|
+
// <Divider />
|
|
755
|
+
// <Typography variant="body2" color="text.secondary" align="center" sx={{ mt: 2 }}>
|
|
756
|
+
// Demo Notes: This uses mock data and simulated API calls.
|
|
757
|
+
// Select images and try the bulk adjustment controls above.
|
|
758
|
+
// </Typography>
|
|
759
|
+
// </Box>
|
|
760
|
+
// </Container>
|
|
761
|
+
// );
|
|
762
|
+
// };
|
|
763
|
+
// // Add debugging function to global scope for manual testing
|
|
764
|
+
// if (typeof window !== 'undefined') {
|
|
765
|
+
// (window as any).testPresetSelection = () => {
|
|
766
|
+
// console.log("๐งช Manual preset test - this would normally be called by the UI");
|
|
767
|
+
// console.log("๐งช To test properly, use the actual preset dropdown in the UI above");
|
|
768
|
+
// };
|
|
769
|
+
// }
|
|
770
|
+
// export default HonchoEditorBulkDemo;
|