@varianta/sdk 0.1.4 → 0.1.6
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/README.md +28 -18
- package/dist/index.cjs.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +331 -318
- package/dist/index.esm.js.map +1 -1
- package/dist/react.cjs.js +2 -0
- package/dist/react.cjs.js.map +1 -0
- package/dist/react.esm.js +303 -0
- package/dist/react.esm.js.map +1 -0
- package/dist/types/react/Customizer.d.ts +19 -0
- package/dist/types/react/Customizer.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +19 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/vanilla/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/dist/index.umd.js +0 -10
- package/dist/index.umd.js.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -129,7 +129,7 @@ class N {
|
|
|
129
129
|
getCenteredPan(e) {
|
|
130
130
|
if (!this.artboard)
|
|
131
131
|
return null;
|
|
132
|
-
const { width: t, height: i } = this.artboard,
|
|
132
|
+
const { width: t, height: i } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s, o = this.canvas.height / s;
|
|
133
133
|
return {
|
|
134
134
|
x: (a / e - t) / 2,
|
|
135
135
|
y: (o / e - i) / 2
|
|
@@ -147,10 +147,10 @@ class N {
|
|
|
147
147
|
fitToView() {
|
|
148
148
|
if (!this.artboard)
|
|
149
149
|
return;
|
|
150
|
-
const e = 0, { width: t, height: i } = this.artboard,
|
|
150
|
+
const e = 0, { width: t, height: i } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s - e * 2, o = this.canvas.height / s - e * 2;
|
|
151
151
|
if (a <= 0 || o <= 0)
|
|
152
152
|
return;
|
|
153
|
-
const
|
|
153
|
+
const n = a / t, r = o / i, d = Math.min(n, r), l = this.canvas.width / s, c = this.canvas.height / s, u = (l / d - t) / 2, p = (c / d - i) / 2;
|
|
154
154
|
this.zoom = d, this.pan = { x: u, y: p }, this.requestRender();
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
@@ -181,8 +181,8 @@ class N {
|
|
|
181
181
|
* Convert screen coordinates to canvas coordinates
|
|
182
182
|
*/
|
|
183
183
|
screenToCanvas(e, t) {
|
|
184
|
-
const i = window.devicePixelRatio || 1,
|
|
185
|
-
return { x: o, y:
|
|
184
|
+
const i = window.devicePixelRatio || 1, s = e / i, a = t / i, o = s / this.zoom - this.pan.x, n = a / this.zoom - this.pan.y;
|
|
185
|
+
return { x: o, y: n };
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
188
|
* Get content bounds for an area (in artboard coordinates)
|
|
@@ -194,9 +194,9 @@ class N {
|
|
|
194
194
|
const i = this.contents.get(e);
|
|
195
195
|
if (!i)
|
|
196
196
|
return null;
|
|
197
|
-
const { location:
|
|
197
|
+
const { location: s } = t, { x: a, y: o, width: n, height: r } = s;
|
|
198
198
|
if (i.type === "text") {
|
|
199
|
-
const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u =
|
|
199
|
+
const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u = n * l, p = r * l, g = a + n / 2 + d.x, m = o + r / 2 + d.y;
|
|
200
200
|
return {
|
|
201
201
|
x: g - u / 2,
|
|
202
202
|
y: m - p / 2,
|
|
@@ -210,10 +210,10 @@ class N {
|
|
|
210
210
|
const d = this.loadedImages.get(e);
|
|
211
211
|
if (!d)
|
|
212
212
|
return null;
|
|
213
|
-
const l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, u = i.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, g =
|
|
213
|
+
const l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, u = i.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, g = n / r;
|
|
214
214
|
let m, v;
|
|
215
|
-
p > g ? (m =
|
|
216
|
-
const b = m * c, y = v * c, x = a +
|
|
215
|
+
p > g ? (m = n, v = n / p) : (v = r, m = r * p);
|
|
216
|
+
const b = m * c, y = v * c, x = a + n / 2 + l.x, w = o + r / 2 + l.y;
|
|
217
217
|
return {
|
|
218
218
|
x: x - b / 2,
|
|
219
219
|
y: w - y / 2,
|
|
@@ -234,9 +234,9 @@ class N {
|
|
|
234
234
|
if (!this.areaSelectionEnabled)
|
|
235
235
|
return null;
|
|
236
236
|
for (let i = this.areas.length - 1; i >= 0; i--) {
|
|
237
|
-
const
|
|
237
|
+
const s = this.areas[i], a = this.getContentBounds(s.id);
|
|
238
238
|
if (a && this.isPointInBounds(e, t, a))
|
|
239
|
-
return
|
|
239
|
+
return s.id;
|
|
240
240
|
}
|
|
241
241
|
return null;
|
|
242
242
|
}
|
|
@@ -248,9 +248,9 @@ class N {
|
|
|
248
248
|
if (!this.areaSelectionEnabled)
|
|
249
249
|
return null;
|
|
250
250
|
for (let i = this.areas.length - 1; i >= 0; i--) {
|
|
251
|
-
const
|
|
252
|
-
if (e >= a && e <= a +
|
|
253
|
-
return
|
|
251
|
+
const s = this.areas[i], { x: a, y: o, width: n, height: r } = s.location;
|
|
252
|
+
if (e >= a && e <= a + n && t >= o && t <= o + r)
|
|
253
|
+
return s.id;
|
|
254
254
|
}
|
|
255
255
|
return null;
|
|
256
256
|
}
|
|
@@ -258,8 +258,8 @@ class N {
|
|
|
258
258
|
* Test if a point is inside rotated bounds
|
|
259
259
|
*/
|
|
260
260
|
isPointInBounds(e, t, i) {
|
|
261
|
-
const { centerX:
|
|
262
|
-
return g >= -o / 2 && g <= o / 2 && m >= -
|
|
261
|
+
const { centerX: s, centerY: a, width: o, height: n, rotation: r } = i, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), u = e - s, p = t - a, g = u * l - p * c, m = u * c + p * l;
|
|
262
|
+
return g >= -o / 2 && g <= o / 2 && m >= -n / 2 && m <= n / 2;
|
|
263
263
|
}
|
|
264
264
|
/**
|
|
265
265
|
* Get handle positions for the selected content
|
|
@@ -271,22 +271,22 @@ class N {
|
|
|
271
271
|
const i = this.contents.get(e);
|
|
272
272
|
if (!i)
|
|
273
273
|
return null;
|
|
274
|
-
const
|
|
275
|
-
if (!
|
|
274
|
+
const s = this.getContentBounds(e);
|
|
275
|
+
if (!s)
|
|
276
276
|
return null;
|
|
277
|
-
const { centerX: a, centerY: o, width:
|
|
277
|
+
const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, l = 6 / this.zoom, c = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), g = Math.sin(u), m = (b, y) => ({
|
|
278
278
|
x: a + b * p - y * g,
|
|
279
279
|
y: o + b * g + y * p
|
|
280
280
|
}), v = [];
|
|
281
281
|
if (i.type === "image" && t.imageOptions?.allowScaling || i.type === "text" && t.textOptions?.allowScaling) {
|
|
282
|
-
const b =
|
|
282
|
+
const b = n / 2, y = r / 2, x = m(-b, -y);
|
|
283
283
|
v.push({ type: "nw", ...x, radius: l });
|
|
284
284
|
const w = m(b, -y);
|
|
285
285
|
v.push({ type: "ne", ...w, radius: l });
|
|
286
286
|
const C = m(b, y);
|
|
287
287
|
v.push({ type: "se", ...C, radius: l });
|
|
288
|
-
const
|
|
289
|
-
v.push({ type: "sw", ...
|
|
288
|
+
const z = m(-b, y);
|
|
289
|
+
v.push({ type: "sw", ...z, radius: l });
|
|
290
290
|
}
|
|
291
291
|
if (i.type === "image" && t.imageOptions?.allowRotation || i.type === "text" && t.textOptions?.allowRotation) {
|
|
292
292
|
const b = m(0, -r / 2 - c);
|
|
@@ -298,12 +298,12 @@ class N {
|
|
|
298
298
|
* Hit test handles at given canvas coordinates
|
|
299
299
|
*/
|
|
300
300
|
hitTestHandle(e, t, i) {
|
|
301
|
-
const
|
|
302
|
-
if (!
|
|
301
|
+
const s = this.getContentHandlePositions(i);
|
|
302
|
+
if (!s)
|
|
303
303
|
return null;
|
|
304
|
-
for (const a of
|
|
305
|
-
const o = e - a.x,
|
|
306
|
-
if (Math.sqrt(o * o +
|
|
304
|
+
for (const a of s) {
|
|
305
|
+
const o = e - a.x, n = t - a.y;
|
|
306
|
+
if (Math.sqrt(o * o + n * n) <= a.radius * 2)
|
|
307
307
|
return a.type;
|
|
308
308
|
}
|
|
309
309
|
return null;
|
|
@@ -312,7 +312,7 @@ class N {
|
|
|
312
312
|
* Check if positioning is allowed for an area
|
|
313
313
|
*/
|
|
314
314
|
isPositioningAllowed(e) {
|
|
315
|
-
const t = this.areas.find((
|
|
315
|
+
const t = this.areas.find((s) => s.id === e);
|
|
316
316
|
if (!t)
|
|
317
317
|
return !1;
|
|
318
318
|
const i = this.contents.get(e);
|
|
@@ -344,15 +344,15 @@ class N {
|
|
|
344
344
|
checkSafeAreaViolations() {
|
|
345
345
|
if (!this.safeArea || !this.artboard)
|
|
346
346
|
return [];
|
|
347
|
-
const e = [], { top: t, right: i, bottom:
|
|
347
|
+
const e = [], { top: t, right: i, bottom: s, left: a } = this.safeArea, o = a, n = t, r = this.artboard.width - i, d = this.artboard.height - s;
|
|
348
348
|
for (const l of this.areas) {
|
|
349
349
|
if (!this.contents.get(l.id))
|
|
350
350
|
continue;
|
|
351
351
|
const c = this.getContentBounds(l.id);
|
|
352
352
|
if (!c)
|
|
353
353
|
continue;
|
|
354
|
-
const { centerX: u, centerY: p, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), w = g * y + m * x, C = g * x + m * y,
|
|
355
|
-
(
|
|
354
|
+
const { centerX: u, centerY: p, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), w = g * y + m * x, C = g * x + m * y, z = u - w / 2, A = p - C / 2, D = u + w / 2, R = p + C / 2;
|
|
355
|
+
(z < o || A < n || D > r || R > d) && e.push(l.id);
|
|
356
356
|
}
|
|
357
357
|
return this.safeAreaViolations = new Set(e), this.requestRender(), e;
|
|
358
358
|
}
|
|
@@ -370,8 +370,8 @@ class N {
|
|
|
370
370
|
emit(e, t) {
|
|
371
371
|
const i = this.eventListeners.get(e);
|
|
372
372
|
if (i)
|
|
373
|
-
for (const
|
|
374
|
-
|
|
373
|
+
for (const s of i)
|
|
374
|
+
s(t);
|
|
375
375
|
}
|
|
376
376
|
/**
|
|
377
377
|
* Export to PNG at full resolution
|
|
@@ -379,17 +379,17 @@ class N {
|
|
|
379
379
|
async exportToPNG() {
|
|
380
380
|
if (!this.artboard)
|
|
381
381
|
throw new Error("No artboard configured");
|
|
382
|
-
const { width: e, height: t, backgroundColor: i } = this.artboard,
|
|
383
|
-
|
|
384
|
-
const a =
|
|
382
|
+
const { width: e, height: t, backgroundColor: i } = this.artboard, s = document.createElement("canvas");
|
|
383
|
+
s.width = e, s.height = t;
|
|
384
|
+
const a = s.getContext("2d");
|
|
385
385
|
if (!a)
|
|
386
386
|
throw new Error("Failed to get 2D context for export");
|
|
387
387
|
a.fillStyle = i, a.fillRect(0, 0, e, t), this.backgroundImage?.position === "behind" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t);
|
|
388
388
|
for (const o of this.areas)
|
|
389
389
|
this.drawAreaContentOnContext(a, o);
|
|
390
|
-
return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t), new Promise((o,
|
|
391
|
-
|
|
392
|
-
r ? o(r) :
|
|
390
|
+
return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t), new Promise((o, n) => {
|
|
391
|
+
s.toBlob((r) => {
|
|
392
|
+
r ? o(r) : n(new Error("Failed to create PNG blob"));
|
|
393
393
|
}, "image/png", 1);
|
|
394
394
|
});
|
|
395
395
|
}
|
|
@@ -422,13 +422,13 @@ class N {
|
|
|
422
422
|
e.save();
|
|
423
423
|
const i = window.devicePixelRatio || 1;
|
|
424
424
|
e.scale(i, i), e.scale(this.zoom, this.zoom), e.translate(this.pan.x, this.pan.y);
|
|
425
|
-
const { width:
|
|
426
|
-
e.fillStyle = o, e.fillRect(0, 0,
|
|
427
|
-
for (const
|
|
428
|
-
this.renderAreaWithContent(
|
|
425
|
+
const { width: s, height: a, backgroundColor: o } = this.artboard;
|
|
426
|
+
e.fillStyle = o, e.fillRect(0, 0, s, a), e.strokeStyle = "#d1d5db", e.lineWidth = 1 / this.zoom, e.strokeRect(0, 0, s, a), this.backgroundImage?.position === "behind" && this.renderBackgroundImage();
|
|
427
|
+
for (const n of this.areas)
|
|
428
|
+
this.renderAreaWithContent(n);
|
|
429
429
|
this.backgroundImage?.position === "overlay" && this.renderBackgroundImage(), this.renderSafeAreaGuide();
|
|
430
|
-
for (const
|
|
431
|
-
const r = this.areas.find((p) => p.id ===
|
|
430
|
+
for (const n of this.safeAreaViolations) {
|
|
431
|
+
const r = this.areas.find((p) => p.id === n);
|
|
432
432
|
if (!r)
|
|
433
433
|
continue;
|
|
434
434
|
const { x: d, y: l, width: c, height: u } = r.location;
|
|
@@ -449,15 +449,15 @@ class N {
|
|
|
449
449
|
c.save(), c.strokeStyle = "#3b82f6", c.lineWidth = 2 / this.zoom, c.setLineDash([]), c.strokeRect(u, p, g, m), c.restore();
|
|
450
450
|
return;
|
|
451
451
|
}
|
|
452
|
-
const { ctx: i } = this, { centerX:
|
|
453
|
-
i.save(), i.translate(
|
|
452
|
+
const { ctx: i } = this, { centerX: s, centerY: a, width: o, height: n, rotation: r } = t;
|
|
453
|
+
i.save(), i.translate(s, a), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -n / 2, o, n), i.restore();
|
|
454
454
|
const d = this.getContentHandlePositions(e);
|
|
455
455
|
if (d)
|
|
456
456
|
for (const l of d) {
|
|
457
457
|
i.save();
|
|
458
458
|
const c = this.hoveredHandle === l.type, u = c ? "#dbeafe" : "#ffffff", p = c ? "#2563eb" : "#3b82f6";
|
|
459
459
|
if (l.type === "rotate") {
|
|
460
|
-
const g = this.getRotatedPoint(
|
|
460
|
+
const g = this.getRotatedPoint(s, a - n / 2, s, a, r);
|
|
461
461
|
i.beginPath(), i.strokeStyle = p, i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.moveTo(g.x, g.y), i.lineTo(l.x, l.y), i.stroke(), i.setLineDash([]), i.beginPath(), i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.arc(l.x, l.y, l.radius, 0, Math.PI * 2), i.fill(), i.stroke(), i.beginPath(), i.strokeStyle = p, i.lineWidth = 1.5 / this.zoom, i.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), i.stroke();
|
|
462
462
|
} else
|
|
463
463
|
i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), i.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
|
|
@@ -467,11 +467,11 @@ class N {
|
|
|
467
467
|
/**
|
|
468
468
|
* Rotate a point around a center
|
|
469
469
|
*/
|
|
470
|
-
getRotatedPoint(e, t, i,
|
|
471
|
-
const o = a * Math.PI / 180,
|
|
470
|
+
getRotatedPoint(e, t, i, s, a) {
|
|
471
|
+
const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e - i, l = t - s;
|
|
472
472
|
return {
|
|
473
|
-
x: i + d *
|
|
474
|
-
y:
|
|
473
|
+
x: i + d * n - l * r,
|
|
474
|
+
y: s + d * r + l * n
|
|
475
475
|
};
|
|
476
476
|
}
|
|
477
477
|
/**
|
|
@@ -480,11 +480,11 @@ class N {
|
|
|
480
480
|
renderBackgroundImage() {
|
|
481
481
|
if (!this.bgImageElement || !this.artboard || !this.backgroundImage)
|
|
482
482
|
return;
|
|
483
|
-
const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height:
|
|
483
|
+
const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height: s } = this.artboard;
|
|
484
484
|
e.save(), e.globalAlpha = t;
|
|
485
|
-
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i /
|
|
486
|
-
let
|
|
487
|
-
a > o ? (r =
|
|
485
|
+
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / s;
|
|
486
|
+
let n, r, d, l;
|
|
487
|
+
a > o ? (r = s, n = s * a, d = (i - n) / 2, l = 0) : (n = i, r = i / a, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
|
|
488
488
|
}
|
|
489
489
|
/**
|
|
490
490
|
* Render safe area guide as dashed green rectangle
|
|
@@ -492,17 +492,17 @@ class N {
|
|
|
492
492
|
renderSafeAreaGuide() {
|
|
493
493
|
if (!this.safeArea || !this.artboard)
|
|
494
494
|
return;
|
|
495
|
-
const { ctx: e } = this, { top: t, right: i, bottom:
|
|
496
|
-
r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o,
|
|
495
|
+
const { ctx: e } = this, { top: t, right: i, bottom: s, left: a } = this.safeArea, o = a, n = t, r = this.artboard.width - a - i, d = this.artboard.height - t - s;
|
|
496
|
+
r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, n, r, d), e.setLineDash([]), e.restore());
|
|
497
497
|
}
|
|
498
498
|
/**
|
|
499
499
|
* Render an area with its content
|
|
500
500
|
*/
|
|
501
501
|
renderAreaWithContent(e) {
|
|
502
|
-
const { ctx: t } = this, { location: i, backgroundColor:
|
|
503
|
-
|
|
502
|
+
const { ctx: t } = this, { location: i, backgroundColor: s } = e, { x: a, y: o, width: n, height: r } = i;
|
|
503
|
+
s && (t.fillStyle = s, t.fillRect(a, o, n, r));
|
|
504
504
|
const d = this.contents.get(e.id);
|
|
505
|
-
d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(a, o,
|
|
505
|
+
d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(a, o, n, r), t.setLineDash([]), t.restore());
|
|
506
506
|
}
|
|
507
507
|
/**
|
|
508
508
|
* Render text content in an area
|
|
@@ -510,15 +510,15 @@ class N {
|
|
|
510
510
|
renderTextContent(e, t) {
|
|
511
511
|
if (!t.text.trim())
|
|
512
512
|
return;
|
|
513
|
-
const { ctx: i } = this, { location:
|
|
514
|
-
i.save(), i.beginPath(), i.rect(a, o,
|
|
513
|
+
const { ctx: i } = this, { location: s } = e, { x: a, y: o, width: n, height: r } = s, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, u = a + n / 2 + d.x, p = o + r / 2 + d.y;
|
|
514
|
+
i.save(), i.beginPath(), i.rect(a, o, n, r), i.clip(), i.translate(u, p), i.rotate(c * Math.PI / 180), i.scale(l, l), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
|
|
515
515
|
let g;
|
|
516
516
|
switch (t.align) {
|
|
517
517
|
case "left":
|
|
518
|
-
i.textAlign = "left", g = -
|
|
518
|
+
i.textAlign = "left", g = -n / 2 + 10;
|
|
519
519
|
break;
|
|
520
520
|
case "right":
|
|
521
|
-
i.textAlign = "right", g =
|
|
521
|
+
i.textAlign = "right", g = n / 2 - 10;
|
|
522
522
|
break;
|
|
523
523
|
case "center":
|
|
524
524
|
default:
|
|
@@ -534,13 +534,13 @@ class N {
|
|
|
534
534
|
const i = this.loadedImages.get(e.id);
|
|
535
535
|
if (!i)
|
|
536
536
|
return;
|
|
537
|
-
const { ctx:
|
|
538
|
-
|
|
537
|
+
const { ctx: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, u = t.rotation ?? 0;
|
|
538
|
+
s.save(), s.beginPath(), s.rect(o, n, r, d), s.clip();
|
|
539
539
|
const p = i.naturalWidth / i.naturalHeight, g = r / d;
|
|
540
540
|
let m, v;
|
|
541
541
|
p > g ? (m = r, v = r / p) : (v = d, m = d * p);
|
|
542
|
-
const b = m * c, y = v * c, x = o + r / 2 + l.x, w =
|
|
543
|
-
|
|
542
|
+
const b = m * c, y = v * c, x = o + r / 2 + l.x, w = n + d / 2 + l.y;
|
|
543
|
+
s.translate(x, w), s.rotate(u * Math.PI / 180), s.drawImage(i, -b / 2, -y / 2, b, y), s.restore();
|
|
544
544
|
}
|
|
545
545
|
/**
|
|
546
546
|
* Draw background image on a specific context (for export)
|
|
@@ -548,32 +548,32 @@ class N {
|
|
|
548
548
|
drawBackgroundImageOnContext(e, t, i) {
|
|
549
549
|
if (!this.bgImageElement || !this.backgroundImage)
|
|
550
550
|
return;
|
|
551
|
-
const { opacity:
|
|
552
|
-
e.save(), e.globalAlpha =
|
|
551
|
+
const { opacity: s } = this.backgroundImage;
|
|
552
|
+
e.save(), e.globalAlpha = s;
|
|
553
553
|
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / i;
|
|
554
|
-
let
|
|
555
|
-
a > o ? (r = i,
|
|
554
|
+
let n, r, d, l;
|
|
555
|
+
a > o ? (r = i, n = i * a, d = (t - n) / 2, l = 0) : (n = t, r = t / a, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
|
|
556
556
|
}
|
|
557
557
|
/**
|
|
558
558
|
* Draw area content on a specific context (for export)
|
|
559
559
|
*/
|
|
560
560
|
drawAreaContentOnContext(e, t) {
|
|
561
|
-
const { location: i, backgroundColor:
|
|
562
|
-
|
|
561
|
+
const { location: i, backgroundColor: s } = t, { x: a, y: o, width: n, height: r } = i;
|
|
562
|
+
s && (e.fillStyle = s, e.fillRect(a, o, n, r));
|
|
563
563
|
const d = this.contents.get(t.id);
|
|
564
564
|
if (d) {
|
|
565
565
|
if (d.type === "text") {
|
|
566
566
|
if (!d.text.trim())
|
|
567
567
|
return;
|
|
568
|
-
const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, u = d.rotation ?? 0, p = a +
|
|
569
|
-
e.save(), e.beginPath(), e.rect(a, o,
|
|
568
|
+
const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, u = d.rotation ?? 0, p = a + n / 2 + l.x, g = o + r / 2 + l.y;
|
|
569
|
+
e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip(), e.translate(p, g), e.rotate(u * Math.PI / 180), e.scale(c, c), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
|
|
570
570
|
let m;
|
|
571
571
|
switch (d.align) {
|
|
572
572
|
case "left":
|
|
573
|
-
e.textAlign = "left", m = -
|
|
573
|
+
e.textAlign = "left", m = -n / 2 + 10;
|
|
574
574
|
break;
|
|
575
575
|
case "right":
|
|
576
|
-
e.textAlign = "right", m =
|
|
576
|
+
e.textAlign = "right", m = n / 2 - 10;
|
|
577
577
|
break;
|
|
578
578
|
case "center":
|
|
579
579
|
default:
|
|
@@ -586,11 +586,11 @@ class N {
|
|
|
586
586
|
if (!l)
|
|
587
587
|
return;
|
|
588
588
|
const c = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
|
|
589
|
-
e.save(), e.beginPath(), e.rect(a, o,
|
|
590
|
-
const g = l.naturalWidth / l.naturalHeight, m =
|
|
589
|
+
e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip();
|
|
590
|
+
const g = l.naturalWidth / l.naturalHeight, m = n / r;
|
|
591
591
|
let v, b;
|
|
592
|
-
g > m ? (v =
|
|
593
|
-
const y = v * u, x = b * u, w = a +
|
|
592
|
+
g > m ? (v = n, b = n / g) : (b = r, v = r * g);
|
|
593
|
+
const y = v * u, x = b * u, w = a + n / 2 + c.x, C = o + r / 2 + c.y;
|
|
594
594
|
e.translate(w, C), e.rotate(p * Math.PI / 180), e.drawImage(l, -y / 2, -x / 2, y, x), e.restore();
|
|
595
595
|
}
|
|
596
596
|
}
|
|
@@ -616,11 +616,11 @@ function E(f) {
|
|
|
616
616
|
function S(f) {
|
|
617
617
|
return f.contentType === "text" || f.contentType === "both";
|
|
618
618
|
}
|
|
619
|
-
function
|
|
619
|
+
function T(f) {
|
|
620
620
|
return f.contentType === "image" || f.contentType === "both";
|
|
621
621
|
}
|
|
622
622
|
const U = 50;
|
|
623
|
-
class
|
|
623
|
+
class V {
|
|
624
624
|
constructor(e) {
|
|
625
625
|
this.history = [], this.historyIndex = -1, this.listeners = /* @__PURE__ */ new Set(), this.isRestoring = !1, this.state = this.cloneState(e), this.history.push({
|
|
626
626
|
state: this.cloneState(e),
|
|
@@ -635,8 +635,8 @@ class O {
|
|
|
635
635
|
* Update state and save to history
|
|
636
636
|
*/
|
|
637
637
|
setState(e, t, i = !1) {
|
|
638
|
-
const
|
|
639
|
-
this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(
|
|
638
|
+
const s = this.state;
|
|
639
|
+
this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(s, t), this.notifyListeners();
|
|
640
640
|
}
|
|
641
641
|
/**
|
|
642
642
|
* Update state without saving to history
|
|
@@ -694,7 +694,7 @@ class O {
|
|
|
694
694
|
return JSON.parse(JSON.stringify(e));
|
|
695
695
|
}
|
|
696
696
|
}
|
|
697
|
-
class
|
|
697
|
+
class k {
|
|
698
698
|
constructor(e) {
|
|
699
699
|
this.listeners = /* @__PURE__ */ new Set(), this.areas = e, this.contents = this.buildInitialContents();
|
|
700
700
|
}
|
|
@@ -711,7 +711,7 @@ class T {
|
|
|
711
711
|
return this.contents.get(e);
|
|
712
712
|
}
|
|
713
713
|
setTextContent(e, t) {
|
|
714
|
-
const i = this.contents.get(e),
|
|
714
|
+
const i = this.contents.get(e), s = this.areas.find((o) => o.id === e), a = s ? E(s) : {
|
|
715
715
|
type: "text",
|
|
716
716
|
text: "",
|
|
717
717
|
font: "Inter",
|
|
@@ -722,12 +722,12 @@ class T {
|
|
|
722
722
|
i?.type === "text" ? this.contents.set(e, { ...i, ...t }) : this.contents.set(e, { ...a, ...t }), this.notify();
|
|
723
723
|
}
|
|
724
724
|
setImageContent(e, t, i) {
|
|
725
|
-
const
|
|
725
|
+
const s = {
|
|
726
726
|
type: "image",
|
|
727
727
|
dataUrl: t,
|
|
728
728
|
filename: i
|
|
729
729
|
};
|
|
730
|
-
this.contents.set(e,
|
|
730
|
+
this.contents.set(e, s), this.notify();
|
|
731
731
|
}
|
|
732
732
|
clearContent(e) {
|
|
733
733
|
const t = this.areas.find((i) => i.id === e);
|
|
@@ -740,14 +740,14 @@ class T {
|
|
|
740
740
|
setImageScale(e, t) {
|
|
741
741
|
const i = this.contents.get(e);
|
|
742
742
|
if (!i || i.type !== "image") return;
|
|
743
|
-
const
|
|
744
|
-
this.contents.set(e, { ...i, scale:
|
|
743
|
+
const s = Math.max(0.1, Math.min(5, t));
|
|
744
|
+
this.contents.set(e, { ...i, scale: s }), this.notify();
|
|
745
745
|
}
|
|
746
746
|
setImageRotation(e, t) {
|
|
747
747
|
const i = this.contents.get(e);
|
|
748
748
|
if (!i || i.type !== "image") return;
|
|
749
|
-
let
|
|
750
|
-
|
|
749
|
+
let s = t % 360;
|
|
750
|
+
s < 0 && (s += 360), this.contents.set(e, { ...i, rotation: s }), this.notify();
|
|
751
751
|
}
|
|
752
752
|
reset() {
|
|
753
753
|
this.contents = this.buildInitialContents(), this.notify();
|
|
@@ -798,8 +798,8 @@ class I {
|
|
|
798
798
|
* @returns Unsubscribe function
|
|
799
799
|
*/
|
|
800
800
|
once(e, t) {
|
|
801
|
-
const i = (
|
|
802
|
-
t(
|
|
801
|
+
const i = (s) => {
|
|
802
|
+
t(s), this.off(e, i);
|
|
803
803
|
};
|
|
804
804
|
return this.on(e, i);
|
|
805
805
|
}
|
|
@@ -822,8 +822,8 @@ class I {
|
|
|
822
822
|
emit(e, t) {
|
|
823
823
|
const i = this.listeners.get(e);
|
|
824
824
|
if (i) {
|
|
825
|
-
const
|
|
826
|
-
for (const a of
|
|
825
|
+
const s = Array.from(i);
|
|
826
|
+
for (const a of s)
|
|
827
827
|
try {
|
|
828
828
|
a(t);
|
|
829
829
|
} catch (o) {
|
|
@@ -849,7 +849,7 @@ class I {
|
|
|
849
849
|
return this.listeners.get(e)?.size ?? 0;
|
|
850
850
|
}
|
|
851
851
|
}
|
|
852
|
-
class
|
|
852
|
+
class O extends I {
|
|
853
853
|
constructor() {
|
|
854
854
|
super(...arguments), this.activeTouches = /* @__PURE__ */ new Map(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
|
|
855
855
|
}
|
|
@@ -881,14 +881,14 @@ class V extends I {
|
|
|
881
881
|
}
|
|
882
882
|
if (this.activeTouches.size === 2 && this.initialPinchDistance !== null && this.lastPinchCenter) {
|
|
883
883
|
e.preventDefault();
|
|
884
|
-
const [t, i] = Array.from(this.activeTouches.values()),
|
|
884
|
+
const [t, i] = Array.from(this.activeTouches.values()), s = this.getDistance(t, i), a = this.getCenter(t, i), o = s / this.initialPinchDistance;
|
|
885
885
|
this.emit("pinch", {
|
|
886
886
|
scale: o,
|
|
887
887
|
centerX: a.x,
|
|
888
888
|
centerY: a.y
|
|
889
889
|
});
|
|
890
|
-
const
|
|
891
|
-
(Math.abs(
|
|
890
|
+
const n = a.x - this.lastPinchCenter.x, r = a.y - this.lastPinchCenter.y;
|
|
891
|
+
(Math.abs(n) > 0.5 || Math.abs(r) > 0.5) && this.emit("pan", { deltaX: n, deltaY: r }), this.initialPinchDistance = s, this.lastPinchCenter = a;
|
|
892
892
|
}
|
|
893
893
|
}
|
|
894
894
|
handleTouchEnd(e) {
|
|
@@ -902,8 +902,8 @@ class V extends I {
|
|
|
902
902
|
this.activeTouches.clear(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
|
|
903
903
|
}
|
|
904
904
|
getDistance(e, t) {
|
|
905
|
-
const i = t.x - e.x,
|
|
906
|
-
return Math.sqrt(i * i +
|
|
905
|
+
const i = t.x - e.x, s = t.y - e.y;
|
|
906
|
+
return Math.sqrt(i * i + s * s);
|
|
907
907
|
}
|
|
908
908
|
getCenter(e, t) {
|
|
909
909
|
return {
|
|
@@ -912,23 +912,23 @@ class V extends I {
|
|
|
912
912
|
};
|
|
913
913
|
}
|
|
914
914
|
}
|
|
915
|
-
class
|
|
916
|
-
constructor(e, t, i,
|
|
917
|
-
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId =
|
|
915
|
+
class B extends I {
|
|
916
|
+
constructor(e, t, i, s) {
|
|
917
|
+
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = s, this.gestureHandler = new O(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: a }) => {
|
|
918
918
|
const o = Math.max(0.1, Math.min(5, this.initialPinchZoom * a));
|
|
919
919
|
this.emit("zoom", { zoom: o });
|
|
920
920
|
}), this.gestureHandler.on("pan", ({ deltaX: a, deltaY: o }) => {
|
|
921
|
-
const
|
|
921
|
+
const n = this.engine.getPan(), r = this.engine.getZoom();
|
|
922
922
|
this.emit("pan", {
|
|
923
923
|
pan: {
|
|
924
|
-
x:
|
|
925
|
-
y:
|
|
924
|
+
x: n.x + a / r,
|
|
925
|
+
y: n.y + o / r
|
|
926
926
|
}
|
|
927
927
|
});
|
|
928
928
|
});
|
|
929
929
|
}
|
|
930
930
|
getCanvasPosition(e, t) {
|
|
931
|
-
const i = this.canvas.getBoundingClientRect(),
|
|
931
|
+
const i = this.canvas.getBoundingClientRect(), s = window.devicePixelRatio || 1, a = (e - i.left) * s, o = (t - i.top) * s;
|
|
932
932
|
return this.engine.screenToCanvas(a, o);
|
|
933
933
|
}
|
|
934
934
|
handleMouseDown(e) {
|
|
@@ -978,34 +978,34 @@ class F extends I {
|
|
|
978
978
|
if (t) {
|
|
979
979
|
const o = this.engine.hitTestHandle(e.x, e.y, t);
|
|
980
980
|
if (o) {
|
|
981
|
-
const
|
|
982
|
-
if (o === "rotate" &&
|
|
981
|
+
const n = i.get(t), r = this.engine.getContentBounds(t);
|
|
982
|
+
if (o === "rotate" && n?.type === "image" && r) {
|
|
983
983
|
const d = Math.atan2(e.y - r.centerY, e.x - r.centerX) * (180 / Math.PI);
|
|
984
984
|
return this.interactionState = {
|
|
985
985
|
mode: "rotating",
|
|
986
986
|
areaId: t,
|
|
987
|
-
startRotation:
|
|
987
|
+
startRotation: n.rotation ?? 0,
|
|
988
988
|
centerPoint: { x: r.centerX, y: r.centerY },
|
|
989
989
|
startAngle: d
|
|
990
990
|
}, !0;
|
|
991
|
-
} else if (o !== "rotate" &&
|
|
991
|
+
} else if (o !== "rotate" && n?.type === "image" && r)
|
|
992
992
|
return this.interactionState = {
|
|
993
993
|
mode: "scaling",
|
|
994
994
|
areaId: t,
|
|
995
995
|
handle: o,
|
|
996
|
-
startScale:
|
|
996
|
+
startScale: n.scale ?? 1,
|
|
997
997
|
startMousePos: e,
|
|
998
998
|
pivotPoint: { x: r.centerX, y: r.centerY }
|
|
999
999
|
}, !0;
|
|
1000
1000
|
}
|
|
1001
1001
|
}
|
|
1002
|
-
const
|
|
1003
|
-
if (
|
|
1004
|
-
if (this.emit("area:select", { areaId:
|
|
1005
|
-
const o = i.get(
|
|
1002
|
+
const s = this.engine.hitTestContent(e.x, e.y);
|
|
1003
|
+
if (s) {
|
|
1004
|
+
if (this.emit("area:select", { areaId: s }), this.engine.isPositioningAllowed(s)) {
|
|
1005
|
+
const o = i.get(s)?.offset ?? { x: 0, y: 0 };
|
|
1006
1006
|
return this.interactionState = {
|
|
1007
1007
|
mode: "dragging",
|
|
1008
|
-
areaId:
|
|
1008
|
+
areaId: s,
|
|
1009
1009
|
startOffset: o,
|
|
1010
1010
|
startMousePos: e
|
|
1011
1011
|
}, !0;
|
|
@@ -1017,42 +1017,42 @@ class F extends I {
|
|
|
1017
1017
|
}
|
|
1018
1018
|
processPointerMove(e) {
|
|
1019
1019
|
if (this.interactionState.mode === "dragging") {
|
|
1020
|
-
const { areaId: t, startOffset: i, startMousePos:
|
|
1021
|
-
let
|
|
1020
|
+
const { areaId: t, startOffset: i, startMousePos: s } = this.interactionState, a = e.x - s.x, o = e.y - s.y;
|
|
1021
|
+
let n = {
|
|
1022
1022
|
x: i.x + a,
|
|
1023
1023
|
y: i.y + o
|
|
1024
1024
|
};
|
|
1025
1025
|
const r = this.engine.getAreaLocation(t);
|
|
1026
1026
|
if (r) {
|
|
1027
1027
|
const d = r.width / 2 - 10, l = r.height / 2 - 10;
|
|
1028
|
-
|
|
1028
|
+
n.x = Math.max(-d, Math.min(d, n.x)), n.y = Math.max(-l, Math.min(l, n.y));
|
|
1029
1029
|
}
|
|
1030
|
-
this.emit("content:drag", { areaId: t, offset:
|
|
1030
|
+
this.emit("content:drag", { areaId: t, offset: n });
|
|
1031
1031
|
return;
|
|
1032
1032
|
}
|
|
1033
1033
|
if (this.interactionState.mode === "scaling") {
|
|
1034
|
-
const { areaId: t, startScale: i, startMousePos:
|
|
1035
|
-
Math.pow(
|
|
1036
|
-
),
|
|
1034
|
+
const { areaId: t, startScale: i, startMousePos: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
|
|
1035
|
+
Math.pow(s.x - a.x, 2) + Math.pow(s.y - a.y, 2)
|
|
1036
|
+
), n = Math.sqrt(
|
|
1037
1037
|
Math.pow(e.x - a.x, 2) + Math.pow(e.y - a.y, 2)
|
|
1038
1038
|
);
|
|
1039
1039
|
if (o > 0) {
|
|
1040
|
-
const r =
|
|
1040
|
+
const r = n / o, d = Math.max(0.1, Math.min(5, i * r));
|
|
1041
1041
|
this.emit("content:scale", { areaId: t, scale: d });
|
|
1042
1042
|
}
|
|
1043
1043
|
return;
|
|
1044
1044
|
}
|
|
1045
1045
|
if (this.interactionState.mode === "rotating") {
|
|
1046
|
-
const { areaId: t, startRotation: i, centerPoint:
|
|
1047
|
-
let
|
|
1048
|
-
|
|
1046
|
+
const { areaId: t, startRotation: i, centerPoint: s, startAngle: a } = this.interactionState, o = Math.atan2(e.y - s.y, e.x - s.x) * (180 / Math.PI);
|
|
1047
|
+
let n = i + (o - a);
|
|
1048
|
+
n = n % 360, n < 0 && (n += 360);
|
|
1049
1049
|
const r = [0, 90, 180, 270, 360];
|
|
1050
1050
|
for (const d of r)
|
|
1051
|
-
if (Math.abs(
|
|
1052
|
-
|
|
1051
|
+
if (Math.abs(n - d) < 5) {
|
|
1052
|
+
n = d === 360 ? 0 : d;
|
|
1053
1053
|
break;
|
|
1054
1054
|
}
|
|
1055
|
-
this.emit("content:rotate", { areaId: t, rotation:
|
|
1055
|
+
this.emit("content:rotate", { areaId: t, rotation: n });
|
|
1056
1056
|
return;
|
|
1057
1057
|
}
|
|
1058
1058
|
this.updateCursor(e);
|
|
@@ -1064,8 +1064,8 @@ class F extends I {
|
|
|
1064
1064
|
const t = this.getSelectedAreaId();
|
|
1065
1065
|
let i = "default";
|
|
1066
1066
|
if (t) {
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1067
|
+
const s = this.engine.hitTestHandle(e.x, e.y, t);
|
|
1068
|
+
s ? i = s === "rotate" ? "crosshair" : s === "nw" || s === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === t && (i = this.engine.isPositioningAllowed(t) ? "move" : "pointer");
|
|
1069
1069
|
} else
|
|
1070
1070
|
this.engine.hitTestContent(e.x, e.y) && (i = "pointer");
|
|
1071
1071
|
this.emit("cursor", { cursor: i });
|
|
@@ -1077,7 +1077,7 @@ class F extends I {
|
|
|
1077
1077
|
this.canvas.removeEventListener("mousedown", this.boundMouseDown), this.canvas.removeEventListener("mousemove", this.boundMouseMove), this.canvas.removeEventListener("mouseup", this.boundMouseUp), this.canvas.removeEventListener("mouseleave", this.boundMouseLeave), this.canvas.removeEventListener("contextmenu", this.boundContextMenu), this.canvas.removeEventListener("dblclick", this.boundDoubleClick), this.canvas.removeEventListener("touchstart", this.boundTouchStart), this.canvas.removeEventListener("touchmove", this.boundTouchMove), this.canvas.removeEventListener("touchend", this.boundTouchEnd), this.gestureHandler.reset(), this.clear();
|
|
1078
1078
|
}
|
|
1079
1079
|
}
|
|
1080
|
-
class
|
|
1080
|
+
class F {
|
|
1081
1081
|
constructor(e = "/api") {
|
|
1082
1082
|
this.baseUrl = e;
|
|
1083
1083
|
}
|
|
@@ -1121,14 +1121,14 @@ class $ {
|
|
|
1121
1121
|
})
|
|
1122
1122
|
});
|
|
1123
1123
|
if (!i.ok) {
|
|
1124
|
-
let
|
|
1124
|
+
let s = `Failed to finalize design (${i.status} ${i.statusText})
|
|
1125
1125
|
|
|
1126
1126
|
`;
|
|
1127
1127
|
if (i.status === 400) {
|
|
1128
1128
|
const a = await i.json().catch(() => ({}));
|
|
1129
|
-
|
|
1130
|
-
} else i.status === 429 ?
|
|
1131
|
-
throw new Error(
|
|
1129
|
+
s += a.error || "Invalid design data.";
|
|
1130
|
+
} else i.status === 429 ? s += "Rate limit exceeded." : i.status >= 500 && (s += "Server error.");
|
|
1131
|
+
throw new Error(s);
|
|
1132
1132
|
}
|
|
1133
1133
|
return i.json();
|
|
1134
1134
|
} catch (i) {
|
|
@@ -1139,21 +1139,21 @@ class $ {
|
|
|
1139
1139
|
try {
|
|
1140
1140
|
const i = new FormData();
|
|
1141
1141
|
i.append("shopId", t), i.append("file", e);
|
|
1142
|
-
const
|
|
1142
|
+
const s = await fetch(`${this.baseUrl}/assets/upload`, {
|
|
1143
1143
|
method: "POST",
|
|
1144
1144
|
body: i
|
|
1145
1145
|
});
|
|
1146
|
-
if (!
|
|
1147
|
-
let a = `Failed to upload asset (${
|
|
1146
|
+
if (!s.ok) {
|
|
1147
|
+
let a = `Failed to upload asset (${s.status} ${s.statusText})
|
|
1148
1148
|
|
|
1149
1149
|
`;
|
|
1150
|
-
if (
|
|
1151
|
-
const o = await
|
|
1150
|
+
if (s.status === 400) {
|
|
1151
|
+
const o = await s.json().catch(() => ({}));
|
|
1152
1152
|
a += o.error || "Invalid file.";
|
|
1153
|
-
} else
|
|
1153
|
+
} else s.status === 413 ? a += "File too large (max 15MB)." : s.status >= 500 && (a += "Server error.");
|
|
1154
1154
|
throw new Error(a);
|
|
1155
1155
|
}
|
|
1156
|
-
return
|
|
1156
|
+
return s.json();
|
|
1157
1157
|
} catch (i) {
|
|
1158
1158
|
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : i;
|
|
1159
1159
|
}
|
|
@@ -1171,28 +1171,28 @@ class $ {
|
|
|
1171
1171
|
}
|
|
1172
1172
|
function h(f, e, ...t) {
|
|
1173
1173
|
const i = document.createElement(f);
|
|
1174
|
-
return e && Object.entries(e).forEach(([
|
|
1175
|
-
|
|
1176
|
-
}), t.forEach((
|
|
1177
|
-
typeof
|
|
1174
|
+
return e && Object.entries(e).forEach(([s, a]) => {
|
|
1175
|
+
s === "class" ? i.className = a : s in i ? i[s] = a : i.setAttribute(s, String(a));
|
|
1176
|
+
}), t.forEach((s) => {
|
|
1177
|
+
typeof s == "string" ? i.appendChild(document.createTextNode(s)) : i.appendChild(s);
|
|
1178
1178
|
}), i;
|
|
1179
1179
|
}
|
|
1180
|
-
function
|
|
1180
|
+
function $(f, e) {
|
|
1181
1181
|
let t = null, i = null;
|
|
1182
|
-
const
|
|
1182
|
+
const s = (...a) => {
|
|
1183
1183
|
i = a, t && clearTimeout(t), t = setTimeout(() => {
|
|
1184
1184
|
i && f(...i), t = null, i = null;
|
|
1185
1185
|
}, e);
|
|
1186
1186
|
};
|
|
1187
|
-
return
|
|
1187
|
+
return s.flush = () => {
|
|
1188
1188
|
t && (clearTimeout(t), t = null), i && (f(...i), i = null);
|
|
1189
|
-
},
|
|
1189
|
+
}, s.cancel = () => {
|
|
1190
1190
|
t && (clearTimeout(t), t = null), i = null;
|
|
1191
|
-
},
|
|
1191
|
+
}, s;
|
|
1192
1192
|
}
|
|
1193
|
-
class
|
|
1193
|
+
class H extends I {
|
|
1194
1194
|
constructor() {
|
|
1195
|
-
super(), this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
|
|
1195
|
+
super(), this.saveEnabled = !0, this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
|
|
1196
1196
|
const e = h("button", {
|
|
1197
1197
|
class: "zoom-btn",
|
|
1198
1198
|
title: "Zoom out"
|
|
@@ -1210,13 +1210,11 @@ class B extends I {
|
|
|
1210
1210
|
i.addEventListener("click", () => this.emit("zoom-in", void 0)), this.viewSelect = document.createElement("select"), this.viewSelect.className = "view-select", this.viewSelect.title = "Switch view", this.viewSelect.style.display = "none", this.viewSelect.addEventListener("change", () => {
|
|
1211
1211
|
this.emit("view-change", { viewName: this.viewSelect.value });
|
|
1212
1212
|
}), this.element = h("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(t), this.element.appendChild(i), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
|
|
1213
|
-
const
|
|
1214
|
-
this.element.appendChild(
|
|
1215
|
-
const a = h("button", {
|
|
1213
|
+
const s = h("div", { class: "zoom-toolbar-spacer" });
|
|
1214
|
+
this.element.appendChild(s), this.closeBtn = h("button", {
|
|
1216
1215
|
class: "zoom-btn zoom-close-btn",
|
|
1217
1216
|
title: "Close editor"
|
|
1218
|
-
}, "✕ Close")
|
|
1219
|
-
a.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(a), this.saveBtn = h("button", {
|
|
1217
|
+
}, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn), this.saveBtn = h("button", {
|
|
1220
1218
|
class: "zoom-btn zoom-save-btn",
|
|
1221
1219
|
title: "Save customization"
|
|
1222
1220
|
}, "Save Customization"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn);
|
|
@@ -1225,7 +1223,13 @@ class B extends I {
|
|
|
1225
1223
|
this.percentLabel.textContent = `${Math.round(e * 100)}%`;
|
|
1226
1224
|
}
|
|
1227
1225
|
showSaveButton(e) {
|
|
1228
|
-
this.saveBtn.style.display = e ? "" : "none";
|
|
1226
|
+
this.saveEnabled && (this.saveBtn.style.display = e ? "" : "none");
|
|
1227
|
+
}
|
|
1228
|
+
setCloseButtonVisible(e) {
|
|
1229
|
+
this.closeBtn.style.display = e ? "" : "none";
|
|
1230
|
+
}
|
|
1231
|
+
setSaveButtonEnabled(e) {
|
|
1232
|
+
this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
|
|
1229
1233
|
}
|
|
1230
1234
|
setSaveDisabled(e, t) {
|
|
1231
1235
|
this.saveBtn.disabled = e, t && (this.saveBtn.textContent = t);
|
|
@@ -1259,23 +1263,23 @@ const Z = [
|
|
|
1259
1263
|
class L extends I {
|
|
1260
1264
|
constructor(e) {
|
|
1261
1265
|
super(), this.mode = "text", this.isSelected = !1, this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null, this.area = e;
|
|
1262
|
-
const t = S(e), i =
|
|
1266
|
+
const t = S(e), i = T(e);
|
|
1263
1267
|
this.mode = i && !t ? "image" : "text", this.element = h("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
|
|
1264
1268
|
r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
|
|
1265
1269
|
});
|
|
1266
|
-
const
|
|
1270
|
+
const s = h("div", { class: "area-card-header" }), a = h("div", { class: "area-card-name-row" }), o = h("span", { class: "area-card-name" }, e.name);
|
|
1267
1271
|
if (a.appendChild(o), e.required) {
|
|
1268
1272
|
const r = h("span", { class: "area-card-badge" }, "Required");
|
|
1269
1273
|
a.appendChild(r);
|
|
1270
1274
|
}
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1275
|
+
s.appendChild(a);
|
|
1276
|
+
const n = h("button", {
|
|
1273
1277
|
class: "area-card-reset-btn",
|
|
1274
1278
|
title: "Reset content"
|
|
1275
1279
|
}, "Reset");
|
|
1276
|
-
if (
|
|
1280
|
+
if (n.addEventListener("click", (r) => {
|
|
1277
1281
|
r.stopPropagation(), this.emit("clear", { areaId: e.id });
|
|
1278
|
-
}),
|
|
1282
|
+
}), s.appendChild(n), this.element.appendChild(s), t && i) {
|
|
1279
1283
|
const r = h("div", { class: "area-card-mode-switcher" }), d = h("button", {
|
|
1280
1284
|
class: "mode-btn mode-btn-active"
|
|
1281
1285
|
}, "Text");
|
|
@@ -1295,8 +1299,8 @@ class L extends I {
|
|
|
1295
1299
|
setContent(e) {
|
|
1296
1300
|
const t = this.currentContent?.type;
|
|
1297
1301
|
this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1302
|
+
const s = i;
|
|
1303
|
+
s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
|
|
1300
1304
|
}), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
|
|
1301
1305
|
}
|
|
1302
1306
|
setSelected(e) {
|
|
@@ -1304,14 +1308,14 @@ class L extends I {
|
|
|
1304
1308
|
}
|
|
1305
1309
|
updateTextValues(e) {
|
|
1306
1310
|
this.textInputEl && this.textInputEl.value !== e.text && (this.textInputEl.value = e.text), this.fontSelectEl && this.fontSelectEl.value !== e.font && (this.fontSelectEl.value = e.font), this.sizeInputEl && this.sizeInputEl.value !== String(e.size) && (this.sizeInputEl.value = String(e.size)), this.colorInputEl && this.colorInputEl.value !== e.color && (this.colorInputEl.value = e.color), this.alignGroupEl && this.alignGroupEl.querySelectorAll(".align-btn").forEach((t) => {
|
|
1307
|
-
const i = t,
|
|
1308
|
-
i.classList.toggle("align-btn-active",
|
|
1311
|
+
const i = t, s = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
|
|
1312
|
+
i.classList.toggle("align-btn-active", s === e.align);
|
|
1309
1313
|
});
|
|
1310
1314
|
}
|
|
1311
1315
|
renderContent() {
|
|
1312
1316
|
this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
|
|
1313
|
-
const e = S(this.area), t =
|
|
1314
|
-
e && i && this.renderTextControls(), t &&
|
|
1317
|
+
const e = S(this.area), t = T(this.area), i = !t || e && t && this.mode === "text", s = !e || e && t && this.mode === "image";
|
|
1318
|
+
e && i && this.renderTextControls(), t && s && this.renderImageControls();
|
|
1315
1319
|
}
|
|
1316
1320
|
renderTextControls() {
|
|
1317
1321
|
const e = this.currentContent, t = e?.type === "text" ? e : E(this.area), i = h("input", {
|
|
@@ -1322,14 +1326,14 @@ class L extends I {
|
|
|
1322
1326
|
i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
|
|
1323
1327
|
this.emit("text:change", { areaId: this.area.id, updates: { text: i.value } });
|
|
1324
1328
|
}), this.contentContainer.appendChild(i), this.textInputEl = i;
|
|
1325
|
-
const
|
|
1329
|
+
const s = h("div", { class: "area-input-row" }), a = h("select", { class: "area-input-font" });
|
|
1326
1330
|
for (const l of Z) {
|
|
1327
1331
|
const c = h("option");
|
|
1328
1332
|
c.value = l.value, c.textContent = l.label, l.value === t.font && (c.selected = !0), a.appendChild(c);
|
|
1329
1333
|
}
|
|
1330
1334
|
a.addEventListener("change", () => {
|
|
1331
1335
|
this.emit("text:change", { areaId: this.area.id, updates: { font: a.value } });
|
|
1332
|
-
}),
|
|
1336
|
+
}), s.appendChild(a), this.fontSelectEl = a;
|
|
1333
1337
|
const o = h("input", {
|
|
1334
1338
|
class: "area-input-size",
|
|
1335
1339
|
type: "number"
|
|
@@ -1339,8 +1343,8 @@ class L extends I {
|
|
|
1339
1343
|
areaId: this.area.id,
|
|
1340
1344
|
updates: { size: parseInt(o.value, 10) || 24 }
|
|
1341
1345
|
});
|
|
1342
|
-
}),
|
|
1343
|
-
const
|
|
1346
|
+
}), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
|
|
1347
|
+
const n = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
|
|
1344
1348
|
for (const l of q) {
|
|
1345
1349
|
const c = h("button", {
|
|
1346
1350
|
class: `align-btn${t.align === l.value ? " align-btn-active" : ""}`,
|
|
@@ -1350,68 +1354,68 @@ class L extends I {
|
|
|
1350
1354
|
u.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: l.value } }), r.querySelectorAll(".align-btn").forEach((p) => p.classList.remove("align-btn-active")), c.classList.add("align-btn-active");
|
|
1351
1355
|
}), r.appendChild(c);
|
|
1352
1356
|
}
|
|
1353
|
-
|
|
1357
|
+
n.appendChild(r), this.alignGroupEl = r;
|
|
1354
1358
|
const d = h("input", {
|
|
1355
1359
|
class: "area-input-color",
|
|
1356
1360
|
type: "color"
|
|
1357
1361
|
});
|
|
1358
1362
|
d.value = t.color, d.addEventListener("input", () => {
|
|
1359
1363
|
this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
|
|
1360
|
-
}),
|
|
1364
|
+
}), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
|
|
1361
1365
|
}
|
|
1362
1366
|
renderImageControls() {
|
|
1363
1367
|
const e = this.currentContent;
|
|
1364
1368
|
if (e?.type === "image") {
|
|
1365
1369
|
const t = h("div", { class: "area-image-preview" }), i = h("img", { class: "area-image-thumb" });
|
|
1366
1370
|
i.src = e.dataUrl, i.alt = e.filename || "Uploaded image", t.appendChild(i);
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1371
|
+
const s = h("div", { class: "area-image-info" });
|
|
1372
|
+
s.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
|
|
1369
1373
|
const a = h("button", { class: "area-image-remove-btn" }, "Remove");
|
|
1370
1374
|
a.addEventListener("click", (o) => {
|
|
1371
1375
|
o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
|
|
1372
|
-
}),
|
|
1376
|
+
}), s.appendChild(a), t.appendChild(s), this.contentContainer.appendChild(t);
|
|
1373
1377
|
} else {
|
|
1374
1378
|
const t = h("button", { class: "area-upload-btn" }, "Upload Image"), i = h("input", { type: "file" });
|
|
1375
1379
|
i.accept = P.join(","), i.style.display = "none";
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1380
|
+
const s = h("div", { class: "area-validation-error" });
|
|
1381
|
+
s.style.display = "none", i.addEventListener("change", () => {
|
|
1378
1382
|
const a = i.files?.[0];
|
|
1379
1383
|
if (!a) return;
|
|
1380
1384
|
if (!P.includes(a.type)) {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1385
|
+
const n = "Only PNG, JPEG, WebP, and SVG images are accepted";
|
|
1386
|
+
s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
|
|
1383
1387
|
return;
|
|
1384
1388
|
}
|
|
1385
1389
|
if (a.size > Y) {
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1390
|
+
const n = "File must be smaller than 15MB";
|
|
1391
|
+
s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
|
|
1388
1392
|
return;
|
|
1389
1393
|
}
|
|
1390
|
-
|
|
1394
|
+
s.style.display = "none";
|
|
1391
1395
|
const o = new FileReader();
|
|
1392
1396
|
o.onload = () => {
|
|
1393
|
-
const
|
|
1394
|
-
this.emit("image:change", { areaId: this.area.id, dataUrl:
|
|
1397
|
+
const n = o.result;
|
|
1398
|
+
this.emit("image:change", { areaId: this.area.id, dataUrl: n, filename: a.name });
|
|
1395
1399
|
}, o.readAsDataURL(a), i.value = "";
|
|
1396
1400
|
}), t.addEventListener("click", (a) => {
|
|
1397
1401
|
a.stopPropagation(), i.click();
|
|
1398
|
-
}), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(
|
|
1402
|
+
}), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(s);
|
|
1399
1403
|
}
|
|
1400
1404
|
}
|
|
1401
1405
|
renderTransforms() {
|
|
1402
1406
|
this.transformContainer.innerHTML = "";
|
|
1403
1407
|
const e = this.currentContent;
|
|
1404
1408
|
if (!this.isSelected || !e) return;
|
|
1405
|
-
const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling,
|
|
1406
|
-
if (!t && !i && !
|
|
1409
|
+
const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
|
|
1410
|
+
if (!t && !i && !s) return;
|
|
1407
1411
|
const a = h("div", { class: "area-card-divider" });
|
|
1408
1412
|
this.transformContainer.appendChild(a);
|
|
1409
1413
|
const o = h("div", { class: "transform-header" });
|
|
1410
1414
|
o.appendChild(h("span", { class: "transform-title" }, "Transform"));
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }),
|
|
1414
|
-
}), o.appendChild(
|
|
1415
|
+
const n = h("button", { class: "transform-reset-btn" }, "Reset");
|
|
1416
|
+
n.addEventListener("click", (d) => {
|
|
1417
|
+
d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
|
|
1418
|
+
}), o.appendChild(n), this.transformContainer.appendChild(o);
|
|
1415
1419
|
const r = e.offset ?? { x: 0, y: 0 };
|
|
1416
1420
|
if (t) {
|
|
1417
1421
|
const d = h("div", { class: "area-input-row" }), l = h("label", { class: "transform-label" }, "X"), c = h("input", { class: "transform-input", type: "number" });
|
|
@@ -1436,7 +1440,7 @@ class L extends I {
|
|
|
1436
1440
|
c.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
|
|
1437
1441
|
}), l.appendChild(c), l.appendChild(u), this.transformContainer.appendChild(l);
|
|
1438
1442
|
}
|
|
1439
|
-
if (
|
|
1443
|
+
if (s && e.type === "image") {
|
|
1440
1444
|
const d = e.rotation ?? 0, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), u = h("input", { class: "transform-slider", type: "range" });
|
|
1441
1445
|
u.min = "0", u.max = "360", u.step = "1", u.value = String(Math.round(d)), u.addEventListener("input", () => {
|
|
1442
1446
|
const p = parseInt(u.value, 10);
|
|
@@ -1450,8 +1454,8 @@ class L extends I {
|
|
|
1450
1454
|
}
|
|
1451
1455
|
class j extends I {
|
|
1452
1456
|
constructor(e) {
|
|
1453
|
-
super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (
|
|
1454
|
-
|
|
1457
|
+
super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
|
|
1458
|
+
s.key === "Escape" && this.emit("dismiss", void 0);
|
|
1455
1459
|
}, this.backdrop = h("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
|
|
1456
1460
|
this.emit("dismiss", void 0);
|
|
1457
1461
|
}), this.element = h("div", { class: "area-panel" });
|
|
@@ -1461,12 +1465,12 @@ class j extends I {
|
|
|
1461
1465
|
if (i.addEventListener("click", () => {
|
|
1462
1466
|
this.emit("dismiss", void 0);
|
|
1463
1467
|
}), t.appendChild(i), this.element.appendChild(t), this.panelContent = h("div", { class: "area-panel-content" }), e.length === 0) {
|
|
1464
|
-
const
|
|
1465
|
-
this.panelContent.appendChild(
|
|
1468
|
+
const s = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
|
|
1469
|
+
this.panelContent.appendChild(s);
|
|
1466
1470
|
} else
|
|
1467
|
-
for (const
|
|
1468
|
-
const a = new L(
|
|
1469
|
-
this.cards.set(
|
|
1471
|
+
for (const s of e) {
|
|
1472
|
+
const a = new L(s);
|
|
1473
|
+
this.cards.set(s.id, a), a.on("text:change", (o) => this.emit("text:change", o)), a.on("image:change", (o) => this.emit("image:change", o)), a.on("clear", (o) => this.emit("clear", o)), a.on("select", (o) => this.emit("select", o)), a.on("offset:change", (o) => this.emit("offset:change", o)), a.on("scale:change", (o) => this.emit("scale:change", o)), a.on("rotation:change", (o) => this.emit("rotation:change", o)), a.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(a.getElement());
|
|
1470
1474
|
}
|
|
1471
1475
|
this.element.appendChild(this.panelContent);
|
|
1472
1476
|
}
|
|
@@ -1477,7 +1481,7 @@ class j extends I {
|
|
|
1477
1481
|
} else
|
|
1478
1482
|
for (const t of e) {
|
|
1479
1483
|
const i = new L(t);
|
|
1480
|
-
this.cards.set(t.id, i), i.on("text:change", (
|
|
1484
|
+
this.cards.set(t.id, i), i.on("text:change", (s) => this.emit("text:change", s)), i.on("image:change", (s) => this.emit("image:change", s)), i.on("clear", (s) => this.emit("clear", s)), i.on("select", (s) => this.emit("select", s)), i.on("offset:change", (s) => this.emit("offset:change", s)), i.on("scale:change", (s) => this.emit("scale:change", s)), i.on("rotation:change", (s) => this.emit("rotation:change", s)), i.on("validation:error", (s) => this.emit("validation:error", s)), this.panelContent.appendChild(i.getElement());
|
|
1481
1485
|
}
|
|
1482
1486
|
}
|
|
1483
1487
|
setContents(e) {
|
|
@@ -1486,8 +1490,8 @@ class j extends I {
|
|
|
1486
1490
|
}
|
|
1487
1491
|
setSelectedArea(e) {
|
|
1488
1492
|
for (const [t, i] of this.cards) {
|
|
1489
|
-
const
|
|
1490
|
-
i.setSelected(
|
|
1493
|
+
const s = t === e;
|
|
1494
|
+
i.setSelected(s), i.getElement().style.display = s ? "" : "none";
|
|
1491
1495
|
}
|
|
1492
1496
|
e ? (this.element.classList.add("area-panel-visible"), this.isMobile && this.backdrop.classList.add("area-panel-backdrop-visible"), document.addEventListener("keydown", this.boundEscapeHandler)) : (this.element.classList.remove("area-panel-visible"), this.isMobile && this.backdrop.classList.remove("area-panel-backdrop-visible"), document.removeEventListener("keydown", this.boundEscapeHandler));
|
|
1493
1497
|
}
|
|
@@ -1507,9 +1511,9 @@ class X {
|
|
|
1507
1511
|
if (!this.isEnabled) return;
|
|
1508
1512
|
const i = t.target;
|
|
1509
1513
|
if (!(i.tagName === "INPUT" || i.tagName === "TEXTAREA" || i.isContentEditable)) {
|
|
1510
|
-
for (const
|
|
1511
|
-
if (this.matchesCommand(t,
|
|
1512
|
-
t.preventDefault(),
|
|
1514
|
+
for (const s of this.commands)
|
|
1515
|
+
if (this.matchesCommand(t, s)) {
|
|
1516
|
+
t.preventDefault(), s.handler(t);
|
|
1513
1517
|
break;
|
|
1514
1518
|
}
|
|
1515
1519
|
}
|
|
@@ -1562,24 +1566,24 @@ function J() {
|
|
|
1562
1566
|
return W() ? "meta" : "ctrl";
|
|
1563
1567
|
}
|
|
1564
1568
|
function G(f, e, t, i) {
|
|
1565
|
-
const
|
|
1566
|
-
return Math.min(
|
|
1569
|
+
const s = f / t, a = e / i;
|
|
1570
|
+
return Math.min(s, a);
|
|
1567
1571
|
}
|
|
1568
|
-
function K(f, e, t = 8, i = 10,
|
|
1569
|
-
const a = G(f, e, t, i), o = a >=
|
|
1572
|
+
function K(f, e, t = 8, i = 10, s = 300) {
|
|
1573
|
+
const a = G(f, e, t, i), o = a >= s;
|
|
1570
1574
|
return {
|
|
1571
1575
|
actualDPI: Math.round(a),
|
|
1572
|
-
targetDPI:
|
|
1576
|
+
targetDPI: s,
|
|
1573
1577
|
width: f,
|
|
1574
1578
|
height: e,
|
|
1575
1579
|
passed: o,
|
|
1576
|
-
warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${
|
|
1580
|
+
warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`
|
|
1577
1581
|
};
|
|
1578
1582
|
}
|
|
1579
|
-
const z = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', Q = "2.0.0", _ = 768;
|
|
1583
|
+
const M = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', Q = "2.0.0", _ = 768;
|
|
1580
1584
|
class ee extends HTMLElement {
|
|
1581
1585
|
constructor() {
|
|
1582
|
-
super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange =
|
|
1586
|
+
super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = $(() => {
|
|
1583
1587
|
this.dispatchEvent(
|
|
1584
1588
|
new CustomEvent("change", {
|
|
1585
1589
|
detail: { design: this.getDesign() },
|
|
@@ -1590,7 +1594,7 @@ class ee extends HTMLElement {
|
|
|
1590
1594
|
}, 300), this.shadow = this.attachShadow({ mode: "open" });
|
|
1591
1595
|
}
|
|
1592
1596
|
static get observedAttributes() {
|
|
1593
|
-
return ["template-id", "product-id", "theme", "mode", "store-id", "api-url"];
|
|
1597
|
+
return ["template-id", "product-id", "theme", "mode", "store-id", "api-url", "show-close-button", "show-save-button"];
|
|
1594
1598
|
}
|
|
1595
1599
|
async connectedCallback() {
|
|
1596
1600
|
try {
|
|
@@ -1603,7 +1607,7 @@ class ee extends HTMLElement {
|
|
|
1603
1607
|
this.cleanup();
|
|
1604
1608
|
}
|
|
1605
1609
|
attributeChangedCallback(e, t, i) {
|
|
1606
|
-
t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i));
|
|
1610
|
+
t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i), e === "show-close-button" && this.zoomToolbar && this.zoomToolbar.setCloseButtonVisible(i !== "false"), e === "show-save-button" && this.zoomToolbar && this.zoomToolbar.setSaveButtonEnabled(i !== "false"));
|
|
1607
1611
|
}
|
|
1608
1612
|
// ─── Public API ───
|
|
1609
1613
|
getDesign() {
|
|
@@ -1672,7 +1676,7 @@ class ee extends HTMLElement {
|
|
|
1672
1676
|
async finalize() {
|
|
1673
1677
|
const e = this.engine.checkSafeAreaViolations();
|
|
1674
1678
|
if (e.length > 0) {
|
|
1675
|
-
const a = this.currentTemplate?.areas || [], o = e.map((
|
|
1679
|
+
const a = this.currentTemplate?.areas || [], o = e.map((n) => a.find((r) => r.id === n)?.name || n).join(", ");
|
|
1676
1680
|
if (!await this.showConfirmation(
|
|
1677
1681
|
"Content outside safe area",
|
|
1678
1682
|
`Content in ${o} extends beyond the safe area. It may be trimmed during printing. Continue?`,
|
|
@@ -1681,25 +1685,25 @@ class ee extends HTMLElement {
|
|
|
1681
1685
|
))
|
|
1682
1686
|
throw new Error("Finalization cancelled by user");
|
|
1683
1687
|
}
|
|
1684
|
-
const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop",
|
|
1688
|
+
const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop", s = await this.apiClient.finalizeDesign(i, t);
|
|
1685
1689
|
return this.dispatchEvent(
|
|
1686
1690
|
new CustomEvent("customizer:finalize", {
|
|
1687
1691
|
detail: {
|
|
1688
|
-
designId:
|
|
1689
|
-
proofUrl:
|
|
1692
|
+
designId: s.designId,
|
|
1693
|
+
proofUrl: s.proofUrl,
|
|
1690
1694
|
templateId: t.templateId,
|
|
1691
1695
|
designJson: t,
|
|
1692
|
-
status:
|
|
1696
|
+
status: s.status
|
|
1693
1697
|
},
|
|
1694
1698
|
bubbles: !0,
|
|
1695
1699
|
composed: !0
|
|
1696
1700
|
})
|
|
1697
|
-
),
|
|
1701
|
+
), s;
|
|
1698
1702
|
}
|
|
1699
1703
|
// ─── Initialization ───
|
|
1700
1704
|
renderLoading() {
|
|
1701
1705
|
const e = document.createElement("style");
|
|
1702
|
-
e.textContent =
|
|
1706
|
+
e.textContent = M, this.shadow.appendChild(e);
|
|
1703
1707
|
const t = h(
|
|
1704
1708
|
"div",
|
|
1705
1709
|
{ class: "loading-container" },
|
|
@@ -1709,7 +1713,7 @@ class ee extends HTMLElement {
|
|
|
1709
1713
|
}
|
|
1710
1714
|
async initialize() {
|
|
1711
1715
|
const e = this.getAttribute("api-url") || "http://localhost:4000";
|
|
1712
|
-
this.apiClient = new
|
|
1716
|
+
this.apiClient = new F(`${e.replace(/\/+$/, "")}/public`);
|
|
1713
1717
|
const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
|
|
1714
1718
|
if (!t && !i)
|
|
1715
1719
|
throw new Error("Either template-id or product-id attribute is required");
|
|
@@ -1730,7 +1734,7 @@ class ee extends HTMLElement {
|
|
|
1730
1734
|
async loadTemplateData(e) {
|
|
1731
1735
|
this.currentTemplate = e;
|
|
1732
1736
|
const t = e.areas || [];
|
|
1733
|
-
this.contentManager = new
|
|
1737
|
+
this.contentManager = new k(t);
|
|
1734
1738
|
const i = {
|
|
1735
1739
|
template: e,
|
|
1736
1740
|
contents: this.contentManager.toJSON(),
|
|
@@ -1740,16 +1744,16 @@ class ee extends HTMLElement {
|
|
|
1740
1744
|
isDirty: !1,
|
|
1741
1745
|
warnings: []
|
|
1742
1746
|
};
|
|
1743
|
-
this.stateManager = new
|
|
1747
|
+
this.stateManager = new V(i), this.buildUI(e, t), this.engine = new N(this.canvas), this.engine.setArtboard({
|
|
1744
1748
|
width: e.artboard.width,
|
|
1745
1749
|
height: e.artboard.height,
|
|
1746
1750
|
backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
|
|
1747
1751
|
}), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1752
|
+
const s = e.artboard.safeArea ?? e.safeArea;
|
|
1753
|
+
s && this.engine.setSafeArea(s), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
|
|
1750
1754
|
zoom: this.engine.getZoom(),
|
|
1751
1755
|
pan: this.engine.getPan()
|
|
1752
|
-
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new
|
|
1756
|
+
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new B(
|
|
1753
1757
|
this.canvas,
|
|
1754
1758
|
this.engine,
|
|
1755
1759
|
() => this.contentManager.getContents(),
|
|
@@ -1771,7 +1775,7 @@ class ee extends HTMLElement {
|
|
|
1771
1775
|
if (t.views.length === 0)
|
|
1772
1776
|
throw new Error("Product has no active templates");
|
|
1773
1777
|
this.productViews = t.views, this.perViewContents.clear();
|
|
1774
|
-
const i = t.views.find((
|
|
1778
|
+
const i = t.views.find((s) => s.isDefault) || t.views[0];
|
|
1775
1779
|
await this.loadTemplateData(i.template.templateJson), this.activeViewName = i.viewName, this.zoomToolbar && t.views.length > 1 && (this.zoomToolbar.setViews(this.getViews()), this.zoomToolbar.setActiveView(i.viewName));
|
|
1776
1780
|
} catch (t) {
|
|
1777
1781
|
throw new Error(`Failed to load product: ${t.message}`);
|
|
@@ -1782,9 +1786,9 @@ class ee extends HTMLElement {
|
|
|
1782
1786
|
const t = e.template.templateJson;
|
|
1783
1787
|
this.currentTemplate = t;
|
|
1784
1788
|
const i = t.areas || [];
|
|
1785
|
-
this.contentManager = new
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1789
|
+
this.contentManager = new k(i);
|
|
1790
|
+
const s = this.perViewContents.get(e.viewName);
|
|
1791
|
+
s && this.contentManager.fromJSON(s), this.engine.setArtboard({
|
|
1788
1792
|
width: t.artboard.width,
|
|
1789
1793
|
height: t.artboard.height,
|
|
1790
1794
|
backgroundColor: t.artboard.backgroundColor ?? "#ffffff"
|
|
@@ -1809,9 +1813,9 @@ class ee extends HTMLElement {
|
|
|
1809
1813
|
buildUI(e, t) {
|
|
1810
1814
|
this.shadow.innerHTML = "";
|
|
1811
1815
|
const i = document.createElement("style");
|
|
1812
|
-
i.textContent =
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1816
|
+
i.textContent = M, this.shadow.appendChild(i), this.container = h("div", { class: "editor-container" }), this.zoomToolbar = new H(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
|
|
1817
|
+
const n = this.productViews.find((r) => r.viewName === o);
|
|
1818
|
+
n && this.switchToView(n);
|
|
1815
1819
|
})), this.subscriptions.push(this.zoomToolbar.on("close", () => {
|
|
1816
1820
|
this.dispatchEvent(new CustomEvent("customizer:close", {
|
|
1817
1821
|
bubbles: !0,
|
|
@@ -1820,21 +1824,26 @@ class ee extends HTMLElement {
|
|
|
1820
1824
|
})), this.subscriptions.push(this.zoomToolbar.on("save", async () => {
|
|
1821
1825
|
this.zoomToolbar.setSaveDisabled(!0, "Saving...");
|
|
1822
1826
|
try {
|
|
1823
|
-
|
|
1827
|
+
const o = await this.finalize();
|
|
1828
|
+
this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
|
|
1829
|
+
detail: o,
|
|
1830
|
+
bubbles: !0,
|
|
1831
|
+
composed: !0
|
|
1832
|
+
})), setTimeout(() => {
|
|
1824
1833
|
this.zoomToolbar.setSaveDisabled(!1, "Save Customization"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
|
|
1825
1834
|
}, 2e3);
|
|
1826
1835
|
} catch (o) {
|
|
1827
1836
|
console.error("Customizer: finalize failed", o);
|
|
1828
|
-
const
|
|
1829
|
-
this.showToast(
|
|
1837
|
+
const n = o instanceof Error ? o.message : "Save failed. Please try again.";
|
|
1838
|
+
this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save Customization");
|
|
1830
1839
|
}
|
|
1831
|
-
}));
|
|
1832
|
-
const
|
|
1833
|
-
|
|
1840
|
+
})), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
|
|
1841
|
+
const s = h("div", { class: "canvas-area" });
|
|
1842
|
+
s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = h("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), s.appendChild(this.canvasWrapper), this.inputPanel = new j(t), this.wireInputPanelEvents();
|
|
1834
1843
|
const a = h("div", { class: "editor-main" });
|
|
1835
|
-
a.appendChild(
|
|
1836
|
-
for (const
|
|
1837
|
-
|
|
1844
|
+
a.appendChild(s), a.appendChild(this.inputPanel.getBackdrop()), a.appendChild(this.inputPanel.getElement()), this.container.appendChild(a), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
|
|
1845
|
+
for (const n of o)
|
|
1846
|
+
n.target === this.canvasWrapper && this.handleCanvasResize(n.contentRect), n.target === this.container && this.handleLayoutResize(n.contentRect);
|
|
1838
1847
|
}), this.resizeObserver.observe(this.canvasWrapper), this.resizeObserver.observe(this.container);
|
|
1839
1848
|
}
|
|
1840
1849
|
// ─── Event Wiring ───
|
|
@@ -1882,10 +1891,10 @@ class ee extends HTMLElement {
|
|
|
1882
1891
|
})
|
|
1883
1892
|
), this.subscriptions.push(
|
|
1884
1893
|
this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
|
|
1885
|
-
const
|
|
1894
|
+
const s = this.contentManager.getContents();
|
|
1886
1895
|
let a = 0;
|
|
1887
|
-
for (const [
|
|
1888
|
-
r.type === "image" &&
|
|
1896
|
+
for (const [n, r] of s)
|
|
1897
|
+
r.type === "image" && n !== e && a++;
|
|
1889
1898
|
if (a >= 10) {
|
|
1890
1899
|
this.showToast("Maximum of 10 images per design reached", "error");
|
|
1891
1900
|
return;
|
|
@@ -1894,28 +1903,28 @@ class ee extends HTMLElement {
|
|
|
1894
1903
|
this.showToast("Storage quota exceeded. Upgrade your plan for more storage.", "error");
|
|
1895
1904
|
return;
|
|
1896
1905
|
}
|
|
1897
|
-
const o = this.currentTemplate?.areas.find((
|
|
1906
|
+
const o = this.currentTemplate?.areas.find((n) => n.id === e);
|
|
1898
1907
|
if (o && this.currentTemplate) {
|
|
1899
|
-
const
|
|
1908
|
+
const n = this.currentTemplate.print.targetDpi;
|
|
1900
1909
|
try {
|
|
1901
1910
|
const r = new Image(), d = await new Promise((p, g) => {
|
|
1902
1911
|
r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => g(new Error("Failed to load image")), r.src = t;
|
|
1903
|
-
}), l = o.location.width /
|
|
1912
|
+
}), l = o.location.width / n, c = o.location.height / n, u = K(
|
|
1904
1913
|
d.width,
|
|
1905
1914
|
d.height,
|
|
1906
1915
|
l,
|
|
1907
1916
|
c,
|
|
1908
|
-
|
|
1917
|
+
n
|
|
1909
1918
|
);
|
|
1910
1919
|
if (u.actualDPI < 150) {
|
|
1911
1920
|
if (!await this.showConfirmation(
|
|
1912
1921
|
"Low resolution detected",
|
|
1913
|
-
`This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${
|
|
1922
|
+
`This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${n} DPI.`,
|
|
1914
1923
|
"Cancel",
|
|
1915
1924
|
"Use Anyway"
|
|
1916
1925
|
)) return;
|
|
1917
|
-
} else u.actualDPI <
|
|
1918
|
-
`Image resolution is ${u.actualDPI} DPI (${
|
|
1926
|
+
} else u.actualDPI < n && (this.showToast(
|
|
1927
|
+
`Image resolution is ${u.actualDPI} DPI (${n} recommended)`,
|
|
1919
1928
|
"warning"
|
|
1920
1929
|
), this.stateManager.update({
|
|
1921
1930
|
warnings: [
|
|
@@ -1927,8 +1936,8 @@ class ee extends HTMLElement {
|
|
|
1927
1936
|
}
|
|
1928
1937
|
}
|
|
1929
1938
|
if (this.contentManager.setImageContent(e, t, i), this.saveContentState(), this.storageUsage) {
|
|
1930
|
-
const
|
|
1931
|
-
this.storageUsage.storageUsed +=
|
|
1939
|
+
const n = Math.round(t.length * 0.75);
|
|
1940
|
+
this.storageUsage.storageUsed += n, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
|
|
1932
1941
|
}
|
|
1933
1942
|
})
|
|
1934
1943
|
), this.subscriptions.push(
|
|
@@ -1988,8 +1997,8 @@ class ee extends HTMLElement {
|
|
|
1988
1997
|
handleCanvasResize(e) {
|
|
1989
1998
|
const { width: t, height: i } = e;
|
|
1990
1999
|
if (t === 0 || i === 0) return;
|
|
1991
|
-
const
|
|
1992
|
-
this.canvas.width = t *
|
|
2000
|
+
const s = window.devicePixelRatio || 1;
|
|
2001
|
+
this.canvas.width = t * s, this.canvas.height = i * s, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
|
|
1993
2002
|
}
|
|
1994
2003
|
handleLayoutResize(e) {
|
|
1995
2004
|
const t = e.width < _;
|
|
@@ -2040,18 +2049,18 @@ class ee extends HTMLElement {
|
|
|
2040
2049
|
const i = h("div", { class: `editor-toast editor-toast-${t}` }, e);
|
|
2041
2050
|
this.shadow.appendChild(i), setTimeout(() => i.remove(), 4e3);
|
|
2042
2051
|
}
|
|
2043
|
-
showConfirmation(e, t, i = "Cancel",
|
|
2052
|
+
showConfirmation(e, t, i = "Cancel", s = "Continue") {
|
|
2044
2053
|
return new Promise((a) => {
|
|
2045
|
-
const o = h("div", { class: "editor-modal-overlay" }),
|
|
2046
|
-
|
|
2054
|
+
const o = h("div", { class: "editor-modal-overlay" }), n = h("div", { class: "editor-modal" });
|
|
2055
|
+
n.appendChild(h("div", { class: "editor-modal-title" }, e)), n.appendChild(h("div", { class: "editor-modal-body" }, t));
|
|
2047
2056
|
const r = h("div", { class: "editor-modal-actions" }), d = h("button", { class: "editor-modal-btn" }, i);
|
|
2048
2057
|
d.addEventListener("click", () => {
|
|
2049
2058
|
o.remove(), a(!1);
|
|
2050
2059
|
});
|
|
2051
|
-
const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" },
|
|
2060
|
+
const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
|
|
2052
2061
|
l.addEventListener("click", () => {
|
|
2053
2062
|
o.remove(), a(!0);
|
|
2054
|
-
}), r.appendChild(d), r.appendChild(l),
|
|
2063
|
+
}), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (c) => {
|
|
2055
2064
|
c.target === o && (o.remove(), a(!1));
|
|
2056
2065
|
}), this.shadow.appendChild(o);
|
|
2057
2066
|
});
|
|
@@ -2060,9 +2069,9 @@ class ee extends HTMLElement {
|
|
|
2060
2069
|
showError(e, t) {
|
|
2061
2070
|
this.shadow.innerHTML = "";
|
|
2062
2071
|
const i = document.createElement("style");
|
|
2063
|
-
i.textContent =
|
|
2064
|
-
const
|
|
2065
|
-
|
|
2072
|
+
i.textContent = M, this.shadow.appendChild(i);
|
|
2073
|
+
const s = h("div", { class: "error-container" });
|
|
2074
|
+
s.appendChild(h("div", { class: "error-title" }, e)), s.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
|
|
2066
2075
|
new CustomEvent("error", {
|
|
2067
2076
|
detail: { message: e, error: t },
|
|
2068
2077
|
bubbles: !0,
|
|
@@ -2088,9 +2097,9 @@ function te(f, e) {
|
|
|
2088
2097
|
if (e.templateId && e.productId)
|
|
2089
2098
|
throw new Error("Only one of templateId or productId should be provided, not both");
|
|
2090
2099
|
const i = document.createElement("customizer-editor");
|
|
2091
|
-
e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), i.style.width = "100%", i.style.height = "100%";
|
|
2092
|
-
const
|
|
2093
|
-
i.addEventListener(
|
|
2100
|
+
e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), e.showCloseButton === !1 && i.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && i.setAttribute("show-save-button", "false"), i.style.width = "100%", i.style.height = "100%";
|
|
2101
|
+
const s = [], a = (n, r) => {
|
|
2102
|
+
i.addEventListener(n, r), s.push({ event: n, handler: r });
|
|
2094
2103
|
};
|
|
2095
2104
|
t.innerHTML = "", t.appendChild(i);
|
|
2096
2105
|
const o = {
|
|
@@ -2099,10 +2108,10 @@ function te(f, e) {
|
|
|
2099
2108
|
throw new Error("Editor not ready: getDesign method not available");
|
|
2100
2109
|
return i.getDesign();
|
|
2101
2110
|
},
|
|
2102
|
-
setDesign(
|
|
2111
|
+
setDesign(n) {
|
|
2103
2112
|
if (typeof i.setDesign != "function")
|
|
2104
2113
|
throw new Error("Editor not ready: setDesign method not available");
|
|
2105
|
-
i.setDesign(
|
|
2114
|
+
i.setDesign(n);
|
|
2106
2115
|
},
|
|
2107
2116
|
undo() {
|
|
2108
2117
|
if (typeof i.undo != "function")
|
|
@@ -2121,7 +2130,7 @@ function te(f, e) {
|
|
|
2121
2130
|
return typeof i.canRedo != "function" ? !1 : i.canRedo();
|
|
2122
2131
|
},
|
|
2123
2132
|
async finalize() {
|
|
2124
|
-
const
|
|
2133
|
+
const n = this.getDesign(), r = e.apiUrl || "https://api.varianta.io";
|
|
2125
2134
|
try {
|
|
2126
2135
|
const d = await fetch(`${r}/public/finalize`, {
|
|
2127
2136
|
method: "POST",
|
|
@@ -2129,8 +2138,8 @@ function te(f, e) {
|
|
|
2129
2138
|
"Content-Type": "application/json"
|
|
2130
2139
|
},
|
|
2131
2140
|
body: JSON.stringify({
|
|
2132
|
-
templateId:
|
|
2133
|
-
designJson:
|
|
2141
|
+
templateId: n.templateId || e.templateId,
|
|
2142
|
+
designJson: n
|
|
2134
2143
|
})
|
|
2135
2144
|
});
|
|
2136
2145
|
if (!d.ok) {
|
|
@@ -2173,25 +2182,25 @@ function te(f, e) {
|
|
|
2173
2182
|
throw e.onError && e.onError(l), d;
|
|
2174
2183
|
}
|
|
2175
2184
|
},
|
|
2176
|
-
addTextLayer(
|
|
2185
|
+
addTextLayer(n) {
|
|
2177
2186
|
if (typeof i.addTextLayer != "function")
|
|
2178
2187
|
throw new Error("Editor not ready: addTextLayer method not available");
|
|
2179
|
-
i.addTextLayer(
|
|
2188
|
+
i.addTextLayer(n);
|
|
2180
2189
|
},
|
|
2181
|
-
async addImageLayer(
|
|
2190
|
+
async addImageLayer(n) {
|
|
2182
2191
|
if (typeof i.addImageLayer != "function")
|
|
2183
2192
|
throw new Error("Editor not ready: addImageLayer method not available");
|
|
2184
|
-
return i.addImageLayer(
|
|
2193
|
+
return i.addImageLayer(n);
|
|
2185
2194
|
},
|
|
2186
|
-
removeLayer(
|
|
2195
|
+
removeLayer(n) {
|
|
2187
2196
|
if (typeof i.removeLayer != "function")
|
|
2188
2197
|
throw new Error("Editor not ready: removeLayer method not available");
|
|
2189
|
-
i.removeLayer(
|
|
2198
|
+
i.removeLayer(n);
|
|
2190
2199
|
},
|
|
2191
|
-
selectLayer(
|
|
2200
|
+
selectLayer(n) {
|
|
2192
2201
|
if (typeof i.selectLayer != "function")
|
|
2193
2202
|
throw new Error("Editor not ready: selectLayer method not available");
|
|
2194
|
-
i.selectLayer(
|
|
2203
|
+
i.selectLayer(n);
|
|
2195
2204
|
},
|
|
2196
2205
|
getSelectedLayerId() {
|
|
2197
2206
|
return typeof i.getSelectedLayerId != "function" ? null : i.getSelectedLayerId();
|
|
@@ -2202,20 +2211,20 @@ function te(f, e) {
|
|
|
2202
2211
|
getViews() {
|
|
2203
2212
|
return typeof i.getViews != "function" ? [] : i.getViews();
|
|
2204
2213
|
},
|
|
2205
|
-
setActiveView(
|
|
2214
|
+
setActiveView(n) {
|
|
2206
2215
|
if (typeof i.setActiveView != "function")
|
|
2207
2216
|
throw new Error("Editor not ready: setActiveView method not available");
|
|
2208
|
-
i.setActiveView(
|
|
2217
|
+
i.setActiveView(n);
|
|
2209
2218
|
},
|
|
2210
|
-
setTheme(
|
|
2211
|
-
i.setAttribute("theme",
|
|
2219
|
+
setTheme(n) {
|
|
2220
|
+
i.setAttribute("theme", n);
|
|
2212
2221
|
},
|
|
2213
|
-
setMode(
|
|
2214
|
-
i.setAttribute("mode",
|
|
2222
|
+
setMode(n) {
|
|
2223
|
+
i.setAttribute("mode", n);
|
|
2215
2224
|
},
|
|
2216
2225
|
destroy() {
|
|
2217
|
-
|
|
2218
|
-
i.removeEventListener(
|
|
2226
|
+
s.forEach(({ event: n, handler: r }) => {
|
|
2227
|
+
i.removeEventListener(n, r);
|
|
2219
2228
|
}), i.parentNode && i.parentNode.removeChild(i);
|
|
2220
2229
|
},
|
|
2221
2230
|
getElement() {
|
|
@@ -2224,22 +2233,26 @@ function te(f, e) {
|
|
|
2224
2233
|
};
|
|
2225
2234
|
return e.onReady && a("ready", (() => {
|
|
2226
2235
|
e.onReady?.(o);
|
|
2227
|
-
})), e.onChange && a("change", ((
|
|
2228
|
-
const r =
|
|
2236
|
+
})), e.onChange && a("change", ((n) => {
|
|
2237
|
+
const r = n.detail.design;
|
|
2229
2238
|
e.onChange?.(r);
|
|
2230
|
-
})), e.onLayerSelect && a("layer:select", ((
|
|
2231
|
-
e.onLayerSelect?.(
|
|
2232
|
-
})), e.onLayerAdd && a("layer:add", ((
|
|
2233
|
-
e.onLayerAdd?.(
|
|
2234
|
-
})), e.onLayerRemove && a("layer:remove", ((
|
|
2235
|
-
e.onLayerRemove?.(
|
|
2236
|
-
})), e.onLayerUpdate && a("layer:update", ((
|
|
2237
|
-
e.onLayerUpdate?.(
|
|
2238
|
-
})), e.onError && a("error", ((
|
|
2239
|
-
const r =
|
|
2239
|
+
})), e.onLayerSelect && a("layer:select", ((n) => {
|
|
2240
|
+
e.onLayerSelect?.(n.detail.layerId);
|
|
2241
|
+
})), e.onLayerAdd && a("layer:add", ((n) => {
|
|
2242
|
+
e.onLayerAdd?.(n.detail.layerId);
|
|
2243
|
+
})), e.onLayerRemove && a("layer:remove", ((n) => {
|
|
2244
|
+
e.onLayerRemove?.(n.detail.layerId);
|
|
2245
|
+
})), e.onLayerUpdate && a("layer:update", ((n) => {
|
|
2246
|
+
e.onLayerUpdate?.(n.detail.layerId);
|
|
2247
|
+
})), e.onError && a("error", ((n) => {
|
|
2248
|
+
const r = n.detail.error;
|
|
2240
2249
|
e.onError?.(r);
|
|
2241
|
-
})), e.onViewChange && a("view-change", ((
|
|
2242
|
-
e.onViewChange?.(
|
|
2250
|
+
})), e.onViewChange && a("view-change", ((n) => {
|
|
2251
|
+
e.onViewChange?.(n.detail.viewName);
|
|
2252
|
+
})), e.onClose && a("customizer:close", (() => {
|
|
2253
|
+
e.onClose?.();
|
|
2254
|
+
})), e.onSave && a("customizer:save", ((n) => {
|
|
2255
|
+
e.onSave?.(n.detail);
|
|
2243
2256
|
})), e.initialDesign && i.addEventListener(
|
|
2244
2257
|
"ready",
|
|
2245
2258
|
() => {
|