@merchi/product-editor 0.1.9

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.
Files changed (123) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/README.md +174 -0
  3. package/dist/components/AlignMenu.d.ts +3 -0
  4. package/dist/components/AlignMenu.js +61 -0
  5. package/dist/components/FloatingToolbar.d.ts +4 -0
  6. package/dist/components/FloatingToolbar.js +97 -0
  7. package/dist/components/FormatButtons.d.ts +2 -0
  8. package/dist/components/FormatButtons.js +22 -0
  9. package/dist/components/ImageNavButton.d.ts +7 -0
  10. package/dist/components/ImageNavButton.js +14 -0
  11. package/dist/components/ImageZoomModal.d.ts +12 -0
  12. package/dist/components/ImageZoomModal.js +129 -0
  13. package/dist/components/LayerPanel.d.ts +4 -0
  14. package/dist/components/LayerPanel.js +223 -0
  15. package/dist/components/LoadingOverlay.d.ts +9 -0
  16. package/dist/components/LoadingOverlay.js +17 -0
  17. package/dist/components/ProductEditor.d.ts +4 -0
  18. package/dist/components/ProductEditor.js +67 -0
  19. package/dist/components/ProductImageGallery.d.ts +6 -0
  20. package/dist/components/ProductImageGallery.js +148 -0
  21. package/dist/components/ProductPreviews.d.ts +7 -0
  22. package/dist/components/ProductPreviews.js +281 -0
  23. package/dist/components/TextToolbar.d.ts +4 -0
  24. package/dist/components/TextToolbar.js +117 -0
  25. package/dist/components/Toolbar.d.ts +3 -0
  26. package/dist/components/Toolbar.js +47 -0
  27. package/dist/config/colorConfig.d.ts +1 -0
  28. package/dist/config/colorConfig.js +10 -0
  29. package/dist/config/fontConfig.d.ts +5 -0
  30. package/dist/config/fontConfig.js +21 -0
  31. package/dist/context/ProductEditorContext.d.ts +60 -0
  32. package/dist/context/ProductEditorContext.js +743 -0
  33. package/dist/hooks/useSwipeNavigate.d.ts +11 -0
  34. package/dist/hooks/useSwipeNavigate.js +35 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.js +30 -0
  37. package/dist/styles/BottomPreviewDisplay.css +65 -0
  38. package/dist/styles/FloatingToolbar.css +41 -0
  39. package/dist/styles/ImageGallery.css +147 -0
  40. package/dist/styles/ImageNavButton.css +35 -0
  41. package/dist/styles/ImageZoomModal.css +101 -0
  42. package/dist/styles/LayerPanel.css +179 -0
  43. package/dist/styles/LoadingOverlay.css +48 -0
  44. package/dist/styles/ProductEditor.css +192 -0
  45. package/dist/styles/TextToolbar.css +327 -0
  46. package/dist/styles/Toolbar.css +125 -0
  47. package/dist/styles/index.css +5 -0
  48. package/dist/styles/textToolbarSelectStyles.d.ts +3 -0
  49. package/dist/styles/textToolbarSelectStyles.js +30 -0
  50. package/dist/types.d.ts +228 -0
  51. package/dist/types.js +17 -0
  52. package/dist/utils/canvasAddText.d.ts +11 -0
  53. package/dist/utils/canvasAddText.js +66 -0
  54. package/dist/utils/canvasUtils.d.ts +37 -0
  55. package/dist/utils/canvasUtils.js +342 -0
  56. package/dist/utils/deviceUtils.d.ts +5 -0
  57. package/dist/utils/deviceUtils.js +33 -0
  58. package/dist/utils/draftTemplateUtils.d.ts +23 -0
  59. package/dist/utils/draftTemplateUtils.js +66 -0
  60. package/dist/utils/grid.d.ts +32 -0
  61. package/dist/utils/grid.js +151 -0
  62. package/dist/utils/imageUtils.d.ts +12 -0
  63. package/dist/utils/imageUtils.js +178 -0
  64. package/dist/utils/job.d.ts +40 -0
  65. package/dist/utils/job.js +115 -0
  66. package/dist/utils/keyboard.d.ts +1 -0
  67. package/dist/utils/keyboard.js +26 -0
  68. package/dist/utils/previewUtils.d.ts +9 -0
  69. package/dist/utils/previewUtils.js +39 -0
  70. package/dist/utils/psdConverter.d.ts +40 -0
  71. package/dist/utils/psdConverter.js +516 -0
  72. package/dist/utils/psdRenderUtils.d.ts +12 -0
  73. package/dist/utils/psdRenderUtils.js +465 -0
  74. package/dist/utils/renderUtils.d.ts +2 -0
  75. package/dist/utils/renderUtils.js +85 -0
  76. package/package.json +66 -0
  77. package/src/components/AlignMenu.tsx +49 -0
  78. package/src/components/FloatingToolbar.tsx +83 -0
  79. package/src/components/FormatButtons.tsx +31 -0
  80. package/src/components/ImageNavButton.tsx +23 -0
  81. package/src/components/ImageZoomModal.tsx +163 -0
  82. package/src/components/LayerPanel.tsx +256 -0
  83. package/src/components/LoadingOverlay.tsx +27 -0
  84. package/src/components/ProductEditor.tsx +104 -0
  85. package/src/components/ProductImageGallery.tsx +181 -0
  86. package/src/components/ProductPreviews.tsx +243 -0
  87. package/src/components/TextToolbar.tsx +164 -0
  88. package/src/components/Toolbar.tsx +86 -0
  89. package/src/config/colorConfig.ts +7 -0
  90. package/src/config/fontConfig.ts +23 -0
  91. package/src/context/ProductEditorContext.tsx +829 -0
  92. package/src/hooks/useSwipeNavigate.ts +40 -0
  93. package/src/index.ts +6 -0
  94. package/src/styles/BottomPreviewDisplay.css +65 -0
  95. package/src/styles/FloatingToolbar.css +41 -0
  96. package/src/styles/ImageGallery.css +147 -0
  97. package/src/styles/ImageNavButton.css +35 -0
  98. package/src/styles/ImageZoomModal.css +101 -0
  99. package/src/styles/LayerPanel.css +179 -0
  100. package/src/styles/LoadingOverlay.css +48 -0
  101. package/src/styles/ProductEditor.css +192 -0
  102. package/src/styles/TextToolbar.css +327 -0
  103. package/src/styles/Toolbar.css +125 -0
  104. package/src/styles/index.css +5 -0
  105. package/src/styles/textToolbarSelectStyles.ts +60 -0
  106. package/src/types/ag-psd/index.d.ts +1 -0
  107. package/src/types/fabric.d.ts +14 -0
  108. package/src/types/lodash/index.d.ts +1 -0
  109. package/src/types/psd.d.ts +53 -0
  110. package/src/types.ts +247 -0
  111. package/src/utils/canvasAddText.ts +72 -0
  112. package/src/utils/canvasUtils.ts +406 -0
  113. package/src/utils/deviceUtils.ts +31 -0
  114. package/src/utils/draftTemplateUtils.ts +70 -0
  115. package/src/utils/grid.ts +154 -0
  116. package/src/utils/imageUtils.ts +218 -0
  117. package/src/utils/job.ts +128 -0
  118. package/src/utils/keyboard.ts +28 -0
  119. package/src/utils/previewUtils.ts +43 -0
  120. package/src/utils/psdConverter.ts +595 -0
  121. package/src/utils/psdRenderUtils.ts +453 -0
  122. package/src/utils/renderUtils.ts +44 -0
  123. package/tsconfig.json +17 -0
@@ -0,0 +1,743 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __generator = (this && this.__generator) || function (thisArg, body) {
46
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48
+ function verb(n) { return function (v) { return step([n, v]); }; }
49
+ function step(op) {
50
+ if (f) throw new TypeError("Generator is already executing.");
51
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
52
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
53
+ if (y = 0, t) op = [op[0] & 2, t.value];
54
+ switch (op[0]) {
55
+ case 0: case 1: t = op; break;
56
+ case 4: _.label++; return { value: op[1], done: false };
57
+ case 5: _.label++; y = op[1]; op = [0]; continue;
58
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
59
+ default:
60
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64
+ if (t[2]) _.ops.pop();
65
+ _.trys.pop(); continue;
66
+ }
67
+ op = body.call(thisArg, _);
68
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70
+ }
71
+ };
72
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
73
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
74
+ if (ar || !(i in from)) {
75
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
76
+ ar[i] = from[i];
77
+ }
78
+ }
79
+ return to.concat(ar || Array.prototype.slice.call(from));
80
+ };
81
+ Object.defineProperty(exports, "__esModule", { value: true });
82
+ exports.useProductEditor = exports.ProductEditorProvider = void 0;
83
+ var react_1 = __importStar(require("react"));
84
+ var fabric_1 = require("fabric");
85
+ var grid_1 = require("../utils/grid");
86
+ var job_1 = require("../utils/job");
87
+ var renderUtils_1 = require("../utils/renderUtils");
88
+ var keyboard_1 = require("../utils/keyboard");
89
+ var draftTemplateUtils_1 = require("../utils/draftTemplateUtils");
90
+ var previewUtils_1 = require("../utils/previewUtils");
91
+ var lodash_1 = require("lodash");
92
+ var canvasUtils_1 = require("../utils/canvasUtils");
93
+ var fontConfig_1 = require("../config/fontConfig");
94
+ var colorConfig_1 = require("../config/colorConfig");
95
+ var uuid_1 = require("uuid");
96
+ var deviceUtils_1 = require("../utils/deviceUtils");
97
+ var ProductEditorContext = (0, react_1.createContext)(undefined);
98
+ var ProductEditorProvider = function (_a) {
99
+ var _b, _c;
100
+ var children = _a.children, _d = _a.groupIndex, groupIndex = _d === void 0 ? 0 : _d, product = _a.product, _e = _a.width, initialWidth = _e === void 0 ? 800 : _e, // Rename prop
101
+ _f = _a.height, // Rename prop
102
+ initialHeight = _f === void 0 ? 600 : _f, // Rename prop
103
+ job = _a.job, onSave = _a.onSave, onCancel = _a.onCancel, _g = _a.hookForm, hookForm = _g === void 0 ? null : _g, // Initialize with null
104
+ _h = _a.fontOptions, // Initialize with null
105
+ fontOptions = _h === void 0 ? fontConfig_1.defaultFontOptions : _h, _j = _a.colorPalette, colorPalette = _j === void 0 ? colorConfig_1.defaultPalette : _j;
106
+ var inputName = "ownDrafts[0].images[".concat(groupIndex || 0, "]");
107
+ // Create refs to store the latest values to prevent excessive re-renders
108
+ var allVariationsRef = (0, react_1.useRef)([]);
109
+ var productRef = (0, react_1.useRef)(product);
110
+ // Update product ref when it changes
111
+ (0, react_1.useEffect)(function () {
112
+ productRef.current = product;
113
+ }, [product]);
114
+ // Determine dimensions and mobile status once at initialization
115
+ var deviceSettings = (0, react_1.useMemo)(function () {
116
+ return (0, deviceUtils_1.getDeviceAdjustedDimensions)(initialWidth, initialHeight);
117
+ }, [initialWidth, initialHeight]);
118
+ // Use dimensions from device settings
119
+ var canvasWidth = deviceSettings.width;
120
+ var canvasHeight = deviceSettings.height;
121
+ var isMobileView = deviceSettings.isMobile;
122
+ var _k = (0, react_1.useState)([]), draftTemplates = _k[0], setDraftTemplates = _k[1];
123
+ var _l = (0, react_1.useState)([]), draftPreviews = _l[0], setDraftPreviews = _l[1];
124
+ var canvasRef = (0, react_1.useRef)(null);
125
+ var _m = (0, react_1.useState)(null), canvas = _m[0], setCanvas = _m[1];
126
+ var _o = (0, react_1.useState)(((_c = (_b = draftTemplates === null || draftTemplates === void 0 ? void 0 : draftTemplates[0]) === null || _b === void 0 ? void 0 : _b.template) === null || _c === void 0 ? void 0 : _c.id) || null), selectedTemplate = _o[0], setSelectedTemplate = _o[1];
127
+ var _p = (0, react_1.useState)(true), showGrid = _p[0], setShowGrid = _p[1];
128
+ var _q = (0, react_1.useState)(true), showPreview = _q[0], setShowPreview = _q[1];
129
+ var _r = (0, react_1.useState)(true), isCanvasLoading = _r[0], setIsCanvasLoading = _r[1];
130
+ var _s = (0, react_1.useState)(null), selectedTextObject = _s[0], setSelectedTextObject = _s[1];
131
+ var _t = (0, react_1.useState)(false), showLayerPanel = _t[0], setShowLayerPanel = _t[1];
132
+ var _u = (0, react_1.useState)(null), selectedObjectId = _u[0], setSelectedObjectId = _u[1];
133
+ // Add a new state to track saved objects
134
+ var _v = (0, react_1.useState)([]), savedObjects = _v[0], setSavedObjects = _v[1];
135
+ // Add this ref to track initial mounting
136
+ var initialMountRef = (0, react_1.useRef)(false);
137
+ // Initialize savedObjects from localStorage on component mount
138
+ (0, react_1.useEffect)(function () {
139
+ // Only reset on initial mount
140
+ if (!initialMountRef.current) {
141
+ initialMountRef.current = true;
142
+ // Reset savedObjects on page reload instead of loading from localStorage
143
+ setSavedObjects([]);
144
+ }
145
+ }, [product]);
146
+ // Function to record the current state of variation field objects
147
+ var recordVariationFieldObjectsOnCanvas = (0, react_1.useCallback)(function () {
148
+ if (!canvas || selectedTemplate === null)
149
+ return;
150
+ // Create a fresh array instead of appending to existing
151
+ var updatedSavedObjects = [];
152
+ var hasChanges = false;
153
+ // Get all objects from the canvas
154
+ var objects = canvas.getObjects();
155
+ // Filter for objects that have a fieldId property (variation field objects)
156
+ objects.forEach(function (obj) {
157
+ // Get fieldId from either direct property or from variationField
158
+ var fieldId = obj.fieldId;
159
+ if (fieldId) {
160
+ // Also save a serialized version for the savedObjects state
161
+ var updatedObject = __assign({}, obj);
162
+ if (obj instanceof fabric_1.fabric.Image) {
163
+ Object.assign(updatedObject, {
164
+ width: obj.width,
165
+ height: obj.height,
166
+ src: obj.getSrc(),
167
+ });
168
+ }
169
+ updatedSavedObjects.push(updatedObject);
170
+ hasChanges = true;
171
+ }
172
+ });
173
+ // Only update state if we found objects with fieldIds
174
+ if (hasChanges) {
175
+ // Update the savedObjects state with the new complete array
176
+ setSavedObjects(updatedSavedObjects);
177
+ }
178
+ }, [canvas, selectedTemplate]); // Remove savedObjects from the dependencies
179
+ // Function to toggle preview visibility
180
+ var togglePreview = function () {
181
+ setShowPreview(function (prev) { return !prev; });
182
+ };
183
+ // Function to toggle layer panel visibility
184
+ var toggleLayerPanel = function () {
185
+ setShowLayerPanel(function (prev) { return !prev; });
186
+ };
187
+ // Function to update selected text object properties
188
+ var updateSelectedText = function (props) {
189
+ if (selectedTextObject && canvas && selectedTemplate !== null) {
190
+ selectedTextObject.set(props);
191
+ canvas.requestRenderAll();
192
+ // Record the state changes after updating text properties
193
+ // This ensures color changes and other styling updates are saved
194
+ setTimeout(function () {
195
+ if (canvas && selectedTemplate !== null) {
196
+ recordVariationFieldObjectsOnCanvas();
197
+ }
198
+ }, 50);
199
+ }
200
+ else {
201
+ console.warn('Cannot update text - missing canvas, text object, or template');
202
+ }
203
+ };
204
+ var handleSave = function () {
205
+ if (canvas) {
206
+ var dataUrl = canvas.toDataURL();
207
+ onSave();
208
+ }
209
+ };
210
+ // Handle template change with improved state tracking
211
+ var handleTemplateChange = function (draftTemplate) { return __awaiter(void 0, void 0, void 0, function () {
212
+ var currentCanvas, containers, i, newCanvas_1, templateData;
213
+ return __generator(this, function (_a) {
214
+ switch (_a.label) {
215
+ case 0:
216
+ if (!canvas)
217
+ return [2 /*return*/];
218
+ // Record the current state of variation field objects
219
+ recordVariationFieldObjectsOnCanvas();
220
+ // Check if this is already the selected template - prevent reloading the same template
221
+ if (draftTemplate.id && selectedTemplate === draftTemplate.id) {
222
+ return [2 /*return*/];
223
+ }
224
+ // Update the selected template
225
+ if (draftTemplate.id) {
226
+ setSelectedTemplate(draftTemplate.id);
227
+ }
228
+ currentCanvas = canvas;
229
+ if (!canvasRef.current) return [3 /*break*/, 2];
230
+ // Dispose the old canvas properly
231
+ try {
232
+ currentCanvas.dispose();
233
+ // Remove any lingering canvas containers to prevent duplicates
234
+ if (canvasRef.current.parentElement) {
235
+ containers = canvasRef.current.parentElement.querySelectorAll('.canvas-container');
236
+ if (containers.length > 1) {
237
+ // Keep only the first container
238
+ for (i = 1; i < containers.length; i++) {
239
+ containers[i].remove();
240
+ }
241
+ }
242
+ }
243
+ }
244
+ catch (e) {
245
+ console.error('Error during canvas cleanup:', e);
246
+ }
247
+ newCanvas_1 = new fabric_1.fabric.Canvas(canvasRef.current, {
248
+ width: canvasWidth,
249
+ height: canvasHeight,
250
+ backgroundColor: '#ffffff',
251
+ // not to bring the selected object to the top visually
252
+ preserveObjectStacking: true
253
+ });
254
+ templateData = draftTemplates.find(function (dt) { return dt.template.id === draftTemplate.id; });
255
+ return [4 /*yield*/, (0, renderUtils_1.renderEditorOrPreview)(newCanvas_1, templateData.template, templateData.variationObjects, savedObjects, canvasHeight, canvasWidth)];
256
+ case 1:
257
+ _a.sent();
258
+ // Update the state with the new canvas
259
+ setCanvas(newCanvas_1);
260
+ // Since selectedTemplate was just updated, we need a slight delay to ensure
261
+ // grid is drawn and loading is set to false
262
+ setTimeout(function () {
263
+ // Draw grid if needed
264
+ if (showGrid) {
265
+ try {
266
+ if (newCanvas_1.getElement() && newCanvas_1.getElement().parentNode) {
267
+ (0, grid_1.drawGrid)(newCanvas_1, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid);
268
+ }
269
+ }
270
+ catch (e) {
271
+ console.error('Error drawing grid after template change:', e);
272
+ }
273
+ }
274
+ setIsCanvasLoading(false);
275
+ }, 50);
276
+ // Re-setup keyboard events for the new canvas
277
+ (0, keyboard_1.setupKeyboardEvents)(newCanvas_1, function (dataUrl) {
278
+ if (document.activeElement === newCanvas_1.upperCanvasEl) {
279
+ onSave && onSave();
280
+ }
281
+ });
282
+ _a.label = 2;
283
+ case 2: return [2 /*return*/];
284
+ }
285
+ });
286
+ }); };
287
+ // Add cleanup event handlers that properly use the callback function
288
+ var createEventHandler = (0, react_1.useCallback)(function (callback) {
289
+ return function () {
290
+ if (callback)
291
+ callback();
292
+ };
293
+ }, []);
294
+ // Replace the direct watch with a debounced version
295
+ var debouncedWatch = (0, react_1.useMemo)(function () {
296
+ return (0, lodash_1.debounce)(function (watchValues) { return __awaiter(void 0, void 0, void 0, function () {
297
+ var variationsGroups, variations, newAllVariations, newDraftTemplates, currentSelectedIdStillExists, fabricCanvasInstance, templateToLoad, previouslySelected, templateData;
298
+ var _a, _b;
299
+ return __generator(this, function (_c) {
300
+ switch (_c.label) {
301
+ case 0:
302
+ // Start loading
303
+ setIsCanvasLoading(true);
304
+ // Record the current state of variation field objects before changes
305
+ if (canvas) {
306
+ recordVariationFieldObjectsOnCanvas();
307
+ }
308
+ variationsGroups = watchValues.variationsGroups;
309
+ variations = watchValues.variations;
310
+ newAllVariations = ((_a = variationsGroups === null || variationsGroups === void 0 ? void 0 : variationsGroups[groupIndex]) === null || _a === void 0 ? void 0 : _a.variations)
311
+ ? __spreadArray([], variationsGroups[groupIndex].variations, true) : variations || [];
312
+ // Store in ref to avoid triggering effects
313
+ allVariationsRef.current = newAllVariations;
314
+ newDraftTemplates = (0, job_1.initDraftTemplates)(newAllVariations, productRef.current);
315
+ currentSelectedIdStillExists = newDraftTemplates.some(function (dt) { return dt.template.id === selectedTemplate; });
316
+ fabricCanvasInstance = canvas;
317
+ // Only update if they've actually changed
318
+ if ((0, draftTemplateUtils_1.haveDraftTemplatesChanged)(draftTemplates, newDraftTemplates)) {
319
+ setDraftTemplates(newDraftTemplates);
320
+ setDraftPreviews((0, previewUtils_1.setNewDraftPreviews)(newDraftTemplates));
321
+ }
322
+ templateToLoad = (_b = newDraftTemplates[0]) === null || _b === void 0 ? void 0 : _b.template;
323
+ if (currentSelectedIdStillExists && selectedTemplate) {
324
+ previouslySelected = newDraftTemplates.find(function (dt) { return dt.template.id === selectedTemplate; });
325
+ templateToLoad = (previouslySelected === null || previouslySelected === void 0 ? void 0 : previouslySelected.template) || templateToLoad;
326
+ }
327
+ setSelectedTemplate((templateToLoad === null || templateToLoad === void 0 ? void 0 : templateToLoad.id) || null);
328
+ // Handle canvas setup
329
+ if (fabricCanvasInstance) {
330
+ // if we have a canvas we need to clear all the old templates
331
+ fabricCanvasInstance.clear();
332
+ fabricCanvasInstance.setBackgroundColor('#ffffff', function () { return fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.renderAll(); });
333
+ }
334
+ else if (canvasRef.current) {
335
+ fabricCanvasInstance = new fabric_1.fabric.Canvas(canvasRef.current, {
336
+ width: canvasWidth,
337
+ height: canvasHeight,
338
+ backgroundColor: '#ffffff',
339
+ preserveObjectStacking: true
340
+ });
341
+ // Update state immediately to ensure it's available for event handlers
342
+ setCanvas(fabricCanvasInstance);
343
+ }
344
+ else {
345
+ console.error('No canvas ref available');
346
+ setIsCanvasLoading(false);
347
+ return [2 /*return*/];
348
+ }
349
+ if (!(templateToLoad && templateToLoad.id)) return [3 /*break*/, 2];
350
+ templateData = newDraftTemplates.find(function (dt) { return dt.template.id === templateToLoad.id; });
351
+ // Render the template onto the canvas
352
+ return [4 /*yield*/, (0, renderUtils_1.renderEditorOrPreview)(fabricCanvasInstance, (templateData === null || templateData === void 0 ? void 0 : templateData.template) || {}, (templateData === null || templateData === void 0 ? void 0 : templateData.variationObjects) || [], savedObjects, canvasHeight, canvasWidth)];
353
+ case 1:
354
+ // Render the template onto the canvas
355
+ _c.sent();
356
+ if (fabricCanvasInstance.getElement() && fabricCanvasInstance.getElement().parentNode) {
357
+ (0, grid_1.drawGrid)(fabricCanvasInstance, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid);
358
+ }
359
+ setIsCanvasLoading(false);
360
+ return [3 /*break*/, 3];
361
+ case 2:
362
+ setIsCanvasLoading(false);
363
+ _c.label = 3;
364
+ case 3: return [2 /*return*/];
365
+ }
366
+ });
367
+ }); }, 500);
368
+ }, [
369
+ groupIndex,
370
+ draftTemplates,
371
+ canvasRef,
372
+ showGrid,
373
+ canvasWidth,
374
+ canvasHeight,
375
+ savedObjects,
376
+ canvas,
377
+ selectedTemplate,
378
+ recordVariationFieldObjectsOnCanvas,
379
+ setSelectedTemplate
380
+ ]);
381
+ // Set up the debounced watch subscription
382
+ (0, react_1.useEffect)(function () {
383
+ if (!hookForm)
384
+ return;
385
+ // Subscribe to form changes
386
+ var subscription = hookForm.watch(function (value) {
387
+ debouncedWatch({
388
+ variationsGroups: value.variationsGroups,
389
+ variations: value.variations,
390
+ });
391
+ });
392
+ // Clean up subscription
393
+ return function () { return subscription.unsubscribe(); };
394
+ }, [hookForm, debouncedWatch]);
395
+ // This effect is used to trigger the debounced watch when the selected template is not set
396
+ // This helps with things like add group.
397
+ (0, react_1.useEffect)(function () {
398
+ if (!selectedTemplate) {
399
+ debouncedWatch({
400
+ variationsGroups: job.variationsGroups,
401
+ variations: job.variations,
402
+ });
403
+ }
404
+ }, [groupIndex]);
405
+ var _w = (0, react_1.useState)(false), loadingPreviews = _w[0], setLoadingPreviews = _w[1];
406
+ var _x = (0, react_1.useState)([]), renderedDraftPreviews = _x[0], setRenderedDraftPreviews = _x[1];
407
+ // Extract event handler setup to a separate function to avoid recreating in debounced watch
408
+ var setupEventHandlers = function (fabricCanvasInstance, selectedTemplate) {
409
+ if (!fabricCanvasInstance)
410
+ return function () { };
411
+ // Add a flag to prevent recursive calls
412
+ var isRendering = false;
413
+ // Define the record changes function with canvas and template closure
414
+ var recordChanges = function () {
415
+ // Use the passed instance and template ID instead of state variables
416
+ if (fabricCanvasInstance) {
417
+ var objects = fabricCanvasInstance.getObjects();
418
+ var updatedSavedObjects_1 = [];
419
+ var hasChanges_1 = false;
420
+ objects.forEach(function (obj) {
421
+ var fieldId = obj.fieldId;
422
+ if (fieldId) {
423
+ var updatedObject = __assign({}, obj);
424
+ if (obj instanceof fabric_1.fabric.Image) {
425
+ Object.assign(updatedObject, {
426
+ width: obj.width,
427
+ height: obj.height,
428
+ src: obj.getSrc(),
429
+ });
430
+ }
431
+ updatedSavedObjects_1.push(updatedObject);
432
+ hasChanges_1 = true;
433
+ }
434
+ });
435
+ if (hasChanges_1) {
436
+ setSavedObjects(updatedSavedObjects_1);
437
+ }
438
+ }
439
+ else {
440
+ console.warn('Cannot record changes - missing canvas or templateId');
441
+ }
442
+ };
443
+ // Create a bound version for event handling
444
+ var boundRecordChanges = createEventHandler(recordChanges);
445
+ // Add Text Toolbar Event Listener
446
+ var handleSelection = function (e) {
447
+ var activeObject = fabricCanvasInstance.getActiveObject();
448
+ // Check if the active object is an IText instance
449
+ // Ensure the selected object has an ID
450
+ if (activeObject && !activeObject.id) {
451
+ activeObject.id = (0, uuid_1.v4)();
452
+ }
453
+ setSelectedObjectId((activeObject === null || activeObject === void 0 ? void 0 : activeObject.id) || null);
454
+ if (activeObject instanceof fabric_1.fabric.IText) {
455
+ setSelectedTextObject(activeObject);
456
+ }
457
+ else {
458
+ setSelectedTextObject(null);
459
+ }
460
+ };
461
+ var handleSelectionCleared = function (e) {
462
+ setSelectedObjectId(null); // Clear selection in context
463
+ setSelectedTextObject(null);
464
+ };
465
+ var updateRenderedDraftPreviews = function () { return __awaiter(void 0, void 0, void 0, function () {
466
+ var templateId, renderedImage, fullCanvasImage, activeTemplateIds, previews, existingPreviewIndex, productDraftTemplate, storedData;
467
+ return __generator(this, function (_a) {
468
+ switch (_a.label) {
469
+ case 0:
470
+ templateId = selectedTemplate;
471
+ return [4 /*yield*/, (0, canvasUtils_1.renderClippedImage)(fabricCanvasInstance, { format: 'png' })];
472
+ case 1:
473
+ renderedImage = _a.sent();
474
+ return [4 /*yield*/, (0, canvasUtils_1.renderCanvasWithoutGrid)(fabricCanvasInstance, { format: 'png' })];
475
+ case 2:
476
+ fullCanvasImage = _a.sent();
477
+ activeTemplateIds = draftTemplates.map(function (dt) { return dt.template.id; });
478
+ if (renderedImage && templateId) {
479
+ previews = __spreadArray([], renderedDraftPreviews.filter(function (rdp) { return activeTemplateIds.includes(rdp.templateId); }), true);
480
+ existingPreviewIndex = previews.findIndex(function (preview) { return preview.templateId === templateId; });
481
+ if (existingPreviewIndex !== -1) {
482
+ // Update existing preview
483
+ previews[existingPreviewIndex] = {
484
+ templateId: templateId,
485
+ draft: renderedImage,
486
+ canvasPreview: fullCanvasImage || '',
487
+ };
488
+ }
489
+ else {
490
+ // Add new preview
491
+ previews.push({
492
+ templateId: templateId,
493
+ draft: renderedImage,
494
+ canvasPreview: fullCanvasImage || '',
495
+ });
496
+ }
497
+ productDraftTemplate = localStorage.getItem("productDraftTemplate-".concat(product.id));
498
+ storedData = productDraftTemplate ? JSON.parse(productDraftTemplate) : [];
499
+ storedData[groupIndex] = {
500
+ groupIndex: groupIndex,
501
+ productId: product.id,
502
+ templateData: __spreadArray([], previews, true),
503
+ previews: __spreadArray([], previews, true),
504
+ };
505
+ localStorage.setItem("productDraftTemplate-".concat(product.id), JSON.stringify(storedData));
506
+ setRenderedDraftPreviews(previews);
507
+ setLoadingPreviews(false);
508
+ }
509
+ return [2 /*return*/];
510
+ }
511
+ });
512
+ }); };
513
+ // Add after:render event listener to call renderClippedImage
514
+ var handleAfterRender = function () { return __awaiter(void 0, void 0, void 0, function () {
515
+ var error_1;
516
+ return __generator(this, function (_a) {
517
+ switch (_a.label) {
518
+ case 0:
519
+ // Prevent recursive calls
520
+ if (isRendering)
521
+ return [2 /*return*/];
522
+ _a.label = 1;
523
+ case 1:
524
+ _a.trys.push([1, 3, 4, 5]);
525
+ isRendering = true;
526
+ setLoadingPreviews(true);
527
+ return [4 /*yield*/, updateRenderedDraftPreviews()];
528
+ case 2:
529
+ _a.sent();
530
+ return [3 /*break*/, 5];
531
+ case 3:
532
+ error_1 = _a.sent();
533
+ console.error('Error rendering clipped image after render:', error_1);
534
+ return [3 /*break*/, 5];
535
+ case 4:
536
+ isRendering = false;
537
+ return [7 /*endfinally*/];
538
+ case 5: return [2 /*return*/];
539
+ }
540
+ });
541
+ }); };
542
+ // Add unique ID to objects if they don't have one
543
+ var ensureObjectIds = function (canvasInstance) {
544
+ canvasInstance.getObjects().forEach(function (obj) {
545
+ if (!obj.id) {
546
+ obj.id = (0, uuid_1.v4)();
547
+ }
548
+ });
549
+ };
550
+ // Ensure initial objects have IDs
551
+ if (fabricCanvasInstance) {
552
+ ensureObjectIds(fabricCanvasInstance);
553
+ }
554
+ // Attach listeners
555
+ fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:created', handleSelection);
556
+ fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:updated', handleSelection);
557
+ fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:cleared', handleSelectionCleared);
558
+ // Also update IDs when new objects are added
559
+ fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('object:added', function (e) {
560
+ if (e.target && !e.target.id) {
561
+ e.target.id = (0, uuid_1.v4)();
562
+ }
563
+ });
564
+ // setup keyboard events
565
+ var cleanupKeyboardEvents = (0, keyboard_1.setupKeyboardEvents)(fabricCanvasInstance, function () {
566
+ if (document.activeElement === fabricCanvasInstance.upperCanvasEl) {
567
+ onSave && onSave();
568
+ }
569
+ });
570
+ fabricCanvasInstance.on('selection:created', handleSelection);
571
+ fabricCanvasInstance.on('selection:updated', handleSelection);
572
+ fabricCanvasInstance.on('selection:cleared', handleSelectionCleared);
573
+ fabricCanvasInstance.on('after:render', handleAfterRender);
574
+ // Add event listeners for object modifications
575
+ fabricCanvasInstance.on('object:modified', boundRecordChanges);
576
+ fabricCanvasInstance.on('object:moved', boundRecordChanges);
577
+ fabricCanvasInstance.on('object:scaled', boundRecordChanges);
578
+ fabricCanvasInstance.on('object:rotated', boundRecordChanges);
579
+ fabricCanvasInstance.on('text:changed', boundRecordChanges);
580
+ // Add handlers for more specific events that might be triggered by color changes
581
+ fabricCanvasInstance.on('object:changed', boundRecordChanges);
582
+ fabricCanvasInstance.on('canvas:updated', boundRecordChanges);
583
+ // Return cleanup function
584
+ return function () {
585
+ fabricCanvasInstance.off('selection:created', handleSelection);
586
+ fabricCanvasInstance.off('selection:updated', handleSelection);
587
+ fabricCanvasInstance.off('selection:cleared', handleSelectionCleared);
588
+ fabricCanvasInstance.off('after:render', handleAfterRender);
589
+ fabricCanvasInstance.off('object:modified', boundRecordChanges);
590
+ fabricCanvasInstance.off('object:moved', boundRecordChanges);
591
+ fabricCanvasInstance.off('object:scaled', boundRecordChanges);
592
+ fabricCanvasInstance.off('object:rotated', boundRecordChanges);
593
+ fabricCanvasInstance.off('text:changed', boundRecordChanges);
594
+ fabricCanvasInstance.off('object:changed', boundRecordChanges);
595
+ fabricCanvasInstance.off('canvas:updated', boundRecordChanges);
596
+ cleanupKeyboardEvents();
597
+ };
598
+ };
599
+ // Move setupEventHandlers call to a useEffect hook
600
+ (0, react_1.useEffect)(function () {
601
+ if (canvas) {
602
+ try {
603
+ if (canvas.getElement() && canvas.getElement().parentNode) {
604
+ (0, grid_1.drawGrid)(canvas, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid);
605
+ canvas.renderAll();
606
+ }
607
+ }
608
+ catch (error) {
609
+ console.error('Error drawing/clearing grid:', error);
610
+ }
611
+ // Setup event handlers
612
+ var cleanupEventHandlers_1 = setupEventHandlers(canvas, selectedTemplate);
613
+ return function () {
614
+ cleanupEventHandlers_1();
615
+ };
616
+ }
617
+ }, [canvas, selectedTemplate, showGrid, canvasWidth, canvasHeight]);
618
+ var selectObject = function (obj) {
619
+ if (canvas) {
620
+ if (obj) {
621
+ if (!obj.id) {
622
+ obj.id = (0, uuid_1.v4)();
623
+ }
624
+ canvas.setActiveObject(obj);
625
+ setSelectedObjectId(obj.id);
626
+ }
627
+ else {
628
+ canvas.discardActiveObject();
629
+ setSelectedObjectId(null);
630
+ }
631
+ canvas.requestRenderAll();
632
+ }
633
+ };
634
+ var deleteObject = (0, react_1.useCallback)(function (obj) {
635
+ if (!canvas || !obj)
636
+ return;
637
+ try {
638
+ setIsCanvasLoading(true);
639
+ // remove the object from the canvas
640
+ canvas.remove(obj);
641
+ canvas.renderAll();
642
+ var fieldId_1 = obj === null || obj === void 0 ? void 0 : obj.fieldId;
643
+ var fileId_1 = obj === null || obj === void 0 ? void 0 : obj.fileId;
644
+ if (fieldId_1) {
645
+ var currentTemplateId_1 = selectedTemplate;
646
+ // update draftTemplates
647
+ var updatedDraftTemplates = draftTemplates.map(function (dt) {
648
+ if (dt.template.id === currentTemplateId_1) {
649
+ return __assign(__assign({}, dt), { variationObjects: dt.variationObjects.filter(function (vo) { return vo.fieldId !== fieldId_1; }) });
650
+ }
651
+ return dt;
652
+ });
653
+ setDraftTemplates(updatedDraftTemplates);
654
+ // update the form state
655
+ if (hookForm) {
656
+ var hasVariationsGroups = 'variationsGroups' in hookForm.getValues();
657
+ var variationsField = hasVariationsGroups ?
658
+ "variationsGroups.".concat(groupIndex, ".variations") : 'variations';
659
+ var values = hookForm.getValues(variationsField) || [];
660
+ var updatedValues = values.map(function (variation) {
661
+ var _a, _b;
662
+ if (((_a = variation === null || variation === void 0 ? void 0 : variation.variationField) === null || _a === void 0 ? void 0 : _a.id) === fieldId_1) {
663
+ // file type variation and has fileId
664
+ // only delete the specific file
665
+ if (((_b = variation.variationFiles) === null || _b === void 0 ? void 0 : _b.length) > 0 && fileId_1) {
666
+ var updatedFiles = variation.variationFiles.filter(function (file) { return file.id !== fileId_1; });
667
+ return __assign(__assign({}, variation), { value: updatedFiles.length === 0 ? null : variation.value, variationFiles: updatedFiles });
668
+ }
669
+ // non file type or no fileId
670
+ // clear the whole field
671
+ else if (!fileId_1) {
672
+ return __assign(__assign({}, variation), { value: null, variationFiles: variation.variationFiles ? [] : undefined });
673
+ }
674
+ }
675
+ return variation;
676
+ });
677
+ hookForm.setValue(variationsField, updatedValues);
678
+ }
679
+ }
680
+ // clear the selection state
681
+ setSelectedObjectId(null);
682
+ setSelectedTextObject(null);
683
+ setIsCanvasLoading(false);
684
+ onSave && onSave();
685
+ }
686
+ catch (error) {
687
+ console.error('Error in deleteObject:', error);
688
+ setIsCanvasLoading(false);
689
+ }
690
+ }, [canvas, draftTemplates, selectedTemplate, hookForm, groupIndex, onSave]);
691
+ return (react_1.default.createElement(ProductEditorContext.Provider, { value: {
692
+ // States
693
+ canvas: canvas,
694
+ draftPreviews: draftPreviews,
695
+ draftTemplates: draftTemplates,
696
+ isCanvasLoading: isCanvasLoading,
697
+ isMobileView: isMobileView,
698
+ loadingPreviews: loadingPreviews,
699
+ renderedDraftPreviews: renderedDraftPreviews,
700
+ savedObjects: savedObjects,
701
+ selectedTemplate: selectedTemplate,
702
+ selectedTextObject: selectedTextObject,
703
+ showGrid: showGrid,
704
+ showPreview: showPreview,
705
+ // State Setters
706
+ setCanvas: setCanvas,
707
+ setSelectedTemplate: setSelectedTemplate,
708
+ setShowGrid: setShowGrid,
709
+ // Functions
710
+ handleCancel: function () { return onCancel(); },
711
+ handleSave: handleSave,
712
+ handleTemplateChange: handleTemplateChange,
713
+ recordVariationFieldObjectsOnCanvas: recordVariationFieldObjectsOnCanvas,
714
+ togglePreview: togglePreview,
715
+ updateSelectedText: updateSelectedText,
716
+ fontOptions: fontOptions,
717
+ colorPalette: colorPalette,
718
+ showLayerPanel: showLayerPanel,
719
+ toggleLayerPanel: toggleLayerPanel,
720
+ selectedObjectId: selectedObjectId,
721
+ setSelectedObjectId: setSelectedObjectId,
722
+ selectObject: selectObject,
723
+ deleteObject: deleteObject,
724
+ // Props
725
+ canvasRef: canvasRef,
726
+ groupIndex: groupIndex,
727
+ height: canvasHeight,
728
+ inputName: inputName,
729
+ job: job,
730
+ product: product,
731
+ width: canvasWidth,
732
+ hookForm: hookForm,
733
+ } }, children));
734
+ };
735
+ exports.ProductEditorProvider = ProductEditorProvider;
736
+ var useProductEditor = function () {
737
+ var context = (0, react_1.useContext)(ProductEditorContext);
738
+ if (context === undefined) {
739
+ throw new Error('useProductEditor must be used within a ProductEditorProvider');
740
+ }
741
+ return context;
742
+ };
743
+ exports.useProductEditor = useProductEditor;