web-to-print 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
- package/dist/cjs/canvas-helpers-A6rp5rPD.js +765 -0
- package/dist/cjs/index-IFGFRm-i.js +1649 -0
- package/dist/cjs/index.cjs.js +232 -0
- package/dist/cjs/loader.cjs.js +13 -0
- package/dist/cjs/logo-BUX-b45R.js +18 -0
- package/dist/cjs/web-to-print.cjs.js +25 -0
- package/dist/cjs/wtp-editor_2.cjs.entry.js +12386 -0
- package/dist/cjs/wtp-logo-renderer.cjs.entry.js +353 -0
- package/dist/cjs/wtp-print-area-editor.cjs.entry.js +431 -0
- package/dist/collection/collection-manifest.json +16 -0
- package/dist/collection/components/wtp-editor/wtp-editor.css +124 -0
- package/dist/collection/components/wtp-editor/wtp-editor.js +1114 -0
- package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.css +30 -0
- package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.js +455 -0
- package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.css +428 -0
- package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.js +573 -0
- package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.css +20 -0
- package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.js +600 -0
- package/dist/collection/examples/schaeffler--big.svg +1 -0
- package/dist/collection/index.js +8 -0
- package/dist/collection/types/editor.js +1 -0
- package/dist/collection/types/index.js +2 -0
- package/dist/collection/types/labels.js +30 -0
- package/dist/collection/types/logo.js +13 -0
- package/dist/collection/utils/background-removal.js +717 -0
- package/dist/collection/utils/canvas-helpers.js +380 -0
- package/dist/collection/utils/format-detection.js +48 -0
- package/dist/collection/utils/html-render-helpers.js +106 -0
- package/dist/collection/utils/image-preview.js +54 -0
- package/dist/collection/utils/logo-validation.js +141 -0
- package/dist/collection/utils/pdf-export.js +224 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/p-5qCsRzlt.js +1 -0
- package/dist/components/p-Bn9gR_8e.js +1 -0
- package/dist/components/p-D8pVJRuX.js +1 -0
- package/dist/components/wtp-editor.d.ts +11 -0
- package/dist/components/wtp-editor.js +1 -0
- package/dist/components/wtp-logo-renderer.d.ts +11 -0
- package/dist/components/wtp-logo-renderer.js +1 -0
- package/dist/components/wtp-logo-upload.d.ts +11 -0
- package/dist/components/wtp-logo-upload.js +1 -0
- package/dist/components/wtp-print-area-editor.d.ts +11 -0
- package/dist/components/wtp-print-area-editor.js +1 -0
- package/dist/esm/app-globals-DQuL1Twl.js +3 -0
- package/dist/esm/canvas-helpers-CK8OAq2J.js +748 -0
- package/dist/esm/index-CUetmLbL.js +1641 -0
- package/dist/esm/index.js +228 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/logo-D8pVJRuX.js +15 -0
- package/dist/esm/web-to-print.js +21 -0
- package/dist/esm/wtp-editor_2.entry.js +12383 -0
- package/dist/esm/wtp-logo-renderer.entry.js +351 -0
- package/dist/esm/wtp-print-area-editor.entry.js +429 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/types/components/wtp-editor/wtp-editor.d.ts +101 -0
- package/dist/types/components/wtp-logo-renderer/wtp-logo-renderer.d.ts +55 -0
- package/dist/types/components/wtp-logo-upload/wtp-logo-upload.d.ts +76 -0
- package/dist/types/components/wtp-print-area-editor/wtp-print-area-editor.d.ts +43 -0
- package/dist/types/components.d.ts +507 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/stencil-public-runtime.d.ts +1860 -0
- package/dist/types/types/editor.d.ts +79 -0
- package/dist/types/types/index.d.ts +5 -0
- package/dist/types/types/labels.d.ts +30 -0
- package/dist/types/types/logo.d.ts +47 -0
- package/dist/types/utils/background-removal.d.ts +95 -0
- package/dist/types/utils/canvas-helpers.d.ts +60 -0
- package/dist/types/utils/format-detection.d.ts +4 -0
- package/dist/types/utils/html-render-helpers.d.ts +44 -0
- package/dist/types/utils/image-preview.d.ts +13 -0
- package/dist/types/utils/logo-validation.d.ts +2 -0
- package/dist/types/utils/pdf-export.d.ts +32 -0
- package/dist/web-to-print/index.esm.js +1 -0
- package/dist/web-to-print/p-611ec561.entry.js +1 -0
- package/dist/web-to-print/p-703e4c52.entry.js +1 -0
- package/dist/web-to-print/p-CK8OAq2J.js +1 -0
- package/dist/web-to-print/p-CUetmLbL.js +2 -0
- package/dist/web-to-print/p-D8pVJRuX.js +1 -0
- package/dist/web-to-print/p-DQuL1Twl.js +1 -0
- package/dist/web-to-print/p-b532777b.entry.js +1 -0
- package/dist/web-to-print/web-to-print.esm.js +1 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +68 -0
- package/readme.md +490 -0
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
import { Canvas, FabricImage, IText } from "fabric";
|
|
3
|
+
import { DEFAULT_EDITOR_LABELS } from "../../types";
|
|
4
|
+
import { setCanvasBackground, generateObjectId, upscaleSvgDataUrl, fitLogoToPrintArea, printAreaToPixelCorners } from "../../utils/canvas-helpers";
|
|
5
|
+
export class WtpEditor {
|
|
6
|
+
el;
|
|
7
|
+
/** Canvas width in pixels. */
|
|
8
|
+
width = 800;
|
|
9
|
+
/** Canvas height in pixels. */
|
|
10
|
+
height = 600;
|
|
11
|
+
/** Product background image URL. */
|
|
12
|
+
productImage;
|
|
13
|
+
/** JSON-serialized initial editor state. */
|
|
14
|
+
initialState;
|
|
15
|
+
/** Available font families for the text tool. */
|
|
16
|
+
fonts = ['Arial', 'Helvetica', 'Times New Roman', 'Georgia', 'Verdana'];
|
|
17
|
+
/** Print area definition (0-1 relative coordinates) to constrain objects. */
|
|
18
|
+
printArea;
|
|
19
|
+
/** Show print area overlay and bounding box for debugging. */
|
|
20
|
+
debug = false;
|
|
21
|
+
/** Override any of the user-facing toolbar strings. Missing keys fall back to English defaults. */
|
|
22
|
+
labels = {};
|
|
23
|
+
getLabels() {
|
|
24
|
+
return { ...DEFAULT_EDITOR_LABELS, ...this.labels };
|
|
25
|
+
}
|
|
26
|
+
selectedObjectId = null;
|
|
27
|
+
selectedObjectType = null;
|
|
28
|
+
selectedFont = 'Arial';
|
|
29
|
+
selectedTextColor = '#000000';
|
|
30
|
+
/** Fires when the canvas is initialized and ready. */
|
|
31
|
+
wtpEditorReady;
|
|
32
|
+
/** Fires when the editor state changes (object add/move/remove). */
|
|
33
|
+
wtpEditorStateChanged;
|
|
34
|
+
/** Fires when an object is selected on the canvas. */
|
|
35
|
+
wtpEditorObjectSelected;
|
|
36
|
+
/** Fires when the current selection is cleared. */
|
|
37
|
+
wtpEditorObjectDeselected;
|
|
38
|
+
canvas;
|
|
39
|
+
canvasEl;
|
|
40
|
+
objectMap = new Map();
|
|
41
|
+
previewUrlMap = new Map();
|
|
42
|
+
/** Resolves when the current background image has been loaded and the canvas resized. */
|
|
43
|
+
backgroundReady = Promise.resolve();
|
|
44
|
+
componentDidLoad() {
|
|
45
|
+
this.initCanvas();
|
|
46
|
+
}
|
|
47
|
+
disconnectedCallback() {
|
|
48
|
+
this.canvas?.dispose();
|
|
49
|
+
}
|
|
50
|
+
onProductImageChange() {
|
|
51
|
+
if (this.canvas !== undefined && this.productImage !== undefined) {
|
|
52
|
+
// Reset to bounding box before setCanvasBackground auto-sizes
|
|
53
|
+
this.canvas.setDimensions({ width: this.width, height: this.height });
|
|
54
|
+
this.backgroundReady = setCanvasBackground(this.canvas, this.productImage);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
onSizeChange() {
|
|
58
|
+
if (this.canvas !== undefined) {
|
|
59
|
+
this.canvas.setDimensions({ width: this.width, height: this.height });
|
|
60
|
+
if (this.productImage !== undefined && this.productImage !== '') {
|
|
61
|
+
this.backgroundReady = setCanvasBackground(this.canvas, this.productImage);
|
|
62
|
+
}
|
|
63
|
+
this.canvas.renderAll();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
onDebugChange() {
|
|
67
|
+
this.canvas?.renderAll();
|
|
68
|
+
}
|
|
69
|
+
async onPrintAreaChange() {
|
|
70
|
+
// Wait for background to finish loading so canvas dimensions are final
|
|
71
|
+
await this.backgroundReady;
|
|
72
|
+
// Re-constrain existing user objects to the new bounds
|
|
73
|
+
if (this.canvas !== undefined) {
|
|
74
|
+
for (const obj of this.objectMap.values()) {
|
|
75
|
+
this.clampObjectToPrintArea(obj);
|
|
76
|
+
}
|
|
77
|
+
this.canvas.renderAll();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Add a logo image to the canvas and return its object ID. */
|
|
81
|
+
async addLogo(logoData) {
|
|
82
|
+
if (this.canvas === undefined)
|
|
83
|
+
throw new Error('Canvas not initialized');
|
|
84
|
+
// Wait for background image to load so canvas dimensions are final
|
|
85
|
+
await this.backgroundReady;
|
|
86
|
+
const id = generateObjectId();
|
|
87
|
+
const { dataUrl } = upscaleSvgDataUrl(logoData.dataUrl);
|
|
88
|
+
const img = await FabricImage.fromURL(dataUrl);
|
|
89
|
+
const canvasWidth = this.canvas.getWidth();
|
|
90
|
+
const canvasHeight = this.canvas.getHeight();
|
|
91
|
+
if (this.printArea !== undefined) {
|
|
92
|
+
// Fit logo into the print area: 0-1 coords map directly to canvas pixels
|
|
93
|
+
const transform = fitLogoToPrintArea(img.width ?? 100, img.height ?? 100, this.printArea, canvasWidth, canvasHeight);
|
|
94
|
+
img.set({
|
|
95
|
+
left: transform.x,
|
|
96
|
+
top: transform.y,
|
|
97
|
+
originX: 'center',
|
|
98
|
+
originY: 'center',
|
|
99
|
+
scaleX: transform.scaleX,
|
|
100
|
+
scaleY: transform.scaleY,
|
|
101
|
+
angle: transform.angle,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Scale logo to fit within 30% of canvas
|
|
106
|
+
const maxScale = Math.min((canvasWidth * 0.3) / (img.width ?? 100), (canvasHeight * 0.3) / (img.height ?? 100));
|
|
107
|
+
const scale = Math.min(maxScale, 1);
|
|
108
|
+
img.set({
|
|
109
|
+
left: canvasWidth / 2,
|
|
110
|
+
top: canvasHeight / 2,
|
|
111
|
+
originX: 'center',
|
|
112
|
+
originY: 'center',
|
|
113
|
+
scaleX: scale,
|
|
114
|
+
scaleY: scale,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
img._objectId = id;
|
|
118
|
+
this.objectMap.set(id, img);
|
|
119
|
+
if (logoData.previewDataUrl !== undefined) {
|
|
120
|
+
this.previewUrlMap.set(id, logoData.previewDataUrl);
|
|
121
|
+
}
|
|
122
|
+
this.canvas.add(img);
|
|
123
|
+
this.canvas.setActiveObject(img);
|
|
124
|
+
this.canvas.renderAll();
|
|
125
|
+
this.emitStateChanged();
|
|
126
|
+
return id;
|
|
127
|
+
}
|
|
128
|
+
/** Add a text object to the canvas and return its object ID. */
|
|
129
|
+
async addText(text, options) {
|
|
130
|
+
if (this.canvas === undefined)
|
|
131
|
+
throw new Error('Canvas not initialized');
|
|
132
|
+
// Wait for background image to load so canvas dimensions are final
|
|
133
|
+
await this.backgroundReady;
|
|
134
|
+
const id = generateObjectId();
|
|
135
|
+
let centerX = this.canvas.getWidth() / 2;
|
|
136
|
+
let centerY = this.canvas.getHeight() / 2;
|
|
137
|
+
if (this.printArea !== undefined) {
|
|
138
|
+
const corners = printAreaToPixelCorners(this.printArea, this.canvas.getWidth(), this.canvas.getHeight());
|
|
139
|
+
centerX = (corners[0].x + corners[1].x + corners[2].x + corners[3].x) / 4;
|
|
140
|
+
centerY = (corners[0].y + corners[1].y + corners[2].y + corners[3].y) / 4;
|
|
141
|
+
}
|
|
142
|
+
const iText = new IText(text, {
|
|
143
|
+
left: centerX,
|
|
144
|
+
top: centerY,
|
|
145
|
+
originX: 'center',
|
|
146
|
+
originY: 'center',
|
|
147
|
+
fontFamily: options?.fontFamily ?? this.selectedFont,
|
|
148
|
+
fontSize: options?.fontSize ?? 24,
|
|
149
|
+
fill: options?.fill ?? '#000000',
|
|
150
|
+
});
|
|
151
|
+
iText._objectId = id;
|
|
152
|
+
this.objectMap.set(id, iText);
|
|
153
|
+
this.canvas.add(iText);
|
|
154
|
+
this.canvas.setActiveObject(iText);
|
|
155
|
+
this.canvas.renderAll();
|
|
156
|
+
this.emitStateChanged();
|
|
157
|
+
return id;
|
|
158
|
+
}
|
|
159
|
+
/** Remove an object from the canvas by its ID. */
|
|
160
|
+
async removeObject(id) {
|
|
161
|
+
if (this.canvas === undefined)
|
|
162
|
+
return;
|
|
163
|
+
const obj = this.objectMap.get(id);
|
|
164
|
+
if (obj !== undefined) {
|
|
165
|
+
this.canvas.remove(obj);
|
|
166
|
+
this.objectMap.delete(id);
|
|
167
|
+
this.canvas.renderAll();
|
|
168
|
+
this.emitStateChanged();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** Export the current editor state as a serializable object. */
|
|
172
|
+
async exportState() {
|
|
173
|
+
return this.buildEditorState();
|
|
174
|
+
}
|
|
175
|
+
/** Load a previously exported editor state. */
|
|
176
|
+
async loadState(state) {
|
|
177
|
+
if (this.canvas === undefined)
|
|
178
|
+
throw new Error('Canvas not initialized');
|
|
179
|
+
// Clear canvas
|
|
180
|
+
this.canvas.clear();
|
|
181
|
+
this.objectMap.clear();
|
|
182
|
+
this.previewUrlMap.clear();
|
|
183
|
+
// Rebuild preview URL map from state
|
|
184
|
+
for (const logo of state.logos) {
|
|
185
|
+
if (logo.previewDataUrl !== undefined) {
|
|
186
|
+
this.previewUrlMap.set(logo.id, logo.previewDataUrl);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.canvas.setDimensions({ width: state.width, height: state.height });
|
|
190
|
+
// Restore from fabricJson first (loadFromJSON clears the canvas)
|
|
191
|
+
if (state.fabricJson !== undefined && state.fabricJson !== '') {
|
|
192
|
+
await this.canvas.loadFromJSON(state.fabricJson);
|
|
193
|
+
// Rebuild object map from loaded objects
|
|
194
|
+
for (const obj of this.canvas.getObjects()) {
|
|
195
|
+
const id = obj._objectId;
|
|
196
|
+
if (id !== undefined && id !== '') {
|
|
197
|
+
this.objectMap.set(id, obj);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Restore product image after JSON load so it inserts at index 0
|
|
202
|
+
if (state.productImage !== null && state.productImage !== undefined && state.productImage !== '') {
|
|
203
|
+
this.backgroundReady = setCanvasBackground(this.canvas, state.productImage);
|
|
204
|
+
await this.backgroundReady;
|
|
205
|
+
}
|
|
206
|
+
this.canvas.renderAll();
|
|
207
|
+
}
|
|
208
|
+
/** Clear all user objects from the canvas, keeping the instance alive. */
|
|
209
|
+
async resetCanvas() {
|
|
210
|
+
if (this.canvas === undefined)
|
|
211
|
+
return;
|
|
212
|
+
this.canvas.discardActiveObject();
|
|
213
|
+
for (const obj of this.canvas.getObjects().slice()) {
|
|
214
|
+
this.canvas.remove(obj);
|
|
215
|
+
}
|
|
216
|
+
this.objectMap.clear();
|
|
217
|
+
this.previewUrlMap.clear();
|
|
218
|
+
this.selectedObjectId = null;
|
|
219
|
+
this.canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
|
|
220
|
+
this.canvas.setDimensions({ width: this.width, height: this.height });
|
|
221
|
+
this.canvas.backgroundColor = '#ffffff';
|
|
222
|
+
this.canvas.requestRenderAll();
|
|
223
|
+
}
|
|
224
|
+
/** Export the canvas as a data URL image. */
|
|
225
|
+
async exportImage(format = 'png', quality = 1) {
|
|
226
|
+
if (this.canvas === undefined)
|
|
227
|
+
throw new Error('Canvas not initialized');
|
|
228
|
+
try {
|
|
229
|
+
return this.canvas.toDataURL({ multiplier: 1, format, quality });
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
if (e instanceof DOMException && e.name === 'SecurityError') {
|
|
233
|
+
throw new Error('Cannot export: product image is cross-origin. Use a CORS proxy or serve images from the same domain.');
|
|
234
|
+
}
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** Export the canvas as a high-resolution data URL image (for PDF/print).
|
|
239
|
+
* Returns the data URL plus the actual canvas dimensions (which may differ
|
|
240
|
+
* from the width/height props after setCanvasBackground resizes the canvas). */
|
|
241
|
+
async exportImageHighRes(format = 'png', quality = 1, multiplier = 3) {
|
|
242
|
+
if (this.canvas === undefined)
|
|
243
|
+
throw new Error('Canvas not initialized');
|
|
244
|
+
// Hide selection handles
|
|
245
|
+
this.canvas.discardActiveObject();
|
|
246
|
+
// Temporarily disable debug overlay during export
|
|
247
|
+
const wasDebug = this.debug;
|
|
248
|
+
this.debug = false;
|
|
249
|
+
this.canvas.renderAll();
|
|
250
|
+
let dataUrl;
|
|
251
|
+
try {
|
|
252
|
+
dataUrl = this.canvas.toDataURL({ multiplier, format, quality });
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
this.debug = wasDebug;
|
|
256
|
+
this.canvas.renderAll();
|
|
257
|
+
if (e instanceof DOMException && e.name === 'SecurityError') {
|
|
258
|
+
throw new Error('Cannot export: product image is cross-origin. Use a CORS proxy or serve images from the same domain.');
|
|
259
|
+
}
|
|
260
|
+
throw e;
|
|
261
|
+
}
|
|
262
|
+
const width = this.canvas.getWidth();
|
|
263
|
+
const height = this.canvas.getHeight();
|
|
264
|
+
// Restore debug state
|
|
265
|
+
this.debug = wasDebug;
|
|
266
|
+
this.canvas.renderAll();
|
|
267
|
+
return { dataUrl, width, height };
|
|
268
|
+
}
|
|
269
|
+
/** Get a list of all objects on the canvas with their IDs and types. */
|
|
270
|
+
async getObjects() {
|
|
271
|
+
const result = [];
|
|
272
|
+
for (const [id, obj] of this.objectMap) {
|
|
273
|
+
result.push({ id, type: obj.type ?? 'unknown' });
|
|
274
|
+
}
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
/** Update the text content of a text object by its ID. */
|
|
278
|
+
async updateText(id, text) {
|
|
279
|
+
if (this.canvas === undefined)
|
|
280
|
+
return;
|
|
281
|
+
const obj = this.objectMap.get(id);
|
|
282
|
+
if (obj !== undefined && obj.type === 'i-text') {
|
|
283
|
+
obj.set('text', text);
|
|
284
|
+
this.canvas.renderAll();
|
|
285
|
+
this.emitStateChanged();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
initCanvas() {
|
|
289
|
+
if (this.canvasEl === undefined)
|
|
290
|
+
return;
|
|
291
|
+
this.canvas = new Canvas(this.canvasEl, {
|
|
292
|
+
width: this.width,
|
|
293
|
+
height: this.height,
|
|
294
|
+
backgroundColor: '#ffffff',
|
|
295
|
+
});
|
|
296
|
+
this.canvas.on('selection:created', e => {
|
|
297
|
+
this.handleSelection(e.selected?.[0]);
|
|
298
|
+
});
|
|
299
|
+
this.canvas.on('selection:updated', e => {
|
|
300
|
+
this.handleSelection(e.selected?.[0]);
|
|
301
|
+
});
|
|
302
|
+
this.canvas.on('selection:cleared', () => {
|
|
303
|
+
this.selectedObjectId = null;
|
|
304
|
+
this.selectedObjectType = null;
|
|
305
|
+
this.wtpEditorObjectDeselected.emit();
|
|
306
|
+
});
|
|
307
|
+
this.canvas.on('object:modified', () => {
|
|
308
|
+
this.emitStateChanged();
|
|
309
|
+
});
|
|
310
|
+
this.canvas.on('text:changed', () => {
|
|
311
|
+
this.emitStateChanged();
|
|
312
|
+
});
|
|
313
|
+
this.canvas.on('object:moving', e => {
|
|
314
|
+
if (e.target !== undefined)
|
|
315
|
+
this.clampObjectToPrintArea(e.target);
|
|
316
|
+
});
|
|
317
|
+
this.canvas.on('object:scaling', e => {
|
|
318
|
+
if (e.target !== undefined)
|
|
319
|
+
this.clampObjectToPrintArea(e.target);
|
|
320
|
+
});
|
|
321
|
+
this.canvas.on('after:render', () => this.drawDebugOverlay());
|
|
322
|
+
// Load initial state if provided
|
|
323
|
+
if (this.initialState !== undefined && this.initialState !== '') {
|
|
324
|
+
try {
|
|
325
|
+
const state = JSON.parse(this.initialState);
|
|
326
|
+
this.loadState(state);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Invalid JSON, ignore
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if (this.productImage !== undefined && this.productImage !== '') {
|
|
333
|
+
this.backgroundReady = setCanvasBackground(this.canvas, this.productImage);
|
|
334
|
+
}
|
|
335
|
+
this.wtpEditorReady.emit();
|
|
336
|
+
}
|
|
337
|
+
/** Draw print area outline and bounding box on the canvas when debug mode is active. */
|
|
338
|
+
drawDebugOverlay() {
|
|
339
|
+
if (!this.debug || this.canvas === undefined || this.printArea === undefined)
|
|
340
|
+
return;
|
|
341
|
+
const ctx = this.canvas.getContext();
|
|
342
|
+
const corners = printAreaToPixelCorners(this.printArea, this.canvas.getWidth(), this.canvas.getHeight());
|
|
343
|
+
ctx.save();
|
|
344
|
+
// Draw the quad outline (actual print area shape)
|
|
345
|
+
ctx.beginPath();
|
|
346
|
+
ctx.moveTo(corners[0].x, corners[0].y);
|
|
347
|
+
ctx.lineTo(corners[1].x, corners[1].y);
|
|
348
|
+
ctx.lineTo(corners[2].x, corners[2].y);
|
|
349
|
+
ctx.lineTo(corners[3].x, corners[3].y);
|
|
350
|
+
ctx.closePath();
|
|
351
|
+
ctx.fillStyle = 'rgba(37, 99, 235, 0.08)';
|
|
352
|
+
ctx.fill();
|
|
353
|
+
ctx.strokeStyle = '#2563eb';
|
|
354
|
+
ctx.lineWidth = 1.5;
|
|
355
|
+
ctx.setLineDash([6, 4]);
|
|
356
|
+
ctx.stroke();
|
|
357
|
+
ctx.setLineDash([]);
|
|
358
|
+
// Draw corner dots
|
|
359
|
+
for (const c of corners) {
|
|
360
|
+
ctx.beginPath();
|
|
361
|
+
ctx.arc(c.x, c.y, 4, 0, Math.PI * 2);
|
|
362
|
+
ctx.fillStyle = '#2563eb';
|
|
363
|
+
ctx.fill();
|
|
364
|
+
ctx.strokeStyle = '#fff';
|
|
365
|
+
ctx.lineWidth = 1.5;
|
|
366
|
+
ctx.stroke();
|
|
367
|
+
}
|
|
368
|
+
// Draw axis-aligned bounding box (used for clamping)
|
|
369
|
+
const xs = corners.map(c => c.x);
|
|
370
|
+
const ys = corners.map(c => c.y);
|
|
371
|
+
const minX = Math.min(...xs);
|
|
372
|
+
const maxX = Math.max(...xs);
|
|
373
|
+
const minY = Math.min(...ys);
|
|
374
|
+
const maxY = Math.max(...ys);
|
|
375
|
+
ctx.strokeStyle = 'rgba(220, 38, 38, 0.5)';
|
|
376
|
+
ctx.lineWidth = 1;
|
|
377
|
+
ctx.setLineDash([3, 3]);
|
|
378
|
+
ctx.strokeRect(minX, minY, maxX - minX, maxY - minY);
|
|
379
|
+
ctx.setLineDash([]);
|
|
380
|
+
// Corner labels
|
|
381
|
+
ctx.font = '10px monospace';
|
|
382
|
+
ctx.fillStyle = '#2563eb';
|
|
383
|
+
const labels = ['TL', 'TR', 'BR', 'BL'];
|
|
384
|
+
for (let i = 0; i < 4; i++) {
|
|
385
|
+
ctx.fillText(labels[i], corners[i].x + 6, corners[i].y - 6);
|
|
386
|
+
}
|
|
387
|
+
ctx.restore();
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get the print area's rotated frame: center, local axes, and dimensions
|
|
391
|
+
* along those axes. This lets us clamp in the print area's own coordinate
|
|
392
|
+
* system instead of using an axis-aligned bounding box.
|
|
393
|
+
*/
|
|
394
|
+
getPrintAreaFrame() {
|
|
395
|
+
if (this.printArea === undefined || this.canvas === undefined)
|
|
396
|
+
return null;
|
|
397
|
+
const corners = printAreaToPixelCorners(this.printArea, this.canvas.getWidth(), this.canvas.getHeight());
|
|
398
|
+
const [tl, tr, br, bl] = corners;
|
|
399
|
+
const cx = (tl.x + tr.x + br.x + bl.x) / 4;
|
|
400
|
+
const cy = (tl.y + tr.y + br.y + bl.y) / 4;
|
|
401
|
+
const topLen = Math.hypot(tr.x - tl.x, tr.y - tl.y);
|
|
402
|
+
const botLen = Math.hypot(br.x - bl.x, br.y - bl.y);
|
|
403
|
+
const leftLen = Math.hypot(bl.x - tl.x, bl.y - tl.y);
|
|
404
|
+
const rightLen = Math.hypot(br.x - tr.x, br.y - tr.y);
|
|
405
|
+
const halfW = (topLen + botLen) / 4;
|
|
406
|
+
const halfH = (leftLen + rightLen) / 4;
|
|
407
|
+
// Angle from bottom edge (same as fitLogoToPrintArea)
|
|
408
|
+
const angle = Math.atan2(br.y - bl.y, br.x - bl.x);
|
|
409
|
+
return { cx, cy, halfW, halfH, cos: Math.cos(angle), sin: Math.sin(angle) };
|
|
410
|
+
}
|
|
411
|
+
clampObjectToPrintArea(obj) {
|
|
412
|
+
const frame = this.getPrintAreaFrame();
|
|
413
|
+
if (frame === null)
|
|
414
|
+
return;
|
|
415
|
+
// Skip background objects
|
|
416
|
+
if (obj._isBackground === true)
|
|
417
|
+
return;
|
|
418
|
+
const { cx, cy, halfW, halfH, cos, sin } = frame;
|
|
419
|
+
// Use the actual visual size of the object (not getBoundingRect which includes control handles)
|
|
420
|
+
const objW = (obj.width ?? 0) * (obj.scaleX ?? 1);
|
|
421
|
+
const objH = (obj.height ?? 0) * (obj.scaleY ?? 1);
|
|
422
|
+
// Relative angle between object and print area frame
|
|
423
|
+
const objAngleRad = ((obj.angle ?? 0) * Math.PI) / 180;
|
|
424
|
+
const frameAngle = Math.atan2(sin, cos);
|
|
425
|
+
const relAngle = objAngleRad - frameAngle;
|
|
426
|
+
const relCos = Math.abs(Math.cos(relAngle));
|
|
427
|
+
const relSin = Math.abs(Math.sin(relAngle));
|
|
428
|
+
// Object's half-size projected onto the print area's local axes
|
|
429
|
+
let projHalfW = (objW * relCos + objH * relSin) / 2;
|
|
430
|
+
let projHalfH = (objW * relSin + objH * relCos) / 2;
|
|
431
|
+
// Cap scale if the object exceeds the print area in its local frame
|
|
432
|
+
if (projHalfW > halfW || projHalfH > halfH) {
|
|
433
|
+
const scaleRatio = Math.min(halfW / Math.max(projHalfW, 1), halfH / Math.max(projHalfH, 1));
|
|
434
|
+
obj.set({
|
|
435
|
+
scaleX: (obj.scaleX ?? 1) * scaleRatio,
|
|
436
|
+
scaleY: (obj.scaleY ?? 1) * scaleRatio,
|
|
437
|
+
});
|
|
438
|
+
obj.setCoords();
|
|
439
|
+
// Recompute projected sizes after scaling
|
|
440
|
+
const newObjW = (obj.width ?? 0) * (obj.scaleX ?? 1);
|
|
441
|
+
const newObjH = (obj.height ?? 0) * (obj.scaleY ?? 1);
|
|
442
|
+
projHalfW = (newObjW * relCos + newObjH * relSin) / 2;
|
|
443
|
+
projHalfH = (newObjW * relSin + newObjH * relCos) / 2;
|
|
444
|
+
}
|
|
445
|
+
// Object center in world coords (use getCenterPoint for accuracy with all origins)
|
|
446
|
+
obj.setCoords();
|
|
447
|
+
const objCenter = obj.getCenterPoint();
|
|
448
|
+
// Transform object center into the print area's local frame
|
|
449
|
+
const relX = objCenter.x - cx;
|
|
450
|
+
const relY = objCenter.y - cy;
|
|
451
|
+
const localX = relX * cos + relY * sin;
|
|
452
|
+
const localY = -relX * sin + relY * cos;
|
|
453
|
+
// Clamp in local frame so the visual object stays within the print area
|
|
454
|
+
const clampedX = Math.max(-halfW + projHalfW, Math.min(halfW - projHalfW, localX));
|
|
455
|
+
const clampedY = Math.max(-halfH + projHalfH, Math.min(halfH - projHalfH, localY));
|
|
456
|
+
if (clampedX !== localX || clampedY !== localY) {
|
|
457
|
+
// Transform back to world coordinates
|
|
458
|
+
const newCx = cx + clampedX * cos - clampedY * sin;
|
|
459
|
+
const newCy = cy + clampedX * sin + clampedY * cos;
|
|
460
|
+
const dx = newCx - objCenter.x;
|
|
461
|
+
const dy = newCy - objCenter.y;
|
|
462
|
+
obj.set({
|
|
463
|
+
left: (obj.left ?? 0) + dx,
|
|
464
|
+
top: (obj.top ?? 0) + dy,
|
|
465
|
+
});
|
|
466
|
+
obj.setCoords();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
handleSelection(obj) {
|
|
470
|
+
if (obj === undefined)
|
|
471
|
+
return;
|
|
472
|
+
const id = obj._objectId;
|
|
473
|
+
if (id !== undefined && id !== '') {
|
|
474
|
+
this.selectedObjectId = id;
|
|
475
|
+
this.selectedObjectType = obj.type ?? null;
|
|
476
|
+
if (obj.type === 'i-text') {
|
|
477
|
+
const textObj = obj;
|
|
478
|
+
this.selectedFont = textObj.fontFamily ?? 'Arial';
|
|
479
|
+
this.selectedTextColor = textObj.fill ?? '#000000';
|
|
480
|
+
}
|
|
481
|
+
this.wtpEditorObjectSelected.emit({ id, type: obj.type ?? 'unknown' });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
getObjectTransform(obj) {
|
|
485
|
+
return {
|
|
486
|
+
x: obj.left ?? 0,
|
|
487
|
+
y: obj.top ?? 0,
|
|
488
|
+
scaleX: obj.scaleX ?? 1,
|
|
489
|
+
scaleY: obj.scaleY ?? 1,
|
|
490
|
+
angle: obj.angle ?? 0,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
buildEditorState() {
|
|
494
|
+
const logos = [];
|
|
495
|
+
const texts = [];
|
|
496
|
+
for (const [id, obj] of this.objectMap) {
|
|
497
|
+
if (obj.type === 'image') {
|
|
498
|
+
const previewDataUrl = this.previewUrlMap.get(id);
|
|
499
|
+
logos.push({
|
|
500
|
+
id,
|
|
501
|
+
dataUrl: obj.getSrc(),
|
|
502
|
+
...(previewDataUrl !== undefined ? { previewDataUrl } : {}),
|
|
503
|
+
transform: this.getObjectTransform(obj),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
else if (obj.type === 'i-text') {
|
|
507
|
+
const textObj = obj;
|
|
508
|
+
texts.push({
|
|
509
|
+
id,
|
|
510
|
+
text: textObj.text ?? '',
|
|
511
|
+
fontFamily: textObj.fontFamily ?? 'Arial',
|
|
512
|
+
fontSize: textObj.fontSize ?? 24,
|
|
513
|
+
fill: textObj.fill ?? '#000000',
|
|
514
|
+
transform: this.getObjectTransform(obj),
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
fabricJson: this.canvas !== undefined ? JSON.stringify(this.canvas.toJSON()) : '',
|
|
520
|
+
logos,
|
|
521
|
+
texts,
|
|
522
|
+
productImage: this.productImage ?? null,
|
|
523
|
+
width: this.width,
|
|
524
|
+
height: this.height,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
emitStateChanged() {
|
|
528
|
+
this.wtpEditorStateChanged.emit(this.buildEditorState());
|
|
529
|
+
}
|
|
530
|
+
handleAddText = () => {
|
|
531
|
+
this.addText(this.getLabels().defaultText, { fontFamily: this.selectedFont });
|
|
532
|
+
};
|
|
533
|
+
handleFontChange = (e) => {
|
|
534
|
+
const select = e.target;
|
|
535
|
+
this.selectedFont = select.value;
|
|
536
|
+
// Update selected text object's font
|
|
537
|
+
if (this.canvas !== undefined && this.selectedObjectId !== null) {
|
|
538
|
+
const obj = this.objectMap.get(this.selectedObjectId);
|
|
539
|
+
if (obj !== undefined && obj.type === 'i-text') {
|
|
540
|
+
obj.set('fontFamily', this.selectedFont);
|
|
541
|
+
this.canvas.renderAll();
|
|
542
|
+
this.emitStateChanged();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
handleColorChange = (e) => {
|
|
547
|
+
const input = e.target;
|
|
548
|
+
this.selectedTextColor = input.value;
|
|
549
|
+
if (this.canvas !== undefined && this.selectedObjectId !== null) {
|
|
550
|
+
const obj = this.objectMap.get(this.selectedObjectId);
|
|
551
|
+
if (obj !== undefined && obj.type === 'i-text') {
|
|
552
|
+
obj.set('fill', this.selectedTextColor);
|
|
553
|
+
this.canvas.renderAll();
|
|
554
|
+
this.emitStateChanged();
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
handleDeleteSelected = () => {
|
|
559
|
+
if (this.selectedObjectId !== null) {
|
|
560
|
+
this.removeObject(this.selectedObjectId);
|
|
561
|
+
this.selectedObjectId = null;
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
render() {
|
|
565
|
+
const labels = this.getLabels();
|
|
566
|
+
return (h("div", { key: 'c9ef130a1fa731852990de6c3c926d0bc52e628d', class: "wtp-editor" }, h("div", { key: '80f434e1784fdbe1ffc48686a2eb05e8db522dec', class: "toolbar" }, h("button", { key: '8f03a61f63a605ca732f227e709db12f005888a3', class: "toolbar-btn", onClick: this.handleAddText, title: labels.addTextTooltip }, h("svg", { key: '990dad4cdb46e74fae7c887dde4416f5f16ac1fc', viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { key: 'a1182b18466e0f31b95648543bd436e743a0bc09', points: "4 7 4 4 20 4 20 7" }), h("line", { key: 'bb74bd40a58c5cb25a9842989bc7f5dbe94df684', x1: "9", y1: "20", x2: "15", y2: "20" }), h("line", { key: 'be1eee4ddee7fadddfe00f694196861d582950e7', x1: "12", y1: "4", x2: "12", y2: "20" })), h("span", { key: '6d95e21682aa3a14de50c1b19c6f9ff01b0ba9e6' }, labels.addTextButton)), h("div", { key: '7180e61d20f4a5a6420dd313c78df5809a2e258e', class: "toolbar-separator" }), h("select", { key: 'e44709cb401e316392dc178e2173dec0990cbadc', class: "font-select", onChange: this.handleFontChange, title: labels.fontSelectTooltip }, this.fonts.map(font => (h("option", { value: font, style: { fontFamily: font } }, font)))), this.selectedObjectType === 'i-text' && (h("input", { key: '1f369b84753de1f363e76d0b6815318055738b9c', class: "color-input", type: "color", value: this.selectedTextColor, onInput: this.handleColorChange, title: labels.colorPickerTooltip })), h("div", { key: 'abed11c63044bf6d27ab59ed7262e665ddf6ea32', class: "toolbar-separator" }), h("button", { key: 'a7fa4df142fca5d1160a801731c3be41bddf6373', class: "toolbar-btn danger", onClick: this.handleDeleteSelected, disabled: this.selectedObjectId === null, title: labels.deleteButtonTooltip }, h("svg", { key: 'a41f1fb373bdd2d1830b7627f6c3f9234a2a86ce', viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { key: 'c731ac84aec857802f1ea1b85268c880c85ab254', points: "3 6 5 6 21 6" }), h("path", { key: '70a5b08147e64f940f55afd4680e9a8b5a8ec0ab', d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })))), h("div", { key: '1f04ba454f9c834a4554b018be6dba40607dbebc', class: "canvas-container" }, h("canvas", { key: '4894f481c67e7021cdd575f276ac1b4c5d20951d', ref: el => (this.canvasEl = el) }))));
|
|
567
|
+
}
|
|
568
|
+
static get is() { return "wtp-editor"; }
|
|
569
|
+
static get encapsulation() { return "scoped"; }
|
|
570
|
+
static get originalStyleUrls() {
|
|
571
|
+
return {
|
|
572
|
+
"$": ["wtp-editor.scss"]
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
static get styleUrls() {
|
|
576
|
+
return {
|
|
577
|
+
"$": ["wtp-editor.css"]
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
static get properties() {
|
|
581
|
+
return {
|
|
582
|
+
"width": {
|
|
583
|
+
"type": "number",
|
|
584
|
+
"mutable": false,
|
|
585
|
+
"complexType": {
|
|
586
|
+
"original": "number",
|
|
587
|
+
"resolved": "number",
|
|
588
|
+
"references": {}
|
|
589
|
+
},
|
|
590
|
+
"required": false,
|
|
591
|
+
"optional": false,
|
|
592
|
+
"docs": {
|
|
593
|
+
"tags": [],
|
|
594
|
+
"text": "Canvas width in pixels."
|
|
595
|
+
},
|
|
596
|
+
"getter": false,
|
|
597
|
+
"setter": false,
|
|
598
|
+
"reflect": false,
|
|
599
|
+
"attribute": "width",
|
|
600
|
+
"defaultValue": "800"
|
|
601
|
+
},
|
|
602
|
+
"height": {
|
|
603
|
+
"type": "number",
|
|
604
|
+
"mutable": false,
|
|
605
|
+
"complexType": {
|
|
606
|
+
"original": "number",
|
|
607
|
+
"resolved": "number",
|
|
608
|
+
"references": {}
|
|
609
|
+
},
|
|
610
|
+
"required": false,
|
|
611
|
+
"optional": false,
|
|
612
|
+
"docs": {
|
|
613
|
+
"tags": [],
|
|
614
|
+
"text": "Canvas height in pixels."
|
|
615
|
+
},
|
|
616
|
+
"getter": false,
|
|
617
|
+
"setter": false,
|
|
618
|
+
"reflect": false,
|
|
619
|
+
"attribute": "height",
|
|
620
|
+
"defaultValue": "600"
|
|
621
|
+
},
|
|
622
|
+
"productImage": {
|
|
623
|
+
"type": "string",
|
|
624
|
+
"mutable": false,
|
|
625
|
+
"complexType": {
|
|
626
|
+
"original": "string | undefined",
|
|
627
|
+
"resolved": "string",
|
|
628
|
+
"references": {}
|
|
629
|
+
},
|
|
630
|
+
"required": false,
|
|
631
|
+
"optional": false,
|
|
632
|
+
"docs": {
|
|
633
|
+
"tags": [],
|
|
634
|
+
"text": "Product background image URL."
|
|
635
|
+
},
|
|
636
|
+
"getter": false,
|
|
637
|
+
"setter": false,
|
|
638
|
+
"reflect": false,
|
|
639
|
+
"attribute": "product-image"
|
|
640
|
+
},
|
|
641
|
+
"initialState": {
|
|
642
|
+
"type": "string",
|
|
643
|
+
"mutable": false,
|
|
644
|
+
"complexType": {
|
|
645
|
+
"original": "string | undefined",
|
|
646
|
+
"resolved": "string",
|
|
647
|
+
"references": {}
|
|
648
|
+
},
|
|
649
|
+
"required": false,
|
|
650
|
+
"optional": false,
|
|
651
|
+
"docs": {
|
|
652
|
+
"tags": [],
|
|
653
|
+
"text": "JSON-serialized initial editor state."
|
|
654
|
+
},
|
|
655
|
+
"getter": false,
|
|
656
|
+
"setter": false,
|
|
657
|
+
"reflect": false,
|
|
658
|
+
"attribute": "initial-state"
|
|
659
|
+
},
|
|
660
|
+
"fonts": {
|
|
661
|
+
"type": "unknown",
|
|
662
|
+
"mutable": false,
|
|
663
|
+
"complexType": {
|
|
664
|
+
"original": "string[]",
|
|
665
|
+
"resolved": "string[]",
|
|
666
|
+
"references": {}
|
|
667
|
+
},
|
|
668
|
+
"required": false,
|
|
669
|
+
"optional": false,
|
|
670
|
+
"docs": {
|
|
671
|
+
"tags": [],
|
|
672
|
+
"text": "Available font families for the text tool."
|
|
673
|
+
},
|
|
674
|
+
"getter": false,
|
|
675
|
+
"setter": false,
|
|
676
|
+
"defaultValue": "['Arial', 'Helvetica', 'Times New Roman', 'Georgia', 'Verdana']"
|
|
677
|
+
},
|
|
678
|
+
"printArea": {
|
|
679
|
+
"type": "unknown",
|
|
680
|
+
"mutable": false,
|
|
681
|
+
"complexType": {
|
|
682
|
+
"original": "PrintArea | undefined",
|
|
683
|
+
"resolved": "PrintArea",
|
|
684
|
+
"references": {
|
|
685
|
+
"PrintArea": {
|
|
686
|
+
"location": "import",
|
|
687
|
+
"path": "../../types",
|
|
688
|
+
"id": "src/types/index.ts::PrintArea",
|
|
689
|
+
"referenceLocation": "PrintArea"
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
"required": false,
|
|
694
|
+
"optional": false,
|
|
695
|
+
"docs": {
|
|
696
|
+
"tags": [],
|
|
697
|
+
"text": "Print area definition (0-1 relative coordinates) to constrain objects."
|
|
698
|
+
},
|
|
699
|
+
"getter": false,
|
|
700
|
+
"setter": false
|
|
701
|
+
},
|
|
702
|
+
"debug": {
|
|
703
|
+
"type": "boolean",
|
|
704
|
+
"mutable": false,
|
|
705
|
+
"complexType": {
|
|
706
|
+
"original": "boolean",
|
|
707
|
+
"resolved": "boolean",
|
|
708
|
+
"references": {}
|
|
709
|
+
},
|
|
710
|
+
"required": false,
|
|
711
|
+
"optional": false,
|
|
712
|
+
"docs": {
|
|
713
|
+
"tags": [],
|
|
714
|
+
"text": "Show print area overlay and bounding box for debugging."
|
|
715
|
+
},
|
|
716
|
+
"getter": false,
|
|
717
|
+
"setter": false,
|
|
718
|
+
"reflect": false,
|
|
719
|
+
"attribute": "debug",
|
|
720
|
+
"defaultValue": "false"
|
|
721
|
+
},
|
|
722
|
+
"labels": {
|
|
723
|
+
"type": "unknown",
|
|
724
|
+
"mutable": false,
|
|
725
|
+
"complexType": {
|
|
726
|
+
"original": "Partial<EditorLabels>",
|
|
727
|
+
"resolved": "EditorLabels",
|
|
728
|
+
"references": {
|
|
729
|
+
"Partial": {
|
|
730
|
+
"location": "global",
|
|
731
|
+
"id": "global::Partial"
|
|
732
|
+
},
|
|
733
|
+
"EditorLabels": {
|
|
734
|
+
"location": "import",
|
|
735
|
+
"path": "../../types",
|
|
736
|
+
"id": "src/types/index.ts::EditorLabels",
|
|
737
|
+
"referenceLocation": "EditorLabels"
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
"required": false,
|
|
742
|
+
"optional": false,
|
|
743
|
+
"docs": {
|
|
744
|
+
"tags": [],
|
|
745
|
+
"text": "Override any of the user-facing toolbar strings. Missing keys fall back to English defaults."
|
|
746
|
+
},
|
|
747
|
+
"getter": false,
|
|
748
|
+
"setter": false,
|
|
749
|
+
"defaultValue": "{}"
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
static get states() {
|
|
754
|
+
return {
|
|
755
|
+
"selectedObjectId": {},
|
|
756
|
+
"selectedObjectType": {},
|
|
757
|
+
"selectedFont": {},
|
|
758
|
+
"selectedTextColor": {}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
static get events() {
|
|
762
|
+
return [{
|
|
763
|
+
"method": "wtpEditorReady",
|
|
764
|
+
"name": "wtpEditorReady",
|
|
765
|
+
"bubbles": true,
|
|
766
|
+
"cancelable": true,
|
|
767
|
+
"composed": true,
|
|
768
|
+
"docs": {
|
|
769
|
+
"tags": [],
|
|
770
|
+
"text": "Fires when the canvas is initialized and ready."
|
|
771
|
+
},
|
|
772
|
+
"complexType": {
|
|
773
|
+
"original": "void",
|
|
774
|
+
"resolved": "void",
|
|
775
|
+
"references": {}
|
|
776
|
+
}
|
|
777
|
+
}, {
|
|
778
|
+
"method": "wtpEditorStateChanged",
|
|
779
|
+
"name": "wtpEditorStateChanged",
|
|
780
|
+
"bubbles": true,
|
|
781
|
+
"cancelable": true,
|
|
782
|
+
"composed": true,
|
|
783
|
+
"docs": {
|
|
784
|
+
"tags": [],
|
|
785
|
+
"text": "Fires when the editor state changes (object add/move/remove)."
|
|
786
|
+
},
|
|
787
|
+
"complexType": {
|
|
788
|
+
"original": "EditorState",
|
|
789
|
+
"resolved": "EditorState",
|
|
790
|
+
"references": {
|
|
791
|
+
"EditorState": {
|
|
792
|
+
"location": "import",
|
|
793
|
+
"path": "../../types",
|
|
794
|
+
"id": "src/types/index.ts::EditorState",
|
|
795
|
+
"referenceLocation": "EditorState"
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}, {
|
|
800
|
+
"method": "wtpEditorObjectSelected",
|
|
801
|
+
"name": "wtpEditorObjectSelected",
|
|
802
|
+
"bubbles": true,
|
|
803
|
+
"cancelable": true,
|
|
804
|
+
"composed": true,
|
|
805
|
+
"docs": {
|
|
806
|
+
"tags": [],
|
|
807
|
+
"text": "Fires when an object is selected on the canvas."
|
|
808
|
+
},
|
|
809
|
+
"complexType": {
|
|
810
|
+
"original": "{ id: string; type: string }",
|
|
811
|
+
"resolved": "{ id: string; type: string; }",
|
|
812
|
+
"references": {}
|
|
813
|
+
}
|
|
814
|
+
}, {
|
|
815
|
+
"method": "wtpEditorObjectDeselected",
|
|
816
|
+
"name": "wtpEditorObjectDeselected",
|
|
817
|
+
"bubbles": true,
|
|
818
|
+
"cancelable": true,
|
|
819
|
+
"composed": true,
|
|
820
|
+
"docs": {
|
|
821
|
+
"tags": [],
|
|
822
|
+
"text": "Fires when the current selection is cleared."
|
|
823
|
+
},
|
|
824
|
+
"complexType": {
|
|
825
|
+
"original": "void",
|
|
826
|
+
"resolved": "void",
|
|
827
|
+
"references": {}
|
|
828
|
+
}
|
|
829
|
+
}];
|
|
830
|
+
}
|
|
831
|
+
static get methods() {
|
|
832
|
+
return {
|
|
833
|
+
"addLogo": {
|
|
834
|
+
"complexType": {
|
|
835
|
+
"signature": "(logoData: LogoData) => Promise<string>",
|
|
836
|
+
"parameters": [{
|
|
837
|
+
"name": "logoData",
|
|
838
|
+
"type": "LogoData",
|
|
839
|
+
"docs": ""
|
|
840
|
+
}],
|
|
841
|
+
"references": {
|
|
842
|
+
"Promise": {
|
|
843
|
+
"location": "global",
|
|
844
|
+
"id": "global::Promise"
|
|
845
|
+
},
|
|
846
|
+
"LogoData": {
|
|
847
|
+
"location": "import",
|
|
848
|
+
"path": "../../types",
|
|
849
|
+
"id": "src/types/index.ts::LogoData",
|
|
850
|
+
"referenceLocation": "LogoData"
|
|
851
|
+
},
|
|
852
|
+
"FabricObject": {
|
|
853
|
+
"location": "import",
|
|
854
|
+
"path": "fabric",
|
|
855
|
+
"id": "node_modules::FabricObject",
|
|
856
|
+
"referenceLocation": "FabricObject"
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
"return": "Promise<string>"
|
|
860
|
+
},
|
|
861
|
+
"docs": {
|
|
862
|
+
"text": "Add a logo image to the canvas and return its object ID.",
|
|
863
|
+
"tags": []
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
"addText": {
|
|
867
|
+
"complexType": {
|
|
868
|
+
"signature": "(text: string, options?: { fontFamily?: string; fontSize?: number; fill?: string; }) => Promise<string>",
|
|
869
|
+
"parameters": [{
|
|
870
|
+
"name": "text",
|
|
871
|
+
"type": "string",
|
|
872
|
+
"docs": ""
|
|
873
|
+
}, {
|
|
874
|
+
"name": "options",
|
|
875
|
+
"type": "{ fontFamily?: string; fontSize?: number; fill?: string; }",
|
|
876
|
+
"docs": ""
|
|
877
|
+
}],
|
|
878
|
+
"references": {
|
|
879
|
+
"Promise": {
|
|
880
|
+
"location": "global",
|
|
881
|
+
"id": "global::Promise"
|
|
882
|
+
},
|
|
883
|
+
"FabricObject": {
|
|
884
|
+
"location": "import",
|
|
885
|
+
"path": "fabric",
|
|
886
|
+
"id": "node_modules::FabricObject",
|
|
887
|
+
"referenceLocation": "FabricObject"
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
"return": "Promise<string>"
|
|
891
|
+
},
|
|
892
|
+
"docs": {
|
|
893
|
+
"text": "Add a text object to the canvas and return its object ID.",
|
|
894
|
+
"tags": []
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
"removeObject": {
|
|
898
|
+
"complexType": {
|
|
899
|
+
"signature": "(id: string) => Promise<void>",
|
|
900
|
+
"parameters": [{
|
|
901
|
+
"name": "id",
|
|
902
|
+
"type": "string",
|
|
903
|
+
"docs": ""
|
|
904
|
+
}],
|
|
905
|
+
"references": {
|
|
906
|
+
"Promise": {
|
|
907
|
+
"location": "global",
|
|
908
|
+
"id": "global::Promise"
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
"return": "Promise<void>"
|
|
912
|
+
},
|
|
913
|
+
"docs": {
|
|
914
|
+
"text": "Remove an object from the canvas by its ID.",
|
|
915
|
+
"tags": []
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
"exportState": {
|
|
919
|
+
"complexType": {
|
|
920
|
+
"signature": "() => Promise<EditorState>",
|
|
921
|
+
"parameters": [],
|
|
922
|
+
"references": {
|
|
923
|
+
"Promise": {
|
|
924
|
+
"location": "global",
|
|
925
|
+
"id": "global::Promise"
|
|
926
|
+
},
|
|
927
|
+
"EditorState": {
|
|
928
|
+
"location": "import",
|
|
929
|
+
"path": "../../types",
|
|
930
|
+
"id": "src/types/index.ts::EditorState",
|
|
931
|
+
"referenceLocation": "EditorState"
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
"return": "Promise<EditorState>"
|
|
935
|
+
},
|
|
936
|
+
"docs": {
|
|
937
|
+
"text": "Export the current editor state as a serializable object.",
|
|
938
|
+
"tags": []
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
"loadState": {
|
|
942
|
+
"complexType": {
|
|
943
|
+
"signature": "(state: EditorState) => Promise<void>",
|
|
944
|
+
"parameters": [{
|
|
945
|
+
"name": "state",
|
|
946
|
+
"type": "EditorState",
|
|
947
|
+
"docs": ""
|
|
948
|
+
}],
|
|
949
|
+
"references": {
|
|
950
|
+
"Promise": {
|
|
951
|
+
"location": "global",
|
|
952
|
+
"id": "global::Promise"
|
|
953
|
+
},
|
|
954
|
+
"EditorState": {
|
|
955
|
+
"location": "import",
|
|
956
|
+
"path": "../../types",
|
|
957
|
+
"id": "src/types/index.ts::EditorState",
|
|
958
|
+
"referenceLocation": "EditorState"
|
|
959
|
+
},
|
|
960
|
+
"FabricObject": {
|
|
961
|
+
"location": "import",
|
|
962
|
+
"path": "fabric",
|
|
963
|
+
"id": "node_modules::FabricObject",
|
|
964
|
+
"referenceLocation": "FabricObject"
|
|
965
|
+
}
|
|
966
|
+
},
|
|
967
|
+
"return": "Promise<void>"
|
|
968
|
+
},
|
|
969
|
+
"docs": {
|
|
970
|
+
"text": "Load a previously exported editor state.",
|
|
971
|
+
"tags": []
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
"resetCanvas": {
|
|
975
|
+
"complexType": {
|
|
976
|
+
"signature": "() => Promise<void>",
|
|
977
|
+
"parameters": [],
|
|
978
|
+
"references": {
|
|
979
|
+
"Promise": {
|
|
980
|
+
"location": "global",
|
|
981
|
+
"id": "global::Promise"
|
|
982
|
+
}
|
|
983
|
+
},
|
|
984
|
+
"return": "Promise<void>"
|
|
985
|
+
},
|
|
986
|
+
"docs": {
|
|
987
|
+
"text": "Clear all user objects from the canvas, keeping the instance alive.",
|
|
988
|
+
"tags": []
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
"exportImage": {
|
|
992
|
+
"complexType": {
|
|
993
|
+
"signature": "(format?: \"png\" | \"jpeg\", quality?: number) => Promise<string>",
|
|
994
|
+
"parameters": [{
|
|
995
|
+
"name": "format",
|
|
996
|
+
"type": "\"png\" | \"jpeg\"",
|
|
997
|
+
"docs": ""
|
|
998
|
+
}, {
|
|
999
|
+
"name": "quality",
|
|
1000
|
+
"type": "number",
|
|
1001
|
+
"docs": ""
|
|
1002
|
+
}],
|
|
1003
|
+
"references": {
|
|
1004
|
+
"Promise": {
|
|
1005
|
+
"location": "global",
|
|
1006
|
+
"id": "global::Promise"
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
"return": "Promise<string>"
|
|
1010
|
+
},
|
|
1011
|
+
"docs": {
|
|
1012
|
+
"text": "Export the canvas as a data URL image.",
|
|
1013
|
+
"tags": []
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
"exportImageHighRes": {
|
|
1017
|
+
"complexType": {
|
|
1018
|
+
"signature": "(format?: \"png\" | \"jpeg\", quality?: number, multiplier?: number) => Promise<{ dataUrl: string; width: number; height: number; }>",
|
|
1019
|
+
"parameters": [{
|
|
1020
|
+
"name": "format",
|
|
1021
|
+
"type": "\"png\" | \"jpeg\"",
|
|
1022
|
+
"docs": ""
|
|
1023
|
+
}, {
|
|
1024
|
+
"name": "quality",
|
|
1025
|
+
"type": "number",
|
|
1026
|
+
"docs": ""
|
|
1027
|
+
}, {
|
|
1028
|
+
"name": "multiplier",
|
|
1029
|
+
"type": "number",
|
|
1030
|
+
"docs": ""
|
|
1031
|
+
}],
|
|
1032
|
+
"references": {
|
|
1033
|
+
"Promise": {
|
|
1034
|
+
"location": "global",
|
|
1035
|
+
"id": "global::Promise"
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
"return": "Promise<{ dataUrl: string; width: number; height: number; }>"
|
|
1039
|
+
},
|
|
1040
|
+
"docs": {
|
|
1041
|
+
"text": "Export the canvas as a high-resolution data URL image (for PDF/print).\nReturns the data URL plus the actual canvas dimensions (which may differ\nfrom the width/height props after setCanvasBackground resizes the canvas).",
|
|
1042
|
+
"tags": []
|
|
1043
|
+
}
|
|
1044
|
+
},
|
|
1045
|
+
"getObjects": {
|
|
1046
|
+
"complexType": {
|
|
1047
|
+
"signature": "() => Promise<{ id: string; type: string; }[]>",
|
|
1048
|
+
"parameters": [],
|
|
1049
|
+
"references": {
|
|
1050
|
+
"Promise": {
|
|
1051
|
+
"location": "global",
|
|
1052
|
+
"id": "global::Promise"
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
"return": "Promise<{ id: string; type: string; }[]>"
|
|
1056
|
+
},
|
|
1057
|
+
"docs": {
|
|
1058
|
+
"text": "Get a list of all objects on the canvas with their IDs and types.",
|
|
1059
|
+
"tags": []
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
"updateText": {
|
|
1063
|
+
"complexType": {
|
|
1064
|
+
"signature": "(id: string, text: string) => Promise<void>",
|
|
1065
|
+
"parameters": [{
|
|
1066
|
+
"name": "id",
|
|
1067
|
+
"type": "string",
|
|
1068
|
+
"docs": ""
|
|
1069
|
+
}, {
|
|
1070
|
+
"name": "text",
|
|
1071
|
+
"type": "string",
|
|
1072
|
+
"docs": ""
|
|
1073
|
+
}],
|
|
1074
|
+
"references": {
|
|
1075
|
+
"Promise": {
|
|
1076
|
+
"location": "global",
|
|
1077
|
+
"id": "global::Promise"
|
|
1078
|
+
},
|
|
1079
|
+
"IText": {
|
|
1080
|
+
"location": "import",
|
|
1081
|
+
"path": "fabric",
|
|
1082
|
+
"id": "node_modules::IText",
|
|
1083
|
+
"referenceLocation": "IText"
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
"return": "Promise<void>"
|
|
1087
|
+
},
|
|
1088
|
+
"docs": {
|
|
1089
|
+
"text": "Update the text content of a text object by its ID.",
|
|
1090
|
+
"tags": []
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
static get elementRef() { return "el"; }
|
|
1096
|
+
static get watchers() {
|
|
1097
|
+
return [{
|
|
1098
|
+
"propName": "productImage",
|
|
1099
|
+
"methodName": "onProductImageChange"
|
|
1100
|
+
}, {
|
|
1101
|
+
"propName": "width",
|
|
1102
|
+
"methodName": "onSizeChange"
|
|
1103
|
+
}, {
|
|
1104
|
+
"propName": "height",
|
|
1105
|
+
"methodName": "onSizeChange"
|
|
1106
|
+
}, {
|
|
1107
|
+
"propName": "debug",
|
|
1108
|
+
"methodName": "onDebugChange"
|
|
1109
|
+
}, {
|
|
1110
|
+
"propName": "printArea",
|
|
1111
|
+
"methodName": "onPrintAreaChange"
|
|
1112
|
+
}];
|
|
1113
|
+
}
|
|
1114
|
+
}
|