@varianta/sdk 0.1.6 → 0.1.7
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/dist/index.cjs.js +7 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +861 -671
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +12 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.esm.js.map +1 -1
- package/dist/types/react/Customizer.d.ts +2 -3
- package/dist/types/react/Customizer.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +10 -2
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/vanilla/index.d.ts.map +1 -1
- package/dist/types/vue/types.d.ts +1 -2
- package/dist/types/vue/types.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/index.esm.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
class
|
|
1
|
+
class U {
|
|
2
2
|
constructor(e) {
|
|
3
3
|
this.animationFrameId = null, this.needsRender = !0, this.areas = [], this.contents = /* @__PURE__ */ new Map(), this.artboard = null, this.bgImageElement = null, this.loadedImages = /* @__PURE__ */ new Map(), this.loadingImages = /* @__PURE__ */ new Set(), this.zoom = 1, this.pan = { x: 0, y: 0 }, this.selectedAreaId = null, this.hoveredHandle = null, this.eventListeners = /* @__PURE__ */ new Map(), this.safeArea = null, this.safeAreaViolations = /* @__PURE__ */ new Set(), this.showAreaBorders = !0, this.areaSelectionEnabled = !0, this.renderLoop = () => {
|
|
4
4
|
try {
|
|
5
5
|
this.needsRender && (this.render(), this.needsRender = !1, this.emit("render", void 0));
|
|
6
|
-
} catch (
|
|
7
|
-
console.error("Error in render loop:",
|
|
6
|
+
} catch (t) {
|
|
7
|
+
console.error("Error in render loop:", t);
|
|
8
8
|
}
|
|
9
9
|
this.animationFrameId = requestAnimationFrame(this.renderLoop);
|
|
10
10
|
}, this.canvas = e;
|
|
11
|
-
const
|
|
12
|
-
if (!
|
|
11
|
+
const i = e.getContext("2d");
|
|
12
|
+
if (!i)
|
|
13
13
|
throw new Error("Failed to get 2D context from canvas");
|
|
14
|
-
this.ctx =
|
|
14
|
+
this.ctx = i;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Start the render loop
|
|
@@ -41,7 +41,7 @@ class N {
|
|
|
41
41
|
* Set areas to render
|
|
42
42
|
*/
|
|
43
43
|
setAreas(e) {
|
|
44
|
-
this.areas = [...e].sort((
|
|
44
|
+
this.areas = [...e].sort((i, t) => i.zIndex - t.zIndex), this.requestRender();
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Get areas
|
|
@@ -64,8 +64,8 @@ class N {
|
|
|
64
64
|
/**
|
|
65
65
|
* Update content for a single area
|
|
66
66
|
*/
|
|
67
|
-
updateContent(e,
|
|
68
|
-
this.contents.set(e,
|
|
67
|
+
updateContent(e, i) {
|
|
68
|
+
this.contents.set(e, i), i.type === "image" && this.loadContentImages(), this.requestRender(), this.emit("content:change", { areaId: e, content: i });
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Remove content for an area
|
|
@@ -78,12 +78,12 @@ class N {
|
|
|
78
78
|
*/
|
|
79
79
|
setBackgroundImage(e) {
|
|
80
80
|
if (this.backgroundImage = e, e?.url && e.url !== this.bgImageElement?.src) {
|
|
81
|
-
const t = new Image();
|
|
81
|
+
const i = e.url, t = new Image();
|
|
82
82
|
t.crossOrigin = "anonymous", t.onload = () => {
|
|
83
83
|
this.bgImageElement = t, this.requestRender();
|
|
84
84
|
}, t.onerror = () => {
|
|
85
|
-
this.bgImageElement = null, console.error("Failed to load background image:",
|
|
86
|
-
}, t.src =
|
|
85
|
+
this.bgImageElement = null, console.error("Failed to load background image:", i), this.requestRender();
|
|
86
|
+
}, t.src = i;
|
|
87
87
|
} else e?.url || (this.bgImageElement = null);
|
|
88
88
|
this.requestRender();
|
|
89
89
|
}
|
|
@@ -129,10 +129,10 @@ class N {
|
|
|
129
129
|
getCenteredPan(e) {
|
|
130
130
|
if (!this.artboard)
|
|
131
131
|
return null;
|
|
132
|
-
const { width:
|
|
132
|
+
const { width: i, height: t } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s, o = this.canvas.height / s;
|
|
133
133
|
return {
|
|
134
|
-
x: (a / e -
|
|
135
|
-
y: (o / e -
|
|
134
|
+
x: (a / e - i) / 2,
|
|
135
|
+
y: (o / e - t) / 2
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
@@ -147,10 +147,10 @@ class N {
|
|
|
147
147
|
fitToView() {
|
|
148
148
|
if (!this.artboard)
|
|
149
149
|
return;
|
|
150
|
-
const e = 0, { width:
|
|
150
|
+
const e = 0, { width: i, height: t } = 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 n = a /
|
|
153
|
+
const n = a / i, r = o / t, d = Math.min(n, r), l = this.canvas.width / s, h = this.canvas.height / s, u = (l / d - i) / 2, p = (h / d - t) / 2;
|
|
154
154
|
this.zoom = d, this.pan = { x: u, y: p }, this.requestRender();
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
@@ -180,48 +180,48 @@ class N {
|
|
|
180
180
|
/**
|
|
181
181
|
* Convert screen coordinates to canvas coordinates
|
|
182
182
|
*/
|
|
183
|
-
screenToCanvas(e,
|
|
184
|
-
const
|
|
183
|
+
screenToCanvas(e, i) {
|
|
184
|
+
const t = window.devicePixelRatio || 1, s = e / t, a = i / t, o = s / this.zoom - this.pan.x, n = a / this.zoom - this.pan.y;
|
|
185
185
|
return { x: o, y: n };
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
188
|
* Get content bounds for an area (in artboard coordinates)
|
|
189
189
|
*/
|
|
190
190
|
getContentBounds(e) {
|
|
191
|
-
const
|
|
192
|
-
if (!t)
|
|
193
|
-
return null;
|
|
194
|
-
const i = this.contents.get(e);
|
|
191
|
+
const i = this.areas.find((d) => d.id === e);
|
|
195
192
|
if (!i)
|
|
196
193
|
return null;
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
199
|
-
|
|
194
|
+
const t = this.contents.get(e);
|
|
195
|
+
if (!t)
|
|
196
|
+
return null;
|
|
197
|
+
const { location: s } = i, { x: a, y: o, width: n, height: r } = s;
|
|
198
|
+
if (t.type === "text") {
|
|
199
|
+
const d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, h = t.rotation ?? 0, u = n * l, p = r * l, m = a + n / 2 + d.x, g = o + r / 2 + d.y;
|
|
200
200
|
return {
|
|
201
|
-
x:
|
|
202
|
-
y:
|
|
201
|
+
x: m - u / 2,
|
|
202
|
+
y: g - p / 2,
|
|
203
203
|
width: u,
|
|
204
204
|
height: p,
|
|
205
|
-
rotation:
|
|
206
|
-
centerX:
|
|
207
|
-
centerY:
|
|
205
|
+
rotation: h,
|
|
206
|
+
centerX: m,
|
|
207
|
+
centerY: g
|
|
208
208
|
};
|
|
209
|
-
} else if (
|
|
209
|
+
} else if (t.type === "image") {
|
|
210
210
|
const d = this.loadedImages.get(e);
|
|
211
211
|
if (!d)
|
|
212
212
|
return null;
|
|
213
|
-
const l =
|
|
214
|
-
let
|
|
215
|
-
p >
|
|
216
|
-
const b =
|
|
213
|
+
const l = t.offset || { x: 0, y: 0 }, h = t.scale ?? 1, u = t.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, m = n / r;
|
|
214
|
+
let g, f;
|
|
215
|
+
p > m ? (g = n, f = n / p) : (f = r, g = r * p);
|
|
216
|
+
const b = g * h, w = f * h, y = a + n / 2 + l.x, x = o + r / 2 + l.y;
|
|
217
217
|
return {
|
|
218
|
-
x:
|
|
219
|
-
y:
|
|
218
|
+
x: y - b / 2,
|
|
219
|
+
y: x - w / 2,
|
|
220
220
|
width: b,
|
|
221
|
-
height:
|
|
221
|
+
height: w,
|
|
222
222
|
rotation: u,
|
|
223
|
-
centerX:
|
|
224
|
-
centerY:
|
|
223
|
+
centerX: y,
|
|
224
|
+
centerY: x
|
|
225
225
|
};
|
|
226
226
|
}
|
|
227
227
|
return null;
|
|
@@ -230,12 +230,12 @@ class N {
|
|
|
230
230
|
* Hit test content at given canvas coordinates
|
|
231
231
|
* Returns area ID if content was hit, null otherwise
|
|
232
232
|
*/
|
|
233
|
-
hitTestContent(e,
|
|
233
|
+
hitTestContent(e, i) {
|
|
234
234
|
if (!this.areaSelectionEnabled)
|
|
235
235
|
return null;
|
|
236
|
-
for (let
|
|
237
|
-
const s = this.areas[
|
|
238
|
-
if (a && this.isPointInBounds(e,
|
|
236
|
+
for (let t = this.areas.length - 1; t >= 0; t--) {
|
|
237
|
+
const s = this.areas[t], a = this.getContentBounds(s.id);
|
|
238
|
+
if (a && this.isPointInBounds(e, i, a))
|
|
239
239
|
return s.id;
|
|
240
240
|
}
|
|
241
241
|
return null;
|
|
@@ -244,12 +244,12 @@ class N {
|
|
|
244
244
|
* Hit test area at given canvas coordinates (ignoring content)
|
|
245
245
|
* Returns area ID if point is inside area bounds
|
|
246
246
|
*/
|
|
247
|
-
hitTestArea(e,
|
|
247
|
+
hitTestArea(e, i) {
|
|
248
248
|
if (!this.areaSelectionEnabled)
|
|
249
249
|
return null;
|
|
250
|
-
for (let
|
|
251
|
-
const s = this.areas[
|
|
252
|
-
if (e >= a && e <= a + n &&
|
|
250
|
+
for (let t = this.areas.length - 1; t >= 0; t--) {
|
|
251
|
+
const s = this.areas[t], { x: a, y: o, width: n, height: r } = s.location;
|
|
252
|
+
if (e >= a && e <= a + n && i >= o && i <= o + r)
|
|
253
253
|
return s.id;
|
|
254
254
|
}
|
|
255
255
|
return null;
|
|
@@ -257,52 +257,52 @@ class N {
|
|
|
257
257
|
/**
|
|
258
258
|
* Test if a point is inside rotated bounds
|
|
259
259
|
*/
|
|
260
|
-
isPointInBounds(e,
|
|
261
|
-
const { centerX: s, centerY: a, width: o, height: n, rotation: r } =
|
|
262
|
-
return
|
|
260
|
+
isPointInBounds(e, i, t) {
|
|
261
|
+
const { centerX: s, centerY: a, width: o, height: n, rotation: r } = t, d = -r * Math.PI / 180, l = Math.cos(d), h = Math.sin(d), u = e - s, p = i - a, m = u * l - p * h, g = u * h + p * l;
|
|
262
|
+
return m >= -o / 2 && m <= o / 2 && g >= -n / 2 && g <= n / 2;
|
|
263
263
|
}
|
|
264
264
|
/**
|
|
265
265
|
* Get handle positions for the selected content
|
|
266
266
|
*/
|
|
267
267
|
getContentHandlePositions(e) {
|
|
268
|
-
const
|
|
269
|
-
if (!t)
|
|
270
|
-
return null;
|
|
271
|
-
const i = this.contents.get(e);
|
|
268
|
+
const i = this.areas.find((b) => b.id === e);
|
|
272
269
|
if (!i)
|
|
273
270
|
return null;
|
|
271
|
+
const t = this.contents.get(e);
|
|
272
|
+
if (!t)
|
|
273
|
+
return null;
|
|
274
274
|
const s = this.getContentBounds(e);
|
|
275
275
|
if (!s)
|
|
276
276
|
return null;
|
|
277
|
-
const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, l = 6 / this.zoom,
|
|
278
|
-
x: a + b * p -
|
|
279
|
-
y: o + b *
|
|
280
|
-
}),
|
|
281
|
-
if (
|
|
282
|
-
const b = n / 2,
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
const C =
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
277
|
+
const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, l = 6 / this.zoom, h = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), m = Math.sin(u), g = (b, w) => ({
|
|
278
|
+
x: a + b * p - w * m,
|
|
279
|
+
y: o + b * m + w * p
|
|
280
|
+
}), f = [];
|
|
281
|
+
if (t.type === "image" && i.imageOptions?.allowScaling || t.type === "text" && i.textOptions?.allowScaling) {
|
|
282
|
+
const b = n / 2, w = r / 2, y = g(-b, -w);
|
|
283
|
+
f.push({ type: "nw", ...y, radius: l });
|
|
284
|
+
const x = g(b, -w);
|
|
285
|
+
f.push({ type: "ne", ...x, radius: l });
|
|
286
|
+
const C = g(b, w);
|
|
287
|
+
f.push({ type: "se", ...C, radius: l });
|
|
288
|
+
const M = g(-b, w);
|
|
289
|
+
f.push({ type: "sw", ...M, radius: l });
|
|
290
290
|
}
|
|
291
|
-
if (
|
|
292
|
-
const b =
|
|
293
|
-
|
|
291
|
+
if (t.type === "image" && i.imageOptions?.allowRotation || t.type === "text" && i.textOptions?.allowRotation) {
|
|
292
|
+
const b = g(0, -r / 2 - h);
|
|
293
|
+
f.push({ type: "rotate", ...b, radius: l });
|
|
294
294
|
}
|
|
295
|
-
return
|
|
295
|
+
return f;
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
298
298
|
* Hit test handles at given canvas coordinates
|
|
299
299
|
*/
|
|
300
|
-
hitTestHandle(e,
|
|
301
|
-
const s = this.getContentHandlePositions(
|
|
300
|
+
hitTestHandle(e, i, t) {
|
|
301
|
+
const s = this.getContentHandlePositions(t);
|
|
302
302
|
if (!s)
|
|
303
303
|
return null;
|
|
304
304
|
for (const a of s) {
|
|
305
|
-
const o = e - a.x, n =
|
|
305
|
+
const o = e - a.x, n = i - a.y;
|
|
306
306
|
if (Math.sqrt(o * o + n * n) <= a.radius * 2)
|
|
307
307
|
return a.type;
|
|
308
308
|
}
|
|
@@ -312,24 +312,24 @@ class N {
|
|
|
312
312
|
* Check if positioning is allowed for an area
|
|
313
313
|
*/
|
|
314
314
|
isPositioningAllowed(e) {
|
|
315
|
-
const
|
|
316
|
-
if (!
|
|
315
|
+
const i = this.areas.find((s) => s.id === e);
|
|
316
|
+
if (!i)
|
|
317
317
|
return !1;
|
|
318
|
-
const
|
|
319
|
-
return
|
|
318
|
+
const t = this.contents.get(e);
|
|
319
|
+
return t ? t.type === "text" ? i.textOptions?.allowPositioning ?? !1 : i.imageOptions?.allowPositioning ?? !1 : !1;
|
|
320
320
|
}
|
|
321
321
|
/**
|
|
322
322
|
* Get area location (for constraint calculations)
|
|
323
323
|
*/
|
|
324
324
|
getAreaLocation(e) {
|
|
325
|
-
const
|
|
326
|
-
return
|
|
325
|
+
const i = this.areas.find((t) => t.id === e);
|
|
326
|
+
return i ? i.location : null;
|
|
327
327
|
}
|
|
328
328
|
/**
|
|
329
329
|
* Get area by ID
|
|
330
330
|
*/
|
|
331
331
|
getArea(e) {
|
|
332
|
-
return this.areas.find((
|
|
332
|
+
return this.areas.find((i) => i.id === e);
|
|
333
333
|
}
|
|
334
334
|
/**
|
|
335
335
|
* Set safe area margins for content safety guides
|
|
@@ -344,34 +344,34 @@ class N {
|
|
|
344
344
|
checkSafeAreaViolations() {
|
|
345
345
|
if (!this.safeArea || !this.artboard)
|
|
346
346
|
return [];
|
|
347
|
-
const e = [], { top:
|
|
347
|
+
const e = [], { top: i, right: t, bottom: s, left: a } = this.safeArea, o = a, n = i, r = this.artboard.width - t, 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
|
-
const
|
|
352
|
-
if (!
|
|
351
|
+
const h = this.getContentBounds(l.id);
|
|
352
|
+
if (!h)
|
|
353
353
|
continue;
|
|
354
|
-
const { centerX: u, centerY: p, width:
|
|
355
|
-
(
|
|
354
|
+
const { centerX: u, centerY: p, width: m, height: g, rotation: f } = h, b = f * Math.PI / 180, w = Math.abs(Math.cos(b)), y = Math.abs(Math.sin(b)), x = m * w + g * y, C = m * y + g * w, M = u - x / 2, R = p - C / 2, D = u + x / 2, N = p + C / 2;
|
|
355
|
+
(M < o || R < n || D > r || N > d) && e.push(l.id);
|
|
356
356
|
}
|
|
357
357
|
return this.safeAreaViolations = new Set(e), this.requestRender(), e;
|
|
358
358
|
}
|
|
359
359
|
/**
|
|
360
360
|
* Add event listener
|
|
361
361
|
*/
|
|
362
|
-
on(e,
|
|
363
|
-
return this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(
|
|
364
|
-
this.eventListeners.get(e)?.delete(
|
|
362
|
+
on(e, i) {
|
|
363
|
+
return this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(i), () => {
|
|
364
|
+
this.eventListeners.get(e)?.delete(i);
|
|
365
365
|
};
|
|
366
366
|
}
|
|
367
367
|
/**
|
|
368
368
|
* Emit event
|
|
369
369
|
*/
|
|
370
|
-
emit(e,
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
373
|
-
for (const s of
|
|
374
|
-
s(
|
|
370
|
+
emit(e, i) {
|
|
371
|
+
const t = this.eventListeners.get(e);
|
|
372
|
+
if (t)
|
|
373
|
+
for (const s of t)
|
|
374
|
+
s(i);
|
|
375
375
|
}
|
|
376
376
|
/**
|
|
377
377
|
* Export to PNG at full resolution
|
|
@@ -379,49 +379,53 @@ class N {
|
|
|
379
379
|
async exportToPNG() {
|
|
380
380
|
if (!this.artboard)
|
|
381
381
|
throw new Error("No artboard configured");
|
|
382
|
-
const { width: e, height:
|
|
383
|
-
s.width = e, s.height =
|
|
382
|
+
const { width: e, height: i, backgroundColor: t } = this.artboard, s = document.createElement("canvas");
|
|
383
|
+
s.width = e, s.height = i;
|
|
384
384
|
const a = s.getContext("2d");
|
|
385
385
|
if (!a)
|
|
386
386
|
throw new Error("Failed to get 2D context for export");
|
|
387
|
-
a.fillStyle =
|
|
387
|
+
a.fillStyle = t, a.fillRect(0, 0, e, i), this.backgroundImage?.position === "behind" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, i);
|
|
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,
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, i), new Promise((o, n) => {
|
|
391
|
+
try {
|
|
392
|
+
s.toBlob((r) => {
|
|
393
|
+
r ? o(r) : n(new Error("Failed to create PNG blob"));
|
|
394
|
+
}, "image/png", 1);
|
|
395
|
+
} catch (r) {
|
|
396
|
+
r instanceof DOMException && r.name === "SecurityError" ? n(new Error("Cannot export: images failed to load with CORS. Check CDN configuration.")) : n(r);
|
|
397
|
+
}
|
|
394
398
|
});
|
|
395
399
|
}
|
|
396
400
|
/**
|
|
397
401
|
* Load images from content data URLs
|
|
398
402
|
*/
|
|
399
403
|
loadContentImages() {
|
|
400
|
-
for (const [e,
|
|
401
|
-
if (
|
|
404
|
+
for (const [e, i] of this.contents)
|
|
405
|
+
if (i.type === "image" && !this.loadedImages.has(e) && !this.loadingImages.has(e)) {
|
|
402
406
|
this.loadingImages.add(e);
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
this.loadedImages.set(e,
|
|
406
|
-
},
|
|
407
|
+
const t = new Image();
|
|
408
|
+
t.crossOrigin = "anonymous", t.onload = () => {
|
|
409
|
+
this.loadedImages.set(e, t), this.loadingImages.delete(e), this.requestRender();
|
|
410
|
+
}, t.onerror = () => {
|
|
407
411
|
this.loadingImages.delete(e), console.error("Failed to load image for area:", e);
|
|
408
|
-
},
|
|
412
|
+
}, t.src = i.dataUrl;
|
|
409
413
|
}
|
|
410
414
|
for (const e of this.loadedImages.keys()) {
|
|
411
|
-
const
|
|
412
|
-
(!
|
|
415
|
+
const i = this.contents.get(e);
|
|
416
|
+
(!i || i.type !== "image") && this.loadedImages.delete(e);
|
|
413
417
|
}
|
|
414
418
|
}
|
|
415
419
|
/**
|
|
416
420
|
* Render the canvas
|
|
417
421
|
*/
|
|
418
422
|
render() {
|
|
419
|
-
const { ctx: e, canvas:
|
|
420
|
-
if (e.fillStyle = "#f3f4f6", e.fillRect(0, 0,
|
|
423
|
+
const { ctx: e, canvas: i } = this;
|
|
424
|
+
if (e.fillStyle = "#f3f4f6", e.fillRect(0, 0, i.width, i.height), !this.artboard)
|
|
421
425
|
return;
|
|
422
426
|
e.save();
|
|
423
|
-
const
|
|
424
|
-
e.scale(
|
|
427
|
+
const t = window.devicePixelRatio || 1;
|
|
428
|
+
e.scale(t, t), e.scale(this.zoom, this.zoom), e.translate(this.pan.x, this.pan.y);
|
|
425
429
|
const { width: s, height: a, backgroundColor: o } = this.artboard;
|
|
426
430
|
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
431
|
for (const n of this.areas)
|
|
@@ -431,8 +435,8 @@ class N {
|
|
|
431
435
|
const r = this.areas.find((p) => p.id === n);
|
|
432
436
|
if (!r)
|
|
433
437
|
continue;
|
|
434
|
-
const { x: d, y: l, width:
|
|
435
|
-
e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(d, l,
|
|
438
|
+
const { x: d, y: l, width: h, height: u } = r.location;
|
|
439
|
+
e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(d, l, h, u), e.restore();
|
|
436
440
|
}
|
|
437
441
|
this.selectedAreaId && this.renderSelectionOverlay(this.selectedAreaId), e.restore();
|
|
438
442
|
}
|
|
@@ -440,37 +444,37 @@ class N {
|
|
|
440
444
|
* Render selection overlay with handles
|
|
441
445
|
*/
|
|
442
446
|
renderSelectionOverlay(e) {
|
|
443
|
-
const
|
|
444
|
-
if (!
|
|
445
|
-
const l = this.areas.find((
|
|
447
|
+
const i = this.getContentBounds(e);
|
|
448
|
+
if (!i) {
|
|
449
|
+
const l = this.areas.find((f) => f.id === e);
|
|
446
450
|
if (!l)
|
|
447
451
|
return;
|
|
448
|
-
const { ctx:
|
|
449
|
-
|
|
452
|
+
const { ctx: h } = this, { x: u, y: p, width: m, height: g } = l.location;
|
|
453
|
+
h.save(), h.strokeStyle = "#3b82f6", h.lineWidth = 2 / this.zoom, h.setLineDash([]), h.strokeRect(u, p, m, g), h.restore();
|
|
450
454
|
return;
|
|
451
455
|
}
|
|
452
|
-
const { ctx:
|
|
453
|
-
|
|
456
|
+
const { ctx: t } = this, { centerX: s, centerY: a, width: o, height: n, rotation: r } = i;
|
|
457
|
+
t.save(), t.translate(s, a), t.rotate(r * Math.PI / 180), t.strokeStyle = "#3b82f6", t.lineWidth = 2 / this.zoom, t.setLineDash([]), t.strokeRect(-o / 2, -n / 2, o, n), t.restore();
|
|
454
458
|
const d = this.getContentHandlePositions(e);
|
|
455
459
|
if (d)
|
|
456
460
|
for (const l of d) {
|
|
457
|
-
|
|
458
|
-
const
|
|
461
|
+
t.save();
|
|
462
|
+
const h = this.hoveredHandle === l.type, u = h ? "#dbeafe" : "#ffffff", p = h ? "#2563eb" : "#3b82f6";
|
|
459
463
|
if (l.type === "rotate") {
|
|
460
|
-
const
|
|
461
|
-
|
|
464
|
+
const m = this.getRotatedPoint(s, a - n / 2, s, a, r);
|
|
465
|
+
t.beginPath(), t.strokeStyle = p, t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.moveTo(m.x, m.y), t.lineTo(l.x, l.y), t.stroke(), t.setLineDash([]), t.beginPath(), t.fillStyle = u, t.strokeStyle = p, t.lineWidth = 2 / this.zoom, t.arc(l.x, l.y, l.radius, 0, Math.PI * 2), t.fill(), t.stroke(), t.beginPath(), t.strokeStyle = p, t.lineWidth = 1.5 / this.zoom, t.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), t.stroke();
|
|
462
466
|
} else
|
|
463
|
-
|
|
464
|
-
|
|
467
|
+
t.fillStyle = u, t.strokeStyle = p, t.lineWidth = 2 / this.zoom, t.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), t.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
|
|
468
|
+
t.restore();
|
|
465
469
|
}
|
|
466
470
|
}
|
|
467
471
|
/**
|
|
468
472
|
* Rotate a point around a center
|
|
469
473
|
*/
|
|
470
|
-
getRotatedPoint(e,
|
|
471
|
-
const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e -
|
|
474
|
+
getRotatedPoint(e, i, t, s, a) {
|
|
475
|
+
const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e - t, l = i - s;
|
|
472
476
|
return {
|
|
473
|
-
x:
|
|
477
|
+
x: t + d * n - l * r,
|
|
474
478
|
y: s + d * r + l * n
|
|
475
479
|
};
|
|
476
480
|
}
|
|
@@ -480,11 +484,11 @@ class N {
|
|
|
480
484
|
renderBackgroundImage() {
|
|
481
485
|
if (!this.bgImageElement || !this.artboard || !this.backgroundImage)
|
|
482
486
|
return;
|
|
483
|
-
const { ctx: e } = this, { opacity:
|
|
484
|
-
e.save(), e.globalAlpha =
|
|
485
|
-
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o =
|
|
487
|
+
const { ctx: e } = this, { opacity: i } = this.backgroundImage, { width: t, height: s } = this.artboard;
|
|
488
|
+
e.save(), e.globalAlpha = i;
|
|
489
|
+
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / s;
|
|
486
490
|
let n, r, d, l;
|
|
487
|
-
a > o ? (r = s, n = s * a, d = (
|
|
491
|
+
a > o ? (r = s, n = s * a, d = (t - n) / 2, l = 0) : (n = t, r = t / a, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
|
|
488
492
|
}
|
|
489
493
|
/**
|
|
490
494
|
* Render safe area guide as dashed green rectangle
|
|
@@ -492,106 +496,106 @@ class N {
|
|
|
492
496
|
renderSafeAreaGuide() {
|
|
493
497
|
if (!this.safeArea || !this.artboard)
|
|
494
498
|
return;
|
|
495
|
-
const { ctx: e } = this, { top:
|
|
499
|
+
const { ctx: e } = this, { top: i, right: t, bottom: s, left: a } = this.safeArea, o = a, n = i, r = this.artboard.width - a - t, d = this.artboard.height - i - s;
|
|
496
500
|
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
501
|
}
|
|
498
502
|
/**
|
|
499
503
|
* Render an area with its content
|
|
500
504
|
*/
|
|
501
505
|
renderAreaWithContent(e) {
|
|
502
|
-
const { ctx:
|
|
503
|
-
s && (
|
|
506
|
+
const { ctx: i } = this, { location: t, backgroundColor: s } = e, { x: a, y: o, width: n, height: r } = t;
|
|
507
|
+
s && (i.fillStyle = s, i.fillRect(a, o, n, r));
|
|
504
508
|
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 && (
|
|
509
|
+
d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (i.save(), i.strokeStyle = e.borderColor || "#3b82f6", i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.strokeRect(a, o, n, r), i.setLineDash([]), i.restore());
|
|
506
510
|
}
|
|
507
511
|
/**
|
|
508
512
|
* Render text content in an area
|
|
509
513
|
*/
|
|
510
|
-
renderTextContent(e,
|
|
511
|
-
if (!
|
|
514
|
+
renderTextContent(e, i) {
|
|
515
|
+
if (!i.text.trim())
|
|
512
516
|
return;
|
|
513
|
-
const { ctx:
|
|
514
|
-
|
|
515
|
-
let
|
|
516
|
-
switch (
|
|
517
|
+
const { ctx: t } = this, { location: s } = e, { x: a, y: o, width: n, height: r } = s, d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, h = i.rotation ?? 0, u = a + n / 2 + d.x, p = o + r / 2 + d.y;
|
|
518
|
+
t.save(), t.beginPath(), t.rect(a, o, n, r), t.clip(), t.translate(u, p), t.rotate(h * Math.PI / 180), t.scale(l, l), t.font = `${i.size}px ${i.font}, sans-serif`, t.fillStyle = i.color, t.textBaseline = "middle";
|
|
519
|
+
let m;
|
|
520
|
+
switch (i.align) {
|
|
517
521
|
case "left":
|
|
518
|
-
|
|
522
|
+
t.textAlign = "left", m = -n / 2 + 10;
|
|
519
523
|
break;
|
|
520
524
|
case "right":
|
|
521
|
-
|
|
525
|
+
t.textAlign = "right", m = n / 2 - 10;
|
|
522
526
|
break;
|
|
523
527
|
case "center":
|
|
524
528
|
default:
|
|
525
|
-
|
|
529
|
+
t.textAlign = "center", m = 0;
|
|
526
530
|
break;
|
|
527
531
|
}
|
|
528
|
-
|
|
532
|
+
t.fillText(i.text, m, 0), t.restore();
|
|
529
533
|
}
|
|
530
534
|
/**
|
|
531
535
|
* Render image content in an area
|
|
532
536
|
*/
|
|
533
|
-
renderImageContent(e,
|
|
534
|
-
const
|
|
535
|
-
if (!
|
|
537
|
+
renderImageContent(e, i) {
|
|
538
|
+
const t = this.loadedImages.get(e.id);
|
|
539
|
+
if (!t)
|
|
536
540
|
return;
|
|
537
|
-
const { ctx: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l =
|
|
541
|
+
const { ctx: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l = i.offset || { x: 0, y: 0 }, h = i.scale ?? 1, u = i.rotation ?? 0;
|
|
538
542
|
s.save(), s.beginPath(), s.rect(o, n, r, d), s.clip();
|
|
539
|
-
const p =
|
|
540
|
-
let
|
|
541
|
-
p >
|
|
542
|
-
const b =
|
|
543
|
-
s.translate(
|
|
543
|
+
const p = t.naturalWidth / t.naturalHeight, m = r / d;
|
|
544
|
+
let g, f;
|
|
545
|
+
p > m ? (g = r, f = r / p) : (f = d, g = d * p);
|
|
546
|
+
const b = g * h, w = f * h, y = o + r / 2 + l.x, x = n + d / 2 + l.y;
|
|
547
|
+
s.translate(y, x), s.rotate(u * Math.PI / 180), s.drawImage(t, -b / 2, -w / 2, b, w), s.restore();
|
|
544
548
|
}
|
|
545
549
|
/**
|
|
546
550
|
* Draw background image on a specific context (for export)
|
|
547
551
|
*/
|
|
548
|
-
drawBackgroundImageOnContext(e,
|
|
552
|
+
drawBackgroundImageOnContext(e, i, t) {
|
|
549
553
|
if (!this.bgImageElement || !this.backgroundImage)
|
|
550
554
|
return;
|
|
551
555
|
const { opacity: s } = this.backgroundImage;
|
|
552
556
|
e.save(), e.globalAlpha = s;
|
|
553
|
-
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o =
|
|
557
|
+
const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / t;
|
|
554
558
|
let n, r, d, l;
|
|
555
|
-
a > o ? (r =
|
|
559
|
+
a > o ? (r = t, n = t * a, d = (i - n) / 2, l = 0) : (n = i, r = i / a, d = 0, l = (t - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
|
|
556
560
|
}
|
|
557
561
|
/**
|
|
558
562
|
* Draw area content on a specific context (for export)
|
|
559
563
|
*/
|
|
560
|
-
drawAreaContentOnContext(e,
|
|
561
|
-
const { location:
|
|
564
|
+
drawAreaContentOnContext(e, i) {
|
|
565
|
+
const { location: t, backgroundColor: s } = i, { x: a, y: o, width: n, height: r } = t;
|
|
562
566
|
s && (e.fillStyle = s, e.fillRect(a, o, n, r));
|
|
563
|
-
const d = this.contents.get(
|
|
567
|
+
const d = this.contents.get(i.id);
|
|
564
568
|
if (d) {
|
|
565
569
|
if (d.type === "text") {
|
|
566
570
|
if (!d.text.trim())
|
|
567
571
|
return;
|
|
568
|
-
const l = d.offset || { x: 0, y: 0 },
|
|
569
|
-
e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip(), e.translate(p,
|
|
570
|
-
let
|
|
572
|
+
const l = d.offset || { x: 0, y: 0 }, h = d.scale ?? 1, u = d.rotation ?? 0, p = a + n / 2 + l.x, m = o + r / 2 + l.y;
|
|
573
|
+
e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip(), e.translate(p, m), e.rotate(u * Math.PI / 180), e.scale(h, h), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
|
|
574
|
+
let g;
|
|
571
575
|
switch (d.align) {
|
|
572
576
|
case "left":
|
|
573
|
-
e.textAlign = "left",
|
|
577
|
+
e.textAlign = "left", g = -n / 2 + 10;
|
|
574
578
|
break;
|
|
575
579
|
case "right":
|
|
576
|
-
e.textAlign = "right",
|
|
580
|
+
e.textAlign = "right", g = n / 2 - 10;
|
|
577
581
|
break;
|
|
578
582
|
case "center":
|
|
579
583
|
default:
|
|
580
|
-
e.textAlign = "center",
|
|
584
|
+
e.textAlign = "center", g = 0;
|
|
581
585
|
break;
|
|
582
586
|
}
|
|
583
|
-
e.fillText(d.text,
|
|
587
|
+
e.fillText(d.text, g, 0), e.restore();
|
|
584
588
|
} else if (d.type === "image") {
|
|
585
|
-
const l = this.loadedImages.get(
|
|
589
|
+
const l = this.loadedImages.get(i.id);
|
|
586
590
|
if (!l)
|
|
587
591
|
return;
|
|
588
|
-
const
|
|
592
|
+
const h = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
|
|
589
593
|
e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip();
|
|
590
|
-
const
|
|
591
|
-
let
|
|
592
|
-
|
|
593
|
-
const
|
|
594
|
-
e.translate(
|
|
594
|
+
const m = l.naturalWidth / l.naturalHeight, g = n / r;
|
|
595
|
+
let f, b;
|
|
596
|
+
m > g ? (f = n, b = n / m) : (b = r, f = r * m);
|
|
597
|
+
const w = f * u, y = b * u, x = a + n / 2 + h.x, C = o + r / 2 + h.y;
|
|
598
|
+
e.translate(x, C), e.rotate(p * Math.PI / 180), e.drawImage(l, -w / 2, -y / 2, w, y), e.restore();
|
|
595
599
|
}
|
|
596
600
|
}
|
|
597
601
|
}
|
|
@@ -602,8 +606,8 @@ class N {
|
|
|
602
606
|
this.stop(), this.bgImageElement = null, this.loadedImages.clear(), this.loadingImages.clear(), this.eventListeners.clear();
|
|
603
607
|
}
|
|
604
608
|
}
|
|
605
|
-
function
|
|
606
|
-
const e =
|
|
609
|
+
function S(v) {
|
|
610
|
+
const e = v.textOptions;
|
|
607
611
|
return {
|
|
608
612
|
type: "text",
|
|
609
613
|
text: e?.defaultText || "",
|
|
@@ -613,14 +617,14 @@ function E(f) {
|
|
|
613
617
|
align: e?.defaultAlign || "center"
|
|
614
618
|
};
|
|
615
619
|
}
|
|
616
|
-
function
|
|
617
|
-
return
|
|
620
|
+
function z(v) {
|
|
621
|
+
return v.contentType === "text" || v.contentType === "both";
|
|
618
622
|
}
|
|
619
|
-
function T(
|
|
620
|
-
return
|
|
623
|
+
function T(v) {
|
|
624
|
+
return v.contentType === "image" || v.contentType === "both";
|
|
621
625
|
}
|
|
622
|
-
const
|
|
623
|
-
class
|
|
626
|
+
const V = 50;
|
|
627
|
+
class O {
|
|
624
628
|
constructor(e) {
|
|
625
629
|
this.history = [], this.historyIndex = -1, this.listeners = /* @__PURE__ */ new Set(), this.isRestoring = !1, this.state = this.cloneState(e), this.history.push({
|
|
626
630
|
state: this.cloneState(e),
|
|
@@ -634,9 +638,9 @@ class V {
|
|
|
634
638
|
/**
|
|
635
639
|
* Update state and save to history
|
|
636
640
|
*/
|
|
637
|
-
setState(e,
|
|
641
|
+
setState(e, i, t = !1) {
|
|
638
642
|
const s = this.state;
|
|
639
|
-
this.state = { ...this.state, ...e }, !
|
|
643
|
+
this.state = { ...this.state, ...e }, !t && !this.isRestoring && this.saveToHistory(s, i), this.notifyListeners();
|
|
640
644
|
}
|
|
641
645
|
/**
|
|
642
646
|
* Update state without saving to history
|
|
@@ -666,12 +670,12 @@ class V {
|
|
|
666
670
|
getHistory() {
|
|
667
671
|
return this.history;
|
|
668
672
|
}
|
|
669
|
-
saveToHistory(e,
|
|
673
|
+
saveToHistory(e, i) {
|
|
670
674
|
this.historyIndex < this.history.length - 1 && (this.history = this.history.slice(0, this.historyIndex + 1)), this.history.push({
|
|
671
675
|
state: this.cloneState(e),
|
|
672
676
|
timestamp: Date.now(),
|
|
673
|
-
description:
|
|
674
|
-
}), this.history.length >
|
|
677
|
+
description: i
|
|
678
|
+
}), this.history.length > V && this.history.shift(), this.historyIndex = this.history.length - 1;
|
|
675
679
|
}
|
|
676
680
|
restoreFromHistory() {
|
|
677
681
|
const e = this.history[this.historyIndex];
|
|
@@ -679,11 +683,11 @@ class V {
|
|
|
679
683
|
}
|
|
680
684
|
notifyListeners() {
|
|
681
685
|
const e = this.state;
|
|
682
|
-
for (const
|
|
686
|
+
for (const i of this.listeners)
|
|
683
687
|
try {
|
|
684
|
-
|
|
685
|
-
} catch (
|
|
686
|
-
console.error("Error in state change listener:",
|
|
688
|
+
i(e);
|
|
689
|
+
} catch (t) {
|
|
690
|
+
console.error("Error in state change listener:", t);
|
|
687
691
|
}
|
|
688
692
|
}
|
|
689
693
|
/**
|
|
@@ -694,14 +698,14 @@ class V {
|
|
|
694
698
|
return JSON.parse(JSON.stringify(e));
|
|
695
699
|
}
|
|
696
700
|
}
|
|
697
|
-
class
|
|
701
|
+
class P {
|
|
698
702
|
constructor(e) {
|
|
699
703
|
this.listeners = /* @__PURE__ */ new Set(), this.areas = e, this.contents = this.buildInitialContents();
|
|
700
704
|
}
|
|
701
705
|
buildInitialContents() {
|
|
702
706
|
const e = /* @__PURE__ */ new Map();
|
|
703
|
-
for (const
|
|
704
|
-
|
|
707
|
+
for (const i of this.areas)
|
|
708
|
+
z(i) && e.set(i.id, S(i));
|
|
705
709
|
return e;
|
|
706
710
|
}
|
|
707
711
|
getContents() {
|
|
@@ -710,8 +714,8 @@ class k {
|
|
|
710
714
|
getContent(e) {
|
|
711
715
|
return this.contents.get(e);
|
|
712
716
|
}
|
|
713
|
-
setTextContent(e,
|
|
714
|
-
const
|
|
717
|
+
setTextContent(e, i) {
|
|
718
|
+
const t = this.contents.get(e), s = this.areas.find((o) => o.id === e), a = s ? S(s) : {
|
|
715
719
|
type: "text",
|
|
716
720
|
text: "",
|
|
717
721
|
font: "Inter",
|
|
@@ -719,35 +723,35 @@ class k {
|
|
|
719
723
|
color: "#000000",
|
|
720
724
|
align: "center"
|
|
721
725
|
};
|
|
722
|
-
|
|
726
|
+
t?.type === "text" ? this.contents.set(e, { ...t, ...i }) : this.contents.set(e, { ...a, ...i }), this.notify();
|
|
723
727
|
}
|
|
724
|
-
setImageContent(e,
|
|
728
|
+
setImageContent(e, i, t) {
|
|
725
729
|
const s = {
|
|
726
730
|
type: "image",
|
|
727
|
-
dataUrl:
|
|
728
|
-
filename:
|
|
731
|
+
dataUrl: i,
|
|
732
|
+
filename: t
|
|
729
733
|
};
|
|
730
734
|
this.contents.set(e, s), this.notify();
|
|
731
735
|
}
|
|
732
736
|
clearContent(e) {
|
|
733
|
-
const
|
|
734
|
-
|
|
737
|
+
const i = this.areas.find((t) => t.id === e);
|
|
738
|
+
i && z(i) ? this.contents.set(e, S(i)) : this.contents.delete(e), this.notify();
|
|
735
739
|
}
|
|
736
|
-
setContentOffset(e,
|
|
737
|
-
const
|
|
738
|
-
|
|
740
|
+
setContentOffset(e, i) {
|
|
741
|
+
const t = this.contents.get(e);
|
|
742
|
+
t && (this.contents.set(e, { ...t, offset: i }), this.notify());
|
|
739
743
|
}
|
|
740
|
-
setImageScale(e,
|
|
741
|
-
const
|
|
742
|
-
if (!
|
|
743
|
-
const s = Math.max(0.1, Math.min(5,
|
|
744
|
-
this.contents.set(e, { ...
|
|
744
|
+
setImageScale(e, i) {
|
|
745
|
+
const t = this.contents.get(e);
|
|
746
|
+
if (!t || t.type !== "image") return;
|
|
747
|
+
const s = Math.max(0.1, Math.min(5, i));
|
|
748
|
+
this.contents.set(e, { ...t, scale: s }), this.notify();
|
|
745
749
|
}
|
|
746
|
-
setImageRotation(e,
|
|
747
|
-
const
|
|
748
|
-
if (!
|
|
749
|
-
let s =
|
|
750
|
-
s < 0 && (s += 360), this.contents.set(e, { ...
|
|
750
|
+
setImageRotation(e, i) {
|
|
751
|
+
const t = this.contents.get(e);
|
|
752
|
+
if (!t || t.type !== "image") return;
|
|
753
|
+
let s = i % 360;
|
|
754
|
+
s < 0 && (s += 360), this.contents.set(e, { ...t, rotation: s }), this.notify();
|
|
751
755
|
}
|
|
752
756
|
reset() {
|
|
753
757
|
this.contents = this.buildInitialContents(), this.notify();
|
|
@@ -771,12 +775,12 @@ class k {
|
|
|
771
775
|
for (const e of this.listeners)
|
|
772
776
|
try {
|
|
773
777
|
e();
|
|
774
|
-
} catch (
|
|
775
|
-
console.error("Error in AreaContentManager listener:",
|
|
778
|
+
} catch (i) {
|
|
779
|
+
console.error("Error in AreaContentManager listener:", i);
|
|
776
780
|
}
|
|
777
781
|
}
|
|
778
782
|
}
|
|
779
|
-
class
|
|
783
|
+
class E {
|
|
780
784
|
constructor() {
|
|
781
785
|
this.listeners = /* @__PURE__ */ new Map();
|
|
782
786
|
}
|
|
@@ -787,8 +791,8 @@ class I {
|
|
|
787
791
|
* @param handler - Event handler function
|
|
788
792
|
* @returns Unsubscribe function
|
|
789
793
|
*/
|
|
790
|
-
on(e,
|
|
791
|
-
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(
|
|
794
|
+
on(e, i) {
|
|
795
|
+
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(i), () => this.off(e, i);
|
|
792
796
|
}
|
|
793
797
|
/**
|
|
794
798
|
* Register a one-time event listener
|
|
@@ -797,11 +801,11 @@ class I {
|
|
|
797
801
|
* @param handler - Event handler function
|
|
798
802
|
* @returns Unsubscribe function
|
|
799
803
|
*/
|
|
800
|
-
once(e,
|
|
801
|
-
const
|
|
802
|
-
|
|
804
|
+
once(e, i) {
|
|
805
|
+
const t = (s) => {
|
|
806
|
+
i(s), this.off(e, t);
|
|
803
807
|
};
|
|
804
|
-
return this.on(e,
|
|
808
|
+
return this.on(e, t);
|
|
805
809
|
}
|
|
806
810
|
/**
|
|
807
811
|
* Remove an event listener
|
|
@@ -809,9 +813,9 @@ class I {
|
|
|
809
813
|
* @param event - Event name
|
|
810
814
|
* @param handler - Event handler function to remove
|
|
811
815
|
*/
|
|
812
|
-
off(e,
|
|
813
|
-
const
|
|
814
|
-
|
|
816
|
+
off(e, i) {
|
|
817
|
+
const t = this.listeners.get(e);
|
|
818
|
+
t && (t.delete(i), t.size === 0 && this.listeners.delete(e));
|
|
815
819
|
}
|
|
816
820
|
/**
|
|
817
821
|
* Emit an event to all registered listeners
|
|
@@ -819,13 +823,13 @@ class I {
|
|
|
819
823
|
* @param event - Event name
|
|
820
824
|
* @param data - Event data
|
|
821
825
|
*/
|
|
822
|
-
emit(e,
|
|
823
|
-
const
|
|
824
|
-
if (
|
|
825
|
-
const s = Array.from(
|
|
826
|
+
emit(e, i) {
|
|
827
|
+
const t = this.listeners.get(e);
|
|
828
|
+
if (t) {
|
|
829
|
+
const s = Array.from(t);
|
|
826
830
|
for (const a of s)
|
|
827
831
|
try {
|
|
828
|
-
a(
|
|
832
|
+
a(i);
|
|
829
833
|
} catch (o) {
|
|
830
834
|
console.error(`Error in event handler for "${String(e)}":`, o);
|
|
831
835
|
}
|
|
@@ -849,7 +853,7 @@ class I {
|
|
|
849
853
|
return this.listeners.get(e)?.size ?? 0;
|
|
850
854
|
}
|
|
851
855
|
}
|
|
852
|
-
class
|
|
856
|
+
class $ extends E {
|
|
853
857
|
constructor() {
|
|
854
858
|
super(...arguments), this.activeTouches = /* @__PURE__ */ new Map(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
|
|
855
859
|
}
|
|
@@ -858,30 +862,30 @@ class O extends I {
|
|
|
858
862
|
return this.isTwoFingerGesture;
|
|
859
863
|
}
|
|
860
864
|
handleTouchStart(e) {
|
|
861
|
-
for (let
|
|
862
|
-
const
|
|
863
|
-
this.activeTouches.set(
|
|
864
|
-
x:
|
|
865
|
-
y:
|
|
865
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
866
|
+
const t = e.changedTouches[i];
|
|
867
|
+
this.activeTouches.set(t.identifier, {
|
|
868
|
+
x: t.clientX,
|
|
869
|
+
y: t.clientY
|
|
866
870
|
});
|
|
867
871
|
}
|
|
868
872
|
if (this.activeTouches.size === 2) {
|
|
869
873
|
this.isTwoFingerGesture = !0, e.preventDefault();
|
|
870
|
-
const [
|
|
871
|
-
this.initialPinchDistance = this.getDistance(
|
|
874
|
+
const [i, t] = Array.from(this.activeTouches.values());
|
|
875
|
+
this.initialPinchDistance = this.getDistance(i, t), this.lastPinchCenter = this.getCenter(i, t);
|
|
872
876
|
}
|
|
873
877
|
}
|
|
874
878
|
handleTouchMove(e) {
|
|
875
|
-
for (let
|
|
876
|
-
const
|
|
877
|
-
this.activeTouches.set(
|
|
878
|
-
x:
|
|
879
|
-
y:
|
|
879
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
880
|
+
const t = e.changedTouches[i];
|
|
881
|
+
this.activeTouches.set(t.identifier, {
|
|
882
|
+
x: t.clientX,
|
|
883
|
+
y: t.clientY
|
|
880
884
|
});
|
|
881
885
|
}
|
|
882
886
|
if (this.activeTouches.size === 2 && this.initialPinchDistance !== null && this.lastPinchCenter) {
|
|
883
887
|
e.preventDefault();
|
|
884
|
-
const [
|
|
888
|
+
const [i, t] = Array.from(this.activeTouches.values()), s = this.getDistance(i, t), a = this.getCenter(i, t), o = s / this.initialPinchDistance;
|
|
885
889
|
this.emit("pinch", {
|
|
886
890
|
scale: o,
|
|
887
891
|
centerX: a.x,
|
|
@@ -892,29 +896,29 @@ class O extends I {
|
|
|
892
896
|
}
|
|
893
897
|
}
|
|
894
898
|
handleTouchEnd(e) {
|
|
895
|
-
for (let
|
|
896
|
-
const
|
|
897
|
-
this.activeTouches.delete(
|
|
899
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
900
|
+
const t = e.changedTouches[i];
|
|
901
|
+
this.activeTouches.delete(t.identifier);
|
|
898
902
|
}
|
|
899
903
|
this.activeTouches.size < 2 && (this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1);
|
|
900
904
|
}
|
|
901
905
|
reset() {
|
|
902
906
|
this.activeTouches.clear(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
|
|
903
907
|
}
|
|
904
|
-
getDistance(e,
|
|
905
|
-
const
|
|
906
|
-
return Math.sqrt(
|
|
908
|
+
getDistance(e, i) {
|
|
909
|
+
const t = i.x - e.x, s = i.y - e.y;
|
|
910
|
+
return Math.sqrt(t * t + s * s);
|
|
907
911
|
}
|
|
908
|
-
getCenter(e,
|
|
912
|
+
getCenter(e, i) {
|
|
909
913
|
return {
|
|
910
|
-
x: (e.x +
|
|
911
|
-
y: (e.y +
|
|
914
|
+
x: (e.x + i.x) / 2,
|
|
915
|
+
y: (e.y + i.y) / 2
|
|
912
916
|
};
|
|
913
917
|
}
|
|
914
918
|
}
|
|
915
|
-
class
|
|
916
|
-
constructor(e,
|
|
917
|
-
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine =
|
|
919
|
+
class F extends E {
|
|
920
|
+
constructor(e, i, t, s) {
|
|
921
|
+
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = i, this.getContents = t, this.getSelectedAreaId = s, this.gestureHandler = new $(), 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
922
|
const o = Math.max(0.1, Math.min(5, this.initialPinchZoom * a));
|
|
919
923
|
this.emit("zoom", { zoom: o });
|
|
920
924
|
}), this.gestureHandler.on("pan", ({ deltaX: a, deltaY: o }) => {
|
|
@@ -927,17 +931,17 @@ class B extends I {
|
|
|
927
931
|
});
|
|
928
932
|
});
|
|
929
933
|
}
|
|
930
|
-
getCanvasPosition(e,
|
|
931
|
-
const
|
|
934
|
+
getCanvasPosition(e, i) {
|
|
935
|
+
const t = this.canvas.getBoundingClientRect(), s = window.devicePixelRatio || 1, a = (e - t.left) * s, o = (i - t.top) * s;
|
|
932
936
|
return this.engine.screenToCanvas(a, o);
|
|
933
937
|
}
|
|
934
938
|
handleMouseDown(e) {
|
|
935
|
-
const
|
|
936
|
-
this.processPointerDown(
|
|
939
|
+
const i = this.getCanvasPosition(e.clientX, e.clientY);
|
|
940
|
+
this.processPointerDown(i);
|
|
937
941
|
}
|
|
938
942
|
handleMouseMove(e) {
|
|
939
|
-
const
|
|
940
|
-
this.processPointerMove(
|
|
943
|
+
const i = this.getCanvasPosition(e.clientX, e.clientY);
|
|
944
|
+
this.processPointerMove(i);
|
|
941
945
|
}
|
|
942
946
|
handleMouseUp() {
|
|
943
947
|
this.processPointerUp();
|
|
@@ -949,9 +953,9 @@ class B extends I {
|
|
|
949
953
|
e.preventDefault();
|
|
950
954
|
}
|
|
951
955
|
handleDoubleClick(e) {
|
|
952
|
-
const
|
|
953
|
-
let
|
|
954
|
-
|
|
956
|
+
const i = this.getCanvasPosition(e.clientX, e.clientY);
|
|
957
|
+
let t = this.engine.hitTestContent(i.x, i.y);
|
|
958
|
+
t || (t = this.engine.hitTestArea(i.x, i.y) ?? null), this.emit("context-menu", { areaId: t, clientX: e.clientX, clientY: e.clientY });
|
|
955
959
|
}
|
|
956
960
|
handleTouchStart(e) {
|
|
957
961
|
if (this.gestureHandler.handleTouchStart(e), this.gestureHandler.isMultiTouch) {
|
|
@@ -959,13 +963,13 @@ class B extends I {
|
|
|
959
963
|
return;
|
|
960
964
|
}
|
|
961
965
|
if (e.touches.length !== 1) return;
|
|
962
|
-
const
|
|
963
|
-
this.processPointerDown(
|
|
966
|
+
const i = e.touches[0], t = this.getCanvasPosition(i.clientX, i.clientY);
|
|
967
|
+
this.processPointerDown(t) && e.preventDefault();
|
|
964
968
|
}
|
|
965
969
|
handleTouchMove(e) {
|
|
966
970
|
if (this.gestureHandler.handleTouchMove(e), this.gestureHandler.isMultiTouch || e.touches.length !== 1 || this.interactionState.mode === "idle") return;
|
|
967
|
-
const
|
|
968
|
-
this.processPointerMove(
|
|
971
|
+
const i = e.touches[0], t = this.getCanvasPosition(i.clientX, i.clientY);
|
|
972
|
+
this.processPointerMove(t), e.preventDefault();
|
|
969
973
|
}
|
|
970
974
|
handleTouchEnd(e) {
|
|
971
975
|
this.gestureHandler.handleTouchEnd(e), this.gestureHandler.isMultiTouch || this.processPointerUp();
|
|
@@ -974,16 +978,16 @@ class B extends I {
|
|
|
974
978
|
* Process pointer down. Returns true if an interaction started.
|
|
975
979
|
*/
|
|
976
980
|
processPointerDown(e) {
|
|
977
|
-
const
|
|
978
|
-
if (
|
|
979
|
-
const o = this.engine.hitTestHandle(e.x, e.y,
|
|
981
|
+
const i = this.getSelectedAreaId(), t = this.getContents();
|
|
982
|
+
if (i) {
|
|
983
|
+
const o = this.engine.hitTestHandle(e.x, e.y, i);
|
|
980
984
|
if (o) {
|
|
981
|
-
const n =
|
|
985
|
+
const n = t.get(i), r = this.engine.getContentBounds(i);
|
|
982
986
|
if (o === "rotate" && n?.type === "image" && r) {
|
|
983
987
|
const d = Math.atan2(e.y - r.centerY, e.x - r.centerX) * (180 / Math.PI);
|
|
984
988
|
return this.interactionState = {
|
|
985
989
|
mode: "rotating",
|
|
986
|
-
areaId:
|
|
990
|
+
areaId: i,
|
|
987
991
|
startRotation: n.rotation ?? 0,
|
|
988
992
|
centerPoint: { x: r.centerX, y: r.centerY },
|
|
989
993
|
startAngle: d
|
|
@@ -991,7 +995,7 @@ class B extends I {
|
|
|
991
995
|
} else if (o !== "rotate" && n?.type === "image" && r)
|
|
992
996
|
return this.interactionState = {
|
|
993
997
|
mode: "scaling",
|
|
994
|
-
areaId:
|
|
998
|
+
areaId: i,
|
|
995
999
|
handle: o,
|
|
996
1000
|
startScale: n.scale ?? 1,
|
|
997
1001
|
startMousePos: e,
|
|
@@ -1002,7 +1006,7 @@ class B extends I {
|
|
|
1002
1006
|
const s = this.engine.hitTestContent(e.x, e.y);
|
|
1003
1007
|
if (s) {
|
|
1004
1008
|
if (this.emit("area:select", { areaId: s }), this.engine.isPositioningAllowed(s)) {
|
|
1005
|
-
const o =
|
|
1009
|
+
const o = t.get(s)?.offset ?? { x: 0, y: 0 };
|
|
1006
1010
|
return this.interactionState = {
|
|
1007
1011
|
mode: "dragging",
|
|
1008
1012
|
areaId: s,
|
|
@@ -1017,34 +1021,32 @@ class B extends I {
|
|
|
1017
1021
|
}
|
|
1018
1022
|
processPointerMove(e) {
|
|
1019
1023
|
if (this.interactionState.mode === "dragging") {
|
|
1020
|
-
const { areaId:
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
};
|
|
1025
|
-
const r = this.engine.getAreaLocation(t);
|
|
1024
|
+
const { areaId: i, startOffset: t, startMousePos: s } = this.interactionState, a = e.x - s.x, o = e.y - s.y, n = {
|
|
1025
|
+
x: t.x + a,
|
|
1026
|
+
y: t.y + o
|
|
1027
|
+
}, r = this.engine.getAreaLocation(i);
|
|
1026
1028
|
if (r) {
|
|
1027
1029
|
const d = r.width / 2 - 10, l = r.height / 2 - 10;
|
|
1028
1030
|
n.x = Math.max(-d, Math.min(d, n.x)), n.y = Math.max(-l, Math.min(l, n.y));
|
|
1029
1031
|
}
|
|
1030
|
-
this.emit("content:drag", { areaId:
|
|
1032
|
+
this.emit("content:drag", { areaId: i, offset: n });
|
|
1031
1033
|
return;
|
|
1032
1034
|
}
|
|
1033
1035
|
if (this.interactionState.mode === "scaling") {
|
|
1034
|
-
const { areaId:
|
|
1036
|
+
const { areaId: i, startScale: t, startMousePos: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
|
|
1035
1037
|
Math.pow(s.x - a.x, 2) + Math.pow(s.y - a.y, 2)
|
|
1036
1038
|
), n = Math.sqrt(
|
|
1037
1039
|
Math.pow(e.x - a.x, 2) + Math.pow(e.y - a.y, 2)
|
|
1038
1040
|
);
|
|
1039
1041
|
if (o > 0) {
|
|
1040
|
-
const r = n / o, d = Math.max(0.1, Math.min(5,
|
|
1041
|
-
this.emit("content:scale", { areaId:
|
|
1042
|
+
const r = n / o, d = Math.max(0.1, Math.min(5, t * r));
|
|
1043
|
+
this.emit("content:scale", { areaId: i, scale: d });
|
|
1042
1044
|
}
|
|
1043
1045
|
return;
|
|
1044
1046
|
}
|
|
1045
1047
|
if (this.interactionState.mode === "rotating") {
|
|
1046
|
-
const { areaId:
|
|
1047
|
-
let n =
|
|
1048
|
+
const { areaId: i, startRotation: t, centerPoint: s, startAngle: a } = this.interactionState, o = Math.atan2(e.y - s.y, e.x - s.x) * (180 / Math.PI);
|
|
1049
|
+
let n = t + (o - a);
|
|
1048
1050
|
n = n % 360, n < 0 && (n += 360);
|
|
1049
1051
|
const r = [0, 90, 180, 270, 360];
|
|
1050
1052
|
for (const d of r)
|
|
@@ -1052,7 +1054,7 @@ class B extends I {
|
|
|
1052
1054
|
n = d === 360 ? 0 : d;
|
|
1053
1055
|
break;
|
|
1054
1056
|
}
|
|
1055
|
-
this.emit("content:rotate", { areaId:
|
|
1057
|
+
this.emit("content:rotate", { areaId: i, rotation: n });
|
|
1056
1058
|
return;
|
|
1057
1059
|
}
|
|
1058
1060
|
this.updateCursor(e);
|
|
@@ -1061,14 +1063,14 @@ class B extends I {
|
|
|
1061
1063
|
this.interactionState.mode !== "idle" && (this.interactionState = { mode: "idle" });
|
|
1062
1064
|
}
|
|
1063
1065
|
updateCursor(e) {
|
|
1064
|
-
const
|
|
1065
|
-
let
|
|
1066
|
-
if (
|
|
1067
|
-
const s = this.engine.hitTestHandle(e.x, e.y,
|
|
1068
|
-
s ?
|
|
1066
|
+
const i = this.getSelectedAreaId();
|
|
1067
|
+
let t = "default";
|
|
1068
|
+
if (i) {
|
|
1069
|
+
const s = this.engine.hitTestHandle(e.x, e.y, i);
|
|
1070
|
+
s ? t = s === "rotate" ? "crosshair" : s === "nw" || s === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === i && (t = this.engine.isPositioningAllowed(i) ? "move" : "pointer");
|
|
1069
1071
|
} else
|
|
1070
|
-
this.engine.hitTestContent(e.x, e.y) && (
|
|
1071
|
-
this.emit("cursor", { cursor:
|
|
1072
|
+
this.engine.hitTestContent(e.x, e.y) && (t = "pointer");
|
|
1073
|
+
this.emit("cursor", { cursor: t });
|
|
1072
1074
|
}
|
|
1073
1075
|
setSelectedArea(e) {
|
|
1074
1076
|
this.engine.setSelectedArea(e);
|
|
@@ -1077,71 +1079,133 @@ class B extends I {
|
|
|
1077
1079
|
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
1080
|
}
|
|
1079
1081
|
}
|
|
1080
|
-
class
|
|
1082
|
+
class B {
|
|
1081
1083
|
constructor(e = "/api") {
|
|
1082
1084
|
this.baseUrl = e;
|
|
1083
1085
|
}
|
|
1084
1086
|
async getTemplate(e) {
|
|
1085
1087
|
try {
|
|
1086
|
-
const
|
|
1087
|
-
if (!
|
|
1088
|
-
let
|
|
1088
|
+
const i = await fetch(`${this.baseUrl}/templates/${e}`);
|
|
1089
|
+
if (!i.ok) {
|
|
1090
|
+
let t = `Failed to load template (${i.status} ${i.statusText}): ${e}
|
|
1089
1091
|
|
|
1090
1092
|
`;
|
|
1091
|
-
throw
|
|
1093
|
+
throw i.status === 404 ? t += "Template not found." : i.status === 403 ? t += "Access forbidden." : i.status >= 500 && (t += "Server error."), new Error(t);
|
|
1092
1094
|
}
|
|
1093
|
-
return (await
|
|
1094
|
-
} catch (
|
|
1095
|
-
throw
|
|
1095
|
+
return (await i.json()).templateJson;
|
|
1096
|
+
} catch (i) {
|
|
1097
|
+
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error(`Network error loading template: ${e}`) : i;
|
|
1096
1098
|
}
|
|
1097
1099
|
}
|
|
1098
1100
|
async getProduct(e) {
|
|
1099
1101
|
try {
|
|
1100
|
-
const
|
|
1102
|
+
const i = await fetch(`${this.baseUrl}/products/${e}`);
|
|
1103
|
+
if (!i.ok) {
|
|
1104
|
+
let t = `Failed to load product (${i.status} ${i.statusText}): ${e}
|
|
1105
|
+
|
|
1106
|
+
`;
|
|
1107
|
+
throw i.status === 404 ? t += "Product not found." : i.status === 403 ? t += "Access forbidden." : i.status >= 500 && (t += "Server error."), new Error(t);
|
|
1108
|
+
}
|
|
1109
|
+
return i.json();
|
|
1110
|
+
} catch (i) {
|
|
1111
|
+
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error(`Network error loading product: ${e}`) : i;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
async finalizeDesign(e, i) {
|
|
1115
|
+
try {
|
|
1116
|
+
const t = await fetch(`${this.baseUrl}/finalize`, {
|
|
1117
|
+
method: "POST",
|
|
1118
|
+
headers: { "Content-Type": "application/json" },
|
|
1119
|
+
body: JSON.stringify({
|
|
1120
|
+
shopId: e,
|
|
1121
|
+
templateId: i.templateId,
|
|
1122
|
+
designJson: i
|
|
1123
|
+
})
|
|
1124
|
+
});
|
|
1101
1125
|
if (!t.ok) {
|
|
1102
|
-
let
|
|
1126
|
+
let s = `Failed to finalize design (${t.status} ${t.statusText})
|
|
1103
1127
|
|
|
1104
1128
|
`;
|
|
1105
|
-
|
|
1129
|
+
if (t.status === 400) {
|
|
1130
|
+
const a = await t.json().catch(() => ({}));
|
|
1131
|
+
s += a.error || "Invalid design data.";
|
|
1132
|
+
} else t.status === 429 ? s += "Rate limit exceeded." : t.status >= 500 && (s += "Server error.");
|
|
1133
|
+
throw new Error(s);
|
|
1106
1134
|
}
|
|
1107
1135
|
return t.json();
|
|
1108
1136
|
} catch (t) {
|
|
1109
|
-
throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error(
|
|
1137
|
+
throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error finalizing design") : t;
|
|
1110
1138
|
}
|
|
1111
1139
|
}
|
|
1112
|
-
async
|
|
1140
|
+
async finalizeMultiView(e) {
|
|
1113
1141
|
try {
|
|
1114
|
-
const i =
|
|
1142
|
+
const i = e.designs.map((s) => ({
|
|
1143
|
+
templateId: s.templateId,
|
|
1144
|
+
viewName: s.viewName,
|
|
1145
|
+
areaContents: s.contents.map(([a, o]) => {
|
|
1146
|
+
const n = { areaId: a, isModified: !0 };
|
|
1147
|
+
return o.type === "text" ? n.text = {
|
|
1148
|
+
content: o.text,
|
|
1149
|
+
font: o.font,
|
|
1150
|
+
size: o.size,
|
|
1151
|
+
color: o.color,
|
|
1152
|
+
align: o.align,
|
|
1153
|
+
position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 }
|
|
1154
|
+
} : o.type === "image" && (n.image = {
|
|
1155
|
+
assetId: "inline",
|
|
1156
|
+
assetUrl: "",
|
|
1157
|
+
rotation: o.rotation ?? 0,
|
|
1158
|
+
scale: o.scale ?? 1,
|
|
1159
|
+
position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 },
|
|
1160
|
+
dataUrl: o.dataUrl
|
|
1161
|
+
}), n;
|
|
1162
|
+
})
|
|
1163
|
+
})), t = await fetch(`${this.baseUrl}/finalize/multi`, {
|
|
1115
1164
|
method: "POST",
|
|
1116
1165
|
headers: { "Content-Type": "application/json" },
|
|
1117
1166
|
body: JSON.stringify({
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1167
|
+
productId: e.productId,
|
|
1168
|
+
designs: i,
|
|
1169
|
+
cartContext: e.cartContext,
|
|
1170
|
+
customerId: e.customerId,
|
|
1171
|
+
customerEmail: e.customerEmail,
|
|
1172
|
+
sessionId: e.sessionId
|
|
1121
1173
|
})
|
|
1122
1174
|
});
|
|
1123
|
-
if (!
|
|
1124
|
-
let s = `Failed to finalize design (${
|
|
1175
|
+
if (!t.ok) {
|
|
1176
|
+
let s = `Failed to finalize multi-view design (${t.status} ${t.statusText})
|
|
1125
1177
|
|
|
1126
1178
|
`;
|
|
1127
|
-
if (
|
|
1128
|
-
const a = await
|
|
1179
|
+
if (t.status === 400) {
|
|
1180
|
+
const a = await t.json().catch(() => ({}));
|
|
1129
1181
|
s += a.error || "Invalid design data.";
|
|
1130
|
-
} else
|
|
1182
|
+
} else t.status === 429 ? s += "Rate limit exceeded." : t.status >= 500 && (s += "Server error.");
|
|
1131
1183
|
throw new Error(s);
|
|
1132
1184
|
}
|
|
1185
|
+
return t.json();
|
|
1186
|
+
} catch (i) {
|
|
1187
|
+
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error finalizing multi-view design") : i;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
async pollMultiViewStatus(e) {
|
|
1191
|
+
try {
|
|
1192
|
+
const i = await fetch(
|
|
1193
|
+
`${this.baseUrl}/designs/multi-status?designIds=${e.join(",")}`
|
|
1194
|
+
);
|
|
1195
|
+
if (!i.ok)
|
|
1196
|
+
throw new Error(`Failed to check multi-view design status (${i.status})`);
|
|
1133
1197
|
return i.json();
|
|
1134
1198
|
} catch (i) {
|
|
1135
|
-
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error
|
|
1199
|
+
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error checking multi-view design status") : i;
|
|
1136
1200
|
}
|
|
1137
1201
|
}
|
|
1138
|
-
async uploadAsset(e,
|
|
1202
|
+
async uploadAsset(e, i) {
|
|
1139
1203
|
try {
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1204
|
+
const t = new FormData();
|
|
1205
|
+
t.append("shopId", i), t.append("file", e);
|
|
1142
1206
|
const s = await fetch(`${this.baseUrl}/assets/upload`, {
|
|
1143
1207
|
method: "POST",
|
|
1144
|
-
body:
|
|
1208
|
+
body: t
|
|
1145
1209
|
});
|
|
1146
1210
|
if (!s.ok) {
|
|
1147
1211
|
let a = `Failed to upload asset (${s.status} ${s.statusText})
|
|
@@ -1154,70 +1218,70 @@ class F {
|
|
|
1154
1218
|
throw new Error(a);
|
|
1155
1219
|
}
|
|
1156
1220
|
return s.json();
|
|
1157
|
-
} catch (
|
|
1158
|
-
throw
|
|
1221
|
+
} catch (t) {
|
|
1222
|
+
throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : t;
|
|
1159
1223
|
}
|
|
1160
1224
|
}
|
|
1161
1225
|
async getStorageUsage(e) {
|
|
1162
1226
|
try {
|
|
1163
|
-
const
|
|
1164
|
-
if (!
|
|
1165
|
-
throw new Error(`Failed to fetch storage usage (${
|
|
1166
|
-
return
|
|
1167
|
-
} catch (
|
|
1168
|
-
throw
|
|
1227
|
+
const i = await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);
|
|
1228
|
+
if (!i.ok)
|
|
1229
|
+
throw new Error(`Failed to fetch storage usage (${i.status})`);
|
|
1230
|
+
return i.json();
|
|
1231
|
+
} catch (i) {
|
|
1232
|
+
throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error fetching storage usage") : i;
|
|
1169
1233
|
}
|
|
1170
1234
|
}
|
|
1171
1235
|
}
|
|
1172
|
-
function
|
|
1173
|
-
const
|
|
1236
|
+
function c(v, e, ...i) {
|
|
1237
|
+
const t = document.createElement(v);
|
|
1174
1238
|
return e && Object.entries(e).forEach(([s, a]) => {
|
|
1175
|
-
s === "class" ?
|
|
1176
|
-
}),
|
|
1177
|
-
typeof s == "string" ?
|
|
1178
|
-
}),
|
|
1239
|
+
s === "class" ? t.className = a : s in t ? t[s] = a : t.setAttribute(s, String(a));
|
|
1240
|
+
}), i.forEach((s) => {
|
|
1241
|
+
typeof s == "string" ? t.appendChild(document.createTextNode(s)) : t.appendChild(s);
|
|
1242
|
+
}), t;
|
|
1179
1243
|
}
|
|
1180
|
-
function
|
|
1181
|
-
let
|
|
1244
|
+
function q(v, e) {
|
|
1245
|
+
let i = null, t = null;
|
|
1182
1246
|
const s = (...a) => {
|
|
1183
|
-
|
|
1184
|
-
|
|
1247
|
+
t = a, i && clearTimeout(i), i = setTimeout(() => {
|
|
1248
|
+
t && v(...t), i = null, t = null;
|
|
1185
1249
|
}, e);
|
|
1186
1250
|
};
|
|
1187
1251
|
return s.flush = () => {
|
|
1188
|
-
|
|
1252
|
+
i && (clearTimeout(i), i = null), t && (v(...t), t = null);
|
|
1189
1253
|
}, s.cancel = () => {
|
|
1190
|
-
|
|
1254
|
+
i && (clearTimeout(i), i = null), t = null;
|
|
1191
1255
|
}, s;
|
|
1192
1256
|
}
|
|
1193
|
-
class H extends
|
|
1257
|
+
class H extends E {
|
|
1194
1258
|
constructor() {
|
|
1195
|
-
super(), this.saveEnabled = !0, this.percentLabel =
|
|
1196
|
-
const e =
|
|
1259
|
+
super(), this.saveEnabled = !0, this.percentLabel = c("span", { class: "zoom-percent" }, "100%");
|
|
1260
|
+
const e = c("button", {
|
|
1197
1261
|
class: "zoom-btn",
|
|
1198
1262
|
title: "Zoom out"
|
|
1199
1263
|
}, "−");
|
|
1200
1264
|
e.addEventListener("click", () => this.emit("zoom-out", void 0));
|
|
1201
|
-
const
|
|
1265
|
+
const i = c("button", {
|
|
1202
1266
|
class: "zoom-btn",
|
|
1203
1267
|
title: "Fit to view"
|
|
1204
1268
|
}, "Fit");
|
|
1205
|
-
|
|
1206
|
-
const
|
|
1269
|
+
i.addEventListener("click", () => this.emit("zoom-fit", void 0));
|
|
1270
|
+
const t = c("button", {
|
|
1207
1271
|
class: "zoom-btn",
|
|
1208
1272
|
title: "Zoom in"
|
|
1209
1273
|
}, "+");
|
|
1210
|
-
|
|
1274
|
+
t.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
1275
|
this.emit("view-change", { viewName: this.viewSelect.value });
|
|
1212
|
-
}), this.element =
|
|
1213
|
-
const s =
|
|
1214
|
-
this.element.appendChild(s), this.
|
|
1215
|
-
class: "zoom-btn zoom-close-btn",
|
|
1216
|
-
title: "Close editor"
|
|
1217
|
-
}, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn), this.saveBtn = h("button", {
|
|
1276
|
+
}), this.element = c("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(i), this.element.appendChild(t), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
|
|
1277
|
+
const s = c("div", { class: "zoom-toolbar-spacer" });
|
|
1278
|
+
this.element.appendChild(s), this.saveBtn = c("button", {
|
|
1218
1279
|
class: "zoom-btn zoom-save-btn",
|
|
1219
1280
|
title: "Save customization"
|
|
1220
|
-
}, "Save
|
|
1281
|
+
}, "Save"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn), this.closeBtn = c("button", {
|
|
1282
|
+
class: "zoom-btn zoom-close-btn",
|
|
1283
|
+
title: "Close editor"
|
|
1284
|
+
}, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn);
|
|
1221
1285
|
}
|
|
1222
1286
|
setZoom(e) {
|
|
1223
1287
|
this.percentLabel.textContent = `${Math.round(e * 100)}%`;
|
|
@@ -1231,14 +1295,14 @@ class H extends I {
|
|
|
1231
1295
|
setSaveButtonEnabled(e) {
|
|
1232
1296
|
this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
|
|
1233
1297
|
}
|
|
1234
|
-
setSaveDisabled(e,
|
|
1235
|
-
this.saveBtn.disabled = e,
|
|
1298
|
+
setSaveDisabled(e, i) {
|
|
1299
|
+
this.saveBtn.disabled = e, i && (this.saveBtn.textContent = i);
|
|
1236
1300
|
}
|
|
1237
1301
|
setViews(e) {
|
|
1238
1302
|
this.viewSelect.innerHTML = "";
|
|
1239
|
-
for (const
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1303
|
+
for (const i of e) {
|
|
1304
|
+
const t = document.createElement("option");
|
|
1305
|
+
t.value = i.viewName, t.textContent = i.viewName, this.viewSelect.appendChild(t);
|
|
1242
1306
|
}
|
|
1243
1307
|
this.viewSelect.style.display = e.length > 1 ? "" : "none";
|
|
1244
1308
|
}
|
|
@@ -1255,140 +1319,140 @@ const Z = [
|
|
|
1255
1319
|
{ label: "Georgia", value: "Georgia" },
|
|
1256
1320
|
{ label: "Times New Roman", value: "Times New Roman" },
|
|
1257
1321
|
{ label: "Courier New", value: "Courier New" }
|
|
1258
|
-
],
|
|
1322
|
+
], j = [
|
|
1259
1323
|
{ label: "L", value: "left" },
|
|
1260
1324
|
{ label: "C", value: "center" },
|
|
1261
1325
|
{ label: "R", value: "right" }
|
|
1262
|
-
],
|
|
1263
|
-
class
|
|
1326
|
+
], L = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
|
|
1327
|
+
class A extends E {
|
|
1264
1328
|
constructor(e) {
|
|
1265
1329
|
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;
|
|
1266
|
-
const
|
|
1267
|
-
this.mode =
|
|
1330
|
+
const i = z(e), t = T(e);
|
|
1331
|
+
this.mode = t && !i ? "image" : "text", this.element = c("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
|
|
1268
1332
|
r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
|
|
1269
1333
|
});
|
|
1270
|
-
const s =
|
|
1334
|
+
const s = c("div", { class: "area-card-header" }), a = c("div", { class: "area-card-name-row" }), o = c("span", { class: "area-card-name" }, e.name);
|
|
1271
1335
|
if (a.appendChild(o), e.required) {
|
|
1272
|
-
const r =
|
|
1336
|
+
const r = c("span", { class: "area-card-badge" }, "Required");
|
|
1273
1337
|
a.appendChild(r);
|
|
1274
1338
|
}
|
|
1275
1339
|
s.appendChild(a);
|
|
1276
|
-
const n =
|
|
1340
|
+
const n = c("button", {
|
|
1277
1341
|
class: "area-card-reset-btn",
|
|
1278
1342
|
title: "Reset content"
|
|
1279
1343
|
}, "Reset");
|
|
1280
1344
|
if (n.addEventListener("click", (r) => {
|
|
1281
1345
|
r.stopPropagation(), this.emit("clear", { areaId: e.id });
|
|
1282
|
-
}), s.appendChild(n), this.element.appendChild(s),
|
|
1283
|
-
const r =
|
|
1346
|
+
}), s.appendChild(n), this.element.appendChild(s), i && t) {
|
|
1347
|
+
const r = c("div", { class: "area-card-mode-switcher" }), d = c("button", {
|
|
1284
1348
|
class: "mode-btn mode-btn-active"
|
|
1285
1349
|
}, "Text");
|
|
1286
1350
|
d.dataset.mode = "text";
|
|
1287
|
-
const l =
|
|
1288
|
-
l.dataset.mode = "image", d.addEventListener("click", (
|
|
1289
|
-
|
|
1290
|
-
}), l.addEventListener("click", (
|
|
1291
|
-
|
|
1351
|
+
const l = c("button", { class: "mode-btn" }, "Image");
|
|
1352
|
+
l.dataset.mode = "image", d.addEventListener("click", (h) => {
|
|
1353
|
+
h.stopPropagation(), this.setMode("text"), d.classList.add("mode-btn-active"), l.classList.remove("mode-btn-active"), this.currentContent?.type === "image" && this.emit("clear", { areaId: e.id });
|
|
1354
|
+
}), l.addEventListener("click", (h) => {
|
|
1355
|
+
h.stopPropagation(), this.setMode("image"), l.classList.add("mode-btn-active"), d.classList.remove("mode-btn-active"), this.currentContent?.type === "text" && this.emit("clear", { areaId: e.id });
|
|
1292
1356
|
}), r.appendChild(d), r.appendChild(l), this.element.appendChild(r);
|
|
1293
1357
|
}
|
|
1294
|
-
this.contentContainer =
|
|
1358
|
+
this.contentContainer = c("div", { class: "area-card-content" }), this.element.appendChild(this.contentContainer), this.transformContainer = c("div", { class: "area-card-transforms" }), this.element.appendChild(this.transformContainer), this.renderContent();
|
|
1295
1359
|
}
|
|
1296
1360
|
setMode(e) {
|
|
1297
1361
|
this.mode = e, this.renderContent();
|
|
1298
1362
|
}
|
|
1299
1363
|
setContent(e) {
|
|
1300
|
-
const
|
|
1301
|
-
this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((
|
|
1302
|
-
const s =
|
|
1364
|
+
const i = this.currentContent?.type;
|
|
1365
|
+
this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((t) => {
|
|
1366
|
+
const s = t;
|
|
1303
1367
|
s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
|
|
1304
|
-
}), e?.type !==
|
|
1368
|
+
}), e?.type !== i || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
|
|
1305
1369
|
}
|
|
1306
1370
|
setSelected(e) {
|
|
1307
1371
|
this.isSelected = e, this.element.classList.toggle("area-card-selected", e), this.renderTransforms();
|
|
1308
1372
|
}
|
|
1309
1373
|
updateTextValues(e) {
|
|
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((
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1374
|
+
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((i) => {
|
|
1375
|
+
const t = i, s = t.title === "Left" ? "left" : t.title === "Center" ? "center" : "right";
|
|
1376
|
+
t.classList.toggle("align-btn-active", s === e.align);
|
|
1313
1377
|
});
|
|
1314
1378
|
}
|
|
1315
1379
|
renderContent() {
|
|
1316
1380
|
this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
|
|
1317
|
-
const e =
|
|
1318
|
-
e &&
|
|
1381
|
+
const e = z(this.area), i = T(this.area), t = !i || e && i && this.mode === "text", s = !e || e && i && this.mode === "image";
|
|
1382
|
+
e && t && this.renderTextControls(), i && s && this.renderImageControls();
|
|
1319
1383
|
}
|
|
1320
1384
|
renderTextControls() {
|
|
1321
|
-
const e = this.currentContent,
|
|
1385
|
+
const e = this.currentContent, i = e?.type === "text" ? e : S(this.area), t = c("input", {
|
|
1322
1386
|
class: "area-input-text",
|
|
1323
1387
|
type: "text",
|
|
1324
1388
|
placeholder: this.area.placeholder || "Enter text..."
|
|
1325
1389
|
});
|
|
1326
|
-
|
|
1327
|
-
this.emit("text:change", { areaId: this.area.id, updates: { text:
|
|
1328
|
-
}), this.contentContainer.appendChild(
|
|
1329
|
-
const s =
|
|
1390
|
+
t.value = i.text, this.area.textOptions?.maxLength && (t.maxLength = this.area.textOptions.maxLength), t.addEventListener("input", () => {
|
|
1391
|
+
this.emit("text:change", { areaId: this.area.id, updates: { text: t.value } });
|
|
1392
|
+
}), this.contentContainer.appendChild(t), this.textInputEl = t;
|
|
1393
|
+
const s = c("div", { class: "area-input-row" }), a = c("select", { class: "area-input-font" });
|
|
1330
1394
|
for (const l of Z) {
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1395
|
+
const h = c("option");
|
|
1396
|
+
h.value = l.value, h.textContent = l.label, l.value === i.font && (h.selected = !0), a.appendChild(h);
|
|
1333
1397
|
}
|
|
1334
1398
|
a.addEventListener("change", () => {
|
|
1335
1399
|
this.emit("text:change", { areaId: this.area.id, updates: { font: a.value } });
|
|
1336
1400
|
}), s.appendChild(a), this.fontSelectEl = a;
|
|
1337
|
-
const o =
|
|
1401
|
+
const o = c("input", {
|
|
1338
1402
|
class: "area-input-size",
|
|
1339
1403
|
type: "number"
|
|
1340
1404
|
});
|
|
1341
|
-
o.value = String(
|
|
1405
|
+
o.value = String(i.size), o.min = String(this.area.textOptions?.minSize || 8), o.max = String(this.area.textOptions?.maxSize || 200), o.addEventListener("change", () => {
|
|
1342
1406
|
this.emit("text:change", {
|
|
1343
1407
|
areaId: this.area.id,
|
|
1344
1408
|
updates: { size: parseInt(o.value, 10) || 24 }
|
|
1345
1409
|
});
|
|
1346
1410
|
}), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
|
|
1347
|
-
const n =
|
|
1348
|
-
for (const l of
|
|
1349
|
-
const
|
|
1350
|
-
class: `align-btn${
|
|
1411
|
+
const n = c("div", { class: "area-input-row" }), r = c("div", { class: "area-input-align" });
|
|
1412
|
+
for (const l of j) {
|
|
1413
|
+
const h = c("button", {
|
|
1414
|
+
class: `align-btn${i.align === l.value ? " align-btn-active" : ""}`,
|
|
1351
1415
|
title: l.label === "L" ? "Left" : l.label === "C" ? "Center" : "Right"
|
|
1352
1416
|
}, l.label);
|
|
1353
|
-
|
|
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")),
|
|
1355
|
-
}), r.appendChild(
|
|
1417
|
+
h.addEventListener("click", (u) => {
|
|
1418
|
+
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")), h.classList.add("align-btn-active");
|
|
1419
|
+
}), r.appendChild(h);
|
|
1356
1420
|
}
|
|
1357
1421
|
n.appendChild(r), this.alignGroupEl = r;
|
|
1358
|
-
const d =
|
|
1422
|
+
const d = c("input", {
|
|
1359
1423
|
class: "area-input-color",
|
|
1360
1424
|
type: "color"
|
|
1361
1425
|
});
|
|
1362
|
-
d.value =
|
|
1426
|
+
d.value = i.color, d.addEventListener("input", () => {
|
|
1363
1427
|
this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
|
|
1364
1428
|
}), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
|
|
1365
1429
|
}
|
|
1366
1430
|
renderImageControls() {
|
|
1367
1431
|
const e = this.currentContent;
|
|
1368
1432
|
if (e?.type === "image") {
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
const s =
|
|
1372
|
-
s.appendChild(
|
|
1373
|
-
const a =
|
|
1433
|
+
const i = c("div", { class: "area-image-preview" }), t = c("img", { class: "area-image-thumb" });
|
|
1434
|
+
t.src = e.dataUrl, t.alt = e.filename || "Uploaded image", i.appendChild(t);
|
|
1435
|
+
const s = c("div", { class: "area-image-info" });
|
|
1436
|
+
s.appendChild(c("span", { class: "area-image-name" }, e.filename || "Image"));
|
|
1437
|
+
const a = c("button", { class: "area-image-remove-btn" }, "Remove");
|
|
1374
1438
|
a.addEventListener("click", (o) => {
|
|
1375
1439
|
o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
|
|
1376
|
-
}), s.appendChild(a),
|
|
1440
|
+
}), s.appendChild(a), i.appendChild(s), this.contentContainer.appendChild(i);
|
|
1377
1441
|
} else {
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1380
|
-
const s =
|
|
1381
|
-
s.style.display = "none",
|
|
1382
|
-
const a =
|
|
1442
|
+
const i = c("button", { class: "area-upload-btn" }, "Upload Image"), t = c("input", { type: "file" });
|
|
1443
|
+
t.accept = L.join(","), t.style.display = "none";
|
|
1444
|
+
const s = c("div", { class: "area-validation-error" });
|
|
1445
|
+
s.style.display = "none", t.addEventListener("change", () => {
|
|
1446
|
+
const a = t.files?.[0];
|
|
1383
1447
|
if (!a) return;
|
|
1384
|
-
if (!
|
|
1448
|
+
if (!L.includes(a.type)) {
|
|
1385
1449
|
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 }),
|
|
1450
|
+
s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
|
|
1387
1451
|
return;
|
|
1388
1452
|
}
|
|
1389
1453
|
if (a.size > Y) {
|
|
1390
1454
|
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 }),
|
|
1455
|
+
s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
|
|
1392
1456
|
return;
|
|
1393
1457
|
}
|
|
1394
1458
|
s.style.display = "none";
|
|
@@ -1396,102 +1460,102 @@ class L extends I {
|
|
|
1396
1460
|
o.onload = () => {
|
|
1397
1461
|
const n = o.result;
|
|
1398
1462
|
this.emit("image:change", { areaId: this.area.id, dataUrl: n, filename: a.name });
|
|
1399
|
-
}, o.readAsDataURL(a),
|
|
1400
|
-
}),
|
|
1401
|
-
a.stopPropagation(),
|
|
1402
|
-
}), this.contentContainer.appendChild(
|
|
1463
|
+
}, o.readAsDataURL(a), t.value = "";
|
|
1464
|
+
}), i.addEventListener("click", (a) => {
|
|
1465
|
+
a.stopPropagation(), t.click();
|
|
1466
|
+
}), this.contentContainer.appendChild(i), this.contentContainer.appendChild(t), this.contentContainer.appendChild(s);
|
|
1403
1467
|
}
|
|
1404
1468
|
}
|
|
1405
1469
|
renderTransforms() {
|
|
1406
1470
|
this.transformContainer.innerHTML = "";
|
|
1407
1471
|
const e = this.currentContent;
|
|
1408
1472
|
if (!this.isSelected || !e) return;
|
|
1409
|
-
const
|
|
1410
|
-
if (!
|
|
1411
|
-
const a =
|
|
1473
|
+
const i = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, t = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
|
|
1474
|
+
if (!i && !t && !s) return;
|
|
1475
|
+
const a = c("div", { class: "area-card-divider" });
|
|
1412
1476
|
this.transformContainer.appendChild(a);
|
|
1413
|
-
const o =
|
|
1414
|
-
o.appendChild(
|
|
1415
|
-
const n =
|
|
1477
|
+
const o = c("div", { class: "transform-header" });
|
|
1478
|
+
o.appendChild(c("span", { class: "transform-title" }, "Transform"));
|
|
1479
|
+
const n = c("button", { class: "transform-reset-btn" }, "Reset");
|
|
1416
1480
|
n.addEventListener("click", (d) => {
|
|
1417
|
-
d.stopPropagation(),
|
|
1481
|
+
d.stopPropagation(), i && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), t && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
|
|
1418
1482
|
}), o.appendChild(n), this.transformContainer.appendChild(o);
|
|
1419
1483
|
const r = e.offset ?? { x: 0, y: 0 };
|
|
1420
|
-
if (
|
|
1421
|
-
const d =
|
|
1422
|
-
|
|
1484
|
+
if (i) {
|
|
1485
|
+
const d = c("div", { class: "area-input-row" }), l = c("label", { class: "transform-label" }, "X"), h = c("input", { class: "transform-input", type: "number" });
|
|
1486
|
+
h.value = String(Math.round(r.x)), h.addEventListener("change", () => {
|
|
1423
1487
|
this.emit("offset:change", {
|
|
1424
1488
|
areaId: this.area.id,
|
|
1425
|
-
offset: { ...r, x: parseInt(
|
|
1489
|
+
offset: { ...r, x: parseInt(h.value, 10) || 0 }
|
|
1426
1490
|
});
|
|
1427
1491
|
});
|
|
1428
|
-
const u =
|
|
1492
|
+
const u = c("label", { class: "transform-label" }, "Y"), p = c("input", { class: "transform-input", type: "number" });
|
|
1429
1493
|
p.value = String(Math.round(r.y)), p.addEventListener("change", () => {
|
|
1430
1494
|
this.emit("offset:change", {
|
|
1431
1495
|
areaId: this.area.id,
|
|
1432
1496
|
offset: { ...r, y: parseInt(p.value, 10) || 0 }
|
|
1433
1497
|
});
|
|
1434
|
-
}), d.appendChild(l), d.appendChild(
|
|
1498
|
+
}), d.appendChild(l), d.appendChild(h), d.appendChild(u), d.appendChild(p), this.transformContainer.appendChild(d);
|
|
1435
1499
|
}
|
|
1436
|
-
if (
|
|
1437
|
-
const d = e.scale ?? 1, l =
|
|
1500
|
+
if (t && e.type === "image") {
|
|
1501
|
+
const d = e.scale ?? 1, l = c("div", { class: "area-input-row transform-slider-row" }), h = c("label", { class: "transform-label" }, `Scale: ${Math.round(d * 100)}%`), u = c("input", { class: "transform-slider", type: "range" });
|
|
1438
1502
|
u.min = "10", u.max = "500", u.step = "5", u.value = String(Math.round(d * 100)), u.addEventListener("input", () => {
|
|
1439
1503
|
const p = parseInt(u.value, 10) / 100;
|
|
1440
|
-
|
|
1441
|
-
}), l.appendChild(
|
|
1504
|
+
h.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
|
|
1505
|
+
}), l.appendChild(h), l.appendChild(u), this.transformContainer.appendChild(l);
|
|
1442
1506
|
}
|
|
1443
1507
|
if (s && e.type === "image") {
|
|
1444
|
-
const d = e.rotation ?? 0, l =
|
|
1508
|
+
const d = e.rotation ?? 0, l = c("div", { class: "area-input-row transform-slider-row" }), h = c("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), u = c("input", { class: "transform-slider", type: "range" });
|
|
1445
1509
|
u.min = "0", u.max = "360", u.step = "1", u.value = String(Math.round(d)), u.addEventListener("input", () => {
|
|
1446
1510
|
const p = parseInt(u.value, 10);
|
|
1447
|
-
|
|
1448
|
-
}), l.appendChild(
|
|
1511
|
+
h.textContent = `Rotation: ${p}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: p });
|
|
1512
|
+
}), l.appendChild(h), l.appendChild(u), this.transformContainer.appendChild(l);
|
|
1449
1513
|
}
|
|
1450
1514
|
}
|
|
1451
1515
|
getElement() {
|
|
1452
1516
|
return this.element;
|
|
1453
1517
|
}
|
|
1454
1518
|
}
|
|
1455
|
-
class
|
|
1519
|
+
class J extends E {
|
|
1456
1520
|
constructor(e) {
|
|
1457
1521
|
super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
|
|
1458
1522
|
s.key === "Escape" && this.emit("dismiss", void 0);
|
|
1459
|
-
}, this.backdrop =
|
|
1523
|
+
}, this.backdrop = c("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
|
|
1460
1524
|
this.emit("dismiss", void 0);
|
|
1461
|
-
}), this.element =
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
const
|
|
1465
|
-
if (
|
|
1525
|
+
}), this.element = c("div", { class: "area-panel" });
|
|
1526
|
+
const i = c("div", { class: "area-panel-header" });
|
|
1527
|
+
i.appendChild(c("span", { class: "area-panel-title" }, "Customize"));
|
|
1528
|
+
const t = c("button", { class: "area-panel-close-btn", title: "Close" }, "×");
|
|
1529
|
+
if (t.addEventListener("click", () => {
|
|
1466
1530
|
this.emit("dismiss", void 0);
|
|
1467
|
-
}),
|
|
1468
|
-
const s =
|
|
1531
|
+
}), i.appendChild(t), this.element.appendChild(i), this.panelContent = c("div", { class: "area-panel-content" }), e.length === 0) {
|
|
1532
|
+
const s = c("p", { class: "area-panel-empty" }, "No customization areas defined.");
|
|
1469
1533
|
this.panelContent.appendChild(s);
|
|
1470
1534
|
} else
|
|
1471
1535
|
for (const s of e) {
|
|
1472
|
-
const a = new
|
|
1536
|
+
const a = new A(s);
|
|
1473
1537
|
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());
|
|
1474
1538
|
}
|
|
1475
1539
|
this.element.appendChild(this.panelContent);
|
|
1476
1540
|
}
|
|
1477
1541
|
setAreas(e) {
|
|
1478
1542
|
if (this.cards.clear(), this.panelContent.innerHTML = "", e.length === 0) {
|
|
1479
|
-
const
|
|
1480
|
-
this.panelContent.appendChild(
|
|
1543
|
+
const i = c("p", { class: "area-panel-empty" }, "No customization areas defined.");
|
|
1544
|
+
this.panelContent.appendChild(i);
|
|
1481
1545
|
} else
|
|
1482
|
-
for (const
|
|
1483
|
-
const
|
|
1484
|
-
this.cards.set(
|
|
1546
|
+
for (const i of e) {
|
|
1547
|
+
const t = new A(i);
|
|
1548
|
+
this.cards.set(i.id, t), t.on("text:change", (s) => this.emit("text:change", s)), t.on("image:change", (s) => this.emit("image:change", s)), t.on("clear", (s) => this.emit("clear", s)), t.on("select", (s) => this.emit("select", s)), t.on("offset:change", (s) => this.emit("offset:change", s)), t.on("scale:change", (s) => this.emit("scale:change", s)), t.on("rotation:change", (s) => this.emit("rotation:change", s)), t.on("validation:error", (s) => this.emit("validation:error", s)), this.panelContent.appendChild(t.getElement());
|
|
1485
1549
|
}
|
|
1486
1550
|
}
|
|
1487
1551
|
setContents(e) {
|
|
1488
|
-
for (const [
|
|
1489
|
-
|
|
1552
|
+
for (const [i, t] of this.cards)
|
|
1553
|
+
t.setContent(e.get(i));
|
|
1490
1554
|
}
|
|
1491
1555
|
setSelectedArea(e) {
|
|
1492
|
-
for (const [
|
|
1493
|
-
const s =
|
|
1494
|
-
|
|
1556
|
+
for (const [i, t] of this.cards) {
|
|
1557
|
+
const s = i === e;
|
|
1558
|
+
t.setSelected(s), t.getElement().style.display = s ? "" : "none";
|
|
1495
1559
|
}
|
|
1496
1560
|
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));
|
|
1497
1561
|
}
|
|
@@ -1507,13 +1571,13 @@ class j extends I {
|
|
|
1507
1571
|
}
|
|
1508
1572
|
class X {
|
|
1509
1573
|
constructor(e = window) {
|
|
1510
|
-
this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (
|
|
1574
|
+
this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (i) => {
|
|
1511
1575
|
if (!this.isEnabled) return;
|
|
1512
|
-
const
|
|
1513
|
-
if (!(
|
|
1576
|
+
const t = i.target;
|
|
1577
|
+
if (!(t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) {
|
|
1514
1578
|
for (const s of this.commands)
|
|
1515
|
-
if (this.matchesCommand(
|
|
1516
|
-
|
|
1579
|
+
if (this.matchesCommand(i, s)) {
|
|
1580
|
+
i.preventDefault(), s.handler(i);
|
|
1517
1581
|
break;
|
|
1518
1582
|
}
|
|
1519
1583
|
}
|
|
@@ -1524,8 +1588,8 @@ class X {
|
|
|
1524
1588
|
*/
|
|
1525
1589
|
register(e) {
|
|
1526
1590
|
return this.commands.push(e), () => {
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1591
|
+
const i = this.commands.indexOf(e);
|
|
1592
|
+
i !== -1 && this.commands.splice(i, 1);
|
|
1529
1593
|
};
|
|
1530
1594
|
}
|
|
1531
1595
|
/**
|
|
@@ -1549,8 +1613,8 @@ class X {
|
|
|
1549
1613
|
/**
|
|
1550
1614
|
* Check if event matches a command
|
|
1551
1615
|
*/
|
|
1552
|
-
matchesCommand(e,
|
|
1553
|
-
return !(e.key.toLowerCase() !==
|
|
1616
|
+
matchesCommand(e, i) {
|
|
1617
|
+
return !(e.key.toLowerCase() !== i.key.toLowerCase() || i.ctrl !== void 0 && i.ctrl !== e.ctrlKey || i.shift !== void 0 && i.shift !== e.shiftKey || i.alt !== void 0 && i.alt !== e.altKey || i.meta !== void 0 && i.meta !== e.metaKey);
|
|
1554
1618
|
}
|
|
1555
1619
|
/**
|
|
1556
1620
|
* Cleanup
|
|
@@ -1562,28 +1626,28 @@ class X {
|
|
|
1562
1626
|
function W() {
|
|
1563
1627
|
return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
1564
1628
|
}
|
|
1565
|
-
function
|
|
1629
|
+
function G() {
|
|
1566
1630
|
return W() ? "meta" : "ctrl";
|
|
1567
1631
|
}
|
|
1568
|
-
function
|
|
1569
|
-
const s =
|
|
1632
|
+
function K(v, e, i, t) {
|
|
1633
|
+
const s = v / i, a = e / t;
|
|
1570
1634
|
return Math.min(s, a);
|
|
1571
1635
|
}
|
|
1572
|
-
function
|
|
1573
|
-
const a =
|
|
1636
|
+
function Q(v, e, i = 8, t = 10, s = 300) {
|
|
1637
|
+
const a = K(v, e, i, t), o = a >= s;
|
|
1574
1638
|
return {
|
|
1575
1639
|
actualDPI: Math.round(a),
|
|
1576
1640
|
targetDPI: s,
|
|
1577
|
-
width:
|
|
1641
|
+
width: v,
|
|
1578
1642
|
height: e,
|
|
1579
1643
|
passed: o,
|
|
1580
1644
|
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.`
|
|
1581
1645
|
};
|
|
1582
1646
|
}
|
|
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;
|
|
1584
|
-
class
|
|
1647
|
+
const k = ':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}}', _ = "2.0.0", ee = 768;
|
|
1648
|
+
class I extends HTMLElement {
|
|
1585
1649
|
constructor() {
|
|
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 =
|
|
1650
|
+
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 = q(() => {
|
|
1587
1651
|
this.dispatchEvent(
|
|
1588
1652
|
new CustomEvent("change", {
|
|
1589
1653
|
detail: { design: this.getDesign() },
|
|
@@ -1606,15 +1670,15 @@ class ee extends HTMLElement {
|
|
|
1606
1670
|
disconnectedCallback() {
|
|
1607
1671
|
this.cleanup();
|
|
1608
1672
|
}
|
|
1609
|
-
attributeChangedCallback(e,
|
|
1610
|
-
|
|
1673
|
+
attributeChangedCallback(e, i, t) {
|
|
1674
|
+
i !== t && (e === "template-id" && this.isReady && t && this.loadTemplate(t), e === "product-id" && this.isReady && t && this.loadProduct(t), e === "show-close-button" && this.zoomToolbar && this.zoomToolbar.setCloseButtonVisible(t !== "false"), e === "show-save-button" && this.zoomToolbar && this.zoomToolbar.setSaveButtonEnabled(t !== "false"));
|
|
1611
1675
|
}
|
|
1612
1676
|
// ─── Public API ───
|
|
1613
1677
|
getDesign() {
|
|
1614
1678
|
if (!this.currentTemplate) throw new Error("No design loaded");
|
|
1615
1679
|
return {
|
|
1616
1680
|
templateId: this.currentTemplate.metadata.id,
|
|
1617
|
-
engineVersion:
|
|
1681
|
+
engineVersion: _,
|
|
1618
1682
|
contents: this.contentManager.toJSON(),
|
|
1619
1683
|
userData: {}
|
|
1620
1684
|
};
|
|
@@ -1663,79 +1727,188 @@ class ee extends HTMLElement {
|
|
|
1663
1727
|
}
|
|
1664
1728
|
setActiveView(e) {
|
|
1665
1729
|
if (this.activeViewName === e) return;
|
|
1666
|
-
const
|
|
1667
|
-
if (!
|
|
1668
|
-
this.switchToView(
|
|
1730
|
+
const i = this.productViews.find((t) => t.viewName === e);
|
|
1731
|
+
if (!i) throw new Error(`View not found: ${e}`);
|
|
1732
|
+
this.switchToView(i);
|
|
1669
1733
|
}
|
|
1670
|
-
setAreaContent(e,
|
|
1671
|
-
|
|
1734
|
+
setAreaContent(e, i) {
|
|
1735
|
+
i.type === "text" ? this.contentManager.setTextContent(e, i) : this.contentManager.setImageContent(e, i.dataUrl, i.filename), this.syncEngineContents(), this.saveContentState();
|
|
1672
1736
|
}
|
|
1673
1737
|
getAreaContent(e) {
|
|
1674
1738
|
return this.contentManager.getContent(e);
|
|
1675
1739
|
}
|
|
1740
|
+
static hasEditorContent(e) {
|
|
1741
|
+
return e.type === "text" ? e.text.trim().length > 0 : e.type === "image" ? !!e.dataUrl : !1;
|
|
1742
|
+
}
|
|
1743
|
+
validateRequiredContent() {
|
|
1744
|
+
const e = [];
|
|
1745
|
+
if (this.productViews.length > 1) {
|
|
1746
|
+
this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
|
|
1747
|
+
for (const i of this.productViews) {
|
|
1748
|
+
const t = this.perViewContents.get(i.viewName), s = t?.some(([, o]) => I.hasEditorContent(o)) ?? !1;
|
|
1749
|
+
if (i.isRequired && !s) {
|
|
1750
|
+
e.push(`View "${i.viewName}" is required`);
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
const a = i.template.templateJson.areas || [];
|
|
1754
|
+
for (const o of a) {
|
|
1755
|
+
if (!o.required) continue;
|
|
1756
|
+
const n = t?.find(([r]) => r === o.id);
|
|
1757
|
+
n && I.hasEditorContent(n[1]) || e.push(`"${o.name}" in view "${i.viewName}" is required`);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
} else {
|
|
1761
|
+
const i = this.currentTemplate?.areas || [], t = this.contentManager.getContents();
|
|
1762
|
+
for (const s of i) {
|
|
1763
|
+
if (!s.required) continue;
|
|
1764
|
+
const a = t.get(s.id);
|
|
1765
|
+
a && I.hasEditorContent(a) || e.push(`"${s.name}" is required`);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return e;
|
|
1769
|
+
}
|
|
1770
|
+
showValidationErrors(e) {
|
|
1771
|
+
return new Promise((i) => {
|
|
1772
|
+
const t = c("div", { class: "editor-modal-overlay" }), s = c("div", { class: "editor-modal" });
|
|
1773
|
+
s.appendChild(c("div", { class: "editor-modal-title" }, "Required content missing"));
|
|
1774
|
+
const a = c("div", { class: "editor-modal-body" }), o = document.createElement("ul");
|
|
1775
|
+
o.style.margin = "8px 0", o.style.paddingLeft = "20px";
|
|
1776
|
+
for (const d of e) {
|
|
1777
|
+
const l = document.createElement("li");
|
|
1778
|
+
l.textContent = d, o.appendChild(l);
|
|
1779
|
+
}
|
|
1780
|
+
a.appendChild(o), s.appendChild(a);
|
|
1781
|
+
const n = c("div", { class: "editor-modal-actions" }), r = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, "OK");
|
|
1782
|
+
r.addEventListener("click", () => {
|
|
1783
|
+
t.remove(), i();
|
|
1784
|
+
}), n.appendChild(r), s.appendChild(n), t.appendChild(s), t.addEventListener("click", (d) => {
|
|
1785
|
+
d.target === t && (t.remove(), i());
|
|
1786
|
+
}), this.shadow.appendChild(t);
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1676
1789
|
async finalize() {
|
|
1677
|
-
const e = this.
|
|
1678
|
-
if (e.length > 0)
|
|
1679
|
-
|
|
1790
|
+
const e = this.validateRequiredContent();
|
|
1791
|
+
if (e.length > 0)
|
|
1792
|
+
return await this.showValidationErrors(e), null;
|
|
1793
|
+
const i = this.engine.checkSafeAreaViolations();
|
|
1794
|
+
if (i.length > 0) {
|
|
1795
|
+
const o = this.currentTemplate?.areas || [], n = i.map((r) => o.find((d) => d.id === r)?.name || r).join(", ");
|
|
1680
1796
|
if (!await this.showConfirmation(
|
|
1681
1797
|
"Content outside safe area",
|
|
1682
|
-
`Content in ${
|
|
1798
|
+
`Content in ${n} extends beyond the safe area. It may be trimmed during printing. Continue?`,
|
|
1683
1799
|
"Cancel",
|
|
1684
1800
|
"Proceed Anyway"
|
|
1685
1801
|
))
|
|
1686
1802
|
throw new Error("Finalization cancelled by user");
|
|
1687
1803
|
}
|
|
1688
|
-
const t = this.
|
|
1804
|
+
const t = this.getAttribute("store-id") || "demo-shop";
|
|
1805
|
+
if (this.productViews.length > 1) {
|
|
1806
|
+
this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
|
|
1807
|
+
const o = [];
|
|
1808
|
+
for (const h of this.productViews) {
|
|
1809
|
+
const u = this.perViewContents.get(h.viewName);
|
|
1810
|
+
!u || u.length === 0 || !u.some(([, p]) => I.hasEditorContent(p)) || o.push({
|
|
1811
|
+
templateId: h.template.id,
|
|
1812
|
+
viewName: h.viewName,
|
|
1813
|
+
contents: u
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
if (o.length === 0)
|
|
1817
|
+
throw new Error("No views have content to finalize");
|
|
1818
|
+
const n = this.getAttribute("product-id");
|
|
1819
|
+
if (!n)
|
|
1820
|
+
throw new Error("product-id attribute is required for multi-view finalize");
|
|
1821
|
+
const r = await this.apiClient.finalizeMultiView({
|
|
1822
|
+
productId: n,
|
|
1823
|
+
designs: o
|
|
1824
|
+
});
|
|
1825
|
+
if (!r.allCompleted) {
|
|
1826
|
+
const h = r.views.map((g) => g.designId), u = 1500, p = 40;
|
|
1827
|
+
let m = 0;
|
|
1828
|
+
for (; m < p; ) {
|
|
1829
|
+
await new Promise((f) => setTimeout(f, u)), m++;
|
|
1830
|
+
const g = await this.apiClient.pollMultiViewStatus(h);
|
|
1831
|
+
if (g.allCompleted) {
|
|
1832
|
+
for (const f of g.designs) {
|
|
1833
|
+
const b = r.views.find((w) => w.designId === f.designId);
|
|
1834
|
+
b && (b.status = f.status, b.proofUrl = f.proofUrl, b.errorMessage = f.errorMessage);
|
|
1835
|
+
}
|
|
1836
|
+
r.allCompleted = !0;
|
|
1837
|
+
break;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
const d = r.views.map((h) => ({
|
|
1842
|
+
viewName: h.viewName,
|
|
1843
|
+
designId: h.designId,
|
|
1844
|
+
proofUrl: h.proofUrl || "",
|
|
1845
|
+
status: h.status
|
|
1846
|
+
})), l = d[0];
|
|
1847
|
+
return this.dispatchEvent(
|
|
1848
|
+
new CustomEvent("customizer:finalize", {
|
|
1849
|
+
detail: {
|
|
1850
|
+
designId: l.designId,
|
|
1851
|
+
proofUrl: l.proofUrl,
|
|
1852
|
+
status: l.status,
|
|
1853
|
+
views: d,
|
|
1854
|
+
requestId: r.requestId
|
|
1855
|
+
},
|
|
1856
|
+
bubbles: !0,
|
|
1857
|
+
composed: !0
|
|
1858
|
+
})
|
|
1859
|
+
), { ...l, views: d };
|
|
1860
|
+
}
|
|
1861
|
+
const s = this.getDesign(), a = await this.apiClient.finalizeDesign(t, s);
|
|
1689
1862
|
return this.dispatchEvent(
|
|
1690
1863
|
new CustomEvent("customizer:finalize", {
|
|
1691
1864
|
detail: {
|
|
1692
|
-
designId:
|
|
1693
|
-
proofUrl:
|
|
1694
|
-
templateId:
|
|
1695
|
-
designJson:
|
|
1696
|
-
status:
|
|
1865
|
+
designId: a.designId,
|
|
1866
|
+
proofUrl: a.proofUrl,
|
|
1867
|
+
templateId: s.templateId,
|
|
1868
|
+
designJson: s,
|
|
1869
|
+
status: a.status
|
|
1697
1870
|
},
|
|
1698
1871
|
bubbles: !0,
|
|
1699
1872
|
composed: !0
|
|
1700
1873
|
})
|
|
1701
|
-
),
|
|
1874
|
+
), a;
|
|
1702
1875
|
}
|
|
1703
1876
|
// ─── Initialization ───
|
|
1704
1877
|
renderLoading() {
|
|
1705
1878
|
const e = document.createElement("style");
|
|
1706
|
-
e.textContent =
|
|
1707
|
-
const
|
|
1879
|
+
e.textContent = k, this.shadow.appendChild(e);
|
|
1880
|
+
const i = c(
|
|
1708
1881
|
"div",
|
|
1709
1882
|
{ class: "loading-container" },
|
|
1710
|
-
|
|
1883
|
+
c("div", { class: "loading-spinner" })
|
|
1711
1884
|
);
|
|
1712
|
-
this.shadow.appendChild(
|
|
1885
|
+
this.shadow.appendChild(i);
|
|
1713
1886
|
}
|
|
1714
1887
|
async initialize() {
|
|
1715
1888
|
const e = this.getAttribute("api-url") || "http://localhost:4000";
|
|
1716
|
-
this.apiClient = new
|
|
1717
|
-
const
|
|
1718
|
-
if (!
|
|
1889
|
+
this.apiClient = new B(`${e.replace(/\/+$/, "")}/public`);
|
|
1890
|
+
const i = this.getAttribute("product-id"), t = this.getAttribute("template-id");
|
|
1891
|
+
if (!i && !t)
|
|
1719
1892
|
throw new Error("Either template-id or product-id attribute is required");
|
|
1720
|
-
|
|
1893
|
+
i ? await this.loadProduct(i) : await this.loadTemplate(t), this.isReady = !0, this.dispatchEvent(new CustomEvent("ready", { bubbles: !0, composed: !0 }));
|
|
1721
1894
|
}
|
|
1722
1895
|
async loadTemplate(e) {
|
|
1723
1896
|
if (this.loadingTemplateId !== e) {
|
|
1724
1897
|
this.loadingTemplateId = e;
|
|
1725
1898
|
try {
|
|
1726
|
-
const
|
|
1899
|
+
const i = await this.apiClient.getTemplate(e);
|
|
1727
1900
|
if (this.loadingTemplateId !== e) return;
|
|
1728
|
-
await this.loadTemplateData(
|
|
1729
|
-
} catch (
|
|
1730
|
-
throw this.loadingTemplateId = null, new Error(`Failed to load template: ${
|
|
1901
|
+
await this.loadTemplateData(i), this.loadingTemplateId = null;
|
|
1902
|
+
} catch (i) {
|
|
1903
|
+
throw this.loadingTemplateId = null, new Error(`Failed to load template: ${i.message}`);
|
|
1731
1904
|
}
|
|
1732
1905
|
}
|
|
1733
1906
|
}
|
|
1734
1907
|
async loadTemplateData(e) {
|
|
1735
1908
|
this.currentTemplate = e;
|
|
1736
|
-
const
|
|
1737
|
-
this.contentManager = new
|
|
1738
|
-
const
|
|
1909
|
+
const i = e.areas || [];
|
|
1910
|
+
this.contentManager = new P(i);
|
|
1911
|
+
const t = {
|
|
1739
1912
|
template: e,
|
|
1740
1913
|
contents: this.contentManager.toJSON(),
|
|
1741
1914
|
selectedAreaId: null,
|
|
@@ -1744,16 +1917,16 @@ class ee extends HTMLElement {
|
|
|
1744
1917
|
isDirty: !1,
|
|
1745
1918
|
warnings: []
|
|
1746
1919
|
};
|
|
1747
|
-
this.stateManager = new
|
|
1920
|
+
this.stateManager = new O(t), this.buildUI(e, i), this.engine = new U(this.canvas), this.engine.setArtboard({
|
|
1748
1921
|
width: e.artboard.width,
|
|
1749
1922
|
height: e.artboard.height,
|
|
1750
1923
|
backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
|
|
1751
|
-
}), this.engine.setAreas(
|
|
1924
|
+
}), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
|
|
1752
1925
|
const s = e.artboard.safeArea ?? e.safeArea;
|
|
1753
1926
|
s && this.engine.setSafeArea(s), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
|
|
1754
1927
|
zoom: this.engine.getZoom(),
|
|
1755
1928
|
pan: this.engine.getPan()
|
|
1756
|
-
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new
|
|
1929
|
+
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new F(
|
|
1757
1930
|
this.canvas,
|
|
1758
1931
|
this.engine,
|
|
1759
1932
|
() => this.contentManager.getContents(),
|
|
@@ -1771,38 +1944,38 @@ class ee extends HTMLElement {
|
|
|
1771
1944
|
}
|
|
1772
1945
|
async loadProduct(e) {
|
|
1773
1946
|
try {
|
|
1774
|
-
const
|
|
1775
|
-
if (
|
|
1947
|
+
const i = await this.apiClient.getProduct(e);
|
|
1948
|
+
if (i.views.length === 0)
|
|
1776
1949
|
throw new Error("Product has no active templates");
|
|
1777
|
-
this.productViews =
|
|
1778
|
-
const
|
|
1779
|
-
await this.loadTemplateData(
|
|
1780
|
-
} catch (
|
|
1781
|
-
throw new Error(`Failed to load product: ${
|
|
1950
|
+
this.productViews = i.views, this.perViewContents.clear();
|
|
1951
|
+
const t = i.views.find((s) => s.isDefault) || i.views[0];
|
|
1952
|
+
await this.loadTemplateData(t.template.templateJson), this.activeViewName = t.viewName, this.zoomToolbar && i.views.length > 1 && (this.zoomToolbar.setViews(this.getViews()), this.zoomToolbar.setActiveView(t.viewName));
|
|
1953
|
+
} catch (i) {
|
|
1954
|
+
throw new Error(`Failed to load product: ${i.message}`);
|
|
1782
1955
|
}
|
|
1783
1956
|
}
|
|
1784
1957
|
switchToView(e) {
|
|
1785
1958
|
this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON()), this.activeViewName = e.viewName, this.zoomToolbar?.setActiveView(e.viewName);
|
|
1786
|
-
const
|
|
1787
|
-
this.currentTemplate =
|
|
1788
|
-
const
|
|
1789
|
-
this.contentManager = new
|
|
1959
|
+
const i = e.template.templateJson;
|
|
1960
|
+
this.currentTemplate = i;
|
|
1961
|
+
const t = i.areas || [];
|
|
1962
|
+
this.contentManager = new P(t);
|
|
1790
1963
|
const s = this.perViewContents.get(e.viewName);
|
|
1791
1964
|
s && this.contentManager.fromJSON(s), this.engine.setArtboard({
|
|
1792
|
-
width:
|
|
1793
|
-
height:
|
|
1794
|
-
backgroundColor:
|
|
1795
|
-
}), this.engine.setAreas(
|
|
1796
|
-
const a =
|
|
1965
|
+
width: i.artboard.width,
|
|
1966
|
+
height: i.artboard.height,
|
|
1967
|
+
backgroundColor: i.artboard.backgroundColor ?? "#ffffff"
|
|
1968
|
+
}), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), this.engine.setBackgroundImage(i.backgroundImage ?? void 0);
|
|
1969
|
+
const a = i.artboard.safeArea ?? i.safeArea;
|
|
1797
1970
|
this.engine.setSafeArea(a ?? null), this.engine.fitToView(), this.stateManager.setState({
|
|
1798
|
-
template:
|
|
1971
|
+
template: i,
|
|
1799
1972
|
contents: this.contentManager.toJSON(),
|
|
1800
1973
|
selectedAreaId: null,
|
|
1801
1974
|
isDirty: this.stateManager.getState().isDirty,
|
|
1802
1975
|
zoom: this.engine.getZoom(),
|
|
1803
1976
|
pan: this.engine.getPan(),
|
|
1804
1977
|
warnings: []
|
|
1805
|
-
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.inputPanel.setAreas(
|
|
1978
|
+
}), this.zoomToolbar.setZoom(this.engine.getZoom()), this.inputPanel.setAreas(t), this.inputPanel.setContents(this.contentManager.getContents()), this.inputPanel.setSelectedArea(null), this.engine.setSelectedArea(null), this.dispatchEvent(
|
|
1806
1979
|
new CustomEvent("view-change", {
|
|
1807
1980
|
detail: { viewName: e.viewName },
|
|
1808
1981
|
bubbles: !0,
|
|
@@ -1810,10 +1983,10 @@ class ee extends HTMLElement {
|
|
|
1810
1983
|
})
|
|
1811
1984
|
);
|
|
1812
1985
|
}
|
|
1813
|
-
buildUI(e,
|
|
1986
|
+
buildUI(e, i) {
|
|
1814
1987
|
this.shadow.innerHTML = "";
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1988
|
+
const t = document.createElement("style");
|
|
1989
|
+
t.textContent = k, this.shadow.appendChild(t), this.container = c("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
1990
|
const n = this.productViews.find((r) => r.viewName === o);
|
|
1818
1991
|
n && this.switchToView(n);
|
|
1819
1992
|
})), this.subscriptions.push(this.zoomToolbar.on("close", () => {
|
|
@@ -1825,22 +1998,26 @@ class ee extends HTMLElement {
|
|
|
1825
1998
|
this.zoomToolbar.setSaveDisabled(!0, "Saving...");
|
|
1826
1999
|
try {
|
|
1827
2000
|
const o = await this.finalize();
|
|
2001
|
+
if (!o) {
|
|
2002
|
+
this.zoomToolbar.setSaveDisabled(!1, "Save");
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
1828
2005
|
this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
|
|
1829
2006
|
detail: o,
|
|
1830
2007
|
bubbles: !0,
|
|
1831
2008
|
composed: !0
|
|
1832
2009
|
})), setTimeout(() => {
|
|
1833
|
-
this.zoomToolbar.setSaveDisabled(!1, "Save
|
|
2010
|
+
this.zoomToolbar.setSaveDisabled(!1, "Save"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
|
|
1834
2011
|
}, 2e3);
|
|
1835
2012
|
} catch (o) {
|
|
1836
2013
|
console.error("Customizer: finalize failed", o);
|
|
1837
2014
|
const n = o instanceof Error ? o.message : "Save failed. Please try again.";
|
|
1838
|
-
this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save
|
|
2015
|
+
this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save");
|
|
1839
2016
|
}
|
|
1840
2017
|
})), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
|
|
1841
|
-
const s =
|
|
1842
|
-
s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper =
|
|
1843
|
-
const a =
|
|
2018
|
+
const s = c("div", { class: "canvas-area" });
|
|
2019
|
+
s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = c("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(i), this.wireInputPanelEvents();
|
|
2020
|
+
const a = c("div", { class: "editor-main" });
|
|
1844
2021
|
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
2022
|
for (const n of o)
|
|
1846
2023
|
n.target === this.canvasWrapper && this.handleCanvasResize(n.contentRect), n.target === this.container && this.handleLayoutResize(n.contentRect);
|
|
@@ -1853,22 +2030,22 @@ class ee extends HTMLElement {
|
|
|
1853
2030
|
this.handleAreaSelect(e);
|
|
1854
2031
|
})
|
|
1855
2032
|
), this.subscriptions.push(
|
|
1856
|
-
this.interaction.on("content:drag", ({ areaId: e, offset:
|
|
1857
|
-
this.contentManager.setContentOffset(e,
|
|
2033
|
+
this.interaction.on("content:drag", ({ areaId: e, offset: i }) => {
|
|
2034
|
+
this.contentManager.setContentOffset(e, i), this.saveContentState();
|
|
1858
2035
|
})
|
|
1859
2036
|
), this.subscriptions.push(
|
|
1860
|
-
this.interaction.on("content:scale", ({ areaId: e, scale:
|
|
1861
|
-
this.contentManager.setImageScale(e,
|
|
2037
|
+
this.interaction.on("content:scale", ({ areaId: e, scale: i }) => {
|
|
2038
|
+
this.contentManager.setImageScale(e, i), this.saveContentState();
|
|
1862
2039
|
})
|
|
1863
2040
|
), this.subscriptions.push(
|
|
1864
|
-
this.interaction.on("content:rotate", ({ areaId: e, rotation:
|
|
1865
|
-
this.contentManager.setImageRotation(e,
|
|
2041
|
+
this.interaction.on("content:rotate", ({ areaId: e, rotation: i }) => {
|
|
2042
|
+
this.contentManager.setImageRotation(e, i), this.saveContentState();
|
|
1866
2043
|
})
|
|
1867
2044
|
), this.subscriptions.push(
|
|
1868
2045
|
this.interaction.on("zoom", ({ zoom: e }) => {
|
|
1869
2046
|
this.engine.setZoom(e);
|
|
1870
|
-
const
|
|
1871
|
-
|
|
2047
|
+
const i = this.engine.getCenteredPan(e);
|
|
2048
|
+
i && this.engine.setPan(i), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
|
|
1872
2049
|
})
|
|
1873
2050
|
), this.subscriptions.push(
|
|
1874
2051
|
this.interaction.on("pan", ({ pan: e }) => {
|
|
@@ -1879,18 +2056,18 @@ class ee extends HTMLElement {
|
|
|
1879
2056
|
this.canvas.style.cursor = e;
|
|
1880
2057
|
})
|
|
1881
2058
|
), this.subscriptions.push(
|
|
1882
|
-
this.interaction.on("context-menu", ({ areaId: e, clientX:
|
|
1883
|
-
this.handleContextMenu(e,
|
|
2059
|
+
this.interaction.on("context-menu", ({ areaId: e, clientX: i, clientY: t }) => {
|
|
2060
|
+
this.handleContextMenu(e, i, t);
|
|
1884
2061
|
})
|
|
1885
2062
|
);
|
|
1886
2063
|
}
|
|
1887
2064
|
wireInputPanelEvents() {
|
|
1888
2065
|
this.subscriptions.push(
|
|
1889
|
-
this.inputPanel.on("text:change", ({ areaId: e, updates:
|
|
1890
|
-
this.contentManager.setTextContent(e,
|
|
2066
|
+
this.inputPanel.on("text:change", ({ areaId: e, updates: i }) => {
|
|
2067
|
+
this.contentManager.setTextContent(e, i), this.saveContentState();
|
|
1891
2068
|
})
|
|
1892
2069
|
), this.subscriptions.push(
|
|
1893
|
-
this.inputPanel.on("image:change", async ({ areaId: e, dataUrl:
|
|
2070
|
+
this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: i, filename: t }) => {
|
|
1894
2071
|
const s = this.contentManager.getContents();
|
|
1895
2072
|
let a = 0;
|
|
1896
2073
|
for (const [n, r] of s)
|
|
@@ -1907,13 +2084,15 @@ class ee extends HTMLElement {
|
|
|
1907
2084
|
if (o && this.currentTemplate) {
|
|
1908
2085
|
const n = this.currentTemplate.print.targetDpi;
|
|
1909
2086
|
try {
|
|
1910
|
-
const r = new Image()
|
|
1911
|
-
|
|
1912
|
-
|
|
2087
|
+
const r = new Image();
|
|
2088
|
+
r.crossOrigin = "anonymous";
|
|
2089
|
+
const d = await new Promise((p, m) => {
|
|
2090
|
+
r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => m(new Error("Failed to load image")), r.src = i;
|
|
2091
|
+
}), l = o.location.width / n, h = o.location.height / n, u = Q(
|
|
1913
2092
|
d.width,
|
|
1914
2093
|
d.height,
|
|
1915
2094
|
l,
|
|
1916
|
-
|
|
2095
|
+
h,
|
|
1917
2096
|
n
|
|
1918
2097
|
);
|
|
1919
2098
|
if (u.actualDPI < 150) {
|
|
@@ -1935,8 +2114,8 @@ class ee extends HTMLElement {
|
|
|
1935
2114
|
} catch {
|
|
1936
2115
|
}
|
|
1937
2116
|
}
|
|
1938
|
-
if (this.contentManager.setImageContent(e,
|
|
1939
|
-
const n = Math.round(
|
|
2117
|
+
if (this.contentManager.setImageContent(e, i, t), this.saveContentState(), this.storageUsage) {
|
|
2118
|
+
const n = Math.round(i.length * 0.75);
|
|
1940
2119
|
this.storageUsage.storageUsed += n, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
|
|
1941
2120
|
}
|
|
1942
2121
|
})
|
|
@@ -1953,16 +2132,16 @@ class ee extends HTMLElement {
|
|
|
1953
2132
|
this.handleAreaSelect(e);
|
|
1954
2133
|
})
|
|
1955
2134
|
), this.subscriptions.push(
|
|
1956
|
-
this.inputPanel.on("offset:change", ({ areaId: e, offset:
|
|
1957
|
-
this.contentManager.setContentOffset(e,
|
|
2135
|
+
this.inputPanel.on("offset:change", ({ areaId: e, offset: i }) => {
|
|
2136
|
+
this.contentManager.setContentOffset(e, i), this.saveContentState();
|
|
1958
2137
|
})
|
|
1959
2138
|
), this.subscriptions.push(
|
|
1960
|
-
this.inputPanel.on("scale:change", ({ areaId: e, scale:
|
|
1961
|
-
this.contentManager.setImageScale(e,
|
|
2139
|
+
this.inputPanel.on("scale:change", ({ areaId: e, scale: i }) => {
|
|
2140
|
+
this.contentManager.setImageScale(e, i), this.saveContentState();
|
|
1962
2141
|
})
|
|
1963
2142
|
), this.subscriptions.push(
|
|
1964
|
-
this.inputPanel.on("rotation:change", ({ areaId: e, rotation:
|
|
1965
|
-
this.contentManager.setImageRotation(e,
|
|
2143
|
+
this.inputPanel.on("rotation:change", ({ areaId: e, rotation: i }) => {
|
|
2144
|
+
this.contentManager.setImageRotation(e, i), this.saveContentState();
|
|
1966
2145
|
})
|
|
1967
2146
|
), this.subscriptions.push(
|
|
1968
2147
|
this.inputPanel.on("dismiss", () => {
|
|
@@ -1971,8 +2150,8 @@ class ee extends HTMLElement {
|
|
|
1971
2150
|
);
|
|
1972
2151
|
}
|
|
1973
2152
|
// ─── Handlers ───
|
|
1974
|
-
handleAreaSelect(e,
|
|
1975
|
-
this.stateManager.update({ selectedAreaId: e }), this.engine.setSelectedArea(e), this.inputPanel.setSelectedArea(
|
|
2153
|
+
handleAreaSelect(e, i = !1) {
|
|
2154
|
+
this.stateManager.update({ selectedAreaId: e }), this.engine.setSelectedArea(e), this.inputPanel.setSelectedArea(i ? e : null), e && document.activeElement !== this && this.focus(), this.dispatchEvent(
|
|
1976
2155
|
new CustomEvent("area:select", {
|
|
1977
2156
|
detail: { areaId: e },
|
|
1978
2157
|
bubbles: !0,
|
|
@@ -1980,29 +2159,29 @@ class ee extends HTMLElement {
|
|
|
1980
2159
|
})
|
|
1981
2160
|
);
|
|
1982
2161
|
}
|
|
1983
|
-
handleContextMenu(e,
|
|
2162
|
+
handleContextMenu(e, i, t) {
|
|
1984
2163
|
this.handleAreaSelect(e, !!e);
|
|
1985
2164
|
}
|
|
1986
2165
|
handleZoomIn() {
|
|
1987
|
-
const e = this.engine.getZoom(),
|
|
1988
|
-
this.engine.setZoom(
|
|
2166
|
+
const e = this.engine.getZoom(), i = Math.min(e * 1.25, 5), t = this.engine.getCenteredPan(i);
|
|
2167
|
+
this.engine.setZoom(i), t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
|
|
1989
2168
|
}
|
|
1990
2169
|
handleZoomOut() {
|
|
1991
|
-
const e = this.engine.getZoom(),
|
|
1992
|
-
this.engine.setZoom(
|
|
2170
|
+
const e = this.engine.getZoom(), i = Math.max(e * 0.8, 0.1), t = this.engine.getCenteredPan(i);
|
|
2171
|
+
this.engine.setZoom(i), t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
|
|
1993
2172
|
}
|
|
1994
2173
|
handleZoomFit() {
|
|
1995
2174
|
this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
|
|
1996
2175
|
}
|
|
1997
2176
|
handleCanvasResize(e) {
|
|
1998
|
-
const { width:
|
|
1999
|
-
if (
|
|
2177
|
+
const { width: i, height: t } = e;
|
|
2178
|
+
if (i === 0 || t === 0) return;
|
|
2000
2179
|
const s = window.devicePixelRatio || 1;
|
|
2001
|
-
this.canvas.width =
|
|
2180
|
+
this.canvas.width = i * s, this.canvas.height = t * s, this.canvas.style.width = `${i}px`, this.canvas.style.height = `${t}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
|
|
2002
2181
|
}
|
|
2003
2182
|
handleLayoutResize(e) {
|
|
2004
|
-
const
|
|
2005
|
-
this.container.classList.toggle("layout-desktop", !
|
|
2183
|
+
const i = e.width < ee;
|
|
2184
|
+
this.container.classList.toggle("layout-desktop", !i), this.container.classList.toggle("layout-mobile", i), i !== this.isMobileLayout && (this.isMobileLayout = i, this.inputPanel.setMobile(i));
|
|
2006
2185
|
}
|
|
2007
2186
|
// ─── State Management ───
|
|
2008
2187
|
async refreshStorageUsage() {
|
|
@@ -2029,7 +2208,7 @@ class ee extends HTMLElement {
|
|
|
2029
2208
|
}
|
|
2030
2209
|
// ─── Keyboard ───
|
|
2031
2210
|
setupKeyboardShortcuts() {
|
|
2032
|
-
const e =
|
|
2211
|
+
const e = G();
|
|
2033
2212
|
this.keyboard.register({
|
|
2034
2213
|
key: "z",
|
|
2035
2214
|
[e]: !0,
|
|
@@ -2045,35 +2224,35 @@ class ee extends HTMLElement {
|
|
|
2045
2224
|
});
|
|
2046
2225
|
}
|
|
2047
2226
|
// ─── Toast & Modal ───
|
|
2048
|
-
showToast(e,
|
|
2049
|
-
const
|
|
2050
|
-
this.shadow.appendChild(
|
|
2227
|
+
showToast(e, i = "info") {
|
|
2228
|
+
const t = c("div", { class: `editor-toast editor-toast-${i}` }, e);
|
|
2229
|
+
this.shadow.appendChild(t), setTimeout(() => t.remove(), 4e3);
|
|
2051
2230
|
}
|
|
2052
|
-
showConfirmation(e,
|
|
2231
|
+
showConfirmation(e, i, t = "Cancel", s = "Continue") {
|
|
2053
2232
|
return new Promise((a) => {
|
|
2054
|
-
const o =
|
|
2055
|
-
n.appendChild(
|
|
2056
|
-
const r =
|
|
2233
|
+
const o = c("div", { class: "editor-modal-overlay" }), n = c("div", { class: "editor-modal" });
|
|
2234
|
+
n.appendChild(c("div", { class: "editor-modal-title" }, e)), n.appendChild(c("div", { class: "editor-modal-body" }, i));
|
|
2235
|
+
const r = c("div", { class: "editor-modal-actions" }), d = c("button", { class: "editor-modal-btn" }, t);
|
|
2057
2236
|
d.addEventListener("click", () => {
|
|
2058
2237
|
o.remove(), a(!1);
|
|
2059
2238
|
});
|
|
2060
|
-
const l =
|
|
2239
|
+
const l = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
|
|
2061
2240
|
l.addEventListener("click", () => {
|
|
2062
2241
|
o.remove(), a(!0);
|
|
2063
|
-
}), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (
|
|
2064
|
-
|
|
2242
|
+
}), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (h) => {
|
|
2243
|
+
h.target === o && (o.remove(), a(!1));
|
|
2065
2244
|
}), this.shadow.appendChild(o);
|
|
2066
2245
|
});
|
|
2067
2246
|
}
|
|
2068
2247
|
// ─── Error / Cleanup ───
|
|
2069
|
-
showError(e,
|
|
2248
|
+
showError(e, i) {
|
|
2070
2249
|
this.shadow.innerHTML = "";
|
|
2071
|
-
const
|
|
2072
|
-
|
|
2073
|
-
const s =
|
|
2074
|
-
s.appendChild(
|
|
2250
|
+
const t = document.createElement("style");
|
|
2251
|
+
t.textContent = k, this.shadow.appendChild(t);
|
|
2252
|
+
const s = c("div", { class: "error-container" });
|
|
2253
|
+
s.appendChild(c("div", { class: "error-title" }, e)), s.appendChild(c("div", { class: "error-message" }, i?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
|
|
2075
2254
|
new CustomEvent("error", {
|
|
2076
|
-
detail: { message: e, error:
|
|
2255
|
+
detail: { message: e, error: i },
|
|
2077
2256
|
bubbles: !0,
|
|
2078
2257
|
composed: !0
|
|
2079
2258
|
})
|
|
@@ -2085,150 +2264,161 @@ class ee extends HTMLElement {
|
|
|
2085
2264
|
this.subscriptions = [], this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = null), this.engine?.destroy(), this.interaction?.destroy(), this.keyboard?.destroy(), this.isReady = !1, this.currentTemplate = null, this.productViews = [], this.activeViewName = null, this.perViewContents.clear();
|
|
2086
2265
|
}
|
|
2087
2266
|
}
|
|
2088
|
-
customElements.get("customizer-editor") || customElements.define("customizer-editor",
|
|
2089
|
-
function te(
|
|
2090
|
-
const
|
|
2091
|
-
if (!
|
|
2267
|
+
customElements.get("customizer-editor") || customElements.define("customizer-editor", I);
|
|
2268
|
+
function te(v, e) {
|
|
2269
|
+
const i = typeof v == "string" ? document.querySelector(v) : v;
|
|
2270
|
+
if (!i)
|
|
2092
2271
|
throw new Error(
|
|
2093
|
-
`Container not found: ${typeof
|
|
2272
|
+
`Container not found: ${typeof v == "string" ? v : "provided element is null"}`
|
|
2094
2273
|
);
|
|
2095
2274
|
if (!e.templateId && !e.productId)
|
|
2096
2275
|
throw new Error("Either templateId or productId must be provided");
|
|
2097
2276
|
if (e.templateId && e.productId)
|
|
2098
2277
|
throw new Error("Only one of templateId or productId should be provided, not both");
|
|
2099
|
-
const
|
|
2100
|
-
e.productId ?
|
|
2278
|
+
const t = document.createElement("customizer-editor");
|
|
2279
|
+
e.productId ? t.setAttribute("product-id", e.productId) : t.setAttribute("template-id", e.templateId), e.theme && t.setAttribute("theme", e.theme), e.mode && t.setAttribute("mode", e.mode), e.className && t.classList.add(e.className), e.showCloseButton === !1 && t.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && t.setAttribute("show-save-button", "false"), t.style.width = "100%", t.style.height = "100%";
|
|
2101
2280
|
const s = [], a = (n, r) => {
|
|
2102
|
-
|
|
2281
|
+
t.addEventListener(n, r), s.push({ event: n, handler: r });
|
|
2103
2282
|
};
|
|
2104
|
-
|
|
2283
|
+
i.innerHTML = "", i.appendChild(t);
|
|
2105
2284
|
const o = {
|
|
2106
2285
|
getDesign() {
|
|
2107
|
-
if (typeof
|
|
2286
|
+
if (typeof t.getDesign != "function")
|
|
2108
2287
|
throw new Error("Editor not ready: getDesign method not available");
|
|
2109
|
-
return
|
|
2288
|
+
return t.getDesign();
|
|
2110
2289
|
},
|
|
2111
2290
|
setDesign(n) {
|
|
2112
|
-
if (typeof
|
|
2291
|
+
if (typeof t.setDesign != "function")
|
|
2113
2292
|
throw new Error("Editor not ready: setDesign method not available");
|
|
2114
|
-
|
|
2293
|
+
t.setDesign(n);
|
|
2115
2294
|
},
|
|
2116
2295
|
undo() {
|
|
2117
|
-
if (typeof
|
|
2296
|
+
if (typeof t.undo != "function")
|
|
2118
2297
|
throw new Error("Editor not ready: undo method not available");
|
|
2119
|
-
|
|
2298
|
+
t.undo();
|
|
2120
2299
|
},
|
|
2121
2300
|
redo() {
|
|
2122
|
-
if (typeof
|
|
2301
|
+
if (typeof t.redo != "function")
|
|
2123
2302
|
throw new Error("Editor not ready: redo method not available");
|
|
2124
|
-
|
|
2303
|
+
t.redo();
|
|
2125
2304
|
},
|
|
2126
2305
|
canUndo() {
|
|
2127
|
-
return typeof
|
|
2306
|
+
return typeof t.canUndo != "function" ? !1 : t.canUndo();
|
|
2128
2307
|
},
|
|
2129
2308
|
canRedo() {
|
|
2130
|
-
return typeof
|
|
2309
|
+
return typeof t.canRedo != "function" ? !1 : t.canRedo();
|
|
2131
2310
|
},
|
|
2132
2311
|
async finalize() {
|
|
2133
|
-
const n =
|
|
2312
|
+
const n = e.apiUrl || "https://api.varianta.io";
|
|
2134
2313
|
try {
|
|
2135
|
-
|
|
2314
|
+
if (e.productId && typeof t.finalize == "function") {
|
|
2315
|
+
const g = await t.finalize(), f = {
|
|
2316
|
+
designId: g.designId,
|
|
2317
|
+
status: g.status,
|
|
2318
|
+
proofUrl: g.proofUrl ?? null,
|
|
2319
|
+
errorMessage: null,
|
|
2320
|
+
requestId: g.requestId,
|
|
2321
|
+
views: g.views
|
|
2322
|
+
};
|
|
2323
|
+
return e.onFinalize && e.onFinalize(f), f;
|
|
2324
|
+
}
|
|
2325
|
+
const r = this.getDesign(), d = await fetch(`${n}/public/finalize`, {
|
|
2136
2326
|
method: "POST",
|
|
2137
2327
|
headers: {
|
|
2138
2328
|
"Content-Type": "application/json"
|
|
2139
2329
|
},
|
|
2140
2330
|
body: JSON.stringify({
|
|
2141
|
-
templateId:
|
|
2142
|
-
designJson:
|
|
2331
|
+
templateId: r.templateId || e.templateId,
|
|
2332
|
+
designJson: r
|
|
2143
2333
|
})
|
|
2144
2334
|
});
|
|
2145
2335
|
if (!d.ok) {
|
|
2146
|
-
const
|
|
2336
|
+
const g = await d.json().catch(() => ({
|
|
2147
2337
|
message: "Finalization failed"
|
|
2148
2338
|
}));
|
|
2149
|
-
throw new Error(
|
|
2339
|
+
throw new Error(g.message || "Finalization failed");
|
|
2150
2340
|
}
|
|
2151
2341
|
const l = await d.json();
|
|
2152
|
-
let
|
|
2342
|
+
let h = {
|
|
2153
2343
|
designId: l.designId,
|
|
2154
2344
|
status: l.status,
|
|
2155
2345
|
proofUrl: l.proofUrl ?? null,
|
|
2156
2346
|
errorMessage: l.errorMessage ?? null
|
|
2157
2347
|
};
|
|
2158
2348
|
const u = 1500, p = 40;
|
|
2159
|
-
let
|
|
2160
|
-
for (;
|
|
2161
|
-
await new Promise((b) => setTimeout(b, u)),
|
|
2162
|
-
const
|
|
2163
|
-
`${
|
|
2349
|
+
let m = 0;
|
|
2350
|
+
for (; h.status === "processing" && m < p; ) {
|
|
2351
|
+
await new Promise((b) => setTimeout(b, u)), m++;
|
|
2352
|
+
const g = await fetch(
|
|
2353
|
+
`${n}/public/designs/${h.designId}/status`
|
|
2164
2354
|
);
|
|
2165
|
-
if (!
|
|
2355
|
+
if (!g.ok)
|
|
2166
2356
|
throw new Error("Failed to check design status");
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
designId:
|
|
2170
|
-
status:
|
|
2171
|
-
proofUrl:
|
|
2172
|
-
errorMessage:
|
|
2357
|
+
const f = await g.json();
|
|
2358
|
+
h = {
|
|
2359
|
+
designId: f.designId,
|
|
2360
|
+
status: f.status,
|
|
2361
|
+
proofUrl: f.proofUrl ?? null,
|
|
2362
|
+
errorMessage: f.errorMessage ?? null
|
|
2173
2363
|
};
|
|
2174
2364
|
}
|
|
2175
|
-
return
|
|
2176
|
-
} catch (
|
|
2177
|
-
const
|
|
2365
|
+
return h.status === "processing" && (h = { ...h, status: "failed", errorMessage: "Render timed out" }), e.onFinalize && e.onFinalize(h), h;
|
|
2366
|
+
} catch (r) {
|
|
2367
|
+
const d = {
|
|
2178
2368
|
code: "FINALIZE_ERROR",
|
|
2179
|
-
message:
|
|
2180
|
-
details:
|
|
2369
|
+
message: r instanceof Error ? r.message : "Unknown error",
|
|
2370
|
+
details: r
|
|
2181
2371
|
};
|
|
2182
|
-
throw e.onError && e.onError(
|
|
2372
|
+
throw e.onError && e.onError(d), r;
|
|
2183
2373
|
}
|
|
2184
2374
|
},
|
|
2185
2375
|
addTextLayer(n) {
|
|
2186
|
-
if (typeof
|
|
2376
|
+
if (typeof t.addTextLayer != "function")
|
|
2187
2377
|
throw new Error("Editor not ready: addTextLayer method not available");
|
|
2188
|
-
|
|
2378
|
+
t.addTextLayer(n);
|
|
2189
2379
|
},
|
|
2190
2380
|
async addImageLayer(n) {
|
|
2191
|
-
if (typeof
|
|
2381
|
+
if (typeof t.addImageLayer != "function")
|
|
2192
2382
|
throw new Error("Editor not ready: addImageLayer method not available");
|
|
2193
|
-
return
|
|
2383
|
+
return t.addImageLayer(n);
|
|
2194
2384
|
},
|
|
2195
2385
|
removeLayer(n) {
|
|
2196
|
-
if (typeof
|
|
2386
|
+
if (typeof t.removeLayer != "function")
|
|
2197
2387
|
throw new Error("Editor not ready: removeLayer method not available");
|
|
2198
|
-
|
|
2388
|
+
t.removeLayer(n);
|
|
2199
2389
|
},
|
|
2200
2390
|
selectLayer(n) {
|
|
2201
|
-
if (typeof
|
|
2391
|
+
if (typeof t.selectLayer != "function")
|
|
2202
2392
|
throw new Error("Editor not ready: selectLayer method not available");
|
|
2203
|
-
|
|
2393
|
+
t.selectLayer(n);
|
|
2204
2394
|
},
|
|
2205
2395
|
getSelectedLayerId() {
|
|
2206
|
-
return typeof
|
|
2396
|
+
return typeof t.getSelectedLayerId != "function" ? null : t.getSelectedLayerId();
|
|
2207
2397
|
},
|
|
2208
2398
|
getActiveView() {
|
|
2209
|
-
return typeof
|
|
2399
|
+
return typeof t.getActiveView != "function" ? null : t.getActiveView();
|
|
2210
2400
|
},
|
|
2211
2401
|
getViews() {
|
|
2212
|
-
return typeof
|
|
2402
|
+
return typeof t.getViews != "function" ? [] : t.getViews();
|
|
2213
2403
|
},
|
|
2214
2404
|
setActiveView(n) {
|
|
2215
|
-
if (typeof
|
|
2405
|
+
if (typeof t.setActiveView != "function")
|
|
2216
2406
|
throw new Error("Editor not ready: setActiveView method not available");
|
|
2217
|
-
|
|
2407
|
+
t.setActiveView(n);
|
|
2218
2408
|
},
|
|
2219
2409
|
setTheme(n) {
|
|
2220
|
-
|
|
2410
|
+
t.setAttribute("theme", n);
|
|
2221
2411
|
},
|
|
2222
2412
|
setMode(n) {
|
|
2223
|
-
|
|
2413
|
+
t.setAttribute("mode", n);
|
|
2224
2414
|
},
|
|
2225
2415
|
destroy() {
|
|
2226
2416
|
s.forEach(({ event: n, handler: r }) => {
|
|
2227
|
-
|
|
2228
|
-
}),
|
|
2417
|
+
t.removeEventListener(n, r);
|
|
2418
|
+
}), t.parentNode && t.parentNode.removeChild(t);
|
|
2229
2419
|
},
|
|
2230
2420
|
getElement() {
|
|
2231
|
-
return
|
|
2421
|
+
return t;
|
|
2232
2422
|
}
|
|
2233
2423
|
};
|
|
2234
2424
|
return e.onReady && a("ready", (() => {
|
|
@@ -2253,10 +2443,10 @@ function te(f, e) {
|
|
|
2253
2443
|
e.onClose?.();
|
|
2254
2444
|
})), e.onSave && a("customizer:save", ((n) => {
|
|
2255
2445
|
e.onSave?.(n.detail);
|
|
2256
|
-
})), e.initialDesign &&
|
|
2446
|
+
})), e.initialDesign && t.addEventListener(
|
|
2257
2447
|
"ready",
|
|
2258
2448
|
() => {
|
|
2259
|
-
e.initialDesign &&
|
|
2449
|
+
e.initialDesign && t.setDesign(e.initialDesign);
|
|
2260
2450
|
},
|
|
2261
2451
|
{ once: !0 }
|
|
2262
2452
|
), e.debug && (console.log("[Customizer SDK] Initialized with options:", e), console.log("[Customizer SDK] Instance:", o)), o;
|