@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.
- package/.turbo/turbo-build.log +1 -0
- package/README.md +174 -0
- package/dist/components/AlignMenu.d.ts +3 -0
- package/dist/components/AlignMenu.js +61 -0
- package/dist/components/FloatingToolbar.d.ts +4 -0
- package/dist/components/FloatingToolbar.js +97 -0
- package/dist/components/FormatButtons.d.ts +2 -0
- package/dist/components/FormatButtons.js +22 -0
- package/dist/components/ImageNavButton.d.ts +7 -0
- package/dist/components/ImageNavButton.js +14 -0
- package/dist/components/ImageZoomModal.d.ts +12 -0
- package/dist/components/ImageZoomModal.js +129 -0
- package/dist/components/LayerPanel.d.ts +4 -0
- package/dist/components/LayerPanel.js +223 -0
- package/dist/components/LoadingOverlay.d.ts +9 -0
- package/dist/components/LoadingOverlay.js +17 -0
- package/dist/components/ProductEditor.d.ts +4 -0
- package/dist/components/ProductEditor.js +67 -0
- package/dist/components/ProductImageGallery.d.ts +6 -0
- package/dist/components/ProductImageGallery.js +148 -0
- package/dist/components/ProductPreviews.d.ts +7 -0
- package/dist/components/ProductPreviews.js +281 -0
- package/dist/components/TextToolbar.d.ts +4 -0
- package/dist/components/TextToolbar.js +117 -0
- package/dist/components/Toolbar.d.ts +3 -0
- package/dist/components/Toolbar.js +47 -0
- package/dist/config/colorConfig.d.ts +1 -0
- package/dist/config/colorConfig.js +10 -0
- package/dist/config/fontConfig.d.ts +5 -0
- package/dist/config/fontConfig.js +21 -0
- package/dist/context/ProductEditorContext.d.ts +60 -0
- package/dist/context/ProductEditorContext.js +743 -0
- package/dist/hooks/useSwipeNavigate.d.ts +11 -0
- package/dist/hooks/useSwipeNavigate.js +35 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +30 -0
- package/dist/styles/BottomPreviewDisplay.css +65 -0
- package/dist/styles/FloatingToolbar.css +41 -0
- package/dist/styles/ImageGallery.css +147 -0
- package/dist/styles/ImageNavButton.css +35 -0
- package/dist/styles/ImageZoomModal.css +101 -0
- package/dist/styles/LayerPanel.css +179 -0
- package/dist/styles/LoadingOverlay.css +48 -0
- package/dist/styles/ProductEditor.css +192 -0
- package/dist/styles/TextToolbar.css +327 -0
- package/dist/styles/Toolbar.css +125 -0
- package/dist/styles/index.css +5 -0
- package/dist/styles/textToolbarSelectStyles.d.ts +3 -0
- package/dist/styles/textToolbarSelectStyles.js +30 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.js +17 -0
- package/dist/utils/canvasAddText.d.ts +11 -0
- package/dist/utils/canvasAddText.js +66 -0
- package/dist/utils/canvasUtils.d.ts +37 -0
- package/dist/utils/canvasUtils.js +342 -0
- package/dist/utils/deviceUtils.d.ts +5 -0
- package/dist/utils/deviceUtils.js +33 -0
- package/dist/utils/draftTemplateUtils.d.ts +23 -0
- package/dist/utils/draftTemplateUtils.js +66 -0
- package/dist/utils/grid.d.ts +32 -0
- package/dist/utils/grid.js +151 -0
- package/dist/utils/imageUtils.d.ts +12 -0
- package/dist/utils/imageUtils.js +178 -0
- package/dist/utils/job.d.ts +40 -0
- package/dist/utils/job.js +115 -0
- package/dist/utils/keyboard.d.ts +1 -0
- package/dist/utils/keyboard.js +26 -0
- package/dist/utils/previewUtils.d.ts +9 -0
- package/dist/utils/previewUtils.js +39 -0
- package/dist/utils/psdConverter.d.ts +40 -0
- package/dist/utils/psdConverter.js +516 -0
- package/dist/utils/psdRenderUtils.d.ts +12 -0
- package/dist/utils/psdRenderUtils.js +465 -0
- package/dist/utils/renderUtils.d.ts +2 -0
- package/dist/utils/renderUtils.js +85 -0
- package/package.json +66 -0
- package/src/components/AlignMenu.tsx +49 -0
- package/src/components/FloatingToolbar.tsx +83 -0
- package/src/components/FormatButtons.tsx +31 -0
- package/src/components/ImageNavButton.tsx +23 -0
- package/src/components/ImageZoomModal.tsx +163 -0
- package/src/components/LayerPanel.tsx +256 -0
- package/src/components/LoadingOverlay.tsx +27 -0
- package/src/components/ProductEditor.tsx +104 -0
- package/src/components/ProductImageGallery.tsx +181 -0
- package/src/components/ProductPreviews.tsx +243 -0
- package/src/components/TextToolbar.tsx +164 -0
- package/src/components/Toolbar.tsx +86 -0
- package/src/config/colorConfig.ts +7 -0
- package/src/config/fontConfig.ts +23 -0
- package/src/context/ProductEditorContext.tsx +829 -0
- package/src/hooks/useSwipeNavigate.ts +40 -0
- package/src/index.ts +6 -0
- package/src/styles/BottomPreviewDisplay.css +65 -0
- package/src/styles/FloatingToolbar.css +41 -0
- package/src/styles/ImageGallery.css +147 -0
- package/src/styles/ImageNavButton.css +35 -0
- package/src/styles/ImageZoomModal.css +101 -0
- package/src/styles/LayerPanel.css +179 -0
- package/src/styles/LoadingOverlay.css +48 -0
- package/src/styles/ProductEditor.css +192 -0
- package/src/styles/TextToolbar.css +327 -0
- package/src/styles/Toolbar.css +125 -0
- package/src/styles/index.css +5 -0
- package/src/styles/textToolbarSelectStyles.ts +60 -0
- package/src/types/ag-psd/index.d.ts +1 -0
- package/src/types/fabric.d.ts +14 -0
- package/src/types/lodash/index.d.ts +1 -0
- package/src/types/psd.d.ts +53 -0
- package/src/types.ts +247 -0
- package/src/utils/canvasAddText.ts +72 -0
- package/src/utils/canvasUtils.ts +406 -0
- package/src/utils/deviceUtils.ts +31 -0
- package/src/utils/draftTemplateUtils.ts +70 -0
- package/src/utils/grid.ts +154 -0
- package/src/utils/imageUtils.ts +218 -0
- package/src/utils/job.ts +128 -0
- package/src/utils/keyboard.ts +28 -0
- package/src/utils/previewUtils.ts +43 -0
- package/src/utils/psdConverter.ts +595 -0
- package/src/utils/psdRenderUtils.ts +453 -0
- package/src/utils/renderUtils.ts +44 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { fabric } from 'fabric';
|
|
2
|
+
import { DraftTemplate } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Loads a template image onto a canvas with enhanced error handling and fallbacks
|
|
6
|
+
*
|
|
7
|
+
* @param fabricCanvas The Fabric.js canvas instance
|
|
8
|
+
* @param template The template containing image information
|
|
9
|
+
* @param width The canvas width
|
|
10
|
+
* @param height The canvas height
|
|
11
|
+
* @returns Promise that resolves when the image is loaded
|
|
12
|
+
*/
|
|
13
|
+
export const loadRegularImagePromise = (
|
|
14
|
+
fabricCanvas: fabric.Canvas,
|
|
15
|
+
template: DraftTemplate,
|
|
16
|
+
width: number,
|
|
17
|
+
height: number,
|
|
18
|
+
): Promise<void> => {
|
|
19
|
+
// Get the canvas ID for tracking if it exists
|
|
20
|
+
const canvasId = (fabricCanvas as any).loadingId;
|
|
21
|
+
const trackingEnabled = !!canvasId;
|
|
22
|
+
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
// Check if canvas is still valid when tracking is enabled
|
|
25
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
26
|
+
resolve();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Make sure template has a file with a viewUrl
|
|
31
|
+
if (!template.file || !template.file.viewUrl) {
|
|
32
|
+
console.error('Template has no viewUrl:', template);
|
|
33
|
+
resolve();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const imageUrl = template.file.viewUrl;
|
|
38
|
+
|
|
39
|
+
// Check if this might be a problematic file format
|
|
40
|
+
const isPossiblyUnsupported = !imageUrl.match(/\.(jpg|jpeg|png|gif|svg|webp)$/i);
|
|
41
|
+
|
|
42
|
+
// If we have a thumbnailUrl or previewUrl, use that instead for unsupported formats
|
|
43
|
+
let urlToUse = imageUrl;
|
|
44
|
+
if (isPossiblyUnsupported) {
|
|
45
|
+
|
|
46
|
+
// Try to use alternative URLs if available
|
|
47
|
+
if (template.file?.thumbnailUrl) {
|
|
48
|
+
urlToUse = template.file.thumbnailUrl;
|
|
49
|
+
} else if (template.file?.previewUrl) {
|
|
50
|
+
urlToUse = template.file.previewUrl;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add error handling for image loading
|
|
55
|
+
fabric.Image.fromURL(
|
|
56
|
+
urlToUse,
|
|
57
|
+
(img: fabric.Image) => {
|
|
58
|
+
// Check if canvas is still valid when tracking is enabled
|
|
59
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
60
|
+
console.warn('Canvas changed after image loaded, aborting');
|
|
61
|
+
resolve();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!img || !img.width || !img.height) {
|
|
66
|
+
console.error('Failed to load image or image has invalid dimensions:', img);
|
|
67
|
+
// Try loading the image with a different approach - create an HTML image first
|
|
68
|
+
const imgElement = new Image();
|
|
69
|
+
imgElement.crossOrigin = 'anonymous';
|
|
70
|
+
imgElement.onload = () => {
|
|
71
|
+
// Check if canvas is still valid when tracking is enabled
|
|
72
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
73
|
+
console.warn('Canvas changed after alternative image loaded, aborting');
|
|
74
|
+
resolve();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const fabricImg = new fabric.Image(imgElement);
|
|
79
|
+
|
|
80
|
+
// Continue with the same scaling and positioning logic
|
|
81
|
+
const scale = Math.min(
|
|
82
|
+
width / fabricImg.width!,
|
|
83
|
+
height / fabricImg.height!
|
|
84
|
+
);
|
|
85
|
+
fabricImg.scale(scale);
|
|
86
|
+
|
|
87
|
+
fabricImg.set({
|
|
88
|
+
left: (width - fabricImg.width! * scale) / 2,
|
|
89
|
+
top: (height - fabricImg.height! * scale) / 2,
|
|
90
|
+
selectable: false,
|
|
91
|
+
evented: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Check again before adding to canvas when tracking is enabled
|
|
95
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
96
|
+
console.warn('Canvas changed before adding alternative image, aborting');
|
|
97
|
+
resolve();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fabricCanvas.add(fabricImg);
|
|
102
|
+
fabricCanvas.sendToBack(fabricImg);
|
|
103
|
+
|
|
104
|
+
// Final check before render when tracking is enabled
|
|
105
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
106
|
+
console.warn('Canvas changed before rendering alternative image, aborting');
|
|
107
|
+
resolve();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
fabricCanvas.renderAll();
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.error('Error rendering canvas with alternative image:', e);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
resolve();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
imgElement.onerror = () => {
|
|
121
|
+
// Check if canvas is still valid when tracking is enabled
|
|
122
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
123
|
+
console.warn('Canvas changed after image load error, aborting');
|
|
124
|
+
resolve();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.error('Alternative loading also failed. Using placeholder image.');
|
|
129
|
+
// Use a placeholder SVG as absolute fallback
|
|
130
|
+
const placeholderURL = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22600%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20600%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_1891%20text%20%7Bfill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A40pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder%22%3E%3Crect%20width%3D%22800%22%20height%3D%22600%22%20fill%3D%22%23EEEEEE%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285%22%20y%3D%22300%22%3EThumbnail%20Unavailable%3C%2Ftext%3E%3Ctext%20x%3D%22205%22%20y%3D%22350%22%3ETemplate%3A%20' + template.name + '%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
|
|
131
|
+
fabric.Image.fromURL(placeholderURL, (placeholderImg) => {
|
|
132
|
+
// Check if canvas is still valid when tracking is enabled
|
|
133
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
134
|
+
console.warn('Canvas changed before adding placeholder image, aborting');
|
|
135
|
+
resolve();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
placeholderImg.set({
|
|
140
|
+
left: 0,
|
|
141
|
+
top: 0,
|
|
142
|
+
width: width,
|
|
143
|
+
height: height,
|
|
144
|
+
selectable: false,
|
|
145
|
+
evented: false,
|
|
146
|
+
});
|
|
147
|
+
fabricCanvas.add(placeholderImg);
|
|
148
|
+
|
|
149
|
+
// Final check before render when tracking is enabled
|
|
150
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
151
|
+
console.warn('Canvas changed before rendering placeholder image, aborting');
|
|
152
|
+
resolve();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
fabricCanvas.renderAll();
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.error('Error rendering canvas with placeholder image:', e);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
resolve();
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
imgElement.src = urlToUse;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Scale image to fit canvas while maintaining aspect ratio
|
|
171
|
+
const scale = Math.min(
|
|
172
|
+
width / img.width!,
|
|
173
|
+
height / img.height!
|
|
174
|
+
);
|
|
175
|
+
img.scale(scale);
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// Center the image
|
|
179
|
+
img.set({
|
|
180
|
+
left: (width - img.width! * scale) / 2,
|
|
181
|
+
top: (height - img.height! * scale) / 2,
|
|
182
|
+
selectable: false, // template image is not selectable
|
|
183
|
+
evented: false, // template image is not responsive to events
|
|
184
|
+
crossOrigin: 'anonymous',
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Check if canvas is still valid when tracking is enabled
|
|
188
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
189
|
+
console.warn('Canvas changed before adding image, aborting');
|
|
190
|
+
resolve();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Add the image to the canvas
|
|
195
|
+
fabricCanvas.add(img);
|
|
196
|
+
|
|
197
|
+
// Send the template image to the back
|
|
198
|
+
fabricCanvas.sendToBack(img);
|
|
199
|
+
|
|
200
|
+
// Final check before render when tracking is enabled
|
|
201
|
+
if (trackingEnabled && (fabricCanvas as any).loadingId !== canvasId) {
|
|
202
|
+
console.warn('Canvas changed before rendering, aborting');
|
|
203
|
+
resolve();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
fabricCanvas.renderAll();
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.error('Error rendering canvas:', e);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
resolve();
|
|
214
|
+
},
|
|
215
|
+
{ crossOrigin: 'anonymous' }
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
};
|
package/src/utils/job.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldType,
|
|
3
|
+
MerchiFile,
|
|
4
|
+
Product,
|
|
5
|
+
Variation,
|
|
6
|
+
VariationField,
|
|
7
|
+
VariationFieldsOption,
|
|
8
|
+
DraftTemplate,
|
|
9
|
+
DraftTemplateData,
|
|
10
|
+
} from '../types';
|
|
11
|
+
|
|
12
|
+
// Add a UUID generator for unique IDs
|
|
13
|
+
export const generateUniqueId = () => {
|
|
14
|
+
return 'canvas-obj-' + Math.random().toString(36).substring(2, 11) + Date.now().toString(36);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function findTemplatesSelectedByVarations(draftTemplates: DraftTemplate[], variations: Variation[]) {
|
|
18
|
+
// filter all the selected variation values
|
|
19
|
+
const selectedVarationValues: string[] = variations
|
|
20
|
+
.filter((v: Variation) => v.value !== undefined && v.value !== null && v.value !== '')
|
|
21
|
+
.map((v: Variation) => String(v.value));
|
|
22
|
+
|
|
23
|
+
// return all the draft templates that have a selectedByVariationFieldOptions that includes
|
|
24
|
+
// any of the selected variation values
|
|
25
|
+
return draftTemplates.filter((template: DraftTemplate) => {
|
|
26
|
+
if (!template?.selectedByVariationFieldOptions) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return !!template.selectedByVariationFieldOptions?.find(
|
|
30
|
+
(o: VariationFieldsOption) => selectedVarationValues.includes(String(o.id))
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function filterVariationsByTemplate(variations: Variation[], template: DraftTemplate) {
|
|
36
|
+
// filter the variations by the template and only return the variations which edit the template
|
|
37
|
+
// we will use these variations to add canvas objects to the canvas
|
|
38
|
+
const editFieldIds = template.editedByVariationFields?.map((field: VariationField) => field.id);
|
|
39
|
+
return variations.filter(
|
|
40
|
+
(v: Variation) => editFieldIds?.includes(v.variationField?.id)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildVariationFieldCanvasObject(variation: Variation) {
|
|
45
|
+
// takes a variation and returns an associated canvas object to redner
|
|
46
|
+
const { value, variationField, variationFiles = [] } = variation;
|
|
47
|
+
const { fieldType = FieldType.TEXT_INPUT, id } = variationField || {};
|
|
48
|
+
if ([FieldType.TEXT_INPUT, FieldType.TEXT_AREA, FieldType.NUMBER_INPUT].includes(fieldType)) {
|
|
49
|
+
// if value is null or empty, do not create a text object
|
|
50
|
+
if (value === null || value === undefined || value === '') {
|
|
51
|
+
return {
|
|
52
|
+
fieldId: id,
|
|
53
|
+
value: null,
|
|
54
|
+
uniqueId: generateUniqueId()
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Used to add a text to the canvas
|
|
58
|
+
return [{
|
|
59
|
+
canvasObjectType: 'text',
|
|
60
|
+
value,
|
|
61
|
+
fieldId: id,
|
|
62
|
+
text: value,
|
|
63
|
+
fontSize: 24,
|
|
64
|
+
fontFamily: 'Nunito',
|
|
65
|
+
uniqueId: generateUniqueId(),
|
|
66
|
+
}];
|
|
67
|
+
}
|
|
68
|
+
if (fieldType === FieldType.FILE_UPLOAD) {
|
|
69
|
+
// Used to add an image to the canvas
|
|
70
|
+
return variationFiles.map((file: MerchiFile) => ({
|
|
71
|
+
canvasObjectType: 'image',
|
|
72
|
+
fieldId: id,
|
|
73
|
+
fileId: file.id,
|
|
74
|
+
value,
|
|
75
|
+
file: file,
|
|
76
|
+
uniqueId: generateUniqueId(),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
if ([FieldType.COLOUR_PICKER, FieldType.COLOUR_SELECT].includes(fieldType)) {
|
|
80
|
+
// Used to add a colour to the canvas
|
|
81
|
+
return [{
|
|
82
|
+
canvasObjectType: 'colour',
|
|
83
|
+
fieldId: id,
|
|
84
|
+
value,
|
|
85
|
+
colour: value,
|
|
86
|
+
uniqueId: generateUniqueId(),
|
|
87
|
+
}];
|
|
88
|
+
}
|
|
89
|
+
return [{
|
|
90
|
+
fieldId: id,
|
|
91
|
+
value,
|
|
92
|
+
uniqueId: generateUniqueId(),
|
|
93
|
+
}];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function canvasTemplateVariationObjects(variations: Variation[], template: DraftTemplate) {
|
|
97
|
+
// takes a list of variations and a template and returns a list of canvas objects to render
|
|
98
|
+
const templateVariations = filterVariationsByTemplate(variations, template);
|
|
99
|
+
|
|
100
|
+
return templateVariations
|
|
101
|
+
.flatMap((v: Variation) => buildVariationFieldCanvasObject(v))
|
|
102
|
+
.filter((obj: any) => obj.canvasObjectType);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function initDraftTemplates(variations: Variation[], product: Product): DraftTemplateData[] {
|
|
106
|
+
const { draftTemplates = [] } = product;
|
|
107
|
+
|
|
108
|
+
// Check for any templates that are selected by the variation options
|
|
109
|
+
const templates = findTemplatesSelectedByVarations(draftTemplates, variations) || [];
|
|
110
|
+
|
|
111
|
+
// If there are no templates selected, return the draft templates
|
|
112
|
+
const useTemplates = templates.length ? templates : draftTemplates;
|
|
113
|
+
|
|
114
|
+
return useTemplates.map((template: DraftTemplate) => {
|
|
115
|
+
// find the variations which edit the template
|
|
116
|
+
const templateVariations = filterVariationsByTemplate(variations, template);
|
|
117
|
+
// build the canvas objects for the template
|
|
118
|
+
const variationObjects = canvasTemplateVariationObjects(templateVariations, template);
|
|
119
|
+
return {
|
|
120
|
+
template,
|
|
121
|
+
variationFieldIds: templateVariations
|
|
122
|
+
.map((v: Variation) => v.variationField?.id)
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.map(id => Number(id)),
|
|
125
|
+
variationObjects,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const setupKeyboardEvents = (
|
|
2
|
+
canvas: fabric.Canvas,
|
|
3
|
+
onObjectRemoved?: (dataUrl: string) => void
|
|
4
|
+
) => {
|
|
5
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
6
|
+
if (e.key === 'Delete') {
|
|
7
|
+
const activeObject = canvas.getActiveObject();
|
|
8
|
+
if (activeObject) {
|
|
9
|
+
canvas.remove(activeObject);
|
|
10
|
+
canvas.renderAll();
|
|
11
|
+
|
|
12
|
+
if (onObjectRemoved) {
|
|
13
|
+
const dataUrl = canvas.toDataURL({
|
|
14
|
+
format: 'png',
|
|
15
|
+
quality: 1,
|
|
16
|
+
});
|
|
17
|
+
onObjectRemoved(dataUrl);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DraftTemplateData, DraftPreview, DraftPreviewLayer } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gets all unique draft previews from the draft templates
|
|
5
|
+
* Loops through each template's draftPreviewLayers to collect associated previews
|
|
6
|
+
*
|
|
7
|
+
* @param draftTemplates Array of draft templates with their variation data
|
|
8
|
+
* @returns Array of unique draft previews
|
|
9
|
+
*/
|
|
10
|
+
export function setNewDraftPreviews(draftTemplates: DraftTemplateData[]): DraftPreview[] {
|
|
11
|
+
// Create a map to track which previews we've already included by ID
|
|
12
|
+
const includedPreviewIds = new Set<number>();
|
|
13
|
+
const draftPreviews: DraftPreview[] = [];
|
|
14
|
+
|
|
15
|
+
// Loop through each draft template
|
|
16
|
+
draftTemplates.forEach((draftTemplateData) => {
|
|
17
|
+
const { template } = draftTemplateData;
|
|
18
|
+
|
|
19
|
+
// Skip if the template doesn't have preview layers
|
|
20
|
+
if (!template.draftPreviewLayers || template.draftPreviewLayers.length === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Look through each preview layer to find associated previews
|
|
25
|
+
template.draftPreviewLayers.forEach((layer: DraftPreviewLayer) => {
|
|
26
|
+
// If the layer has a preview and we haven't already included it
|
|
27
|
+
if (layer.draftPreview && layer.draftPreview.id) {
|
|
28
|
+
const previewId = layer.draftPreview.id;
|
|
29
|
+
|
|
30
|
+
if (!includedPreviewIds.has(previewId)) {
|
|
31
|
+
// Add to our tracking set
|
|
32
|
+
includedPreviewIds.add(previewId);
|
|
33
|
+
|
|
34
|
+
// Add the preview to our result array
|
|
35
|
+
draftPreviews.push(layer.draftPreview);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Return the unique list of previews
|
|
42
|
+
return draftPreviews;
|
|
43
|
+
}
|