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,431 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-IFGFRm-i.js');
4
+ var canvasHelpers = require('./canvas-helpers-A6rp5rPD.js');
5
+
6
+ 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}`;
7
+
8
+ const CORNER_RADIUS = 7;
9
+ /**
10
+ * Custom Fabric.js object that renders a quadrilateral defined by 4 corner
11
+ * offsets from the bounding-box center. Top/bottom edges can be curved
12
+ * via `bulge` (-1 to 1) using quadratic Bezier curves.
13
+ */
14
+ class PrintAreaQuad extends canvasHelpers.J {
15
+ constructor(options) {
16
+ super(options);
17
+ this.cornerOffsets = options?.cornerOffsets ?? [
18
+ new canvasHelpers.N(-50, -50), new canvasHelpers.N(50, -50),
19
+ new canvasHelpers.N(50, 50), new canvasHelpers.N(-50, 50),
20
+ ];
21
+ this.bulge = options?.bulge ?? 0;
22
+ this.objectCaching = false;
23
+ }
24
+ _render(ctx) {
25
+ const [tl, tr, br, bl] = this.cornerOffsets;
26
+ ctx.beginPath();
27
+ ctx.moveTo(tl.x, tl.y);
28
+ if (this.bulge !== 0) {
29
+ // Curved top edge: control point at midpoint shifted by bulge
30
+ const topMidX = (tl.x + tr.x) / 2;
31
+ const topMidY = (tl.y + tr.y) / 2;
32
+ const bulgePixels = this.bulge * (this.height ?? 1);
33
+ ctx.quadraticCurveTo(topMidX, topMidY - bulgePixels, tr.x, tr.y);
34
+ }
35
+ else {
36
+ ctx.lineTo(tr.x, tr.y);
37
+ }
38
+ ctx.lineTo(br.x, br.y);
39
+ if (this.bulge !== 0) {
40
+ // Curved bottom edge: same direction as top for cylindrical appearance
41
+ const botMidX = (br.x + bl.x) / 2;
42
+ const botMidY = (br.y + bl.y) / 2;
43
+ const bulgePixels = this.bulge * (this.height ?? 1);
44
+ ctx.quadraticCurveTo(botMidX, botMidY - bulgePixels, bl.x, bl.y);
45
+ }
46
+ else {
47
+ ctx.lineTo(bl.x, bl.y);
48
+ }
49
+ ctx.closePath();
50
+ this._renderPaintInOrder(ctx);
51
+ }
52
+ /** Recompute left/top/width/height from absolute corner positions. */
53
+ recalcBounds() {
54
+ const center = this.getCenterPoint();
55
+ const abs = this.cornerOffsets.map(o => new canvasHelpers.N(center.x + o.x, center.y + o.y));
56
+ const xs = abs.map(p => p.x);
57
+ const ys = abs.map(p => p.y);
58
+ const minX = Math.min(...xs);
59
+ const maxX = Math.max(...xs);
60
+ const minY = Math.min(...ys);
61
+ const maxY = Math.max(...ys);
62
+ const newCenterX = (minX + maxX) / 2;
63
+ const newCenterY = (minY + maxY) / 2;
64
+ this.cornerOffsets = abs.map(p => new canvasHelpers.N(p.x - newCenterX, p.y - newCenterY));
65
+ this.set({
66
+ left: newCenterX,
67
+ top: newCenterY,
68
+ width: Math.max(maxX - minX, 1),
69
+ height: Math.max(maxY - minY, 1),
70
+ });
71
+ this.setCoords();
72
+ }
73
+ }
74
+ function buildCornerControl(index) {
75
+ return new canvasHelpers.q({
76
+ x: 0,
77
+ y: 0,
78
+ sizeX: CORNER_RADIUS * 2,
79
+ sizeY: CORNER_RADIUS * 2,
80
+ touchSizeX: CORNER_RADIUS * 4,
81
+ touchSizeY: CORNER_RADIUS * 4,
82
+ cursorStyleHandler: () => 'move',
83
+ positionHandler: (_dim, finalMatrix, fabricObject) => {
84
+ const quad = fabricObject;
85
+ return new canvasHelpers.N(quad.cornerOffsets[index].x, quad.cornerOffsets[index].y).transform(finalMatrix);
86
+ },
87
+ actionHandler: (_eventData, transformData, x, y) => {
88
+ const quad = transformData.target;
89
+ const center = quad.getCenterPoint();
90
+ quad.cornerOffsets[index] = new canvasHelpers.N(x - center.x, y - center.y);
91
+ quad.recalcBounds();
92
+ return true;
93
+ },
94
+ render: (ctx, left, top) => {
95
+ ctx.save();
96
+ ctx.fillStyle = '#2563eb';
97
+ ctx.strokeStyle = '#ffffff';
98
+ ctx.lineWidth = 2;
99
+ ctx.beginPath();
100
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
101
+ ctx.fill();
102
+ ctx.stroke();
103
+ ctx.restore();
104
+ },
105
+ });
106
+ }
107
+ function buildBulgeControl() {
108
+ const HANDLE_OFFSET_X = 24;
109
+ const TRACK_GAP = 4;
110
+ return new canvasHelpers.q({
111
+ x: 0.5,
112
+ y: 0,
113
+ offsetX: HANDLE_OFFSET_X,
114
+ sizeX: CORNER_RADIUS * 2,
115
+ sizeY: CORNER_RADIUS * 2,
116
+ touchSizeX: CORNER_RADIUS * 4,
117
+ touchSizeY: CORNER_RADIUS * 4,
118
+ cursorStyleHandler: () => 'ns-resize',
119
+ positionHandler: (dim, finalMatrix, fabricObject) => {
120
+ const quad = fabricObject;
121
+ return new canvasHelpers.N(0.5 * dim.x + HANDLE_OFFSET_X, -quad.bulge * dim.y / 2).transform(finalMatrix);
122
+ },
123
+ actionHandler: (_eventData, transformData, _x, y) => {
124
+ const quad = transformData.target;
125
+ const center = quad.getCenterPoint();
126
+ const halfH = (quad.height ?? 0) * (quad.scaleY ?? 1) / 2;
127
+ const bulge = Math.max(-1, Math.min(1, (center.y - y) / Math.max(halfH, 1)));
128
+ quad.bulge = bulge;
129
+ return true;
130
+ },
131
+ render: (ctx, left, top, _styleOverride, fabricObject) => {
132
+ const quad = fabricObject;
133
+ const halfH = ((quad.height ?? 0) * (quad.scaleY ?? 1)) / 2;
134
+ const angleRad = ((quad.angle ?? 0) * Math.PI) / 180;
135
+ const trackTopX = left + Math.sin(angleRad) * halfH;
136
+ const trackTopY = top - Math.cos(angleRad) * halfH;
137
+ const trackBotX = left - Math.sin(angleRad) * halfH;
138
+ const trackBotY = top + Math.cos(angleRad) * halfH;
139
+ ctx.save();
140
+ // Track line
141
+ ctx.strokeStyle = 'rgba(37, 99, 235, 0.3)';
142
+ ctx.lineWidth = 2;
143
+ ctx.setLineDash([3, 3]);
144
+ ctx.beginPath();
145
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
146
+ ctx.lineTo(trackBotX, trackBotY + TRACK_GAP);
147
+ ctx.stroke();
148
+ ctx.setLineDash([]);
149
+ // Handle circle
150
+ ctx.fillStyle = '#2563eb';
151
+ ctx.strokeStyle = '#ffffff';
152
+ ctx.lineWidth = 2;
153
+ ctx.beginPath();
154
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
155
+ ctx.fill();
156
+ ctx.stroke();
157
+ // Arrow up + "+" label
158
+ ctx.fillStyle = 'rgba(37, 99, 235, 0.5)';
159
+ ctx.beginPath();
160
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
161
+ ctx.lineTo(trackTopX - 4, trackTopY - TRACK_GAP + 6);
162
+ ctx.lineTo(trackTopX + 4, trackTopY - TRACK_GAP + 6);
163
+ ctx.closePath();
164
+ ctx.fill();
165
+ ctx.font = 'bold 10px sans-serif';
166
+ ctx.textAlign = 'center';
167
+ ctx.fillText('+', trackTopX, trackTopY - TRACK_GAP - 4);
168
+ // Arrow down + "\u2212" label
169
+ ctx.beginPath();
170
+ ctx.moveTo(trackBotX, trackBotY + TRACK_GAP);
171
+ ctx.lineTo(trackBotX - 4, trackBotY + TRACK_GAP - 6);
172
+ ctx.lineTo(trackBotX + 4, trackBotY + TRACK_GAP - 6);
173
+ ctx.closePath();
174
+ ctx.fill();
175
+ ctx.fillText('\u2212', trackBotX, trackBotY + TRACK_GAP + 12);
176
+ ctx.restore();
177
+ },
178
+ });
179
+ }
180
+ const WtpPrintAreaEditor = class {
181
+ constructor(hostRef) {
182
+ index.registerInstance(this, hostRef);
183
+ this.wtpPrintAreaChange = index.createEvent(this, "wtpPrintAreaChange");
184
+ }
185
+ get el() { return index.getElement(this); }
186
+ /** Product background image URL. */
187
+ productImage;
188
+ /** Canvas width in pixels. */
189
+ width = 800;
190
+ /** Canvas height in pixels. */
191
+ height = 600;
192
+ /** Current print area (relative 0-1 coordinates). */
193
+ printArea;
194
+ /** Fires when the print area rectangle is modified. */
195
+ wtpPrintAreaChange;
196
+ canvas;
197
+ canvasEl;
198
+ areaQuad;
199
+ /** Generation counter to discard stale background loads. */
200
+ bgLoadGen = 0;
201
+ componentDidLoad() {
202
+ this.initCanvas();
203
+ }
204
+ disconnectedCallback() {
205
+ ++this.bgLoadGen;
206
+ this.canvas?.dispose();
207
+ }
208
+ async onProductImageChange() {
209
+ if (this.canvas === undefined || this.productImage === undefined)
210
+ return;
211
+ await this.reloadCanvas();
212
+ }
213
+ async onSizeChange() {
214
+ if (this.canvas === undefined)
215
+ return;
216
+ await this.reloadCanvas();
217
+ }
218
+ onPrintAreaChange() {
219
+ this.syncQuadFromPrintArea();
220
+ }
221
+ /** Get the current print area as relative 0-1 coordinates. */
222
+ async getPrintArea() {
223
+ return this.readQuadAsPrintArea();
224
+ }
225
+ /** Set the print area and update the quad on canvas. */
226
+ async setPrintArea(printArea) {
227
+ this.printArea = printArea;
228
+ this.syncQuadFromPrintArea();
229
+ }
230
+ /** Create the Fabric Canvas once and perform the initial load. */
231
+ async initCanvas() {
232
+ if (this.canvasEl === undefined)
233
+ return;
234
+ this.canvas = new canvasHelpers.ho(this.canvasEl, {
235
+ width: this.width,
236
+ height: this.height,
237
+ backgroundColor: '#ffffff',
238
+ selection: false,
239
+ });
240
+ this.canvas.on('object:modified', (e) => {
241
+ if (e.target === this.areaQuad) {
242
+ this.emitPrintArea();
243
+ }
244
+ });
245
+ this.canvas.on('object:moving', (e) => {
246
+ if (e.target === this.areaQuad) {
247
+ this.clampQuadToBounds();
248
+ }
249
+ });
250
+ await this.reloadCanvas();
251
+ }
252
+ /**
253
+ * Clear the canvas and reload background + quad.
254
+ * Keeps the Fabric Canvas instance alive (no dispose/recreate) to avoid
255
+ * stale DOM state from async dispose in Fabric v7.
256
+ */
257
+ async reloadCanvas() {
258
+ if (this.canvas === undefined)
259
+ return;
260
+ const gen = ++this.bgLoadGen;
261
+ // Remove all objects (background image + quad)
262
+ this.areaQuad = undefined;
263
+ this.canvas.discardActiveObject();
264
+ for (const obj of this.canvas.getObjects().slice()) {
265
+ this.canvas.remove(obj);
266
+ }
267
+ // Reset internal state to bounding-box dimensions
268
+ this.canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
269
+ this.canvas.setDimensions({ width: this.width, height: this.height });
270
+ this.canvas.backgroundColor = '#ffffff';
271
+ this.canvas.requestRenderAll();
272
+ // Load product image (may auto-resize canvas in contain mode)
273
+ if (this.productImage !== undefined && this.productImage !== '') {
274
+ await canvasHelpers.setCanvasBackground(this.canvas, this.productImage);
275
+ }
276
+ if (gen !== this.bgLoadGen)
277
+ return;
278
+ this.createAreaQuad();
279
+ this.canvas.renderAll();
280
+ }
281
+ createAreaQuad() {
282
+ if (this.canvas === undefined)
283
+ return;
284
+ const pa = this.printArea ?? canvasHelpers.defaultPrintArea();
285
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
286
+ const cw = this.canvas.getWidth();
287
+ const ch = this.canvas.getHeight();
288
+ const [tl, tr, br, bl] = canvasHelpers.printAreaToPixelCorners(pa, cw, ch);
289
+ // Compute centroid and bounding box
290
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
291
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
292
+ const xs = [tl.x, tr.x, br.x, bl.x];
293
+ const ys = [tl.y, tr.y, br.y, bl.y];
294
+ const bbW = Math.max(Math.max(...xs) - Math.min(...xs), 1);
295
+ const bbH = Math.max(Math.max(...ys) - Math.min(...ys), 1);
296
+ this.areaQuad = new PrintAreaQuad({
297
+ left: cx,
298
+ top: cy,
299
+ width: bbW,
300
+ height: bbH,
301
+ cornerOffsets: [
302
+ new canvasHelpers.N(tl.x - cx, tl.y - cy),
303
+ new canvasHelpers.N(tr.x - cx, tr.y - cy),
304
+ new canvasHelpers.N(br.x - cx, br.y - cy),
305
+ new canvasHelpers.N(bl.x - cx, bl.y - cy),
306
+ ],
307
+ bulge: pa.bulge ?? 0,
308
+ originX: 'center',
309
+ originY: 'center',
310
+ fill: 'rgba(37, 99, 235, 0.15)',
311
+ stroke: '#2563eb',
312
+ strokeWidth: 2,
313
+ strokeDashArray: [6, 4],
314
+ // Disable standard resize/rotate controls
315
+ lockScalingX: true,
316
+ lockScalingY: true,
317
+ lockRotation: true,
318
+ hasRotatingPoint: false,
319
+ borderColor: '#2563eb',
320
+ });
321
+ // Replace all controls with corner handles + bulge
322
+ this.areaQuad.controls = {
323
+ corner0: buildCornerControl(0),
324
+ corner1: buildCornerControl(1),
325
+ corner2: buildCornerControl(2),
326
+ corner3: buildCornerControl(3),
327
+ bulgeHandle: buildBulgeControl(),
328
+ };
329
+ this.canvas.add(this.areaQuad);
330
+ this.canvas.renderAll();
331
+ }
332
+ syncQuadFromPrintArea() {
333
+ if (this.areaQuad === undefined || this.canvas === undefined)
334
+ return;
335
+ const pa = this.printArea ?? canvasHelpers.defaultPrintArea();
336
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
337
+ const cw = this.canvas.getWidth();
338
+ const ch = this.canvas.getHeight();
339
+ const [tl, tr, br, bl] = canvasHelpers.printAreaToPixelCorners(pa, cw, ch);
340
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
341
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
342
+ const xs = [tl.x, tr.x, br.x, bl.x];
343
+ const ys = [tl.y, tr.y, br.y, bl.y];
344
+ this.areaQuad.cornerOffsets = [
345
+ new canvasHelpers.N(tl.x - cx, tl.y - cy),
346
+ new canvasHelpers.N(tr.x - cx, tr.y - cy),
347
+ new canvasHelpers.N(br.x - cx, br.y - cy),
348
+ new canvasHelpers.N(bl.x - cx, bl.y - cy),
349
+ ];
350
+ this.areaQuad.bulge = pa.bulge ?? 0;
351
+ this.areaQuad.set({
352
+ left: cx,
353
+ top: cy,
354
+ width: Math.max(Math.max(...xs) - Math.min(...xs), 1),
355
+ height: Math.max(Math.max(...ys) - Math.min(...ys), 1),
356
+ scaleX: 1,
357
+ scaleY: 1,
358
+ });
359
+ this.areaQuad.setCoords();
360
+ this.canvas.renderAll();
361
+ }
362
+ clampQuadToBounds() {
363
+ if (this.areaQuad === undefined)
364
+ return;
365
+ const center = this.areaQuad.getCenterPoint();
366
+ const abs = this.areaQuad.cornerOffsets.map(o => ({
367
+ x: center.x + o.x,
368
+ y: center.y + o.y,
369
+ }));
370
+ // Find how much the quad exceeds canvas bounds
371
+ let dx = 0;
372
+ let dy = 0;
373
+ for (const p of abs) {
374
+ if (p.x < 0)
375
+ dx = Math.max(dx, -p.x);
376
+ if (p.x > this.canvas.getWidth())
377
+ dx = Math.min(dx, this.canvas.getWidth() - p.x);
378
+ if (p.y < 0)
379
+ dy = Math.max(dy, -p.y);
380
+ if (p.y > this.canvas.getHeight())
381
+ dy = Math.min(dy, this.canvas.getHeight() - p.y);
382
+ }
383
+ if (dx !== 0 || dy !== 0) {
384
+ this.areaQuad.set({
385
+ left: (this.areaQuad.left ?? 0) + dx,
386
+ top: (this.areaQuad.top ?? 0) + dy,
387
+ });
388
+ }
389
+ }
390
+ readQuadAsPrintArea() {
391
+ if (this.areaQuad === undefined) {
392
+ return this.printArea ?? canvasHelpers.defaultPrintArea();
393
+ }
394
+ const center = this.areaQuad.getCenterPoint();
395
+ const corners = [
396
+ { x: center.x + this.areaQuad.cornerOffsets[0].x, y: center.y + this.areaQuad.cornerOffsets[0].y },
397
+ { x: center.x + this.areaQuad.cornerOffsets[1].x, y: center.y + this.areaQuad.cornerOffsets[1].y },
398
+ { x: center.x + this.areaQuad.cornerOffsets[2].x, y: center.y + this.areaQuad.cornerOffsets[2].y },
399
+ { x: center.x + this.areaQuad.cornerOffsets[3].x, y: center.y + this.areaQuad.cornerOffsets[3].y },
400
+ ];
401
+ // Convert canvas pixels directly to 0-1 image-relative coordinates
402
+ const cw = this.canvas.getWidth();
403
+ const ch = this.canvas.getHeight();
404
+ return canvasHelpers.pixelCornersToPrintArea(corners, cw, ch, this.areaQuad.bulge);
405
+ }
406
+ emitPrintArea() {
407
+ const area = this.readQuadAsPrintArea();
408
+ this.printArea = area;
409
+ this.wtpPrintAreaChange.emit(area);
410
+ }
411
+ render() {
412
+ return (index.h("div", { key: 'fbeaeb8ac49780440ceb1bffa38689a23aeef66a', class: "wtp-print-area-editor" }, index.h("canvas", { key: 'b7e97909efaa17c7ffb4b089cf31e508e20c094e', ref: el => (this.canvasEl = el) })));
413
+ }
414
+ static get watchers() { return {
415
+ "productImage": [{
416
+ "onProductImageChange": 0
417
+ }],
418
+ "width": [{
419
+ "onSizeChange": 0
420
+ }],
421
+ "height": [{
422
+ "onSizeChange": 0
423
+ }],
424
+ "printArea": [{
425
+ "onPrintAreaChange": 0
426
+ }]
427
+ }; }
428
+ };
429
+ WtpPrintAreaEditor.style = wtpPrintAreaEditorCss();
430
+
431
+ exports.wtp_print_area_editor = WtpPrintAreaEditor;
@@ -0,0 +1,16 @@
1
+ {
2
+ "entries": [
3
+ "components/wtp-editor/wtp-editor.js",
4
+ "components/wtp-logo-renderer/wtp-logo-renderer.js",
5
+ "components/wtp-logo-upload/wtp-logo-upload.js",
6
+ "components/wtp-print-area-editor/wtp-print-area-editor.js"
7
+ ],
8
+ "mixins": [],
9
+ "compiler": {
10
+ "name": "@stencil/core",
11
+ "version": "4.43.4",
12
+ "typescriptVersion": "5.8.3"
13
+ },
14
+ "collections": [],
15
+ "bundles": []
16
+ }
@@ -0,0 +1,124 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :host {
8
+ font-family: var(--wtp-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
9
+ color: var(--wtp-color-text, #1e293b);
10
+ line-height: 1.5;
11
+ }
12
+
13
+ .wtp-editor {
14
+ display: flex;
15
+ flex-direction: column;
16
+ border: 1px solid var(--wtp-color-border, #e2e8f0);
17
+ border-radius: 12px;
18
+ overflow: hidden;
19
+ background: var(--wtp-color-bg, #ffffff);
20
+ }
21
+
22
+ .toolbar {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 8px;
26
+ padding: 8px 16px;
27
+ border-bottom: 1px solid var(--wtp-color-border, #e2e8f0);
28
+ background: var(--wtp-color-bg-muted, #f8fafc);
29
+ flex-wrap: wrap;
30
+ }
31
+
32
+ .toolbar-btn {
33
+ display: inline-flex;
34
+ align-items: center;
35
+ gap: 8px;
36
+ padding: 8px 16px;
37
+ font-family: var(--wtp-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
38
+ font-size: 14px;
39
+ font-weight: 500;
40
+ border-radius: 8px;
41
+ border: 1px solid var(--wtp-color-border, #e2e8f0);
42
+ background: var(--wtp-color-bg, #ffffff);
43
+ color: var(--wtp-color-text, #1e293b);
44
+ cursor: pointer;
45
+ transition-property: all;
46
+ transition-duration: 250ms;
47
+ transition-timing-function: ease;
48
+ }
49
+ .toolbar-btn:hover {
50
+ background: var(--wtp-color-bg-muted, #f8fafc);
51
+ }
52
+ .toolbar-btn:focus-visible {
53
+ outline: 2px solid var(--wtp-color-primary, #2563eb);
54
+ outline-offset: 2px;
55
+ }
56
+ .toolbar-btn:disabled {
57
+ opacity: 0.5;
58
+ cursor: not-allowed;
59
+ }
60
+ .toolbar-btn {
61
+ padding: 4px 8px;
62
+ font-size: 12px;
63
+ }
64
+ .toolbar-btn svg {
65
+ width: 16px;
66
+ height: 16px;
67
+ flex-shrink: 0;
68
+ }
69
+ .toolbar-btn.danger:hover:not(:disabled) {
70
+ background: var(--wtp-color-error-light, #fee2e2);
71
+ color: var(--wtp-color-error, #dc2626);
72
+ border-color: var(--wtp-color-error, #dc2626);
73
+ }
74
+
75
+ .font-select {
76
+ padding: 4px 8px;
77
+ font-family: var(--wtp-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
78
+ font-size: 12px;
79
+ border: 1px solid var(--wtp-color-border, #e2e8f0);
80
+ border-radius: 8px;
81
+ background: var(--wtp-color-bg, #ffffff);
82
+ color: var(--wtp-color-text, #1e293b);
83
+ cursor: pointer;
84
+ transition-property: all;
85
+ transition-duration: 250ms;
86
+ transition-timing-function: ease;
87
+ }
88
+ .font-select:focus-visible {
89
+ outline: 2px solid var(--wtp-color-primary, #2563eb);
90
+ outline-offset: 2px;
91
+ }
92
+
93
+ .color-input {
94
+ width: 32px;
95
+ height: 28px;
96
+ padding: 2px;
97
+ border: 1px solid var(--wtp-color-border, #e2e8f0);
98
+ border-radius: 8px;
99
+ background: var(--wtp-color-bg, #ffffff);
100
+ cursor: pointer;
101
+ transition-property: all;
102
+ transition-duration: 250ms;
103
+ transition-timing-function: ease;
104
+ }
105
+ .color-input:focus-visible {
106
+ outline: 2px solid var(--wtp-color-primary, #2563eb);
107
+ outline-offset: 2px;
108
+ }
109
+
110
+ .toolbar-separator {
111
+ width: 1px;
112
+ height: 24px;
113
+ background: var(--wtp-color-border, #e2e8f0);
114
+ }
115
+
116
+ .toolbar-spacer {
117
+ flex: 1;
118
+ }
119
+
120
+ .canvas-container {
121
+ line-height: 0;
122
+ overflow: auto;
123
+ background: repeating-conic-gradient(#e5e7eb 0% 25%, transparent 0% 50%) 50%/20px 20px;
124
+ }