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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/canvas-helpers-A6rp5rPD.js +765 -0
  4. package/dist/cjs/index-IFGFRm-i.js +1649 -0
  5. package/dist/cjs/index.cjs.js +232 -0
  6. package/dist/cjs/loader.cjs.js +13 -0
  7. package/dist/cjs/logo-BUX-b45R.js +18 -0
  8. package/dist/cjs/web-to-print.cjs.js +25 -0
  9. package/dist/cjs/wtp-editor_2.cjs.entry.js +12386 -0
  10. package/dist/cjs/wtp-logo-renderer.cjs.entry.js +353 -0
  11. package/dist/cjs/wtp-print-area-editor.cjs.entry.js +431 -0
  12. package/dist/collection/collection-manifest.json +16 -0
  13. package/dist/collection/components/wtp-editor/wtp-editor.css +124 -0
  14. package/dist/collection/components/wtp-editor/wtp-editor.js +1114 -0
  15. package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.css +30 -0
  16. package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.js +455 -0
  17. package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.css +428 -0
  18. package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.js +573 -0
  19. package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.css +20 -0
  20. package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.js +600 -0
  21. package/dist/collection/examples/schaeffler--big.svg +1 -0
  22. package/dist/collection/index.js +8 -0
  23. package/dist/collection/types/editor.js +1 -0
  24. package/dist/collection/types/index.js +2 -0
  25. package/dist/collection/types/labels.js +30 -0
  26. package/dist/collection/types/logo.js +13 -0
  27. package/dist/collection/utils/background-removal.js +717 -0
  28. package/dist/collection/utils/canvas-helpers.js +380 -0
  29. package/dist/collection/utils/format-detection.js +48 -0
  30. package/dist/collection/utils/html-render-helpers.js +106 -0
  31. package/dist/collection/utils/image-preview.js +54 -0
  32. package/dist/collection/utils/logo-validation.js +141 -0
  33. package/dist/collection/utils/pdf-export.js +224 -0
  34. package/dist/components/index.d.ts +35 -0
  35. package/dist/components/index.js +1 -0
  36. package/dist/components/p-5qCsRzlt.js +1 -0
  37. package/dist/components/p-Bn9gR_8e.js +1 -0
  38. package/dist/components/p-D8pVJRuX.js +1 -0
  39. package/dist/components/wtp-editor.d.ts +11 -0
  40. package/dist/components/wtp-editor.js +1 -0
  41. package/dist/components/wtp-logo-renderer.d.ts +11 -0
  42. package/dist/components/wtp-logo-renderer.js +1 -0
  43. package/dist/components/wtp-logo-upload.d.ts +11 -0
  44. package/dist/components/wtp-logo-upload.js +1 -0
  45. package/dist/components/wtp-print-area-editor.d.ts +11 -0
  46. package/dist/components/wtp-print-area-editor.js +1 -0
  47. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  48. package/dist/esm/canvas-helpers-CK8OAq2J.js +748 -0
  49. package/dist/esm/index-CUetmLbL.js +1641 -0
  50. package/dist/esm/index.js +228 -0
  51. package/dist/esm/loader.js +11 -0
  52. package/dist/esm/logo-D8pVJRuX.js +15 -0
  53. package/dist/esm/web-to-print.js +21 -0
  54. package/dist/esm/wtp-editor_2.entry.js +12383 -0
  55. package/dist/esm/wtp-logo-renderer.entry.js +351 -0
  56. package/dist/esm/wtp-print-area-editor.entry.js +429 -0
  57. package/dist/index.cjs.js +1 -0
  58. package/dist/index.js +1 -0
  59. package/dist/types/components/wtp-editor/wtp-editor.d.ts +101 -0
  60. package/dist/types/components/wtp-logo-renderer/wtp-logo-renderer.d.ts +55 -0
  61. package/dist/types/components/wtp-logo-upload/wtp-logo-upload.d.ts +76 -0
  62. package/dist/types/components/wtp-print-area-editor/wtp-print-area-editor.d.ts +43 -0
  63. package/dist/types/components.d.ts +507 -0
  64. package/dist/types/index.d.ts +11 -0
  65. package/dist/types/stencil-public-runtime.d.ts +1860 -0
  66. package/dist/types/types/editor.d.ts +79 -0
  67. package/dist/types/types/index.d.ts +5 -0
  68. package/dist/types/types/labels.d.ts +30 -0
  69. package/dist/types/types/logo.d.ts +47 -0
  70. package/dist/types/utils/background-removal.d.ts +95 -0
  71. package/dist/types/utils/canvas-helpers.d.ts +60 -0
  72. package/dist/types/utils/format-detection.d.ts +4 -0
  73. package/dist/types/utils/html-render-helpers.d.ts +44 -0
  74. package/dist/types/utils/image-preview.d.ts +13 -0
  75. package/dist/types/utils/logo-validation.d.ts +2 -0
  76. package/dist/types/utils/pdf-export.d.ts +32 -0
  77. package/dist/web-to-print/index.esm.js +1 -0
  78. package/dist/web-to-print/p-611ec561.entry.js +1 -0
  79. package/dist/web-to-print/p-703e4c52.entry.js +1 -0
  80. package/dist/web-to-print/p-CK8OAq2J.js +1 -0
  81. package/dist/web-to-print/p-CUetmLbL.js +2 -0
  82. package/dist/web-to-print/p-D8pVJRuX.js +1 -0
  83. package/dist/web-to-print/p-DQuL1Twl.js +1 -0
  84. package/dist/web-to-print/p-b532777b.entry.js +1 -0
  85. package/dist/web-to-print/web-to-print.esm.js +1 -0
  86. package/loader/cdn.js +1 -0
  87. package/loader/index.cjs.js +1 -0
  88. package/loader/index.d.ts +24 -0
  89. package/loader/index.es2017.js +1 -0
  90. package/loader/index.js +2 -0
  91. package/package.json +68 -0
  92. package/readme.md +490 -0
@@ -0,0 +1,429 @@
1
+ import { r as registerInstance, c as createEvent, g as getElement, h } from './index-CUetmLbL.js';
2
+ import { h as ho, s as setCanvasBackground, c as defaultPrintArea, N, p as printAreaToPixelCorners, e as pixelCornersToPrintArea, J, q } from './canvas-helpers-CK8OAq2J.js';
3
+
4
+ const wtpPrintAreaEditorCss = () => `*.sc-wtp-print-area-editor,*.sc-wtp-print-area-editor::before,*.sc-wtp-print-area-editor::after{box-sizing:border-box}.sc-wtp-print-area-editor-h{font-family:var(--wtp-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--wtp-color-text, #1e293b);line-height:1.5}.wtp-print-area-editor.sc-wtp-print-area-editor{display:inline-block;line-height:0;border:1px solid var(--wtp-color-border, #e2e8f0);border-radius:8px;overflow:hidden;background:repeating-conic-gradient(#e5e7eb 0% 25%, transparent 0% 50%) 50%/20px 20px}`;
5
+
6
+ const CORNER_RADIUS = 7;
7
+ /**
8
+ * Custom Fabric.js object that renders a quadrilateral defined by 4 corner
9
+ * offsets from the bounding-box center. Top/bottom edges can be curved
10
+ * via `bulge` (-1 to 1) using quadratic Bezier curves.
11
+ */
12
+ class PrintAreaQuad extends J {
13
+ constructor(options) {
14
+ super(options);
15
+ this.cornerOffsets = options?.cornerOffsets ?? [
16
+ new N(-50, -50), new N(50, -50),
17
+ new N(50, 50), new N(-50, 50),
18
+ ];
19
+ this.bulge = options?.bulge ?? 0;
20
+ this.objectCaching = false;
21
+ }
22
+ _render(ctx) {
23
+ const [tl, tr, br, bl] = this.cornerOffsets;
24
+ ctx.beginPath();
25
+ ctx.moveTo(tl.x, tl.y);
26
+ if (this.bulge !== 0) {
27
+ // Curved top edge: control point at midpoint shifted by bulge
28
+ const topMidX = (tl.x + tr.x) / 2;
29
+ const topMidY = (tl.y + tr.y) / 2;
30
+ const bulgePixels = this.bulge * (this.height ?? 1);
31
+ ctx.quadraticCurveTo(topMidX, topMidY - bulgePixels, tr.x, tr.y);
32
+ }
33
+ else {
34
+ ctx.lineTo(tr.x, tr.y);
35
+ }
36
+ ctx.lineTo(br.x, br.y);
37
+ if (this.bulge !== 0) {
38
+ // Curved bottom edge: same direction as top for cylindrical appearance
39
+ const botMidX = (br.x + bl.x) / 2;
40
+ const botMidY = (br.y + bl.y) / 2;
41
+ const bulgePixels = this.bulge * (this.height ?? 1);
42
+ ctx.quadraticCurveTo(botMidX, botMidY - bulgePixels, bl.x, bl.y);
43
+ }
44
+ else {
45
+ ctx.lineTo(bl.x, bl.y);
46
+ }
47
+ ctx.closePath();
48
+ this._renderPaintInOrder(ctx);
49
+ }
50
+ /** Recompute left/top/width/height from absolute corner positions. */
51
+ recalcBounds() {
52
+ const center = this.getCenterPoint();
53
+ const abs = this.cornerOffsets.map(o => new N(center.x + o.x, center.y + o.y));
54
+ const xs = abs.map(p => p.x);
55
+ const ys = abs.map(p => p.y);
56
+ const minX = Math.min(...xs);
57
+ const maxX = Math.max(...xs);
58
+ const minY = Math.min(...ys);
59
+ const maxY = Math.max(...ys);
60
+ const newCenterX = (minX + maxX) / 2;
61
+ const newCenterY = (minY + maxY) / 2;
62
+ this.cornerOffsets = abs.map(p => new N(p.x - newCenterX, p.y - newCenterY));
63
+ this.set({
64
+ left: newCenterX,
65
+ top: newCenterY,
66
+ width: Math.max(maxX - minX, 1),
67
+ height: Math.max(maxY - minY, 1),
68
+ });
69
+ this.setCoords();
70
+ }
71
+ }
72
+ function buildCornerControl(index) {
73
+ return new q({
74
+ x: 0,
75
+ y: 0,
76
+ sizeX: CORNER_RADIUS * 2,
77
+ sizeY: CORNER_RADIUS * 2,
78
+ touchSizeX: CORNER_RADIUS * 4,
79
+ touchSizeY: CORNER_RADIUS * 4,
80
+ cursorStyleHandler: () => 'move',
81
+ positionHandler: (_dim, finalMatrix, fabricObject) => {
82
+ const quad = fabricObject;
83
+ return new N(quad.cornerOffsets[index].x, quad.cornerOffsets[index].y).transform(finalMatrix);
84
+ },
85
+ actionHandler: (_eventData, transformData, x, y) => {
86
+ const quad = transformData.target;
87
+ const center = quad.getCenterPoint();
88
+ quad.cornerOffsets[index] = new N(x - center.x, y - center.y);
89
+ quad.recalcBounds();
90
+ return true;
91
+ },
92
+ render: (ctx, left, top) => {
93
+ ctx.save();
94
+ ctx.fillStyle = '#2563eb';
95
+ ctx.strokeStyle = '#ffffff';
96
+ ctx.lineWidth = 2;
97
+ ctx.beginPath();
98
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
99
+ ctx.fill();
100
+ ctx.stroke();
101
+ ctx.restore();
102
+ },
103
+ });
104
+ }
105
+ function buildBulgeControl() {
106
+ const HANDLE_OFFSET_X = 24;
107
+ const TRACK_GAP = 4;
108
+ return new q({
109
+ x: 0.5,
110
+ y: 0,
111
+ offsetX: HANDLE_OFFSET_X,
112
+ sizeX: CORNER_RADIUS * 2,
113
+ sizeY: CORNER_RADIUS * 2,
114
+ touchSizeX: CORNER_RADIUS * 4,
115
+ touchSizeY: CORNER_RADIUS * 4,
116
+ cursorStyleHandler: () => 'ns-resize',
117
+ positionHandler: (dim, finalMatrix, fabricObject) => {
118
+ const quad = fabricObject;
119
+ return new N(0.5 * dim.x + HANDLE_OFFSET_X, -quad.bulge * dim.y / 2).transform(finalMatrix);
120
+ },
121
+ actionHandler: (_eventData, transformData, _x, y) => {
122
+ const quad = transformData.target;
123
+ const center = quad.getCenterPoint();
124
+ const halfH = (quad.height ?? 0) * (quad.scaleY ?? 1) / 2;
125
+ const bulge = Math.max(-1, Math.min(1, (center.y - y) / Math.max(halfH, 1)));
126
+ quad.bulge = bulge;
127
+ return true;
128
+ },
129
+ render: (ctx, left, top, _styleOverride, fabricObject) => {
130
+ const quad = fabricObject;
131
+ const halfH = ((quad.height ?? 0) * (quad.scaleY ?? 1)) / 2;
132
+ const angleRad = ((quad.angle ?? 0) * Math.PI) / 180;
133
+ const trackTopX = left + Math.sin(angleRad) * halfH;
134
+ const trackTopY = top - Math.cos(angleRad) * halfH;
135
+ const trackBotX = left - Math.sin(angleRad) * halfH;
136
+ const trackBotY = top + Math.cos(angleRad) * halfH;
137
+ ctx.save();
138
+ // Track line
139
+ ctx.strokeStyle = 'rgba(37, 99, 235, 0.3)';
140
+ ctx.lineWidth = 2;
141
+ ctx.setLineDash([3, 3]);
142
+ ctx.beginPath();
143
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
144
+ ctx.lineTo(trackBotX, trackBotY + TRACK_GAP);
145
+ ctx.stroke();
146
+ ctx.setLineDash([]);
147
+ // Handle circle
148
+ ctx.fillStyle = '#2563eb';
149
+ ctx.strokeStyle = '#ffffff';
150
+ ctx.lineWidth = 2;
151
+ ctx.beginPath();
152
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
153
+ ctx.fill();
154
+ ctx.stroke();
155
+ // Arrow up + "+" label
156
+ ctx.fillStyle = 'rgba(37, 99, 235, 0.5)';
157
+ ctx.beginPath();
158
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
159
+ ctx.lineTo(trackTopX - 4, trackTopY - TRACK_GAP + 6);
160
+ ctx.lineTo(trackTopX + 4, trackTopY - TRACK_GAP + 6);
161
+ ctx.closePath();
162
+ ctx.fill();
163
+ ctx.font = 'bold 10px sans-serif';
164
+ ctx.textAlign = 'center';
165
+ ctx.fillText('+', trackTopX, trackTopY - TRACK_GAP - 4);
166
+ // Arrow down + "\u2212" label
167
+ ctx.beginPath();
168
+ ctx.moveTo(trackBotX, trackBotY + TRACK_GAP);
169
+ ctx.lineTo(trackBotX - 4, trackBotY + TRACK_GAP - 6);
170
+ ctx.lineTo(trackBotX + 4, trackBotY + TRACK_GAP - 6);
171
+ ctx.closePath();
172
+ ctx.fill();
173
+ ctx.fillText('\u2212', trackBotX, trackBotY + TRACK_GAP + 12);
174
+ ctx.restore();
175
+ },
176
+ });
177
+ }
178
+ const WtpPrintAreaEditor = class {
179
+ constructor(hostRef) {
180
+ registerInstance(this, hostRef);
181
+ this.wtpPrintAreaChange = createEvent(this, "wtpPrintAreaChange");
182
+ }
183
+ get el() { return getElement(this); }
184
+ /** Product background image URL. */
185
+ productImage;
186
+ /** Canvas width in pixels. */
187
+ width = 800;
188
+ /** Canvas height in pixels. */
189
+ height = 600;
190
+ /** Current print area (relative 0-1 coordinates). */
191
+ printArea;
192
+ /** Fires when the print area rectangle is modified. */
193
+ wtpPrintAreaChange;
194
+ canvas;
195
+ canvasEl;
196
+ areaQuad;
197
+ /** Generation counter to discard stale background loads. */
198
+ bgLoadGen = 0;
199
+ componentDidLoad() {
200
+ this.initCanvas();
201
+ }
202
+ disconnectedCallback() {
203
+ ++this.bgLoadGen;
204
+ this.canvas?.dispose();
205
+ }
206
+ async onProductImageChange() {
207
+ if (this.canvas === undefined || this.productImage === undefined)
208
+ return;
209
+ await this.reloadCanvas();
210
+ }
211
+ async onSizeChange() {
212
+ if (this.canvas === undefined)
213
+ return;
214
+ await this.reloadCanvas();
215
+ }
216
+ onPrintAreaChange() {
217
+ this.syncQuadFromPrintArea();
218
+ }
219
+ /** Get the current print area as relative 0-1 coordinates. */
220
+ async getPrintArea() {
221
+ return this.readQuadAsPrintArea();
222
+ }
223
+ /** Set the print area and update the quad on canvas. */
224
+ async setPrintArea(printArea) {
225
+ this.printArea = printArea;
226
+ this.syncQuadFromPrintArea();
227
+ }
228
+ /** Create the Fabric Canvas once and perform the initial load. */
229
+ async initCanvas() {
230
+ if (this.canvasEl === undefined)
231
+ return;
232
+ this.canvas = new ho(this.canvasEl, {
233
+ width: this.width,
234
+ height: this.height,
235
+ backgroundColor: '#ffffff',
236
+ selection: false,
237
+ });
238
+ this.canvas.on('object:modified', (e) => {
239
+ if (e.target === this.areaQuad) {
240
+ this.emitPrintArea();
241
+ }
242
+ });
243
+ this.canvas.on('object:moving', (e) => {
244
+ if (e.target === this.areaQuad) {
245
+ this.clampQuadToBounds();
246
+ }
247
+ });
248
+ await this.reloadCanvas();
249
+ }
250
+ /**
251
+ * Clear the canvas and reload background + quad.
252
+ * Keeps the Fabric Canvas instance alive (no dispose/recreate) to avoid
253
+ * stale DOM state from async dispose in Fabric v7.
254
+ */
255
+ async reloadCanvas() {
256
+ if (this.canvas === undefined)
257
+ return;
258
+ const gen = ++this.bgLoadGen;
259
+ // Remove all objects (background image + quad)
260
+ this.areaQuad = undefined;
261
+ this.canvas.discardActiveObject();
262
+ for (const obj of this.canvas.getObjects().slice()) {
263
+ this.canvas.remove(obj);
264
+ }
265
+ // Reset internal state to bounding-box dimensions
266
+ this.canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
267
+ this.canvas.setDimensions({ width: this.width, height: this.height });
268
+ this.canvas.backgroundColor = '#ffffff';
269
+ this.canvas.requestRenderAll();
270
+ // Load product image (may auto-resize canvas in contain mode)
271
+ if (this.productImage !== undefined && this.productImage !== '') {
272
+ await setCanvasBackground(this.canvas, this.productImage);
273
+ }
274
+ if (gen !== this.bgLoadGen)
275
+ return;
276
+ this.createAreaQuad();
277
+ this.canvas.renderAll();
278
+ }
279
+ createAreaQuad() {
280
+ if (this.canvas === undefined)
281
+ return;
282
+ const pa = this.printArea ?? defaultPrintArea();
283
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
284
+ const cw = this.canvas.getWidth();
285
+ const ch = this.canvas.getHeight();
286
+ const [tl, tr, br, bl] = printAreaToPixelCorners(pa, cw, ch);
287
+ // Compute centroid and bounding box
288
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
289
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
290
+ const xs = [tl.x, tr.x, br.x, bl.x];
291
+ const ys = [tl.y, tr.y, br.y, bl.y];
292
+ const bbW = Math.max(Math.max(...xs) - Math.min(...xs), 1);
293
+ const bbH = Math.max(Math.max(...ys) - Math.min(...ys), 1);
294
+ this.areaQuad = new PrintAreaQuad({
295
+ left: cx,
296
+ top: cy,
297
+ width: bbW,
298
+ height: bbH,
299
+ cornerOffsets: [
300
+ new N(tl.x - cx, tl.y - cy),
301
+ new N(tr.x - cx, tr.y - cy),
302
+ new N(br.x - cx, br.y - cy),
303
+ new N(bl.x - cx, bl.y - cy),
304
+ ],
305
+ bulge: pa.bulge ?? 0,
306
+ originX: 'center',
307
+ originY: 'center',
308
+ fill: 'rgba(37, 99, 235, 0.15)',
309
+ stroke: '#2563eb',
310
+ strokeWidth: 2,
311
+ strokeDashArray: [6, 4],
312
+ // Disable standard resize/rotate controls
313
+ lockScalingX: true,
314
+ lockScalingY: true,
315
+ lockRotation: true,
316
+ hasRotatingPoint: false,
317
+ borderColor: '#2563eb',
318
+ });
319
+ // Replace all controls with corner handles + bulge
320
+ this.areaQuad.controls = {
321
+ corner0: buildCornerControl(0),
322
+ corner1: buildCornerControl(1),
323
+ corner2: buildCornerControl(2),
324
+ corner3: buildCornerControl(3),
325
+ bulgeHandle: buildBulgeControl(),
326
+ };
327
+ this.canvas.add(this.areaQuad);
328
+ this.canvas.renderAll();
329
+ }
330
+ syncQuadFromPrintArea() {
331
+ if (this.areaQuad === undefined || this.canvas === undefined)
332
+ return;
333
+ const pa = this.printArea ?? defaultPrintArea();
334
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
335
+ const cw = this.canvas.getWidth();
336
+ const ch = this.canvas.getHeight();
337
+ const [tl, tr, br, bl] = printAreaToPixelCorners(pa, cw, ch);
338
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
339
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
340
+ const xs = [tl.x, tr.x, br.x, bl.x];
341
+ const ys = [tl.y, tr.y, br.y, bl.y];
342
+ this.areaQuad.cornerOffsets = [
343
+ new N(tl.x - cx, tl.y - cy),
344
+ new N(tr.x - cx, tr.y - cy),
345
+ new N(br.x - cx, br.y - cy),
346
+ new N(bl.x - cx, bl.y - cy),
347
+ ];
348
+ this.areaQuad.bulge = pa.bulge ?? 0;
349
+ this.areaQuad.set({
350
+ left: cx,
351
+ top: cy,
352
+ width: Math.max(Math.max(...xs) - Math.min(...xs), 1),
353
+ height: Math.max(Math.max(...ys) - Math.min(...ys), 1),
354
+ scaleX: 1,
355
+ scaleY: 1,
356
+ });
357
+ this.areaQuad.setCoords();
358
+ this.canvas.renderAll();
359
+ }
360
+ clampQuadToBounds() {
361
+ if (this.areaQuad === undefined)
362
+ return;
363
+ const center = this.areaQuad.getCenterPoint();
364
+ const abs = this.areaQuad.cornerOffsets.map(o => ({
365
+ x: center.x + o.x,
366
+ y: center.y + o.y,
367
+ }));
368
+ // Find how much the quad exceeds canvas bounds
369
+ let dx = 0;
370
+ let dy = 0;
371
+ for (const p of abs) {
372
+ if (p.x < 0)
373
+ dx = Math.max(dx, -p.x);
374
+ if (p.x > this.canvas.getWidth())
375
+ dx = Math.min(dx, this.canvas.getWidth() - p.x);
376
+ if (p.y < 0)
377
+ dy = Math.max(dy, -p.y);
378
+ if (p.y > this.canvas.getHeight())
379
+ dy = Math.min(dy, this.canvas.getHeight() - p.y);
380
+ }
381
+ if (dx !== 0 || dy !== 0) {
382
+ this.areaQuad.set({
383
+ left: (this.areaQuad.left ?? 0) + dx,
384
+ top: (this.areaQuad.top ?? 0) + dy,
385
+ });
386
+ }
387
+ }
388
+ readQuadAsPrintArea() {
389
+ if (this.areaQuad === undefined) {
390
+ return this.printArea ?? defaultPrintArea();
391
+ }
392
+ const center = this.areaQuad.getCenterPoint();
393
+ const corners = [
394
+ { x: center.x + this.areaQuad.cornerOffsets[0].x, y: center.y + this.areaQuad.cornerOffsets[0].y },
395
+ { x: center.x + this.areaQuad.cornerOffsets[1].x, y: center.y + this.areaQuad.cornerOffsets[1].y },
396
+ { x: center.x + this.areaQuad.cornerOffsets[2].x, y: center.y + this.areaQuad.cornerOffsets[2].y },
397
+ { x: center.x + this.areaQuad.cornerOffsets[3].x, y: center.y + this.areaQuad.cornerOffsets[3].y },
398
+ ];
399
+ // Convert canvas pixels directly to 0-1 image-relative coordinates
400
+ const cw = this.canvas.getWidth();
401
+ const ch = this.canvas.getHeight();
402
+ return pixelCornersToPrintArea(corners, cw, ch, this.areaQuad.bulge);
403
+ }
404
+ emitPrintArea() {
405
+ const area = this.readQuadAsPrintArea();
406
+ this.printArea = area;
407
+ this.wtpPrintAreaChange.emit(area);
408
+ }
409
+ render() {
410
+ return (h("div", { key: 'fbeaeb8ac49780440ceb1bffa38689a23aeef66a', class: "wtp-print-area-editor" }, h("canvas", { key: 'b7e97909efaa17c7ffb4b089cf31e508e20c094e', ref: el => (this.canvasEl = el) })));
411
+ }
412
+ static get watchers() { return {
413
+ "productImage": [{
414
+ "onProductImageChange": 0
415
+ }],
416
+ "width": [{
417
+ "onSizeChange": 0
418
+ }],
419
+ "height": [{
420
+ "onSizeChange": 0
421
+ }],
422
+ "printArea": [{
423
+ "onPrintAreaChange": 0
424
+ }]
425
+ }; }
426
+ };
427
+ WtpPrintAreaEditor.style = wtpPrintAreaEditorCss();
428
+
429
+ export { WtpPrintAreaEditor as wtp_print_area_editor };
@@ -0,0 +1 @@
1
+ module.exports = require('./cjs/index.cjs.js');
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './esm/index.js';
@@ -0,0 +1,101 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ import { EditorState, LogoData, PrintArea, EditorLabels } from '../../types';
3
+ export declare class WtpEditor {
4
+ el: HTMLElement;
5
+ /** Canvas width in pixels. */
6
+ width: number;
7
+ /** Canvas height in pixels. */
8
+ height: number;
9
+ /** Product background image URL. */
10
+ productImage: string | undefined;
11
+ /** JSON-serialized initial editor state. */
12
+ initialState: string | undefined;
13
+ /** Available font families for the text tool. */
14
+ fonts: string[];
15
+ /** Print area definition (0-1 relative coordinates) to constrain objects. */
16
+ printArea: PrintArea | undefined;
17
+ /** Show print area overlay and bounding box for debugging. */
18
+ debug: boolean;
19
+ /** Override any of the user-facing toolbar strings. Missing keys fall back to English defaults. */
20
+ labels: Partial<EditorLabels>;
21
+ private getLabels;
22
+ selectedObjectId: string | null;
23
+ selectedObjectType: string | null;
24
+ selectedFont: string;
25
+ selectedTextColor: string;
26
+ /** Fires when the canvas is initialized and ready. */
27
+ wtpEditorReady: EventEmitter<void>;
28
+ /** Fires when the editor state changes (object add/move/remove). */
29
+ wtpEditorStateChanged: EventEmitter<EditorState>;
30
+ /** Fires when an object is selected on the canvas. */
31
+ wtpEditorObjectSelected: EventEmitter<{
32
+ id: string;
33
+ type: string;
34
+ }>;
35
+ /** Fires when the current selection is cleared. */
36
+ wtpEditorObjectDeselected: EventEmitter<void>;
37
+ private canvas;
38
+ private canvasEl;
39
+ private objectMap;
40
+ private previewUrlMap;
41
+ /** Resolves when the current background image has been loaded and the canvas resized. */
42
+ private backgroundReady;
43
+ componentDidLoad(): void;
44
+ disconnectedCallback(): void;
45
+ onProductImageChange(): void;
46
+ onSizeChange(): void;
47
+ onDebugChange(): void;
48
+ onPrintAreaChange(): Promise<void>;
49
+ /** Add a logo image to the canvas and return its object ID. */
50
+ addLogo(logoData: LogoData): Promise<string>;
51
+ /** Add a text object to the canvas and return its object ID. */
52
+ addText(text: string, options?: {
53
+ fontFamily?: string;
54
+ fontSize?: number;
55
+ fill?: string;
56
+ }): Promise<string>;
57
+ /** Remove an object from the canvas by its ID. */
58
+ removeObject(id: string): Promise<void>;
59
+ /** Export the current editor state as a serializable object. */
60
+ exportState(): Promise<EditorState>;
61
+ /** Load a previously exported editor state. */
62
+ loadState(state: EditorState): Promise<void>;
63
+ /** Clear all user objects from the canvas, keeping the instance alive. */
64
+ resetCanvas(): Promise<void>;
65
+ /** Export the canvas as a data URL image. */
66
+ exportImage(format?: 'png' | 'jpeg', quality?: number): Promise<string>;
67
+ /** Export the canvas as a high-resolution data URL image (for PDF/print).
68
+ * Returns the data URL plus the actual canvas dimensions (which may differ
69
+ * from the width/height props after setCanvasBackground resizes the canvas). */
70
+ exportImageHighRes(format?: 'png' | 'jpeg', quality?: number, multiplier?: number): Promise<{
71
+ dataUrl: string;
72
+ width: number;
73
+ height: number;
74
+ }>;
75
+ /** Get a list of all objects on the canvas with their IDs and types. */
76
+ getObjects(): Promise<{
77
+ id: string;
78
+ type: string;
79
+ }[]>;
80
+ /** Update the text content of a text object by its ID. */
81
+ updateText(id: string, text: string): Promise<void>;
82
+ private initCanvas;
83
+ /** Draw print area outline and bounding box on the canvas when debug mode is active. */
84
+ private drawDebugOverlay;
85
+ /**
86
+ * Get the print area's rotated frame: center, local axes, and dimensions
87
+ * along those axes. This lets us clamp in the print area's own coordinate
88
+ * system instead of using an axis-aligned bounding box.
89
+ */
90
+ private getPrintAreaFrame;
91
+ private clampObjectToPrintArea;
92
+ private handleSelection;
93
+ private getObjectTransform;
94
+ private buildEditorState;
95
+ private emitStateChanged;
96
+ private handleAddText;
97
+ private handleFontChange;
98
+ private handleColorChange;
99
+ private handleDeleteSelected;
100
+ render(): any;
101
+ }
@@ -0,0 +1,55 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ import { PlacedLogo, PrintArea } from '../../types';
3
+ interface RenderedLayer {
4
+ id: string;
5
+ src: string;
6
+ exportImg: HTMLImageElement;
7
+ naturalWidth: number;
8
+ naturalHeight: number;
9
+ left: number;
10
+ top: number;
11
+ scaleX: number;
12
+ scaleY: number;
13
+ angle: number;
14
+ skewX: number;
15
+ skewY: number;
16
+ }
17
+ export declare class WtpLogoRenderer {
18
+ el: HTMLElement;
19
+ /** Product background image URL. */
20
+ productImage: string | undefined;
21
+ /** Container width in pixels. */
22
+ width: number;
23
+ /** Container height in pixels. */
24
+ height: number;
25
+ /** Array of logos to place on the renderer. */
26
+ logos: PlacedLogo[];
27
+ /** Background color. */
28
+ backgroundColor: string;
29
+ /** Print area definition for auto-fitting logos (relative 0-1 coordinates). */
30
+ printArea: PrintArea | undefined;
31
+ /** Fires when the renderer has finished rendering all logos. */
32
+ wtpRenderComplete: EventEmitter<{
33
+ dataUrl: string;
34
+ }>;
35
+ /** Fires when a rendering error occurs. */
36
+ wtpRenderError: EventEmitter<{
37
+ message: string;
38
+ }>;
39
+ layers: RenderedLayer[];
40
+ containerWidth: number;
41
+ containerHeight: number;
42
+ private productImg;
43
+ componentWillLoad(): void;
44
+ onProductImageChange(): void;
45
+ onLogosChange(): void;
46
+ onPrintAreaChange(): void;
47
+ onSizeChange(): void;
48
+ onBackgroundColorChange(): void;
49
+ /** Export the rendered scene as a data URL image. */
50
+ exportImage(format?: 'png' | 'jpeg', quality?: number): Promise<string>;
51
+ private computeLayout;
52
+ private emitRenderComplete;
53
+ render(): any;
54
+ }
55
+ export {};
@@ -0,0 +1,76 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ import { LogoValidationConfig, LogoData, LogoValidationIssue, LogoMetadata, BgRemovalConfig, LogoUploadLabels } from '../../types';
3
+ interface BgRemovalChoice {
4
+ originalDataUrl: string;
5
+ removedBgDataUrl: string | null;
6
+ removedBgWidth: number | null;
7
+ removedBgHeight: number | null;
8
+ metadata: LogoMetadata;
9
+ status: 'processing' | 'ready' | 'error';
10
+ errorMessage: string | null;
11
+ }
12
+ export declare class WtpLogoUpload {
13
+ /** Validation rules for uploaded logos. */
14
+ config: LogoValidationConfig;
15
+ /** Accepted file MIME types for the file input. */
16
+ accept: string;
17
+ /** Whether multiple files can be uploaded at once. */
18
+ multiple: boolean;
19
+ /** Disables the upload component. */
20
+ disabled: boolean;
21
+ /** Enables background removal for raster images after upload. */
22
+ enableBackgroundRemoval: boolean;
23
+ /** Configuration for the color-based background removal algorithm. */
24
+ bgRemovalConfig: Partial<BgRemovalConfig>;
25
+ /** Override any of the user-facing strings. Missing keys fall back to English defaults. */
26
+ labels: Partial<LogoUploadLabels>;
27
+ private getLabels;
28
+ isDragOver: boolean;
29
+ previews: LogoData[];
30
+ selectedIndex: number;
31
+ rejections: {
32
+ fileName: string;
33
+ issues: LogoValidationIssue[];
34
+ }[];
35
+ isProcessing: boolean;
36
+ pendingChoices: BgRemovalChoice[];
37
+ urlInput: string;
38
+ urlError: string | null;
39
+ isUrlFetching: boolean;
40
+ /** Fires when a logo passes validation and is ready for use. */
41
+ wtpLogoValidated: EventEmitter<LogoData>;
42
+ /** Fires when a logo fails validation. */
43
+ wtpLogoRejected: EventEmitter<{
44
+ file: File;
45
+ issues: LogoValidationIssue[];
46
+ }>;
47
+ /** Fires when processing state changes (true = busy, false = idle). */
48
+ wtpLogoProcessing: EventEmitter<boolean>;
49
+ /** Fires when a logo is selected from the preview gallery. */
50
+ wtpLogoSelected: EventEmitter<LogoData>;
51
+ private fileInputRef;
52
+ private buildLogoData;
53
+ private isRasterFormat;
54
+ private processFiles;
55
+ private addPendingChoice;
56
+ private performBackgroundRemoval;
57
+ private selectChoice;
58
+ private extractFileNameFromUrl;
59
+ private handleUrlSubmit;
60
+ private fileToDataUrl;
61
+ private handleDragOver;
62
+ private handleDragLeave;
63
+ private handleDrop;
64
+ private handleInputChange;
65
+ private handleClick;
66
+ private handleKeyDown;
67
+ private handleUrlInput;
68
+ private handleUrlInputKeyDown;
69
+ private handleUrlSubmitClick;
70
+ private handleSelectOriginal;
71
+ private handleSelectRemoved;
72
+ private handleSelectPreview;
73
+ private handleRemovePreview;
74
+ render(): any;
75
+ }
76
+ export {};