@emabuild/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/canvas/column-renderer.d.ts.map +1 -1
  2. package/dist/canvas/content-renderer.d.ts +1 -1
  3. package/dist/canvas/content-renderer.d.ts.map +1 -1
  4. package/dist/canvas/editor-canvas.d.ts +13 -0
  5. package/dist/canvas/editor-canvas.d.ts.map +1 -1
  6. package/dist/canvas/inline-toolbar.d.ts +8 -0
  7. package/dist/canvas/inline-toolbar.d.ts.map +1 -1
  8. package/dist/canvas/row-renderer.d.ts +2 -0
  9. package/dist/canvas/row-renderer.d.ts.map +1 -1
  10. package/dist/compat/unlayer-adapter.d.ts +33 -0
  11. package/dist/compat/unlayer-adapter.d.ts.map +1 -0
  12. package/dist/dnd/drag-manager.d.ts +1 -0
  13. package/dist/dnd/drag-manager.d.ts.map +1 -1
  14. package/dist/dnd/drag-state.d.ts +10 -3
  15. package/dist/dnd/drag-state.d.ts.map +1 -1
  16. package/dist/{form-tool-DdFDrS3b.js → form-tool-DsPgMShR.js} +10 -9
  17. package/dist/{form-tool-DdFDrS3b.js.map → form-tool-DsPgMShR.js.map} +1 -1
  18. package/dist/{html-tool-DMtmrF3n.js → html-tool-BxhBGl4L.js} +2 -2
  19. package/dist/{html-tool-DMtmrF3n.js.map → html-tool-BxhBGl4L.js.map} +1 -1
  20. package/dist/index-2S5kBS5_.js +4436 -0
  21. package/dist/index-2S5kBS5_.js.map +1 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -5
  25. package/dist/mail-editor.d.ts +13 -2
  26. package/dist/mail-editor.d.ts.map +1 -1
  27. package/dist/{menu-tool-CJcQdpcP.js → menu-tool-IZqYp8Vb.js} +14 -13
  28. package/dist/{menu-tool-CJcQdpcP.js.map → menu-tool-IZqYp8Vb.js.map} +1 -1
  29. package/dist/properties/property-panel.d.ts.map +1 -1
  30. package/dist/properties/widgets/image-upload-widget.d.ts +4 -0
  31. package/dist/properties/widgets/image-upload-widget.d.ts.map +1 -0
  32. package/dist/properties/widgets/index.d.ts +1 -0
  33. package/dist/properties/widgets/index.d.ts.map +1 -1
  34. package/dist/register-elements.d.ts.map +1 -1
  35. package/dist/sidebar/a11y-checker.d.ts +27 -0
  36. package/dist/sidebar/a11y-checker.d.ts.map +1 -0
  37. package/dist/sidebar/body-settings.d.ts +2 -0
  38. package/dist/sidebar/body-settings.d.ts.map +1 -1
  39. package/dist/sidebar/editor-sidebar.d.ts +5 -2
  40. package/dist/sidebar/editor-sidebar.d.ts.map +1 -1
  41. package/dist/{social-tool-CRY3-_sU.js → social-tool-C1FeCyUm.js} +13 -12
  42. package/dist/{social-tool-CRY3-_sU.js.map → social-tool-C1FeCyUm.js.map} +1 -1
  43. package/dist/state/editor-store.d.ts +35 -28
  44. package/dist/state/editor-store.d.ts.map +1 -1
  45. package/dist/{table-tool-DeIQci5z.js → table-tool-BD72-Fuj.js} +13 -12
  46. package/dist/{table-tool-DeIQci5z.js.map → table-tool-BD72-Fuj.js.map} +1 -1
  47. package/dist/timer-tool-Dpw9p0uW.js +55 -0
  48. package/dist/timer-tool-Dpw9p0uW.js.map +1 -0
  49. package/dist/tools/built-in/button-tool.d.ts.map +1 -1
  50. package/dist/tools/built-in/divider-tool.d.ts.map +1 -1
  51. package/dist/tools/built-in/heading-tool.d.ts.map +1 -1
  52. package/dist/tools/built-in/image-tool.d.ts.map +1 -1
  53. package/dist/tools/built-in/paragraph-tool.d.ts.map +1 -1
  54. package/dist/tools/built-in/shared-options.d.ts +105 -0
  55. package/dist/tools/built-in/shared-options.d.ts.map +1 -0
  56. package/dist/tools/built-in/text-tool.d.ts.map +1 -1
  57. package/dist/tools/tool-registry.d.ts.map +1 -1
  58. package/dist/{video-tool-g1fIoCWW.js → video-tool-CHhPfHaS.js} +7 -6
  59. package/dist/{video-tool-g1fIoCWW.js.map → video-tool-CHhPfHaS.js.map} +1 -1
  60. package/package.json +4 -4
  61. package/dist/index-CpMbWdgn.js +0 -3092
  62. package/dist/index-CpMbWdgn.js.map +0 -1
  63. package/dist/timer-tool-BVE1shO1.js +0 -54
  64. package/dist/timer-tool-BVE1shO1.js.map +0 -1
@@ -1,3092 +0,0 @@
1
- import { html as p, LitElement as $, css as k } from "lit";
2
- import { property as b, customElement as C, state as ht } from "lit/decorators.js";
3
- import { unsafeHTML as U } from "lit/directives/unsafe-html.js";
4
- import { styleMap as ut } from "lit/directives/style-map.js";
5
- function gt() {
6
- const e = {};
7
- return {
8
- getCounters() {
9
- return { ...e };
10
- },
11
- setCounters(t) {
12
- for (const o of Object.keys(e))
13
- delete e[o];
14
- Object.assign(e, t);
15
- },
16
- next(t) {
17
- const o = e[t] ?? 0;
18
- return e[t] = o + 1, e[t];
19
- }
20
- };
21
- }
22
- class ft {
23
- constructor() {
24
- this.listeners = /* @__PURE__ */ new Map();
25
- }
26
- /** Register a listener for an event */
27
- on(t, o) {
28
- this.listeners.has(t) || this.listeners.set(t, /* @__PURE__ */ new Set()), this.listeners.get(t).add(o);
29
- }
30
- /** Remove a specific listener */
31
- off(t, o) {
32
- this.listeners.get(t)?.delete(o);
33
- }
34
- /** Emit an event with a payload. Errors in listeners are caught and logged. */
35
- emit(t, o) {
36
- this.listeners.get(t)?.forEach((n) => {
37
- try {
38
- n(o);
39
- } catch (i) {
40
- console.error(`[emabuild] Error in "${t}" listener:`, i);
41
- }
42
- });
43
- }
44
- /** Remove all listeners, optionally scoped to a single event */
45
- removeAllListeners(t) {
46
- t ? this.listeners.delete(t) : this.listeners.clear();
47
- }
48
- }
49
- class bt {
50
- constructor(t = 50) {
51
- this.undoStack = [], this.redoStack = [], this.maxHistory = t;
52
- }
53
- /** Whether there are states to undo to */
54
- get canUndo() {
55
- return this.undoStack.length > 0;
56
- }
57
- /** Whether there are states to redo to */
58
- get canRedo() {
59
- return this.redoStack.length > 0;
60
- }
61
- /** Save current design to the undo stack before a mutation */
62
- push(t) {
63
- this.undoStack.push(structuredClone(t)), this.undoStack.length > this.maxHistory && this.undoStack.shift(), this.redoStack = [];
64
- }
65
- /** Restore the previous state, pushing current state to redo. Returns the restored design or undefined. */
66
- undo(t) {
67
- const o = this.undoStack.pop();
68
- if (o)
69
- return this.redoStack.push(structuredClone(t)), o;
70
- }
71
- /** Restore the next state, pushing current state to undo. Returns the restored design or undefined. */
72
- redo(t) {
73
- const o = this.redoStack.pop();
74
- if (o)
75
- return this.undoStack.push(structuredClone(t)), o;
76
- }
77
- /** Clear all history (e.g. when loading a new design) */
78
- clear() {
79
- this.undoStack = [], this.redoStack = [];
80
- }
81
- }
82
- function mt() {
83
- return {
84
- counters: { u_row: 0, u_column: 0 },
85
- body: {
86
- id: "u_body",
87
- rows: [],
88
- headers: [],
89
- footers: [],
90
- values: {
91
- backgroundColor: "#e7e7e7",
92
- contentAlign: "center",
93
- contentVerticalAlign: "center",
94
- contentWidth: "600px",
95
- fontFamily: { label: "Arial", value: "arial,helvetica,sans-serif" },
96
- textColor: "#000000",
97
- linkStyle: {
98
- body: !0,
99
- linkColor: "#0000ee",
100
- linkHoverColor: "#0000ee",
101
- linkUnderline: !0,
102
- linkHoverUnderline: !0
103
- },
104
- preheaderText: "",
105
- popupPosition: "center",
106
- popupWidth: "600px",
107
- popupHeight: "auto",
108
- borderRadius: "10px",
109
- popupBackgroundColor: "#FFFFFF",
110
- popupBackgroundImage: { url: "", fullWidth: !0, repeat: "no-repeat", center: !0, cover: !0 },
111
- popupOverlay_backgroundColor: "rgba(0, 0, 0, 0.1)",
112
- popupCloseButton_position: "top-right",
113
- popupCloseButton_backgroundColor: "#DDDDDD",
114
- popupCloseButton_iconColor: "#000000",
115
- popupCloseButton_borderRadius: "0px",
116
- popupCloseButton_margin: "0px",
117
- popupCloseButton_action: {
118
- name: "close_popup",
119
- attrs: { onClick: "document.querySelector('.u-popup-container').style.display = 'none';" }
120
- },
121
- _meta: { htmlID: "u_body", htmlClassNames: "u_body" }
122
- }
123
- },
124
- schemaVersion: 16
125
- };
126
- }
127
- function xt(e, t) {
128
- const o = e.next("u_row"), n = t.map(() => {
129
- const i = e.next("u_column");
130
- return {
131
- id: `u_column_${i}`,
132
- contents: [],
133
- values: {
134
- backgroundColor: "",
135
- padding: "0px",
136
- border: {},
137
- borderRadius: "0px",
138
- _meta: { htmlID: `u_column_${i}`, htmlClassNames: "u_column" }
139
- }
140
- };
141
- });
142
- return {
143
- id: `u_row_${o}`,
144
- cells: t,
145
- columns: n,
146
- values: {
147
- displayCondition: null,
148
- columns: !1,
149
- backgroundColor: "",
150
- columnsBackgroundColor: "",
151
- backgroundImage: { url: "", fullWidth: !0, repeat: !1, center: !0, cover: !1 },
152
- padding: "0px",
153
- anchor: "",
154
- hideDesktop: !1,
155
- hideMobile: !1,
156
- _meta: { htmlID: `u_row_${o}`, htmlClassNames: "u_row" }
157
- }
158
- };
159
- }
160
- function yt(e, t, o = {}) {
161
- const n = e.next(`u_content_${t}`), i = `u_content_${t}_${n}`;
162
- return {
163
- id: i,
164
- type: t,
165
- values: {
166
- containerPadding: "10px",
167
- anchor: "",
168
- hideDesktop: !1,
169
- hideMobile: !1,
170
- displayCondition: null,
171
- _meta: { htmlID: i, htmlClassNames: `u_content_${t}` },
172
- ...o
173
- }
174
- };
175
- }
176
- function N(e, t) {
177
- return e.body.rows.find((o) => o.id === t);
178
- }
179
- function j(e, t) {
180
- for (const o of e.body.rows) {
181
- const n = o.columns.find((i) => i.id === t);
182
- if (n) return n;
183
- }
184
- }
185
- function W(e, t) {
186
- for (const o of e.body.rows)
187
- for (const n of o.columns) {
188
- const i = n.contents.find((s) => s.id === t);
189
- if (i) return i;
190
- }
191
- }
192
- function wt(e, t) {
193
- for (const o of e.body.rows)
194
- for (const n of o.columns)
195
- if (n.contents.some((i) => i.id === t)) return n;
196
- }
197
- function vt(e, t) {
198
- for (const o of e.body.rows)
199
- if (o.columns.some((n) => n.id === t)) return o;
200
- }
201
- function q(e, t) {
202
- return e.body.rows.findIndex((o) => o.id === t);
203
- }
204
- class $t {
205
- constructor() {
206
- this.history = new bt(), this.counterManager = gt(), this.subscribers = /* @__PURE__ */ new Set(), this.channelSubscribers = /* @__PURE__ */ new Map(), this.events = new ft(), this._selectedId = null, this._hoveredId = null, this._viewMode = "desktop", this._activeTab = "content", this.design = mt();
207
- }
208
- // ── Subscriptions ──────────────────────────────────────────
209
- /** Subscribe to ALL state changes (legacy). Returns an unsubscribe function. */
210
- subscribe(t) {
211
- return this.subscribers.add(t), () => this.subscribers.delete(t);
212
- }
213
- /**
214
- * Subscribe to specific channels only. The callback is invoked only
215
- * when one of the listed channels fires. Returns an unsubscribe function.
216
- *
217
- * @example
218
- * ```ts
219
- * // Only re-render when the design changes, not on hover/selection
220
- * store.subscribeChannels(['design'], () => this.requestUpdate());
221
- * ```
222
- */
223
- subscribeChannels(t, o) {
224
- for (const n of t)
225
- this.channelSubscribers.has(n) || this.channelSubscribers.set(n, /* @__PURE__ */ new Set()), this.channelSubscribers.get(n).add(o);
226
- return () => {
227
- for (const n of t)
228
- this.channelSubscribers.get(n)?.delete(o);
229
- };
230
- }
231
- /** Notify legacy (all) subscribers */
232
- notify() {
233
- this.subscribers.forEach((t) => t());
234
- }
235
- /** Notify only subscribers of specific channels + legacy subscribers */
236
- notifyChannels(...t) {
237
- const o = /* @__PURE__ */ new Set();
238
- for (const n of this.subscribers) o.add(n);
239
- for (const n of t) {
240
- const i = this.channelSubscribers.get(n);
241
- if (i) for (const s of i) o.add(s);
242
- }
243
- o.forEach((n) => n());
244
- }
245
- // ── Getters ────────────────────────────────────────────────
246
- /** Get the full design document */
247
- getDesign() {
248
- return this.design;
249
- }
250
- /** Get the design body */
251
- getBody() {
252
- return this.design.body;
253
- }
254
- /** Get all rows */
255
- getRows() {
256
- return this.design.body.rows;
257
- }
258
- /** Get body-level values (background, fonts, etc.) */
259
- getBodyValues() {
260
- return this.design.body.values;
261
- }
262
- get selectedId() {
263
- return this._selectedId;
264
- }
265
- get hoveredId() {
266
- return this._hoveredId;
267
- }
268
- get viewMode() {
269
- return this._viewMode;
270
- }
271
- get activeTab() {
272
- return this._activeTab;
273
- }
274
- get canUndo() {
275
- return this.history.canUndo;
276
- }
277
- get canRedo() {
278
- return this.history.canRedo;
279
- }
280
- // ── Design Loading ─────────────────────────────────────────
281
- /** Load a design document, resetting history and selection */
282
- loadDesign(t) {
283
- this.design = structuredClone(t), this.counterManager.setCounters(this.design.counters), this.history.clear(), this._selectedId = null, this.notifyChannels("design", "selection"), this.events.emit("design:loaded", { design: this.design });
284
- }
285
- // ── Undo / Redo ────────────────────────────────────────────
286
- undo() {
287
- const t = this.history.undo(this.design);
288
- t && (this.design = t, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
289
- }
290
- redo() {
291
- const t = this.history.redo(this.design);
292
- t && (this.design = t, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
293
- }
294
- // ── Selection / UI State ───────────────────────────────────
295
- select(t) {
296
- this._selectedId = t, this.notifyChannels("selection");
297
- }
298
- hover(t) {
299
- this._hoveredId = t, this.notifyChannels("hover");
300
- }
301
- setViewMode(t) {
302
- this._viewMode = t, this.notifyChannels("viewMode");
303
- }
304
- setActiveTab(t) {
305
- this._activeTab = t, this.notifyChannels("activeTab");
306
- }
307
- // ── Row Operations ─────────────────────────────────────────
308
- /** Add a row at the given index (or at the end) */
309
- addRow(t, o) {
310
- this.history.push(this.design);
311
- const n = this.design.body.rows, i = structuredClone(t);
312
- o !== void 0 && o >= 0 && o <= n.length ? n.splice(o, 0, i) : n.push(i), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", i);
313
- }
314
- /** Remove a row by ID */
315
- removeRow(t) {
316
- const o = q(this.design, t);
317
- o !== -1 && (this.history.push(this.design), this.design.body.rows.splice(o, 1), this._selectedId === t && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("row_removed"));
318
- }
319
- /** Move a row from one index to another */
320
- moveRow(t, o) {
321
- this.history.push(this.design);
322
- const n = this.design.body.rows, [i] = n.splice(t, 1);
323
- n.splice(o, 0, i), this.notifyChannels("design"), this.emitUpdate("row_reordered");
324
- }
325
- /** Duplicate a row, assigning fresh IDs to all nested elements */
326
- duplicateRow(t) {
327
- const o = N(this.design, t);
328
- if (!o) return;
329
- this.history.push(this.design);
330
- const n = structuredClone(o), i = this.counterManager.next("u_row");
331
- n.id = `u_row_${i}`, n.values._meta = { htmlID: n.id, htmlClassNames: "u_row" };
332
- for (const r of n.columns) {
333
- const a = this.counterManager.next("u_column");
334
- r.id = `u_column_${a}`, r.values._meta = { htmlID: r.id, htmlClassNames: "u_column" };
335
- for (const d of r.contents) {
336
- const c = this.counterManager.next(`u_content_${d.type}`);
337
- d.id = `u_content_${d.type}_${c}`, d.values._meta = { htmlID: d.id, htmlClassNames: `u_content_${d.type}` };
338
- }
339
- }
340
- const s = q(this.design, t);
341
- this.design.body.rows.splice(s + 1, 0, n), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", n);
342
- }
343
- /** Get the index of a row */
344
- getRowIndex(t) {
345
- return q(this.design, t);
346
- }
347
- /** Update row-level values */
348
- updateRowValues(t, o) {
349
- const n = N(this.design, t);
350
- n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
351
- }
352
- // ── Column Operations ──────────────────────────────────────
353
- /** Update column-level values */
354
- updateColumnValues(t, o) {
355
- const n = j(this.design, t);
356
- n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
357
- }
358
- // ── Content Operations ─────────────────────────────────────
359
- /** Add content to a column at the given index */
360
- addContent(t, o, n) {
361
- const i = j(this.design, t);
362
- if (!i) return;
363
- this.history.push(this.design);
364
- const s = structuredClone(o);
365
- n !== void 0 && n >= 0 && n <= i.contents.length ? i.contents.splice(n, 0, s) : i.contents.push(s), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", s);
366
- }
367
- /** Remove a content block by ID */
368
- removeContent(t) {
369
- for (const o of this.design.body.rows)
370
- for (const n of o.columns) {
371
- const i = n.contents.findIndex((s) => s.id === t);
372
- if (i !== -1) {
373
- this.history.push(this.design), n.contents.splice(i, 1), this._selectedId === t && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("content_removed");
374
- return;
375
- }
376
- }
377
- }
378
- /** Update content values by ID */
379
- updateContentValues(t, o) {
380
- const n = W(this.design, t);
381
- n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
382
- }
383
- /** Move a content block to a different column at a given index */
384
- moveContent(t, o, n) {
385
- const i = W(this.design, t);
386
- if (!i) return;
387
- this.history.push(this.design);
388
- for (const r of this.design.body.rows)
389
- for (const a of r.columns) {
390
- const d = a.contents.findIndex((c) => c.id === t);
391
- if (d !== -1) {
392
- a.contents.splice(d, 1);
393
- break;
394
- }
395
- }
396
- const s = j(this.design, o);
397
- s && s.contents.splice(n, 0, i), this.notifyChannels("design"), this.emitUpdate("content_reordered");
398
- }
399
- /** Duplicate a content block, inserting the copy right after the original */
400
- duplicateContent(t) {
401
- const o = W(this.design, t);
402
- if (o)
403
- for (const n of this.design.body.rows)
404
- for (const i of n.columns) {
405
- const s = i.contents.findIndex((r) => r.id === t);
406
- if (s !== -1) {
407
- this.history.push(this.design);
408
- const r = structuredClone(o), a = this.counterManager.next(`u_content_${o.type}`);
409
- r.id = `u_content_${o.type}_${a}`, r.values._meta = { htmlID: r.id, htmlClassNames: `u_content_${o.type}` }, i.contents.splice(s + 1, 0, r), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", r);
410
- return;
411
- }
412
- }
413
- }
414
- // ── Body Values ────────────────────────────────────────────
415
- /** Update body-level values (background, fonts, etc.) */
416
- updateBodyValues(t) {
417
- this.history.push(this.design), Object.assign(this.design.body.values, t), this.notifyChannels("design"), this.emitUpdate("body_updated");
418
- }
419
- // ── Lookups (delegate to design-lookup) ────────────────────
420
- findRow(t) {
421
- return N(this.design, t);
422
- }
423
- findColumn(t) {
424
- return j(this.design, t);
425
- }
426
- findContent(t) {
427
- return W(this.design, t);
428
- }
429
- findParentColumn(t) {
430
- return wt(this.design, t);
431
- }
432
- findParentRow(t) {
433
- return vt(this.design, t);
434
- }
435
- // ── Factory Methods (delegate to design-factory) ───────────
436
- /** Create a new row with the given column layout */
437
- createRow(t) {
438
- const o = xt(this.counterManager, t);
439
- return this.syncCounters(), o;
440
- }
441
- /** Create a new content block for the given tool type */
442
- createContent(t, o = {}) {
443
- const n = yt(this.counterManager, t, o);
444
- return this.syncCounters(), n;
445
- }
446
- // ── Private Helpers ────────────────────────────────────────
447
- syncCounters() {
448
- this.design.counters = this.counterManager.getCounters();
449
- }
450
- emitUpdate(t, o) {
451
- this.events.emit("design:updated", { type: t, item: o });
452
- }
453
- }
454
- class kt {
455
- constructor() {
456
- this.tools = /* @__PURE__ */ new Map(), this.lazyLoaders = /* @__PURE__ */ new Map(), this.lazyMeta = /* @__PURE__ */ new Map(), this.loadingPromises = /* @__PURE__ */ new Map();
457
- }
458
- /** Register a tool eagerly (available immediately) */
459
- register(t) {
460
- this.tools.set(t.name, t), this.lazyLoaders.delete(t.name), this.lazyMeta.delete(t.name);
461
- }
462
- /**
463
- * Register a tool lazily. The tool's code is only loaded when first needed.
464
- * Provide metadata (name, label, icon) so the tool can appear in the sidebar.
465
- *
466
- * @param meta - Display metadata for the sidebar palette
467
- * @param loader - Async function that imports and returns the tool definition
468
- *
469
- * @example
470
- * ```ts
471
- * registry.registerLazy(
472
- * { name: 'timer', label: 'Timer', icon: '<svg>...</svg>', position: 11 },
473
- * () => import('./built-in/timer-tool.js').then(m => m.timerTool),
474
- * );
475
- * ```
476
- */
477
- registerLazy(t, o) {
478
- this.tools.has(t.name) || (this.lazyMeta.set(t.name, t), this.lazyLoaders.set(t.name, o));
479
- }
480
- /** Get a tool by name. Returns undefined if not loaded yet (use ensureLoaded for lazy tools). */
481
- get(t) {
482
- return this.tools.get(t);
483
- }
484
- /** Check if a tool is registered (eager or lazy) */
485
- has(t) {
486
- return this.tools.has(t) || this.lazyLoaders.has(t);
487
- }
488
- /** Check if a tool is fully loaded and ready to render */
489
- isLoaded(t) {
490
- return this.tools.has(t);
491
- }
492
- /**
493
- * Ensure a lazy tool is loaded. Returns the tool definition.
494
- * If the tool is already loaded, returns it immediately.
495
- * If it's being loaded, returns the in-flight promise.
496
- */
497
- async ensureLoaded(t) {
498
- if (this.tools.has(t)) return this.tools.get(t);
499
- if (this.loadingPromises.has(t)) return this.loadingPromises.get(t);
500
- const o = this.lazyLoaders.get(t);
501
- if (!o) return;
502
- const n = o().then((i) => (this.tools.set(t, i), this.lazyLoaders.delete(t), this.lazyMeta.delete(t), this.loadingPromises.delete(t), i));
503
- return this.loadingPromises.set(t, n), n;
504
- }
505
- /**
506
- * Get all tools for display in the sidebar palette.
507
- * Returns both loaded tools and lazy tool metadata, sorted by position.
508
- */
509
- getAll() {
510
- return Array.from(this.tools.values()).sort((t, o) => (t.position ?? 0) - (o.position ?? 0));
511
- }
512
- /**
513
- * Get all tool names and display metadata (including lazy tools not yet loaded).
514
- * Used by the sidebar to show all available tools.
515
- */
516
- getAllMeta() {
517
- const t = [];
518
- for (const o of this.tools.values())
519
- t.push({ name: o.name, label: o.label, icon: o.icon, position: o.position });
520
- for (const o of this.lazyMeta.values())
521
- t.push(o);
522
- return t.sort((o, n) => (o.position ?? 0) - (n.position ?? 0));
523
- }
524
- /** Get default values for a tool. Loads lazily if needed (sync — returns empty if not loaded). */
525
- getDefaultValues(t) {
526
- const o = this.tools.get(t);
527
- if (!o) return {};
528
- const n = { ...o.defaultValues };
529
- for (const i of Object.values(o.options))
530
- for (const [s, r] of Object.entries(i.options))
531
- s in n || (n[s] = r.defaultValue);
532
- return n;
533
- }
534
- /** Get property groups for a tool */
535
- getPropertyGroups(t) {
536
- return this.tools.get(t)?.options ?? {};
537
- }
538
- }
539
- const L = {
540
- /** ID of the content currently being dragged (null if not dragging content) */
541
- draggingContentId: null,
542
- startContentDrag(e) {
543
- this.draggingContentId = e;
544
- },
545
- reset() {
546
- this.draggingContentId = null;
547
- }
548
- };
549
- function Z(e) {
550
- const t = document.createElement("div");
551
- return Object.assign(t.style, {
552
- position: "absolute",
553
- left: "0",
554
- right: "0",
555
- height: "3px",
556
- background: e,
557
- borderRadius: "2px",
558
- pointerEvents: "none",
559
- zIndex: "1000",
560
- display: "none",
561
- boxShadow: `0 0 6px ${e}80`
562
- }), t;
563
- }
564
- function tt(e, t, o, n, i = "4px") {
565
- e.parentNode !== t && (e.remove(), t.appendChild(e));
566
- const r = (t instanceof ShadowRoot ? t.host : t).getBoundingClientRect();
567
- let a;
568
- o.length === 0 || n === 0 ? a = o.length === 0 ? 0 : o[0].getBoundingClientRect().top - r.top : n >= o.length ? a = o[o.length - 1].getBoundingClientRect().bottom - r.top : a = o[n].getBoundingClientRect().top - r.top, Object.assign(e.style, {
569
- display: "block",
570
- top: `${a}px`,
571
- left: i,
572
- right: i,
573
- width: "auto"
574
- });
575
- }
576
- function I(e) {
577
- e && (e.style.display = "none");
578
- }
579
- function K(e, t) {
580
- const o = (e instanceof ShadowRoot, e.children);
581
- for (const n of Array.from(o)) {
582
- const i = n;
583
- t(i), i.shadowRoot && K(i.shadowRoot, t), i.children?.length && K(i, t);
584
- }
585
- }
586
- function et(e, t) {
587
- const o = [];
588
- return K(e, (n) => {
589
- n.matches?.(t) && o.push(n);
590
- }), o;
591
- }
592
- class Ct {
593
- constructor(t, o, n) {
594
- this.currentDrop = null, this.contentIndicator = null, this.rowIndicator = null, this.onDragOver = (i) => {
595
- const s = i.dataTransfer?.types || [], r = s.includes("application/maileditor-tool"), a = s.includes("application/maileditor-layout"), d = s.includes("application/maileditor-content") || !!L.draggingContentId;
596
- !r && !d && !a || (i.preventDefault(), i.dataTransfer.dropEffect = r || a ? "copy" : "move", a ? (this.currentDrop = this.findRowDropTarget(i.clientY), I(this.contentIndicator), this.showRowIndicator()) : (this.currentDrop = this.findContentDropTarget(i.clientX, i.clientY), I(this.rowIndicator), this.showContentIndicator()));
597
- }, this.onDrop = (i) => {
598
- i.preventDefault(), this.hideAllIndicators();
599
- const s = this.currentDrop, r = i.dataTransfer?.getData("application/maileditor-layout");
600
- if (r) {
601
- this.handleLayoutDrop(JSON.parse(r), s), this.reset();
602
- return;
603
- }
604
- const a = i.dataTransfer?.getData("application/maileditor-tool");
605
- if (a) {
606
- this.handleToolDrop(a, s), this.reset();
607
- return;
608
- }
609
- const d = i.dataTransfer?.getData("application/maileditor-content") || L.draggingContentId;
610
- d && this.handleContentDrop(d, s), this.reset();
611
- }, this.onDragEnd = () => {
612
- this.hideAllIndicators(), this.reset();
613
- }, this.onDragLeave = (i) => {
614
- const s = i.relatedTarget;
615
- (!s || !this.root.contains(s)) && (this.hideAllIndicators(), this.currentDrop = null);
616
- }, this.store = t, this.toolRegistry = o, this.root = n;
617
- }
618
- /** Attach all drag event listeners to the shadow root */
619
- attach() {
620
- this.root.addEventListener("dragover", this.onDragOver), this.root.addEventListener("drop", this.onDrop), this.root.addEventListener("dragend", this.onDragEnd), this.root.addEventListener("dragleave", this.onDragLeave), this.contentIndicator = Z("#3b82f6"), this.rowIndicator = Z("#8b5cf6");
621
- }
622
- /** Remove all event listeners and clean up indicators */
623
- detach() {
624
- this.root.removeEventListener("dragover", this.onDragOver), this.root.removeEventListener("drop", this.onDrop), this.root.removeEventListener("dragend", this.onDragEnd), this.root.removeEventListener("dragleave", this.onDragLeave), this.contentIndicator?.remove(), this.rowIndicator?.remove();
625
- }
626
- // ── Drop Handlers ──────────────────────────────────────────
627
- handleLayoutDrop(t, o = this.currentDrop) {
628
- const n = this.store.createRow(t), i = o?.type === "row" ? o.rowIndex : void 0;
629
- this.store.addRow(n, i);
630
- }
631
- async handleToolDrop(t, o) {
632
- if (await this.toolRegistry.ensureLoaded(t), o?.type === "content" && o.columnId) {
633
- const n = this.toolRegistry.getDefaultValues(t), i = this.store.createContent(t, n);
634
- this.store.addContent(o.columnId, i, o.contentIndex), this.store.select(i.id);
635
- } else {
636
- const n = this.store.createRow([1]);
637
- this.store.addRow(n);
638
- const i = this.toolRegistry.getDefaultValues(t), s = this.store.createContent(t, i);
639
- this.store.addContent(n.columns[0].id, s), this.store.select(s.id);
640
- }
641
- }
642
- handleContentDrop(t, o) {
643
- o?.type === "content" && o.columnId && (this.store.moveContent(t, o.columnId, o.contentIndex), this.store.select(t));
644
- }
645
- // ── Drop Target Detection ─────────────────────────────────
646
- findRowDropTarget(t) {
647
- const o = this.root.querySelector("me-editor-canvas");
648
- if (!o?.shadowRoot) return null;
649
- const n = Array.from(o.shadowRoot.querySelectorAll("me-row-renderer"));
650
- if (n.length === 0) return { type: "row", rowIndex: 0, y: 0 };
651
- let i = Math.abs(t - n[0].getBoundingClientRect().top), s = { type: "row", rowIndex: 0, y: n[0].getBoundingClientRect().top };
652
- for (let r = 0; r < n.length; r++) {
653
- const a = n[r].getBoundingClientRect().bottom, d = Math.abs(t - a);
654
- d < i && (i = d, s = { type: "row", rowIndex: r + 1, y: a });
655
- }
656
- return s;
657
- }
658
- findContentDropTarget(t, o) {
659
- const n = et(this.root, "me-column-renderer");
660
- let i = null, s = 1 / 0;
661
- for (const r of n) {
662
- const a = r.dataset.columnId;
663
- if (!a || !r.shadowRoot) continue;
664
- const d = r.getBoundingClientRect();
665
- if (t < d.left || t > d.right) continue;
666
- const c = Array.from(r.shadowRoot.querySelectorAll("me-content-renderer"));
667
- if (c.length === 0) {
668
- const u = Math.abs(o - (d.top + d.height / 2));
669
- u < s && (s = u, i = { type: "content", columnId: a, contentIndex: 0, y: d.top + d.height / 2 });
670
- continue;
671
- }
672
- const h = c[0].getBoundingClientRect().top;
673
- let g = Math.abs(o - h);
674
- g < s && (s = g, i = { type: "content", columnId: a, contentIndex: 0, y: h });
675
- for (let u = 0; u < c.length; u++) {
676
- const f = c[u].getBoundingClientRect(), w = c[u + 1]?.getBoundingClientRect(), x = w ? (f.bottom + w.top) / 2 : f.bottom;
677
- g = Math.abs(o - x), g < s && (s = g, i = { type: "content", columnId: a, contentIndex: u + 1, y: x });
678
- }
679
- }
680
- return i;
681
- }
682
- // ── Indicator Positioning ──────────────────────────────────
683
- showContentIndicator() {
684
- if (!this.contentIndicator || !this.currentDrop?.columnId) {
685
- I(this.contentIndicator);
686
- return;
687
- }
688
- const o = et(this.root, "me-column-renderer").find((i) => i.dataset.columnId === this.currentDrop.columnId);
689
- if (!o?.shadowRoot) return;
690
- const n = Array.from(o.shadowRoot.querySelectorAll("me-content-renderer"));
691
- tt(this.contentIndicator, o.shadowRoot, n, this.currentDrop.contentIndex ?? 0, "4px");
692
- }
693
- showRowIndicator() {
694
- if (!this.rowIndicator || !this.currentDrop) {
695
- I(this.rowIndicator);
696
- return;
697
- }
698
- const t = this.root.querySelector("me-editor-canvas"), o = t?.shadowRoot?.querySelector(".canvas-body");
699
- if (!o) return;
700
- const n = Array.from(t.shadowRoot.querySelectorAll("me-row-renderer"));
701
- tt(this.rowIndicator, o, n, this.currentDrop.rowIndex ?? 0, "0");
702
- }
703
- hideAllIndicators() {
704
- I(this.contentIndicator), I(this.rowIndicator);
705
- }
706
- reset() {
707
- this.currentDrop = null, L.reset();
708
- }
709
- }
710
- function l(e, t, o = "") {
711
- const n = e[t];
712
- return typeof n == "string" && n !== "" ? n : typeof n == "number" ? String(n) : o;
713
- }
714
- function ot(e) {
715
- return typeof e == "string" ? e : e && typeof e == "object" && "url" in e && e.url || "";
716
- }
717
- function nt(e) {
718
- if (e && typeof e == "object") {
719
- const t = e;
720
- return {
721
- width: typeof t.width == "number" ? t.width : void 0,
722
- maxWidth: typeof t.maxWidth == "string" ? t.maxWidth : void 0
723
- };
724
- }
725
- return {};
726
- }
727
- function _t(e) {
728
- const t = e.action;
729
- if (t && typeof t == "object") {
730
- const o = t.values;
731
- return {
732
- href: o?.href || "",
733
- target: o?.target || "_blank"
734
- };
735
- }
736
- return {
737
- href: l(e, "href"),
738
- target: l(e, "target", "_blank")
739
- };
740
- }
741
- function it(e, t = "") {
742
- const o = e.text;
743
- if (typeof o == "string" && o !== "") return o;
744
- const n = e.textJson;
745
- if (typeof n == "string")
746
- try {
747
- const i = JSON.parse(n), s = [], r = (a) => {
748
- typeof a.text == "string" && s.push(a.text), a.type === "linebreak" && s.push("<br/>");
749
- const d = a.children;
750
- d && d.forEach(r);
751
- };
752
- if (r(i.root || i), s.length > 0) return s.join("");
753
- } catch {
754
- }
755
- return t;
756
- }
757
- function Y(e, t = "arial,helvetica,sans-serif") {
758
- const o = e.fontFamily;
759
- if (typeof o == "string" && o !== "") return o;
760
- if (o && typeof o == "object") {
761
- const n = o, i = n.value || t, s = n.url;
762
- if (s && typeof document < "u") {
763
- const r = `emabuild-font-${i.replace(/[^a-z]/gi, "")}`;
764
- if (!document.getElementById(r)) {
765
- const a = document.createElement("link");
766
- a.id = r, a.rel = "stylesheet", a.href = s, document.head.appendChild(a);
767
- }
768
- }
769
- return i;
770
- }
771
- return t;
772
- }
773
- function st(e) {
774
- const t = e.buttonColors;
775
- if (t && typeof t == "object") {
776
- const o = t;
777
- return {
778
- bg: o.backgroundColor || l(e, "backgroundColor", "#3b82f6"),
779
- color: o.color || l(e, "textColor", "#ffffff")
780
- };
781
- }
782
- return {
783
- bg: l(e, "backgroundColor", "#3b82f6"),
784
- color: l(e, "textColor", "#ffffff")
785
- };
786
- }
787
- function ve(e, t) {
788
- if (typeof e != "string") return t;
789
- try {
790
- return JSON.parse(e);
791
- } catch {
792
- return t;
793
- }
794
- }
795
- function z(e, t) {
796
- const { padding: o, align: n = "left", extraTdStyle: i = "" } = t;
797
- return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
798
- <tbody><tr><td style="${`padding:${o};font-family:arial,helvetica,sans-serif;${i}`}" align="${n}">
799
- ${e}
800
- </td></tr></tbody>
801
- </table>`;
802
- }
803
- function St(e, t, o) {
804
- const { bgColor: n, textColor: i, fontSize: s, fontWeight: r, borderRadius: a } = o, d = parseInt(a) || 0;
805
- if (d <= 0) return "";
806
- const c = Math.round(d / 20 * 100);
807
- return `<!--[if mso]>
808
- <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${t}" style="height:auto;v-text-anchor:middle;width:auto;" arcsize="${c}%" stroke="f" fillcolor="${n}">
809
- <w:anchorlock/>
810
- <center style="color:${i};font-family:arial,helvetica,sans-serif;font-size:${s};font-weight:${r};">${e}</center>
811
- </v:roundrect>
812
- <![endif]-->`;
813
- }
814
- const Dt = {
815
- name: "text",
816
- label: "Text",
817
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/></svg>',
818
- supportedDisplayModes: ["email", "web"],
819
- position: 1,
820
- options: {
821
- text: {
822
- title: "Text",
823
- options: {
824
- text: {
825
- label: "Text Content",
826
- defaultValue: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
827
- widget: "rich_text"
828
- }
829
- }
830
- },
831
- style: {
832
- title: "Style",
833
- options: {
834
- color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
835
- backgroundColor: { label: "Background Color", defaultValue: "", widget: "color_picker" },
836
- textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
837
- lineHeight: {
838
- label: "Line Height",
839
- defaultValue: "140%",
840
- widget: "dropdown",
841
- widgetParams: { options: [
842
- { label: "100%", value: "100%" },
843
- { label: "120%", value: "120%" },
844
- { label: "140%", value: "140%" },
845
- { label: "160%", value: "160%" },
846
- { label: "180%", value: "180%" },
847
- { label: "200%", value: "200%" }
848
- ] }
849
- }
850
- }
851
- },
852
- spacing: {
853
- title: "Spacing",
854
- options: {
855
- containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
856
- }
857
- },
858
- general: {
859
- title: "General",
860
- options: {
861
- anchor: { label: "Anchor", defaultValue: "", widget: "text" },
862
- hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
863
- hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
864
- }
865
- }
866
- },
867
- defaultValues: {
868
- text: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
869
- color: "#000000",
870
- backgroundColor: "",
871
- lineHeight: "140%",
872
- containerPadding: "10px",
873
- textAlign: "left"
874
- },
875
- renderer: {
876
- renderEditor(e) {
877
- const t = l(e, "backgroundColor", "transparent"), o = l(e, "color", "inherit"), n = l(e, "lineHeight", "140%"), i = l(e, "textAlign", "left"), s = Y(e), r = l(e, "text");
878
- return p`
879
- <div style="background-color:${t};color:${o};line-height:${n};text-align:${i};font-family:${s};">
880
- ${U(r)}
881
- </div>
882
- `;
883
- },
884
- renderHtml(e) {
885
- const t = l(e, "containerPadding", "10px"), o = l(e, "backgroundColor"), n = l(e, "color", "#000000"), i = l(e, "lineHeight", "140%"), s = l(e, "textAlign", "left"), r = l(e, "text"), a = o ? `background-color:${o};` : "", d = `<div style="font-size:14px;color:${n};line-height:${i};text-align:${s};">${r}</div>`;
886
- return z(d, { padding: t, extraTdStyle: a });
887
- }
888
- }
889
- };
890
- const Rt = {
891
- name: "heading",
892
- label: "Heading",
893
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 12h12"/><path d="M6 4v16"/><path d="M18 4v16"/></svg>',
894
- supportedDisplayModes: ["email", "web"],
895
- position: 2,
896
- options: {
897
- text: {
898
- title: "Heading",
899
- options: {
900
- text: { label: "Text", defaultValue: "Heading", widget: "text" },
901
- headingType: {
902
- label: "Heading Type",
903
- defaultValue: "h1",
904
- widget: "dropdown",
905
- widgetParams: { options: [
906
- { label: "H1", value: "h1" },
907
- { label: "H2", value: "h2" },
908
- { label: "H3", value: "h3" },
909
- { label: "H4", value: "h4" }
910
- ] }
911
- }
912
- }
913
- },
914
- style: {
915
- title: "Style",
916
- options: {
917
- fontSize: {
918
- label: "Font Size",
919
- defaultValue: "22px",
920
- widget: "dropdown",
921
- widgetParams: { options: [
922
- { label: "12px", value: "12px" },
923
- { label: "14px", value: "14px" },
924
- { label: "16px", value: "16px" },
925
- { label: "18px", value: "18px" },
926
- { label: "20px", value: "20px" },
927
- { label: "22px", value: "22px" },
928
- { label: "26px", value: "26px" },
929
- { label: "30px", value: "30px" },
930
- { label: "36px", value: "36px" },
931
- { label: "48px", value: "48px" },
932
- { label: "60px", value: "60px" }
933
- ] }
934
- },
935
- color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
936
- textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
937
- fontWeight: {
938
- label: "Font Weight",
939
- defaultValue: "700",
940
- widget: "dropdown",
941
- widgetParams: { options: [
942
- { label: "Normal", value: "400" },
943
- { label: "Medium", value: "500" },
944
- { label: "Semi Bold", value: "600" },
945
- { label: "Bold", value: "700" },
946
- { label: "Extra Bold", value: "800" }
947
- ] }
948
- },
949
- lineHeight: {
950
- label: "Line Height",
951
- defaultValue: "140%",
952
- widget: "dropdown",
953
- widgetParams: { options: [
954
- { label: "100%", value: "100%" },
955
- { label: "120%", value: "120%" },
956
- { label: "140%", value: "140%" },
957
- { label: "160%", value: "160%" },
958
- { label: "180%", value: "180%" },
959
- { label: "200%", value: "200%" }
960
- ] }
961
- },
962
- letterSpacing: {
963
- label: "Letter Spacing",
964
- defaultValue: "0px",
965
- widget: "number_unit",
966
- widgetParams: { unit: "px", min: -5, max: 50, step: 0.1 }
967
- }
968
- }
969
- },
970
- spacing: {
971
- title: "Spacing",
972
- options: {
973
- containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
974
- }
975
- },
976
- general: {
977
- title: "General",
978
- options: {
979
- anchor: { label: "Anchor", defaultValue: "", widget: "text" },
980
- hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
981
- hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
982
- }
983
- }
984
- },
985
- defaultValues: {
986
- text: "Heading",
987
- headingType: "h1",
988
- fontSize: "22px",
989
- color: "#000000",
990
- textAlign: "left",
991
- fontWeight: "700",
992
- lineHeight: "140%",
993
- letterSpacing: "0px",
994
- containerPadding: "10px"
995
- },
996
- renderer: {
997
- renderEditor(e) {
998
- const t = {
999
- padding: l(e, "containerPadding", "10px"),
1000
- fontSize: l(e, "fontSize", "22px"),
1001
- color: l(e, "color", "#000000"),
1002
- textAlign: l(e, "textAlign", "left"),
1003
- fontWeight: l(e, "fontWeight", "700"),
1004
- lineHeight: l(e, "lineHeight", "140%"),
1005
- letterSpacing: l(e, "letterSpacing", "normal"),
1006
- fontFamily: Y(e)
1007
- }, o = it(e, "Heading");
1008
- return p`<div style=${ut(t)}>${U(o)}</div>`;
1009
- },
1010
- renderHtml(e) {
1011
- const t = l(e, "containerPadding", "10px"), o = l(e, "fontSize", "22px"), n = l(e, "color", "#000000"), i = l(e, "textAlign", "left"), s = l(e, "fontWeight", "700"), r = l(e, "lineHeight", "140%"), a = l(e, "letterSpacing", "normal"), d = Y(e), c = l(e, "headingType", "h1"), h = it(e, "Heading"), g = `<${c} style="margin:0;font-size:${o};color:${n};text-align:${i};font-weight:${s};line-height:${r};letter-spacing:${a};font-family:${d};">${h}</${c}>`;
1012
- return z(g, { padding: t });
1013
- }
1014
- }
1015
- }, Tt = {
1016
- name: "paragraph",
1017
- label: "Paragraph",
1018
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="3" y1="14" x2="17" y2="14"/></svg>',
1019
- supportedDisplayModes: ["email", "web"],
1020
- position: 3,
1021
- options: {
1022
- text: {
1023
- title: "Paragraph",
1024
- options: {
1025
- text: {
1026
- label: "Text",
1027
- defaultValue: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>',
1028
- widget: "rich_text"
1029
- }
1030
- }
1031
- },
1032
- style: {
1033
- title: "Style",
1034
- options: {
1035
- color: { label: "Text Color", defaultValue: "#374151", widget: "color_picker" },
1036
- textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
1037
- lineHeight: { label: "Line Height", defaultValue: "160%", widget: "text" },
1038
- letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
1039
- }
1040
- },
1041
- spacing: {
1042
- title: "Spacing",
1043
- options: {
1044
- containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
1045
- }
1046
- }
1047
- },
1048
- defaultValues: {
1049
- text: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>',
1050
- color: "#374151",
1051
- lineHeight: "160%",
1052
- letterSpacing: "normal",
1053
- textAlign: "left",
1054
- containerPadding: "10px"
1055
- },
1056
- renderer: {
1057
- renderEditor(e) {
1058
- const t = l(e, "color", "#374151"), o = l(e, "lineHeight", "160%"), n = l(e, "textAlign", "left");
1059
- return p`<div style="color:${t};line-height:${o};text-align:${n};">${U(l(e, "text"))}</div>`;
1060
- },
1061
- renderHtml(e) {
1062
- const t = l(e, "containerPadding", "10px"), o = l(e, "color", "#374151"), n = l(e, "lineHeight", "160%"), i = l(e, "textAlign", "left"), s = l(e, "letterSpacing", "normal"), r = `<div style="font-size:14px;color:${o};line-height:${n};text-align:${i};letter-spacing:${s};word-wrap:break-word;">${l(e, "text")}</div>`;
1063
- return z(r, { padding: t });
1064
- }
1065
- }
1066
- }, It = {
1067
- name: "image",
1068
- label: "Image",
1069
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
1070
- supportedDisplayModes: ["email", "web"],
1071
- position: 3,
1072
- options: {
1073
- image: {
1074
- title: "Image",
1075
- options: {
1076
- src: { label: "Image URL", defaultValue: "", widget: "text" },
1077
- alt: { label: "Alt Text", defaultValue: "", widget: "text" },
1078
- href: { label: "Link URL", defaultValue: "", widget: "text" },
1079
- target: { label: "Link Target", defaultValue: "_blank", widget: "text" }
1080
- }
1081
- },
1082
- style: {
1083
- title: "Style",
1084
- options: {
1085
- width: {
1086
- label: "Width",
1087
- defaultValue: "100%",
1088
- widget: "dropdown",
1089
- widgetParams: { options: [
1090
- { label: "Auto", value: "auto" },
1091
- { label: "25%", value: "25%" },
1092
- { label: "50%", value: "50%" },
1093
- { label: "75%", value: "75%" },
1094
- { label: "100%", value: "100%" }
1095
- ] }
1096
- },
1097
- align: { label: "Align", defaultValue: "center", widget: "alignment" },
1098
- borderRadius: { label: "Border Radius", defaultValue: "0px", widget: "text" }
1099
- }
1100
- },
1101
- spacing: {
1102
- title: "Spacing",
1103
- options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1104
- },
1105
- general: {
1106
- title: "General",
1107
- options: {
1108
- anchor: { label: "Anchor", defaultValue: "", widget: "text" },
1109
- hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1110
- hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1111
- }
1112
- }
1113
- },
1114
- defaultValues: {
1115
- src: "https://placehold.co/600x200/e2e8f0/64748b?text=Drop+Image+Here",
1116
- alt: "Image",
1117
- href: "",
1118
- target: "_blank",
1119
- width: "100%",
1120
- maxWidth: "100%",
1121
- align: "center",
1122
- borderRadius: "0px",
1123
- containerPadding: "10px"
1124
- },
1125
- renderer: {
1126
- renderEditor(e) {
1127
- const t = ot(e.src), o = l(e, "alt"), i = nt(e.src).maxWidth || l(e, "width", "100%"), s = l(e, "borderRadius", "0px"), r = l(e, "textAlign", l(e, "align", "center"));
1128
- return t ? p`<div style="text-align:${r};"><img src=${t} alt=${o} style="display:inline-block;max-width:100%;width:${i};border-radius:${s};border:0;" /></div>` : p`<div style="text-align:${r};"><div style="background:#f1f5f9;border:2px dashed #cbd5e1;border-radius:8px;padding:40px 20px;text-align:center;color:#94a3b8;font-size:13px;">No image set. Enter a URL in the property panel.</div></div>`;
1129
- },
1130
- renderHtml(e, t) {
1131
- const o = l(e, "containerPadding", "10px"), n = ot(e.src), i = l(e, "alt"), s = _t(e), r = nt(e.src), a = r.maxWidth || l(e, "width", "100%"), d = l(e, "borderRadius", "0px"), c = l(e, "textAlign", l(e, "align", "center"));
1132
- let h;
1133
- a.includes("%") ? h = Math.round(t.columnWidth * (parseFloat(a) / 100)) : a === "auto" ? h = r.width || t.columnWidth : h = parseInt(a) || t.columnWidth;
1134
- const g = d !== "0px" ? `border-radius:${d};` : "", u = `<img align="${c}" border="0" src="${n}" alt="${i}" title="${i}" style="display:block;border:0;height:auto;width:100%;max-width:${h}px;${g}" width="${h}" />`, f = s.href ? `<a href="${s.href}" target="${s.target}" style="text-decoration:none;">${u}</a>` : u;
1135
- return z(f, { padding: o, align: c });
1136
- }
1137
- }
1138
- }, Mt = {
1139
- name: "button",
1140
- label: "Button",
1141
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="10" rx="2"/><path d="M12 7v10"/><path d="m8 12 4-3 4 3"/></svg>',
1142
- supportedDisplayModes: ["email", "web"],
1143
- position: 4,
1144
- options: {
1145
- button: {
1146
- title: "Button",
1147
- options: {
1148
- text: { label: "Button Text", defaultValue: "Click Me", widget: "text" },
1149
- href: { label: "Link URL", defaultValue: "#", widget: "text" },
1150
- target: { label: "Target", defaultValue: "_blank", widget: "text" }
1151
- }
1152
- },
1153
- style: {
1154
- title: "Style",
1155
- options: {
1156
- backgroundColor: { label: "Button Color", defaultValue: "#3b82f6", widget: "color_picker" },
1157
- textColor: { label: "Text Color", defaultValue: "#ffffff", widget: "color_picker" },
1158
- fontSize: {
1159
- label: "Font Size",
1160
- defaultValue: "14px",
1161
- widget: "dropdown",
1162
- widgetParams: { options: [
1163
- { label: "12px", value: "12px" },
1164
- { label: "13px", value: "13px" },
1165
- { label: "14px", value: "14px" },
1166
- { label: "16px", value: "16px" },
1167
- { label: "18px", value: "18px" },
1168
- { label: "20px", value: "20px" }
1169
- ] }
1170
- },
1171
- fontWeight: {
1172
- label: "Font Weight",
1173
- defaultValue: "700",
1174
- widget: "dropdown",
1175
- widgetParams: { options: [{ label: "Normal", value: "400" }, { label: "Bold", value: "700" }] }
1176
- },
1177
- borderRadius: { label: "Border Radius", defaultValue: "4px", widget: "text" },
1178
- buttonWidth: {
1179
- label: "Width",
1180
- defaultValue: "auto",
1181
- widget: "dropdown",
1182
- widgetParams: { options: [
1183
- { label: "Auto", value: "auto" },
1184
- { label: "100%", value: "100%" },
1185
- { label: "50%", value: "50%" }
1186
- ] }
1187
- },
1188
- textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
1189
- buttonPadding: { label: "Button Padding", defaultValue: "10px 20px", widget: "padding" },
1190
- borderColor: { label: "Border Color", defaultValue: "", widget: "color_picker" },
1191
- borderWidth: { label: "Border Width", defaultValue: "0px", widget: "text" }
1192
- }
1193
- },
1194
- spacing: {
1195
- title: "Spacing",
1196
- options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1197
- },
1198
- general: {
1199
- title: "General",
1200
- options: {
1201
- anchor: { label: "Anchor", defaultValue: "", widget: "text" },
1202
- hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1203
- hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1204
- }
1205
- }
1206
- },
1207
- defaultValues: {
1208
- text: "Click Me",
1209
- href: "#",
1210
- target: "_blank",
1211
- backgroundColor: "#3b82f6",
1212
- textColor: "#ffffff",
1213
- fontSize: "14px",
1214
- fontWeight: "700",
1215
- borderRadius: "4px",
1216
- buttonWidth: "auto",
1217
- textAlign: "center",
1218
- buttonPadding: "10px 20px",
1219
- borderColor: "",
1220
- borderWidth: "0px",
1221
- containerPadding: "10px"
1222
- },
1223
- renderer: {
1224
- renderEditor(e) {
1225
- const { bg: t, color: o } = st(e), n = l(e, "fontSize", "14px"), i = l(e, "fontWeight", "700"), s = l(e, "borderRadius", "4px"), r = l(e, "buttonPadding", l(e, "padding", "10px 20px")), a = l(e, "text", "Click Me"), d = l(e, "textAlign", "center"), c = l(e, "buttonWidth", "auto"), h = l(e, "borderWidth", "0px"), g = l(e, "borderColor", t), u = h !== "0px" ? `border:${h} solid ${g};` : "border:none;", f = c === "auto" ? "display:inline-block;" : `display:block;width:${c};`;
1226
- return p`
1227
- <div style="text-align:${d};">
1228
- <a style="${f}background-color:${t};color:${o};font-size:${n};font-weight:${i};border-radius:${s};padding:${r};text-decoration:none;text-align:center;${u}cursor:pointer;font-family:arial,helvetica,sans-serif;">${a}</a>
1229
- </div>
1230
- `;
1231
- },
1232
- renderHtml(e) {
1233
- const t = l(e, "containerPadding", "10px"), { bg: o, color: n } = st(e), i = l(e, "fontSize", "14px"), s = l(e, "fontWeight", "700"), r = l(e, "borderRadius", "4px"), a = l(e, "buttonPadding", l(e, "padding", "10px 20px")), d = l(e, "text", "Click Me"), c = l(e, "textAlign", "center"), h = l(e, "href", "#"), g = l(e, "target", "_blank"), u = l(e, "borderWidth", "0px"), f = l(e, "borderColor", o), w = u !== "0px" ? `border:${u} solid ${f};` : "border:none;", x = St(d, h, { bgColor: o, textColor: n, fontSize: i, fontWeight: s, borderRadius: r }), m = x ? `${x}
1234
- <!--[if !mso]><!-->` : "<!--[if !mso]><!-->", v = `<div align="${c}">
1235
- ${m}
1236
- <a href="${h}" target="${g}" style="display:inline-block;text-decoration:none;text-align:center;color:${n};background-color:${o};border-radius:${r};font-size:${i};font-weight:${s};padding:${a};font-family:arial,helvetica,sans-serif;${w}mso-border-alt:none;"><span style="line-height:120%;">${d}</span></a>
1237
- <!--<![endif]-->
1238
- </div>`;
1239
- return z(v, { padding: t, align: c });
1240
- }
1241
- }
1242
- }, Pt = {
1243
- name: "divider",
1244
- label: "Divider",
1245
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="2" y1="12" x2="22" y2="12"/></svg>',
1246
- supportedDisplayModes: ["email", "web"],
1247
- position: 5,
1248
- options: {
1249
- style: {
1250
- title: "Style",
1251
- options: {
1252
- borderTopWidth: { label: "Width", defaultValue: "1px", widget: "text" },
1253
- borderTopStyle: {
1254
- label: "Style",
1255
- defaultValue: "solid",
1256
- widget: "dropdown",
1257
- widgetParams: { options: [
1258
- { label: "Solid", value: "solid" },
1259
- { label: "Dashed", value: "dashed" },
1260
- { label: "Dotted", value: "dotted" },
1261
- { label: "Double", value: "double" }
1262
- ] }
1263
- },
1264
- borderTopColor: { label: "Color", defaultValue: "#cccccc", widget: "color_picker" },
1265
- width: { label: "Line Width", defaultValue: "100%", widget: "text" }
1266
- }
1267
- },
1268
- spacing: {
1269
- title: "Spacing",
1270
- options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1271
- },
1272
- general: {
1273
- title: "General",
1274
- options: {
1275
- hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1276
- hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1277
- }
1278
- }
1279
- },
1280
- defaultValues: {
1281
- borderTopWidth: "1px",
1282
- borderTopStyle: "solid",
1283
- borderTopColor: "#cccccc",
1284
- width: "100%",
1285
- containerPadding: "10px"
1286
- },
1287
- renderer: {
1288
- renderEditor(e) {
1289
- const t = l(e, "containerPadding", "10px"), o = l(e, "width", "100%"), n = `${l(e, "borderTopWidth", "1px")} ${l(e, "borderTopStyle", "solid")} ${l(e, "borderTopColor", "#cccccc")}`;
1290
- return p`<div><div style="border-top:${n};width:${o};margin:0 auto;"></div></div>`;
1291
- },
1292
- renderHtml(e) {
1293
- const t = l(e, "containerPadding", "10px"), o = l(e, "width", "100%"), n = `${l(e, "borderTopWidth", "1px")} ${l(e, "borderTopStyle", "solid")} ${l(e, "borderTopColor", "#cccccc")}`, i = `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${o}" style="border-collapse:collapse;border-top:${n};"><tbody><tr><td style="font-size:0;line-height:0;">&nbsp;</td></tr></tbody></table>`;
1294
- return z(i, { padding: t, align: "center" });
1295
- }
1296
- }
1297
- }, Et = [
1298
- Dt,
1299
- Rt,
1300
- Tt,
1301
- It,
1302
- Mt,
1303
- Pt
1304
- ], rt = [
1305
- {
1306
- meta: { name: "html", label: "HTML", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>', position: 6 },
1307
- loader: () => import("./html-tool-DMtmrF3n.js").then((e) => e.htmlTool)
1308
- },
1309
- {
1310
- meta: { name: "social", label: "Social", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>', position: 8 },
1311
- loader: () => import("./social-tool-CRY3-_sU.js").then((e) => e.socialTool)
1312
- },
1313
- {
1314
- meta: { name: "menu", label: "Menu", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>', position: 9 },
1315
- loader: () => import("./menu-tool-CJcQdpcP.js").then((e) => e.menuTool)
1316
- },
1317
- {
1318
- meta: { name: "video", label: "Video", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>', position: 10 },
1319
- loader: () => import("./video-tool-g1fIoCWW.js").then((e) => e.videoTool)
1320
- },
1321
- {
1322
- meta: { name: "timer", label: "Timer", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>', position: 11 },
1323
- loader: () => import("./timer-tool-BVE1shO1.js").then((e) => e.timerTool)
1324
- },
1325
- {
1326
- meta: { name: "table", label: "Table", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/><path d="M9 3v18"/><path d="M15 3v18"/></svg>', position: 12 },
1327
- loader: () => import("./table-tool-DeIQci5z.js").then((e) => e.tableTool)
1328
- },
1329
- {
1330
- meta: { name: "form", label: "Form", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 7h10"/><path d="M7 12h10"/><path d="M7 17h6"/></svg>', position: 13 },
1331
- loader: () => import("./form-tool-DdFDrS3b.js").then((e) => e.formTool)
1332
- }
1333
- ];
1334
- function zt(e, t, o) {
1335
- const n = o.backgroundColor || "#e7e7e7", i = o.contentWidth || "600px", s = o.fontFamily?.value || "arial,helvetica,sans-serif", r = o.textColor || "#000000", a = o.preheaderText || "" || "&zwnj;", d = `<div style="display:none;font-size:1px;color:${n};line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">${a}${"&zwnj;&nbsp;".repeat(80)}</div>`;
1336
- return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1337
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
1338
- <head>
1339
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1340
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1341
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
1342
- <meta name="x-apple-disable-message-reformatting">
1343
- <meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
1344
- <meta name="color-scheme" content="light dark">
1345
- <meta name="supported-color-schemes" content="light dark">
1346
- <title></title>
1347
- <!--[if mso]>
1348
- <noscript><xml>
1349
- <o:OfficeDocumentSettings>
1350
- <o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch>
1351
- </o:OfficeDocumentSettings>
1352
- </xml></noscript>
1353
- <style type="text/css">
1354
- table, td, th { font-family: ${s} !important; }
1355
- </style>
1356
- <![endif]-->
1357
- <!--[if !mso]><!-->
1358
- <style type="text/css">
1359
- ${t}
1360
- </style>
1361
- <!--<![endif]-->
1362
- <style type="text/css">
1363
- body { margin: 0; padding: 0; word-break: normal; }
1364
- table, tr, td { vertical-align: top; border-collapse: collapse; }
1365
- p { margin: 0; }
1366
- a[x-apple-data-detectors='true'] { color: inherit !important; text-decoration: none !important; }
1367
- </style>
1368
- </head>
1369
- <body class="clean-body u_body" style="margin:0;padding:0;-webkit-text-size-adjust:100%;background-color:${n};color:${r};">
1370
- ${d}
1371
- <table id="u_body" role="presentation" style="border-collapse:collapse;border-spacing:0;margin:0 auto;background-color:${n};width:100%;" cellpadding="0" cellspacing="0" border="0">
1372
- <tbody>
1373
- <tr>
1374
- <td style="vertical-align:top;">
1375
- <!--[if (mso)|(IE)]><table width="${parseInt(i)}" align="center" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
1376
- ${e}
1377
- <!--[if (mso)|(IE)]></td></tr></table><![endif]-->
1378
- </td>
1379
- </tr>
1380
- </tbody>
1381
- </table>
1382
- </body>
1383
- </html>`;
1384
- }
1385
- function Lt(e, t, o) {
1386
- const n = parseInt(t.contentWidth || "600"), i = e.values.backgroundColor || "", s = e.values.columnsBackgroundColor || "", r = e.values.padding || "0px", a = e.cells.reduce((m, y) => m + y, 0), d = i ? `background-color:${i};` : "", c = e.values.backgroundImage;
1387
- let h = "";
1388
- if (c?.url) {
1389
- const m = c.repeat === !0 || c.repeat === "repeat" ? "repeat" : "no-repeat", y = c.cover === !0 ? "cover" : c.fullWidth === !0 ? "100% auto" : "auto", v = c.center !== !1 ? "center top" : "left top";
1390
- h = `background-image:url('${c.url}');background-repeat:${m};background-position:${v};background-size:${y};`;
1391
- }
1392
- const g = e.columns.length > 1, u = e.columns.map((m, y) => {
1393
- const v = Math.round(e.cells[y] / a * n);
1394
- return { colHtml: Vt(m, v, s, t, o), colWidthPx: v };
1395
- });
1396
- let f;
1397
- if (g) {
1398
- const m = u.map(
1399
- ({ colHtml: y, colWidthPx: v }) => `<!--[if (mso)|(IE)]><td align="center" width="${v}" style="width:${v}px;padding:0px;" valign="top"><![endif]-->${y}<!--[if (mso)|(IE)]></td><![endif]-->`
1400
- );
1401
- f = `<!--[if (mso)|(IE)]><table role="presentation" width="${n}" cellpadding="0" cellspacing="0" border="0"><tr><![endif]-->${m.join("")}<!--[if (mso)|(IE)]></tr></table><![endif]-->`;
1402
- } else
1403
- f = u.map(({ colHtml: m }) => m).join("");
1404
- const w = e.values.hideDesktop ? " u_hide_desktop" : "", x = e.values.hideMobile ? " u_hide_mobile" : "";
1405
- return `<div class="u_row${w}${x}" style="padding:${r};${d}${h}">
1406
- <div style="margin:0 auto;max-width:${n}px;${g ? "font-size:0;" : ""}text-align:center;">${f}</div>
1407
- </div>`;
1408
- }
1409
- function Vt(e, t, o, n, i) {
1410
- const s = e.values.backgroundColor || o || "", r = e.values.padding || "0px", a = e.values.borderRadius || "0px", d = s ? `background-color:${s};` : "", c = e.contents.map((h) => {
1411
- const g = i.get(h.type);
1412
- if (!g) return `<!-- unknown tool: ${h.type} -->`;
1413
- const u = {
1414
- columnWidth: t,
1415
- displayMode: "email",
1416
- contentWidth: parseInt(n.contentWidth || "600"),
1417
- bodyValues: n
1418
- };
1419
- let f = g(h.values, u);
1420
- const w = !!h.values.hideDesktop, x = !!h.values.hideMobile;
1421
- return (w || x) && (f = `<div class="${[w && "u_hide_desktop", x && "u_hide_mobile"].filter(Boolean).join(" ")}">${f}</div>`), f;
1422
- }).join(`
1423
- `);
1424
- return `<div class="u_column" style="display:inline-block;vertical-align:top;width:${t}px;max-width:${t}px;font-size:14px;text-align:left;">
1425
- <div style="width:100%;${d}${a !== "0px" ? `border-radius:${a};` : ""}">
1426
- <div style="padding:${r};">
1427
- ${c || "&nbsp;"}
1428
- </div>
1429
- </div>
1430
- </div>`;
1431
- }
1432
- function At(e) {
1433
- return `
1434
- @media only screen and (min-width: ${e + 20}px) {
1435
- .u_row .u_column { display: inline-block !important; }
1436
- }
1437
-
1438
- @media only screen and (max-width: ${e + 20}px) {
1439
- .u_row .u_column {
1440
- display: block !important;
1441
- width: 100% !important;
1442
- max-width: 100% !important;
1443
- }
1444
- .u_row {
1445
- width: 100% !important;
1446
- }
1447
- }
1448
-
1449
- @media only screen and (max-width: 620px) {
1450
- .u_row-container {
1451
- max-width: 100% !important;
1452
- padding-left: 0 !important;
1453
- padding-right: 0 !important;
1454
- }
1455
- }
1456
-
1457
- @media (prefers-color-scheme: dark) {
1458
- /* Dark mode overrides — add per-client rules as needed */
1459
- }
1460
-
1461
- /* Outlook dark mode */
1462
- [data-ogsb] body,
1463
- [data-ogsb] table,
1464
- [data-ogsb] td {
1465
- /* Preserve original colors */
1466
- }
1467
-
1468
- .u_hide_desktop { display: block !important; }
1469
- .u_hide_mobile { display: block !important; }
1470
-
1471
- @media only screen and (max-width: ${e + 20}px) {
1472
- .u_hide_desktop { display: block !important; }
1473
- .u_hide_mobile { display: none !important; }
1474
- }
1475
-
1476
- @media only screen and (min-width: ${e + 21}px) {
1477
- .u_hide_desktop { display: none !important; }
1478
- .u_hide_mobile { display: block !important; }
1479
- }`;
1480
- }
1481
- const Bt = [
1482
- "box-sizing",
1483
- "overflow-wrap",
1484
- "word-break",
1485
- "word-wrap",
1486
- "outline",
1487
- "cursor",
1488
- "transition",
1489
- "animation",
1490
- "transform",
1491
- "z-index",
1492
- "display:\\s*flex",
1493
- "display:\\s*grid",
1494
- "gap"
1495
- ], Ot = new RegExp(
1496
- `(?:;\\s*|^\\s*)(${Bt.join("|")})\\s*:[^;]*;?`,
1497
- "gi"
1498
- ), Ht = /var\(--[^)]*\)/gi;
1499
- function jt(e) {
1500
- return e.replace(/style="([^"]*)"/gi, (t, o) => {
1501
- let n = o;
1502
- return n = n.replace(Ot, ""), n = n.replace(Ht, "inherit"), n = n.replace(/;\s*;/g, ";").replace(/^\s*;\s*/, "").replace(/;\s*$/, "").trim(), n ? `style="${n}"` : "";
1503
- });
1504
- }
1505
- function Wt(e, t, o) {
1506
- const n = e.body.values, i = parseInt(n.contentWidth || "600"), s = e.body.rows.map((u) => Lt(u, n, t)).join(`
1507
- `), r = At(i), a = jt(s);
1508
- let d = zt(a, r, n);
1509
- if (o?.mergeTags)
1510
- for (const [u, f] of Object.entries(o.mergeTags))
1511
- d = d.replaceAll(`{{${u}}}`, f);
1512
- const c = d.match(/<body[^>]*>([\s\S]*)<\/body>/i), h = d.match(/<style[^>]*>([\s\S]*?)<\/style>/gi), g = [];
1513
- return n.fontFamily?.url && g.push(n.fontFamily.url), {
1514
- design: structuredClone(e),
1515
- html: d,
1516
- chunks: {
1517
- body: c?.[1] ?? s,
1518
- css: h?.map((u) => u.replace(/<\/?style[^>]*>/gi, "")).join(`
1519
- `) ?? r,
1520
- fonts: g,
1521
- js: ""
1522
- }
1523
- };
1524
- }
1525
- class T {
1526
- /**
1527
- * @param host - The Lit component that owns this controller
1528
- * @param channels - Which store channels to subscribe to
1529
- */
1530
- constructor(t, o) {
1531
- this.store = null, this.host = t, this.channels = o, t.addController(this);
1532
- }
1533
- /** Set or change the store reference. Re-subscribes automatically. */
1534
- setStore(t) {
1535
- this.store !== t && (this.unsub?.(), this.store = t, this.subscribe());
1536
- }
1537
- hostConnected() {
1538
- this.subscribe();
1539
- }
1540
- hostDisconnected() {
1541
- this.unsub?.(), this.unsub = void 0;
1542
- }
1543
- subscribe() {
1544
- this.store && (this.unsub = this.store.subscribeChannels(this.channels, () => {
1545
- this.host.requestUpdate();
1546
- }));
1547
- }
1548
- }
1549
- var Ut = Object.defineProperty, Ft = Object.getOwnPropertyDescriptor, dt = (e, t, o, n) => {
1550
- for (var i = n > 1 ? void 0 : n ? Ft(t, o) : t, s = e.length - 1, r; s >= 0; s--)
1551
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
1552
- return n && i && Ut(t, o, i), i;
1553
- };
1554
- let V = class extends $ {
1555
- constructor() {
1556
- super(...arguments), this.visible = !1;
1557
- }
1558
- exec(e, t) {
1559
- document.execCommand(e, !1, t), this.requestUpdate();
1560
- }
1561
- isActive(e) {
1562
- try {
1563
- return document.queryCommandState(e);
1564
- } catch {
1565
- return !1;
1566
- }
1567
- }
1568
- /** Position the toolbar above a given element */
1569
- positionAbove(e) {
1570
- const t = e.getBoundingClientRect();
1571
- this.style.left = `${t.left + t.width / 2}px`, this.style.top = `${t.top - 8}px`, this.style.transform = "translate(-50%, -100%)", this.classList.add("visible");
1572
- }
1573
- hide() {
1574
- this.classList.remove("visible");
1575
- }
1576
- handleLink() {
1577
- if (this.isActive("createLink"))
1578
- this.exec("unlink");
1579
- else {
1580
- const e = prompt("Enter URL:");
1581
- e && this.exec("createLink", e);
1582
- }
1583
- }
1584
- render() {
1585
- return p`
1586
- <div class="toolbar" @mousedown=${(e) => e.preventDefault()}>
1587
- <button class="btn ${this.isActive("bold") ? "active" : ""}"
1588
- @click=${() => this.exec("bold")} title="Bold (Ctrl+B)">
1589
- <strong>B</strong>
1590
- </button>
1591
- <button class="btn ${this.isActive("italic") ? "active" : ""}"
1592
- @click=${() => this.exec("italic")} title="Italic (Ctrl+I)">
1593
- <em>I</em>
1594
- </button>
1595
- <button class="btn ${this.isActive("underline") ? "active" : ""}"
1596
- @click=${() => this.exec("underline")} title="Underline (Ctrl+U)">
1597
- <u>U</u>
1598
- </button>
1599
- <button class="btn ${this.isActive("strikeThrough") ? "active" : ""}"
1600
- @click=${() => this.exec("strikeThrough")} title="Strikethrough">
1601
- <s>S</s>
1602
- </button>
1603
- <div class="separator"></div>
1604
- <button class="btn ${this.isActive("createLink") ? "active" : ""}"
1605
- @click=${this.handleLink} title="Link">
1606
- <svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
1607
- </button>
1608
- <div class="separator"></div>
1609
- <button class="btn" @click=${() => this.exec("justifyLeft")} title="Align Left">
1610
- <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="18" y2="18"/></svg>
1611
- </button>
1612
- <button class="btn" @click=${() => this.exec("justifyCenter")} title="Align Center">
1613
- <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>
1614
- </button>
1615
- <button class="btn" @click=${() => this.exec("justifyRight")} title="Align Right">
1616
- <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="9" y1="12" x2="21" y2="12"/><line x1="6" y1="18" x2="21" y2="18"/></svg>
1617
- </button>
1618
- </div>
1619
- `;
1620
- }
1621
- };
1622
- V.styles = k`
1623
- :host {
1624
- position: fixed;
1625
- z-index: 10000;
1626
- display: none;
1627
- }
1628
- :host(.visible) {
1629
- display: block;
1630
- animation: fadeIn 0.15s ease;
1631
- }
1632
- @keyframes fadeIn {
1633
- from { opacity: 0; transform: translateY(4px); }
1634
- to { opacity: 1; transform: translateY(0); }
1635
- }
1636
- .toolbar {
1637
- display: flex;
1638
- align-items: center;
1639
- gap: 2px;
1640
- padding: 4px 6px;
1641
- background: #1f2937;
1642
- border-radius: 8px;
1643
- box-shadow: 0 4px 16px rgba(0,0,0,0.2);
1644
- }
1645
- .btn {
1646
- width: 32px;
1647
- height: 32px;
1648
- display: flex;
1649
- align-items: center;
1650
- justify-content: center;
1651
- background: none;
1652
- border: none;
1653
- color: #d1d5db;
1654
- cursor: pointer;
1655
- border-radius: 4px;
1656
- font-size: 14px;
1657
- font-weight: 600;
1658
- transition: all 0.1s ease;
1659
- padding: 0;
1660
- }
1661
- .btn:hover { background: rgba(255,255,255,0.1); color: white; }
1662
- .btn.active { background: rgba(59,130,246,0.3); color: #93c5fd; }
1663
- .separator {
1664
- width: 1px;
1665
- height: 20px;
1666
- background: #374151;
1667
- margin: 0 2px;
1668
- }
1669
- .btn svg {
1670
- width: 16px;
1671
- height: 16px;
1672
- stroke: currentColor;
1673
- fill: none;
1674
- stroke-width: 2;
1675
- stroke-linecap: round;
1676
- stroke-linejoin: round;
1677
- }
1678
- `;
1679
- dt([
1680
- b({ type: Boolean })
1681
- ], V.prototype, "visible", 2);
1682
- V = dt([
1683
- C("me-inline-toolbar")
1684
- ], V);
1685
- var Nt = Object.defineProperty, qt = Object.getOwnPropertyDescriptor, O = (e, t, o, n) => {
1686
- for (var i = n > 1 ? void 0 : n ? qt(t, o) : t, s = e.length - 1, r; s >= 0; s--)
1687
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
1688
- return n && i && Nt(t, o, i), i;
1689
- };
1690
- const lt = /* @__PURE__ */ new Set(["text", "heading", "paragraph"]);
1691
- function at(e) {
1692
- return e === "html" ? "html" : "text";
1693
- }
1694
- let S = class extends $ {
1695
- constructor() {
1696
- super(...arguments), this.storeCtrl = new T(this, ["design", "selection", "hover", "viewMode"]), this.editing = !1, this.toolbar = null, this.editableEl = null, this.saveDebounceTimer = null, this.handleInlineInput = () => {
1697
- this.showToolbar();
1698
- }, this.handleInlineBlur = (e) => {
1699
- const t = e.relatedTarget;
1700
- t && (t.closest("me-inline-toolbar") || this.toolbar?.contains(t)) || setTimeout(() => {
1701
- document.activeElement !== this.editableEl && !this.toolbar?.matches(":hover") && this.stopEditing();
1702
- }, 150);
1703
- }, this.handleOverlayBlur = (e) => {
1704
- const t = e.relatedTarget;
1705
- t && (t.closest("me-inline-toolbar") || this.toolbar?.contains(t)) || setTimeout(() => {
1706
- document.activeElement !== this.editableEl && !this.toolbar?.matches(":hover") && this.stopEditing();
1707
- }, 200);
1708
- }, this.handleInlineKeydown = (e) => {
1709
- e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), this.stopEditing());
1710
- };
1711
- }
1712
- set store(e) {
1713
- this.storeCtrl.setStore(e);
1714
- }
1715
- get store() {
1716
- return this.storeCtrl.store;
1717
- }
1718
- // ── Event handlers ───────────────────────────────────────
1719
- handleClick(e) {
1720
- e.stopPropagation(), this.editing || this.store.select(this.content.id);
1721
- }
1722
- handleDblClick(e) {
1723
- e.stopPropagation(), lt.has(this.content.type) && this.startEditing();
1724
- }
1725
- handleMouseEnter() {
1726
- this.store.hover(this.content.id);
1727
- }
1728
- handleMouseLeave() {
1729
- this.store.hover(null);
1730
- }
1731
- handleDelete(e) {
1732
- e.stopPropagation(), this.store.removeContent(this.content.id);
1733
- }
1734
- handleDuplicate(e) {
1735
- e.stopPropagation(), this.store.duplicateContent(this.content.id);
1736
- }
1737
- /** Called from the drag handle only (not the whole block) */
1738
- handleDragStart(e) {
1739
- e.stopPropagation(), e.dataTransfer.setData("application/maileditor-content", this.content.id), e.dataTransfer.effectAllowed = "move", this.style.opacity = "0.4", L.startContentDrag(this.content.id);
1740
- }
1741
- handleDragEnd() {
1742
- this.style.opacity = "1", L.reset();
1743
- }
1744
- // ── Inline editing ───────────────────────────────────────
1745
- startEditing() {
1746
- this.editing = !0, this.classList.add("editing"), this.storeCtrl.hostDisconnected();
1747
- const e = this.getBoundingClientRect(), t = this.getInlineStyles(this.content.values), o = at(this.content.type), n = this.content.values[o] || "", i = document.createElement("div");
1748
- i.contentEditable = "true", i.innerHTML = n, Object.assign(i.style, {
1749
- position: "fixed",
1750
- left: e.left + "px",
1751
- top: e.top + "px",
1752
- width: e.width + "px",
1753
- minHeight: e.height + "px",
1754
- zIndex: "9999",
1755
- background: "white",
1756
- boxShadow: "0 0 0 2px #8b5cf6, 0 4px 16px rgba(0,0,0,0.15)",
1757
- borderRadius: "2px",
1758
- outline: "none",
1759
- boxSizing: "border-box"
1760
- }), t.split(";").forEach((a) => {
1761
- const [d, c] = a.split(":").map((h) => h?.trim());
1762
- if (d && c)
1763
- try {
1764
- i.style.setProperty(d, c);
1765
- } catch {
1766
- }
1767
- }), i.addEventListener("keydown", this.handleInlineKeydown), i.addEventListener("blur", this.handleOverlayBlur), document.body.appendChild(i), this.editableEl = i, i.focus();
1768
- const s = window.getSelection(), r = document.createRange();
1769
- r.selectNodeContents(i), r.collapse(!1), s?.removeAllRanges(), s?.addRange(r), this.showToolbar();
1770
- }
1771
- stopEditing() {
1772
- this.editing && (this.saveInlineContent(), this.editableEl && (this.editableEl.removeEventListener("keydown", this.handleInlineKeydown), this.editableEl.removeEventListener("blur", this.handleOverlayBlur), this.editableEl.remove()), this.editing = !1, this.classList.remove("editing"), this.editableEl = null, this.storeCtrl.hostConnected(), this.requestUpdate(), this.toolbar?.hide());
1773
- }
1774
- saveInlineContent() {
1775
- if (!this.editableEl) return;
1776
- const e = this.editableEl.innerHTML, t = at(this.content.type), o = this.content.values[t];
1777
- e !== o && this.store.updateContentValues(this.content.id, { [t]: e });
1778
- }
1779
- showToolbar() {
1780
- this.toolbar || (this.toolbar = document.createElement("me-inline-toolbar"), document.body.appendChild(this.toolbar)), this.editableEl && this.toolbar.positionAbove(this.editableEl);
1781
- }
1782
- // ── Lifecycle ────────────────────────────────────────────
1783
- connectedCallback() {
1784
- super.connectedCallback();
1785
- }
1786
- disconnectedCallback() {
1787
- super.disconnectedCallback(), this.toolbar?.remove(), this.toolbar = null, this.saveDebounceTimer && clearTimeout(this.saveDebounceTimer);
1788
- }
1789
- // ── Render ───────────────────────────────────────────────
1790
- render() {
1791
- if (!this.store) return p``;
1792
- const e = this.store.selectedId === this.content.id, t = this.store.hoveredId === this.content.id, o = this.store.viewMode, n = !!this.content.values.hideDesktop, i = !!this.content.values.hideMobile, s = o === "desktop" && n || o === "mobile" && i;
1793
- this.classList.toggle("selected", e && !this.editing), this.classList.toggle("hovered", t && !this.editing), this.classList.toggle("hidden-in-view", s), this.removeAttribute("draggable"), this.dataset.contentId = this.content.id, this.editing && !e && this.stopEditing();
1794
- const r = this.toolRegistry.get(this.content.type);
1795
- if (!r && this.toolRegistry.has(this.content.type))
1796
- return this.toolRegistry.ensureLoaded(this.content.type).then(() => this.requestUpdate()), p`<div style="padding:16px;text-align:center;color:#9ca3af;font-size:13px;">Loading ${this.content.type}...</div>`;
1797
- const a = n ? "Hidden on desktop" : i ? "Hidden on mobile" : "", d = lt.has(this.content.type), c = p`
1798
- <button class="drag-handle" draggable="true" title="Drag to move"
1799
- @dragstart=${(u) => this.handleDragStart(u)}
1800
- @dragend=${() => this.handleDragEnd()}
1801
- @mousedown=${(u) => u.stopPropagation()}>
1802
- <svg viewBox="0 0 16 16"><circle cx="5" cy="3" r="1.5"/><circle cx="11" cy="3" r="1.5"/><circle cx="5" cy="8" r="1.5"/><circle cx="11" cy="8" r="1.5"/><circle cx="5" cy="13" r="1.5"/><circle cx="11" cy="13" r="1.5"/></svg>
1803
- </button>
1804
- `, h = r?.renderer.renderEditor(this.content.values, {
1805
- isSelected: e,
1806
- isHovered: t,
1807
- columnWidth: 600,
1808
- displayMode: "email"
1809
- });
1810
- if (this.editing && d)
1811
- return p`${c}<div class="content-wrapper" style="visibility:hidden;">${h}</div>`;
1812
- const g = this.content.values.containerPadding || "0px";
1813
- return p`
1814
- ${s ? p`<div class="hidden-badge">${a}</div>` : ""}
1815
- ${c}
1816
- <div class="action-bar">
1817
- <button class="action-btn" @click=${this.handleDuplicate} title="Duplicate">&#9851;</button>
1818
- <button class="action-btn" @click=${this.handleDelete} title="Delete">&#10005;</button>
1819
- </div>
1820
- <div class="content-wrapper" style="padding:${g};"
1821
- @click=${this.handleClick}
1822
- @dblclick=${this.handleDblClick}
1823
- @mouseenter=${this.handleMouseEnter}
1824
- @mouseleave=${this.handleMouseLeave}>
1825
- ${h ?? p`<div style="padding:10px;color:#999;font-style:italic;">Unknown tool: ${this.content.type}</div>`}
1826
- </div>
1827
- `;
1828
- }
1829
- /** Build inline CSS for the contenteditable area based on content values */
1830
- getInlineStyles(e) {
1831
- const t = [], o = (g, u = "") => {
1832
- const f = e[g];
1833
- return typeof f == "string" ? f : typeof f == "number" ? String(f) : u;
1834
- };
1835
- t.push(`padding:${o("containerPadding", "10px")}`);
1836
- const n = o("backgroundColor");
1837
- n && t.push(`background-color:${n}`);
1838
- const i = o("color");
1839
- i && t.push(`color:${i}`);
1840
- const s = o("fontSize");
1841
- s && t.push(`font-size:${s}`);
1842
- const r = o("fontWeight");
1843
- r && t.push(`font-weight:${r}`);
1844
- const a = o("lineHeight", "140%");
1845
- t.push(`line-height:${a}`);
1846
- const d = o("textAlign");
1847
- d && t.push(`text-align:${d}`);
1848
- const c = o("letterSpacing");
1849
- c && c !== "normal" && t.push(`letter-spacing:${c}`);
1850
- const h = e.fontFamily;
1851
- return typeof h == "string" ? t.push(`font-family:${h}`) : h && typeof h == "object" && t.push(`font-family:${h.value || "inherit"}`), t.push("word-break:break-word", "outline:none", "min-height:1em"), t.join(";");
1852
- }
1853
- };
1854
- S.styles = k`
1855
- :host {
1856
- display: block; position: relative;
1857
- transition: outline 0.15s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
1858
- }
1859
- :host(.selected) { outline: 2px solid #3b82f6; outline-offset: -1px; }
1860
- :host(.hovered:not(.selected)) { outline: 2px dashed #93c5fd; outline-offset: -1px; }
1861
- :host(.editing) { outline: 2px solid #8b5cf6; outline-offset: -1px; }
1862
- :host(.just-dropped) {
1863
- animation: dropIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
1864
- }
1865
- :host(.hidden-in-view) { opacity: 0.3; position: relative; }
1866
- .hidden-badge {
1867
- position: absolute; top: 4px; right: 4px; z-index: 5;
1868
- background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
1869
- padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
1870
- pointer-events: none;
1871
- }
1872
- @keyframes dropIn {
1873
- 0% { opacity: 0; transform: scale(0.92) translateY(-8px); }
1874
- 100% { opacity: 1; transform: scale(1) translateY(0); }
1875
- }
1876
- /* Drag handle — straddles the right border, vertically centered */
1877
- .drag-handle {
1878
- display: none;
1879
- position: absolute;
1880
- top: 50%;
1881
- right: -12px;
1882
- transform: translateY(-50%);
1883
- width: 24px;
1884
- height: 24px;
1885
- background: #3b82f6;
1886
- color: white;
1887
- border: 2px solid white;
1888
- border-radius: 50%;
1889
- cursor: grab;
1890
- z-index: 20;
1891
- align-items: center;
1892
- justify-content: center;
1893
- padding: 0;
1894
- box-shadow: 0 1px 4px rgba(0,0,0,0.2);
1895
- transition: background 0.15s ease, transform 0.15s ease;
1896
- }
1897
- .drag-handle:hover {
1898
- background: #2563eb;
1899
- transform: translateY(-50%) scale(1.15);
1900
- }
1901
- .drag-handle:active { cursor: grabbing; }
1902
- .drag-handle svg {
1903
- width: 12px; height: 12px; fill: currentColor; stroke: none;
1904
- }
1905
- :host(.selected) .drag-handle,
1906
- :host(.hovered) .drag-handle { display: flex; }
1907
- :host(.editing) .drag-handle { display: none; }
1908
- /* Action bar */
1909
- .action-bar {
1910
- display: none; position: absolute; top: -28px; right: 4px;
1911
- background: #3b82f6; border-radius: 4px; padding: 2px; gap: 2px; z-index: 10;
1912
- }
1913
- :host(.selected:not(.editing)) .action-bar { display: flex; }
1914
- .action-btn {
1915
- background: none; border: none; color: white; cursor: pointer;
1916
- padding: 2px 6px; font-size: 12px; line-height: 1; border-radius: 2px;
1917
- }
1918
- .action-btn:hover { background: rgba(255,255,255,0.2); }
1919
- .content-wrapper { position: relative; cursor: default; box-sizing: border-box; }
1920
- /* Reset paragraph margins in user content to match email rendering.
1921
- Override inline margin from external editors (e.g. margin: 1.25em 0px). */
1922
- .content-wrapper p { margin: 0 !important; }
1923
- /* Reset paragraph margins to match email rendering.
1924
- Spacing comes from line-height, <br>, and &nbsp; in the HTML content. */
1925
- .content-wrapper p { margin: 0; }
1926
- .inline-editable {
1927
- outline: none; cursor: text; min-height: 1em;
1928
- }
1929
- .inline-editable:focus { outline: none; }
1930
- `;
1931
- O([
1932
- b({ attribute: !1 })
1933
- ], S.prototype, "content", 2);
1934
- O([
1935
- b({ attribute: !1 })
1936
- ], S.prototype, "store", 1);
1937
- O([
1938
- b({ attribute: !1 })
1939
- ], S.prototype, "toolRegistry", 2);
1940
- O([
1941
- ht()
1942
- ], S.prototype, "editing", 2);
1943
- S = O([
1944
- C("me-content-renderer")
1945
- ], S);
1946
- var Gt = Object.defineProperty, Kt = Object.getOwnPropertyDescriptor, H = (e, t, o, n) => {
1947
- for (var i = n > 1 ? void 0 : n ? Kt(t, o) : t, s = e.length - 1, r; s >= 0; s--)
1948
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
1949
- return n && i && Gt(t, o, i), i;
1950
- };
1951
- let D = class extends $ {
1952
- constructor() {
1953
- super(...arguments), this.storeCtrl = new T(this, ["design"]), this.widthPercent = 100;
1954
- }
1955
- set store(e) {
1956
- this.storeCtrl.setStore(e);
1957
- }
1958
- get store() {
1959
- return this.storeCtrl.store;
1960
- }
1961
- render() {
1962
- const e = this.column.values.padding || "0px", t = this.column.values.backgroundColor || "transparent", o = this.column.contents;
1963
- return this.dataset.columnId = this.column.id, this.style.width = `${this.widthPercent}%`, this.style.padding = e, this.style.backgroundColor = t, this.style.verticalAlign = "top", this.style.boxSizing = "border-box", o.length === 0 ? p`
1964
- <div class="empty-column" data-column-id=${this.column.id}>
1965
- Drag content here
1966
- </div>
1967
- ` : p`
1968
- ${o.map(
1969
- (n) => p`
1970
- <me-content-renderer
1971
- .content=${n}
1972
- .store=${this.store}
1973
- .toolRegistry=${this.toolRegistry}
1974
- ></me-content-renderer>
1975
- `
1976
- )}
1977
- `;
1978
- }
1979
- };
1980
- D.styles = k`
1981
- :host {
1982
- display: block;
1983
- min-height: 40px;
1984
- position: relative;
1985
- }
1986
- .empty-column {
1987
- display: flex;
1988
- align-items: center;
1989
- justify-content: center;
1990
- min-height: 60px;
1991
- border: 2px dashed #d1d5db;
1992
- border-radius: 4px;
1993
- color: #9ca3af;
1994
- font-size: 13px;
1995
- font-family: sans-serif;
1996
- margin: 4px;
1997
- }
1998
- .drop-indicator {
1999
- height: 3px;
2000
- background: #3b82f6;
2001
- border-radius: 2px;
2002
- margin: 0 4px;
2003
- opacity: 0;
2004
- transition: opacity 0.15s ease;
2005
- }
2006
- .drop-indicator.active {
2007
- opacity: 1;
2008
- }
2009
- `;
2010
- H([
2011
- b({ attribute: !1 })
2012
- ], D.prototype, "column", 2);
2013
- H([
2014
- b({ attribute: !1 })
2015
- ], D.prototype, "store", 1);
2016
- H([
2017
- b({ attribute: !1 })
2018
- ], D.prototype, "toolRegistry", 2);
2019
- H([
2020
- b({ type: Number })
2021
- ], D.prototype, "widthPercent", 2);
2022
- D = H([
2023
- C("me-column-renderer")
2024
- ], D);
2025
- var Yt = Object.defineProperty, Jt = Object.getOwnPropertyDescriptor, F = (e, t, o, n) => {
2026
- for (var i = n > 1 ? void 0 : n ? Jt(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2027
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2028
- return n && i && Yt(t, o, i), i;
2029
- };
2030
- let R = class extends $ {
2031
- constructor() {
2032
- super(...arguments), this.storeCtrl = new T(this, ["design", "viewMode"]);
2033
- }
2034
- set store(e) {
2035
- this.storeCtrl.setStore(e);
2036
- }
2037
- get store() {
2038
- return this.storeCtrl.store;
2039
- }
2040
- handleMoveUp(e) {
2041
- e.stopPropagation();
2042
- const t = this.store.getRowIndex(this.row.id);
2043
- t > 0 && this.store.moveRow(t, t - 1);
2044
- }
2045
- handleMoveDown(e) {
2046
- e.stopPropagation();
2047
- const t = this.store.getRowIndex(this.row.id), o = this.store.getRows().length;
2048
- t < o - 1 && this.store.moveRow(t, t + 1);
2049
- }
2050
- handleDuplicate(e) {
2051
- e.stopPropagation(), this.store.duplicateRow(this.row.id);
2052
- }
2053
- handleDelete(e) {
2054
- e.stopPropagation(), this.store.removeRow(this.row.id);
2055
- }
2056
- render() {
2057
- if (!this.store) return p``;
2058
- const { row: e, store: t, toolRegistry: o } = this, n = e.values.backgroundColor || "transparent", i = e.values.columnsBackgroundColor || "transparent", s = e.values.padding || "0px", r = e.cells.reduce((x, m) => x + m, 0), a = e.values.backgroundImage, d = typeof a == "object" && a?.url ? a.url : "";
2059
- let c = "";
2060
- if (d) {
2061
- const x = a.repeat === !0 || a.repeat === "repeat" ? "repeat" : "no-repeat", m = a.cover === !0 ? "cover" : a.fullWidth === !0 ? "100% auto" : "auto", y = a.center !== !1 ? "center" : "top left";
2062
- c = `background-image:url('${d}');background-size:${m};background-position:${y};background-repeat:${x};`;
2063
- }
2064
- const h = t.viewMode, g = !!e.values.hideDesktop, u = !!e.values.hideMobile, f = h === "desktop" && g || h === "mobile" && u, w = g ? "Hidden on desktop" : u ? "Hidden on mobile" : "";
2065
- return this.classList.toggle("hidden-in-view", f), this.setAttribute("draggable", "true"), this.dataset.rowId = e.id, p`
2066
- ${f ? p`<div class="row-hidden-badge">${w}</div>` : ""}
2067
- <div class="row-actions">
2068
- <button class="row-action-btn" @click=${this.handleMoveUp} title="Move Up">
2069
- <svg viewBox="0 0 24 24"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>
2070
- </button>
2071
- <button class="row-action-btn" @click=${this.handleMoveDown} title="Move Down">
2072
- <svg viewBox="0 0 24 24"><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></svg>
2073
- </button>
2074
- <button class="row-action-btn" @click=${this.handleDuplicate} title="Duplicate Row">
2075
- <svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
2076
- </button>
2077
- <button class="row-action-btn danger" @click=${this.handleDelete} title="Delete Row">
2078
- <svg viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
2079
- </button>
2080
- </div>
2081
- <div
2082
- class="row-wrapper"
2083
- style="background-color:${n};padding:${s};${c}"
2084
- >
2085
- ${e.columns.map((x, m) => {
2086
- const y = e.cells[m] / r * 100;
2087
- return p`
2088
- <me-column-renderer
2089
- .column=${x}
2090
- .store=${t}
2091
- .toolRegistry=${o}
2092
- .widthPercent=${y}
2093
- style="background-color:${i};"
2094
- ></me-column-renderer>
2095
- `;
2096
- })}
2097
- </div>
2098
- `;
2099
- }
2100
- };
2101
- R.styles = k`
2102
- :host {
2103
- display: block;
2104
- position: relative;
2105
- transition: opacity 0.2s ease;
2106
- }
2107
- :host(.hidden-in-view) { opacity: 0.3; }
2108
- .row-hidden-badge {
2109
- position: absolute; top: 4px; left: 4px; z-index: 5;
2110
- background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
2111
- padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
2112
- pointer-events: none;
2113
- }
2114
- .row-wrapper {
2115
- display: flex;
2116
- flex-wrap: nowrap;
2117
- position: relative;
2118
- min-height: 30px;
2119
- transition: box-shadow 0.15s ease;
2120
- }
2121
- :host(:hover) .row-wrapper {
2122
- box-shadow: inset 0 0 0 1px #93c5fd;
2123
- }
2124
- .row-actions {
2125
- display: none;
2126
- position: absolute;
2127
- right: -36px;
2128
- top: 50%;
2129
- transform: translateY(-50%);
2130
- flex-direction: column;
2131
- gap: 2px;
2132
- z-index: 10;
2133
- }
2134
- :host(:hover) .row-actions {
2135
- display: flex;
2136
- }
2137
- .row-action-btn {
2138
- width: 28px;
2139
- height: 28px;
2140
- display: flex;
2141
- align-items: center;
2142
- justify-content: center;
2143
- background: #6b7280;
2144
- color: white;
2145
- border: none;
2146
- border-radius: 4px;
2147
- cursor: pointer;
2148
- font-size: 12px;
2149
- line-height: 1;
2150
- transition: background 0.15s ease;
2151
- padding: 0;
2152
- }
2153
- .row-action-btn:hover { background: #3b82f6; }
2154
- .row-action-btn.danger:hover { background: #ef4444; }
2155
- .row-action-btn svg {
2156
- width: 14px;
2157
- height: 14px;
2158
- stroke: currentColor;
2159
- fill: none;
2160
- stroke-width: 2;
2161
- stroke-linecap: round;
2162
- stroke-linejoin: round;
2163
- }
2164
- `;
2165
- F([
2166
- b({ attribute: !1 })
2167
- ], R.prototype, "row", 2);
2168
- F([
2169
- b({ attribute: !1 })
2170
- ], R.prototype, "store", 1);
2171
- F([
2172
- b({ attribute: !1 })
2173
- ], R.prototype, "toolRegistry", 2);
2174
- R = F([
2175
- C("me-row-renderer")
2176
- ], R);
2177
- var Xt = Object.defineProperty, Qt = Object.getOwnPropertyDescriptor, J = (e, t, o, n) => {
2178
- for (var i = n > 1 ? void 0 : n ? Qt(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2179
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2180
- return n && i && Xt(t, o, i), i;
2181
- };
2182
- let M = class extends $ {
2183
- constructor() {
2184
- super(...arguments), this.storeCtrl = new T(this, ["design", "viewMode"]);
2185
- }
2186
- set store(e) {
2187
- this.storeCtrl.setStore(e);
2188
- }
2189
- get store() {
2190
- return this.storeCtrl.store;
2191
- }
2192
- handleCanvasClick() {
2193
- this.store.select(null);
2194
- }
2195
- setViewMode(e) {
2196
- this.store.setViewMode(e);
2197
- }
2198
- render() {
2199
- const e = this.store.getRows(), t = this.store.getBodyValues(), o = t.contentWidth || "600px", n = t.backgroundColor || "#e7e7e7", i = this.store.viewMode;
2200
- return p`
2201
- <div class="view-toggle">
2202
- <button
2203
- class="view-btn ${i === "desktop" ? "active" : ""}"
2204
- @click=${() => this.setViewMode("desktop")}
2205
- >Desktop</button>
2206
- <button
2207
- class="view-btn ${i === "mobile" ? "active" : ""}"
2208
- @click=${() => this.setViewMode("mobile")}
2209
- >Mobile</button>
2210
- </div>
2211
-
2212
- <div
2213
- class="canvas-body"
2214
- style="max-width:${i === "mobile" ? "375px" : o};background-color:${n};"
2215
- @click=${this.handleCanvasClick}
2216
- >
2217
- ${e.length === 0 ? p`
2218
- <div class="empty-canvas">
2219
- <div class="empty-icon">&#9776;</div>
2220
- <div>Drag a content block here</div>
2221
- </div>
2222
- ` : e.map(
2223
- (r) => p`
2224
- <me-row-renderer
2225
- .row=${r}
2226
- .store=${this.store}
2227
- .toolRegistry=${this.toolRegistry}
2228
- ></me-row-renderer>
2229
- `
2230
- )}
2231
- </div>
2232
- `;
2233
- }
2234
- };
2235
- M.styles = k`
2236
- :host {
2237
- display: block;
2238
- flex: 1;
2239
- overflow-y: auto;
2240
- background: #f3f4f6;
2241
- padding: 20px;
2242
- }
2243
- .canvas-body {
2244
- margin: 0 auto;
2245
- background: #ffffff;
2246
- box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
2247
- min-height: 200px;
2248
- position: relative;
2249
- }
2250
- .empty-canvas {
2251
- display: flex;
2252
- flex-direction: column;
2253
- align-items: center;
2254
- justify-content: center;
2255
- min-height: 300px;
2256
- color: #9ca3af;
2257
- font-family: sans-serif;
2258
- font-size: 14px;
2259
- gap: 8px;
2260
- }
2261
- .empty-icon {
2262
- font-size: 40px;
2263
- opacity: 0.4;
2264
- }
2265
- .view-toggle {
2266
- display: flex;
2267
- justify-content: center;
2268
- margin-bottom: 12px;
2269
- gap: 4px;
2270
- }
2271
- .view-btn {
2272
- padding: 6px 16px;
2273
- border: 1px solid #d1d5db;
2274
- background: white;
2275
- cursor: pointer;
2276
- font-size: 13px;
2277
- font-family: sans-serif;
2278
- color: #374151;
2279
- transition: all 0.15s ease;
2280
- }
2281
- .view-btn:first-child { border-radius: 6px 0 0 6px; }
2282
- .view-btn:last-child { border-radius: 0 6px 6px 0; }
2283
- .view-btn.active {
2284
- background: #3b82f6;
2285
- border-color: #3b82f6;
2286
- color: white;
2287
- }
2288
- `;
2289
- J([
2290
- b({ attribute: !1 })
2291
- ], M.prototype, "store", 1);
2292
- J([
2293
- b({ attribute: !1 })
2294
- ], M.prototype, "toolRegistry", 2);
2295
- M = J([
2296
- C("me-editor-canvas")
2297
- ], M);
2298
- var Zt = Object.defineProperty, te = Object.getOwnPropertyDescriptor, ct = (e, t, o, n) => {
2299
- for (var i = n > 1 ? void 0 : n ? te(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2300
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2301
- return n && i && Zt(t, o, i), i;
2302
- };
2303
- const G = [
2304
- // System / email-safe fonts
2305
- { label: "Arial", value: "arial,helvetica,sans-serif", url: "" },
2306
- { label: "Helvetica", value: "helvetica,sans-serif", url: "" },
2307
- { label: "Georgia", value: "georgia,serif", url: "" },
2308
- { label: "Times New Roman", value: "'times new roman',times,serif", url: "" },
2309
- { label: "Trebuchet MS", value: "trebuchet ms,helvetica,sans-serif", url: "" },
2310
- { label: "Verdana", value: "verdana,geneva,sans-serif", url: "" },
2311
- { label: "Courier New", value: "'courier new',courier,monospace", url: "" },
2312
- // Google Fonts
2313
- { label: "Lato", value: "'Lato',sans-serif", url: "https://fonts.googleapis.com/css?family=Lato:400,700" },
2314
- { label: "Montserrat", value: "'Montserrat',sans-serif", url: "https://fonts.googleapis.com/css?family=Montserrat:400,700" },
2315
- { label: "Old Standard TT", value: "'Old Standard TT',serif", url: "https://fonts.googleapis.com/css?family=Old+Standard+TT:400,700" },
2316
- { label: "Open Sans", value: "'Open Sans',sans-serif", url: "https://fonts.googleapis.com/css?family=Open+Sans:400,700" },
2317
- { label: "Pacifico", value: "'Pacifico',cursive", url: "https://fonts.googleapis.com/css?family=Pacifico:400" },
2318
- { label: "Playfair Display", value: "'Playfair Display',serif", url: "https://fonts.googleapis.com/css?family=Playfair+Display:400,700" },
2319
- { label: "Raleway", value: "'Raleway',sans-serif", url: "https://fonts.googleapis.com/css?family=Raleway:400,700" },
2320
- { label: "Roboto", value: "'Roboto',sans-serif", url: "https://fonts.googleapis.com/css?family=Roboto:400,700" },
2321
- { label: "Rubik", value: "'Rubik',sans-serif", url: "https://fonts.googleapis.com/css?family=Rubik:400,700" },
2322
- { label: "Source Sans Pro", value: "'Source Sans Pro',sans-serif", url: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" }
2323
- ];
2324
- let A = class extends $ {
2325
- constructor() {
2326
- super(...arguments), this.storeCtrl = new T(this, ["design"]);
2327
- }
2328
- set store(e) {
2329
- this.storeCtrl.setStore(e);
2330
- }
2331
- get store() {
2332
- return this.storeCtrl.store;
2333
- }
2334
- update_(e, t) {
2335
- this.store.updateBodyValues({ [e]: t });
2336
- }
2337
- updateLinkStyle(e, t) {
2338
- const o = this.store.getBodyValues().linkStyle;
2339
- this.store.updateBodyValues({ linkStyle: { ...o, [e]: t } });
2340
- }
2341
- updateFontFamily(e) {
2342
- const t = G.find((n) => n.value === e), o = { label: t?.label || e, value: e };
2343
- if (t?.url) {
2344
- o.url = t.url;
2345
- const n = `emabuild-font-${e.replace(/[^a-z]/gi, "")}`;
2346
- if (!document.getElementById(n)) {
2347
- const i = document.createElement("link");
2348
- i.id = n, i.rel = "stylesheet", i.href = t.url, document.head.appendChild(i);
2349
- }
2350
- }
2351
- this.store.updateBodyValues({ fontFamily: o });
2352
- }
2353
- render() {
2354
- const e = this.store.getBodyValues();
2355
- return p`
2356
- ${this.renderColorField("Background Color", e.backgroundColor || "#e7e7e7", (t) => this.update_("backgroundColor", t))}
2357
-
2358
- <p class="section-title" style="margin-top:16px;">Content</p>
2359
- <div class="field">
2360
- <label class="field-label">Content Width (px)</label>
2361
- <input class="input" type="number" .value=${parseInt(e.contentWidth || "600")} min="320" max="960" step="10"
2362
- @change=${(t) => this.update_("contentWidth", t.target.value + "px")} />
2363
- </div>
2364
- <div class="field">
2365
- <label class="field-label">Content Align</label>
2366
- <div class="align-group">
2367
- ${["left", "center", "right"].map((t) => p`
2368
- <button class="align-btn ${e.contentAlign === t ? "active" : ""}"
2369
- @click=${() => this.update_("contentAlign", t)}>${t}</button>
2370
- `)}
2371
- </div>
2372
- </div>
2373
-
2374
- <p class="section-title" style="margin-top:16px;">Typography</p>
2375
- <div class="field">
2376
- <label class="field-label">Font Family</label>
2377
- <select class="input" @change=${(t) => this.updateFontFamily(t.target.value)}>
2378
- <optgroup label="System Fonts">
2379
- ${G.filter((t) => !t.url).map((t) => p`<option value=${t.value} ?selected=${e.fontFamily?.value === t.value}>${t.label}</option>`)}
2380
- </optgroup>
2381
- <optgroup label="Google Fonts">
2382
- ${G.filter((t) => !!t.url).map((t) => p`<option value=${t.value} ?selected=${e.fontFamily?.value === t.value}>${t.label}</option>`)}
2383
- </optgroup>
2384
- </select>
2385
- </div>
2386
- ${this.renderColorField("Text Color", e.textColor || "#000000", (t) => this.update_("textColor", t))}
2387
-
2388
- <p class="section-title" style="margin-top:16px;">Links</p>
2389
- ${this.renderColorField("Link Color", e.linkStyle?.linkColor || "#0000ee", (t) => this.updateLinkStyle("linkColor", t))}
2390
- <div class="field">
2391
- <label style="display:flex;align-items:center;gap:8px;font-size:12px;color:#6b7280;cursor:pointer;">
2392
- <input type="checkbox" .checked=${e.linkStyle?.linkUnderline ?? !0}
2393
- @change=${(t) => this.updateLinkStyle("linkUnderline", t.target.checked)} />
2394
- Underline Links
2395
- </label>
2396
- </div>
2397
-
2398
- <p class="section-title" style="margin-top:16px;">Email</p>
2399
- <div class="field">
2400
- <label class="field-label">Preheader Text</label>
2401
- <textarea class="input" .value=${e.preheaderText || ""} placeholder="Preview text shown in inbox..."
2402
- style="min-height:60px;resize:vertical;font-family:inherit;"
2403
- @change=${(t) => this.update_("preheaderText", t.target.value)}></textarea>
2404
- </div>
2405
- `;
2406
- }
2407
- /** Reusable color field (swatch + hex input) */
2408
- renderColorField(e, t, o) {
2409
- return p`
2410
- <div class="field">
2411
- <label class="field-label">${e}</label>
2412
- <div class="color-row">
2413
- <input class="color-swatch" type="color" .value=${t} @input=${(n) => o(n.target.value)} />
2414
- <input class="input" type="text" .value=${t} style="flex:1;" @change=${(n) => o(n.target.value)} />
2415
- </div>
2416
- </div>
2417
- `;
2418
- }
2419
- };
2420
- A.styles = k`
2421
- :host { display: block; }
2422
- .section-title {
2423
- font-size: 11px; font-weight: 600; text-transform: uppercase;
2424
- color: #9ca3af; letter-spacing: 0.05em; margin: 0 0 8px 0;
2425
- }
2426
- .field { margin-bottom: 12px; }
2427
- .field-label { display: block; font-size: 12px; color: #6b7280; margin-bottom: 4px; }
2428
- .input {
2429
- width: 100%; padding: 5px 8px; border: 1px solid #d1d5db; border-radius: 4px;
2430
- font-size: 12px; box-sizing: border-box;
2431
- }
2432
- .input:focus { outline: none; border-color: #3b82f6; }
2433
- .color-row { display: flex; gap: 6px; align-items: center; }
2434
- .color-swatch {
2435
- width: 32px; height: 32px; border: 1px solid #d1d5db; border-radius: 4px;
2436
- padding: 0; cursor: pointer;
2437
- }
2438
- .align-group { display: flex; gap: 2px; }
2439
- .align-btn {
2440
- flex: 1; padding: 5px; border: 1px solid #d1d5db; background: white;
2441
- border-radius: 4px; cursor: pointer; font-size: 11px; color: #6b7280;
2442
- }
2443
- .align-btn.active { border-color: #3b82f6; background: #eff6ff; color: #3b82f6; }
2444
- `;
2445
- ct([
2446
- b({ attribute: !1 })
2447
- ], A.prototype, "store", 1);
2448
- A = ct([
2449
- C("me-body-settings")
2450
- ], A);
2451
- var ee = Object.defineProperty, oe = Object.getOwnPropertyDescriptor, X = (e, t, o, n) => {
2452
- for (var i = n > 1 ? void 0 : n ? oe(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2453
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2454
- return n && i && ee(t, o, i), i;
2455
- };
2456
- let P = class extends $ {
2457
- constructor() {
2458
- super(...arguments), this.storeCtrl = new T(this, ["activeTab"]);
2459
- }
2460
- set store(e) {
2461
- this.storeCtrl.setStore(e);
2462
- }
2463
- get store() {
2464
- return this.storeCtrl.store;
2465
- }
2466
- handleDragStart(e, t) {
2467
- e.dataTransfer.setData("application/maileditor-tool", t), e.dataTransfer.effectAllowed = "copy";
2468
- }
2469
- handleLayoutDragStart(e, t) {
2470
- e.dataTransfer.setData("application/maileditor-layout", JSON.stringify(t)), e.dataTransfer.effectAllowed = "copy";
2471
- }
2472
- addRowWithLayout(e) {
2473
- const t = this.store.createRow(e);
2474
- this.store.addRow(t);
2475
- }
2476
- render() {
2477
- const e = this.store.activeTab, t = this.toolRegistry.getAllMeta();
2478
- return p`
2479
- <div class="tabs">
2480
- <button class="tab ${e === "content" ? "active" : ""}" @click=${() => this.store.setActiveTab("content")}>Content</button>
2481
- <button class="tab ${e === "blocks" ? "active" : ""}" @click=${() => this.store.setActiveTab("blocks")}>Blocks</button>
2482
- <button class="tab ${e === "body" ? "active" : ""}" @click=${() => this.store.setActiveTab("body")}>Body</button>
2483
- </div>
2484
-
2485
- <div class="tab-content">
2486
- ${e === "content" ? this.renderContentTab(t) : ""}
2487
- ${e === "blocks" ? this.renderBlocksTab() : ""}
2488
- ${e === "body" ? this.renderBodyTab() : ""}
2489
- </div>
2490
- `;
2491
- }
2492
- renderContentTab(e) {
2493
- return p`
2494
- <p class="section-title">Content</p>
2495
- <div class="tool-grid">
2496
- ${e.map(
2497
- (t) => p`
2498
- <div
2499
- class="tool-item"
2500
- draggable="true"
2501
- @dragstart=${(o) => this.handleDragStart(o, t.name)}
2502
- >
2503
- <div class="tool-icon">${U(t.icon)}</div>
2504
- <span>${t.label}</span>
2505
- </div>
2506
- `
2507
- )}
2508
- </div>
2509
-
2510
- <p class="section-title" style="margin-top:20px;">Layout</p>
2511
- <div class="layout-grid">
2512
- ${this.renderLayoutOption([1], "100%")}
2513
- ${this.renderLayoutOption([1, 1], "50/50")}
2514
- ${this.renderLayoutOption([1, 1, 1], "33/33/33")}
2515
- ${this.renderLayoutOption([2, 1], "66/33")}
2516
- ${this.renderLayoutOption([1, 2], "33/66")}
2517
- ${this.renderLayoutOption([1, 1, 1, 1], "25x4")}
2518
- </div>
2519
- `;
2520
- }
2521
- renderLayoutOption(e, t) {
2522
- const o = e.reduce((n, i) => n + i, 0);
2523
- return p`
2524
- <div
2525
- class="layout-item"
2526
- draggable="true"
2527
- @click=${() => this.addRowWithLayout(e)}
2528
- @dragstart=${(n) => this.handleLayoutDragStart(n, e)}
2529
- title=${t}
2530
- >
2531
- ${e.map(
2532
- (n) => p`<div class="layout-col" style="width:${n / o * 100}%;"></div>`
2533
- )}
2534
- </div>
2535
- `;
2536
- }
2537
- renderBlocksTab() {
2538
- return p`
2539
- <p style="color:#9ca3af;font-size:13px;text-align:center;margin-top:40px;">
2540
- No saved blocks yet.<br/>Select a row in the editor and save it as a block.
2541
- </p>
2542
- `;
2543
- }
2544
- renderBodyTab() {
2545
- return p`<me-body-settings .store=${this.store}></me-body-settings>`;
2546
- }
2547
- };
2548
- P.styles = k`
2549
- :host {
2550
- display: flex;
2551
- flex-direction: column;
2552
- width: 280px;
2553
- min-width: 280px;
2554
- background: #ffffff;
2555
- border-right: 1px solid #e5e7eb;
2556
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2557
- overflow-y: auto;
2558
- }
2559
- .tabs {
2560
- display: flex;
2561
- border-bottom: 1px solid #e5e7eb;
2562
- background: #f9fafb;
2563
- }
2564
- .tab {
2565
- flex: 1;
2566
- padding: 10px 8px;
2567
- border: none;
2568
- background: none;
2569
- cursor: pointer;
2570
- font-size: 12px;
2571
- font-weight: 500;
2572
- color: #6b7280;
2573
- text-align: center;
2574
- transition: all 0.15s ease;
2575
- border-bottom: 2px solid transparent;
2576
- }
2577
- .tab:hover {
2578
- color: #374151;
2579
- background: #f3f4f6;
2580
- }
2581
- .tab.active {
2582
- color: #3b82f6;
2583
- border-bottom-color: #3b82f6;
2584
- }
2585
- .tab-content {
2586
- padding: 12px;
2587
- flex: 1;
2588
- }
2589
- .section-title {
2590
- font-size: 11px;
2591
- font-weight: 600;
2592
- text-transform: uppercase;
2593
- color: #9ca3af;
2594
- letter-spacing: 0.05em;
2595
- margin: 0 0 8px 0;
2596
- }
2597
- .tool-grid {
2598
- display: grid;
2599
- grid-template-columns: 1fr 1fr;
2600
- gap: 8px;
2601
- }
2602
- .tool-item {
2603
- display: flex;
2604
- flex-direction: column;
2605
- align-items: center;
2606
- gap: 6px;
2607
- padding: 12px 8px;
2608
- border: 1px solid #e5e7eb;
2609
- border-radius: 6px;
2610
- cursor: grab;
2611
- background: white;
2612
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2613
- font-size: 12px;
2614
- color: #374151;
2615
- }
2616
- .tool-item:hover {
2617
- border-color: #3b82f6;
2618
- background: #eff6ff;
2619
- transform: translateY(-2px);
2620
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
2621
- }
2622
- .tool-item:hover .tool-icon {
2623
- transform: scale(1.15);
2624
- }
2625
- .tool-item:active {
2626
- transform: translateY(0px);
2627
- box-shadow: 0 1px 4px rgba(59, 130, 246, 0.1);
2628
- }
2629
- .tool-item:active {
2630
- cursor: grabbing;
2631
- }
2632
- .tool-icon {
2633
- width: 20px;
2634
- height: 20px;
2635
- display: flex;
2636
- align-items: center;
2637
- transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2638
- justify-content: center;
2639
- color: #6b7280;
2640
- }
2641
- .tool-icon svg {
2642
- width: 20px;
2643
- height: 20px;
2644
- }
2645
- .layout-grid {
2646
- display: grid;
2647
- grid-template-columns: 1fr 1fr 1fr;
2648
- gap: 8px;
2649
- margin-top: 16px;
2650
- }
2651
- .layout-item {
2652
- display: flex;
2653
- align-items: center;
2654
- justify-content: center;
2655
- gap: 2px;
2656
- padding: 10px 4px;
2657
- border: 1px solid #e5e7eb;
2658
- border-radius: 6px;
2659
- cursor: pointer;
2660
- background: white;
2661
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2662
- }
2663
- .layout-item:hover {
2664
- border-color: #3b82f6;
2665
- background: #eff6ff;
2666
- transform: translateY(-2px);
2667
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
2668
- }
2669
- .layout-item:hover .layout-col {
2670
- background: #93c5fd;
2671
- }
2672
- .layout-item:active {
2673
- transform: translateY(0px);
2674
- }
2675
- .layout-col {
2676
- height: 20px;
2677
- background: #d1d5db;
2678
- border-radius: 2px;
2679
- transition: background 0.2s ease;
2680
- }
2681
- `;
2682
- X([
2683
- b({ attribute: !1 })
2684
- ], P.prototype, "store", 1);
2685
- X([
2686
- b({ attribute: !1 })
2687
- ], P.prototype, "toolRegistry", 2);
2688
- P = X([
2689
- C("me-editor-sidebar")
2690
- ], P);
2691
- function ne(e, t, o) {
2692
- const n = e && /^#[0-9a-fA-F]{3,8}$/.test(e) ? e : "#000000";
2693
- return p`
2694
- <div class="prop-row">
2695
- <label class="prop-label">${o}</label>
2696
- <div class="prop-color">
2697
- <input class="color-swatch" type="color" .value=${n}
2698
- @input=${(i) => t(i.target.value)} />
2699
- <input class="prop-input" type="text" .value=${e ?? ""} style="flex:1;"
2700
- @change=${(i) => t(i.target.value)} />
2701
- </div>
2702
- </div>
2703
- `;
2704
- }
2705
- function ie(e, t, o, n) {
2706
- const i = n?.options || [];
2707
- return p`
2708
- <div class="prop-row">
2709
- <label class="prop-label">${o}</label>
2710
- <select class="prop-input" @change=${(s) => t(s.target.value)}>
2711
- ${i.map((s) => p`<option value=${s.value} ?selected=${e === s.value}>${s.label}</option>`)}
2712
- </select>
2713
- </div>
2714
- `;
2715
- }
2716
- function se(e, t, o) {
2717
- return p`
2718
- <div class="prop-row">
2719
- <label class="prop-label">${o}</label>
2720
- <div style="display:flex;gap:2px;">
2721
- ${["left", "center", "right"].map((n) => p`
2722
- <button
2723
- style="flex:1;padding:6px;border:1px solid ${e === n ? "#3b82f6" : "#d1d5db"};background:${e === n ? "#eff6ff" : "white"};border-radius:4px;cursor:pointer;font-size:11px;text-transform:capitalize;color:${e === n ? "#3b82f6" : "#6b7280"};"
2724
- @click=${() => t(n)}
2725
- >${n}</button>
2726
- `)}
2727
- </div>
2728
- </div>
2729
- `;
2730
- }
2731
- function re(e) {
2732
- const t = (e || "0px").split(/\s+/).map((r) => parseInt(r) || 0), o = t[0], n = t[1] ?? o, i = t[2] ?? o, s = t[3] ?? n;
2733
- return [o, n, i, s];
2734
- }
2735
- function le(e, t, o, n) {
2736
- return e === t && t === o && o === n ? `${e}px` : e === o && t === n ? `${e}px ${t}px` : `${e}px ${t}px ${o}px ${n}px`;
2737
- }
2738
- function ae(e, t, o) {
2739
- const [n, i, s, r] = re(e), a = (c, h, g, u) => t(le(c, h, g, u));
2740
- return p`
2741
- <div class="prop-row">
2742
- <label class="prop-label">${o}</label>
2743
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;">
2744
- ${[
2745
- { label: "T", val: n, change: (c) => a(c, i, s, r) },
2746
- { label: "R", val: i, change: (c) => a(n, c, s, r) },
2747
- { label: "B", val: s, change: (c) => a(n, i, c, r) },
2748
- { label: "L", val: r, change: (c) => a(n, i, s, c) }
2749
- ].map((c) => p`
2750
- <div style="display:flex;align-items:center;gap:4px;">
2751
- <span style="font-size:10px;color:#9ca3af;width:12px;">${c.label}</span>
2752
- <input type="number" .value=${c.val} min="0"
2753
- @change=${(h) => c.change(parseInt(h.target.value) || 0)}
2754
- style="flex:1;padding:4px 6px;border:1px solid #d1d5db;border-radius:3px;font-size:12px;width:50px;" />
2755
- </div>
2756
- `)}
2757
- </div>
2758
- </div>
2759
- `;
2760
- }
2761
- function de(e, t, o) {
2762
- return p`
2763
- <div class="prop-row">
2764
- <div class="prop-toggle">
2765
- <input type="checkbox" .checked=${!!e}
2766
- @change=${(n) => t(n.target.checked)} />
2767
- <label class="prop-label" style="margin:0;">${o}</label>
2768
- </div>
2769
- </div>
2770
- `;
2771
- }
2772
- function ce(e, t, o) {
2773
- return p`
2774
- <div class="prop-row">
2775
- <label class="prop-label">${o}</label>
2776
- <input class="prop-input" type="text" .value=${e ?? ""}
2777
- @change=${(n) => t(n.target.value)} />
2778
- </div>
2779
- `;
2780
- }
2781
- function pe(e, t, o) {
2782
- return p`
2783
- <div class="prop-row">
2784
- <label class="prop-label">${o}</label>
2785
- <textarea class="prop-input"
2786
- style="min-height:100px;font-family:'SF Mono',Menlo,monospace;font-size:12px;"
2787
- .value=${e ?? ""}
2788
- @change=${(n) => t(n.target.value)}
2789
- ></textarea>
2790
- </div>
2791
- `;
2792
- }
2793
- function he(e, t, o, n) {
2794
- const i = n?.unit || "px", s = n?.min ?? 0, r = n?.max ?? 100, a = n?.step ?? 0.1, d = parseFloat(e) || 0, c = (h) => {
2795
- const g = parseFloat(h.target.value) || 0;
2796
- t(`${g}${i}`);
2797
- };
2798
- return p`
2799
- <div class="prop-row">
2800
- <label class="prop-label">${o}</label>
2801
- <div style="display:flex;align-items:center;gap:4px;">
2802
- <input
2803
- type="number"
2804
- .value=${String(d)}
2805
- min=${s}
2806
- max=${r}
2807
- step=${a}
2808
- @input=${c}
2809
- style="flex:1;padding:6px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;color:#111827;"
2810
- />
2811
- <span style="font-size:12px;color:#9ca3af;min-width:20px;">${i}</span>
2812
- </div>
2813
- </div>
2814
- `;
2815
- }
2816
- var ue = Object.defineProperty, ge = Object.getOwnPropertyDescriptor, Q = (e, t, o, n) => {
2817
- for (var i = n > 1 ? void 0 : n ? ge(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2818
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2819
- return n && i && ue(t, o, i), i;
2820
- };
2821
- let E = class extends $ {
2822
- constructor() {
2823
- super(...arguments), this.storeCtrl = new T(this, ["design", "selection"]);
2824
- }
2825
- set store(e) {
2826
- this.storeCtrl.setStore(e);
2827
- }
2828
- get store() {
2829
- return this.storeCtrl.store;
2830
- }
2831
- /** Create a change handler bound to a specific content ID and property key */
2832
- onChange(e, t) {
2833
- return (o) => {
2834
- this.store.updateContentValues(e, { [t]: o });
2835
- };
2836
- }
2837
- render() {
2838
- const e = this.store.selectedId;
2839
- if (!e)
2840
- return p`<div class="no-selection">Select an element to edit its properties</div>`;
2841
- const t = this.store.findContent(e);
2842
- if (!t)
2843
- return p`<div class="no-selection">Select an element to edit its properties</div>`;
2844
- const o = this.toolRegistry.get(t.type);
2845
- return o ? p`
2846
- <div class="header">
2847
- <p class="header-title">${o.label}</p>
2848
- <p class="header-type">${t.type}</p>
2849
- </div>
2850
- ${Object.entries(o.options).map(([, n]) => this.renderGroup(t, n))}
2851
- ` : p`<div class="no-selection">Unknown tool: ${t.type}</div>`;
2852
- }
2853
- renderGroup(e, t) {
2854
- return p`
2855
- <div class="group">
2856
- <div class="group-title">${t.title}</div>
2857
- <div class="group-body">
2858
- ${Object.entries(t.options).map(
2859
- ([o, n]) => this.renderWidget(e, o, n)
2860
- )}
2861
- </div>
2862
- </div>
2863
- `;
2864
- }
2865
- /** Delegate to the correct widget function based on the property's widget type */
2866
- renderWidget(e, t, o) {
2867
- const n = e.values[t] ?? o.defaultValue, i = this.onChange(e.id, t);
2868
- switch (o.widget) {
2869
- case "color_picker":
2870
- return ne(n, i, o.label);
2871
- case "toggle":
2872
- return de(n, i, o.label);
2873
- case "rich_text":
2874
- return pe(n, i, o.label);
2875
- case "dropdown":
2876
- return ie(n, i, o.label, o.widgetParams);
2877
- case "alignment":
2878
- return se(n, i, o.label);
2879
- case "padding":
2880
- return ae(n, i, o.label);
2881
- case "number_unit":
2882
- return he(n, i, o.label, o.widgetParams);
2883
- case "text":
2884
- default:
2885
- return ce(n, i, o.label);
2886
- }
2887
- }
2888
- };
2889
- E.styles = k`
2890
- :host {
2891
- display: flex; flex-direction: column; width: 300px; min-width: 300px;
2892
- background: #fff; border-left: 1px solid #e5e7eb;
2893
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2894
- overflow-y: auto;
2895
- }
2896
- .header { padding: 12px 16px; border-bottom: 1px solid #e5e7eb; background: #f9fafb; }
2897
- .header-title { font-size: 14px; font-weight: 600; color: #111827; margin: 0; }
2898
- .header-type { font-size: 11px; color: #9ca3af; text-transform: uppercase; margin: 2px 0 0 0; }
2899
- .no-selection {
2900
- display: flex; flex-direction: column; align-items: center; justify-content: center;
2901
- flex: 1; color: #9ca3af; font-size: 13px; gap: 8px; padding: 40px 20px; text-align: center;
2902
- }
2903
- .group { border-bottom: 1px solid #f3f4f6; }
2904
- .group-title {
2905
- padding: 10px 16px; font-size: 12px; font-weight: 600; color: #6b7280;
2906
- background: #f9fafb; cursor: pointer; user-select: none;
2907
- display: flex; align-items: center; justify-content: space-between;
2908
- }
2909
- .group-title:hover { background: #f3f4f6; }
2910
- .group-body { padding: 12px 16px; }
2911
- .prop-row { margin-bottom: 12px; }
2912
- .prop-label { display: block; font-size: 12px; color: #6b7280; margin-bottom: 4px; }
2913
- .prop-input {
2914
- width: 100%; padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 4px;
2915
- font-size: 13px; color: #111827; background: white; box-sizing: border-box;
2916
- }
2917
- .prop-input:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59,130,246,0.15); }
2918
- .prop-toggle { display: flex; align-items: center; gap: 8px; }
2919
- .prop-color { display: flex; align-items: center; gap: 8px; }
2920
- .color-swatch { width: 28px; height: 28px; border-radius: 4px; border: 1px solid #d1d5db; cursor: pointer; padding: 0; }
2921
- textarea.prop-input { min-height: 80px; resize: vertical; font-family: monospace; }
2922
- `;
2923
- Q([
2924
- b({ attribute: !1 })
2925
- ], E.prototype, "store", 1);
2926
- Q([
2927
- b({ attribute: !1 })
2928
- ], E.prototype, "toolRegistry", 2);
2929
- E = Q([
2930
- C("me-property-panel")
2931
- ], E);
2932
- var fe = Object.defineProperty, be = Object.getOwnPropertyDescriptor, pt = (e, t, o, n) => {
2933
- for (var i = n > 1 ? void 0 : n ? be(t, o) : t, s = e.length - 1, r; s >= 0; s--)
2934
- (r = e[s]) && (i = (n ? r(t, o, i) : r(i)) || i);
2935
- return n && i && fe(t, o, i), i;
2936
- };
2937
- let B = class extends $ {
2938
- constructor() {
2939
- super(...arguments), this.options = {}, this.store = new $t(), this.toolRegistry = new kt(), this.dragManager = null, this.callbacks = /* @__PURE__ */ new Map(), this.unsubscribe = null, this._handleKeydown = (e) => {
2940
- const t = e.metaKey || e.ctrlKey, n = e.composedPath().some((i) => {
2941
- const s = i, r = s?.tagName;
2942
- return !!(r === "INPUT" || r === "TEXTAREA" || r === "SELECT" || s?.isContentEditable);
2943
- });
2944
- n && !t || (t && e.key === "z" && !e.shiftKey ? (e.preventDefault(), this.store.undo()) : t && (e.key === "y" || e.key === "z" && e.shiftKey) ? (e.preventDefault(), this.store.redo()) : (e.key === "Delete" || e.key === "Backspace") && this.store.selectedId && !n ? (e.preventDefault(), this.store.removeContent(this.store.selectedId)) : e.key === "Escape" && this.store.select(null));
2945
- };
2946
- }
2947
- connectedCallback() {
2948
- super.connectedCallback(), this.registerBuiltInTools(), this.applyOptions(), this.setAttribute("tabindex", "0"), this.addEventListener("keydown", this._handleKeydown);
2949
- }
2950
- firstUpdated() {
2951
- this.dragManager = new Ct(this.store, this.toolRegistry, this.shadowRoot), this.dragManager.attach(), this.store.events.on("design:loaded", (e) => {
2952
- this.dispatchEvent(new CustomEvent("design:loaded", { detail: e, bubbles: !0, composed: !0 }));
2953
- }), this.store.events.on("design:updated", (e) => {
2954
- this.dispatchEvent(new CustomEvent("design:updated", { detail: e, bubbles: !0, composed: !0 }));
2955
- }), this.dispatchEvent(new CustomEvent("editor:ready", { bubbles: !0, composed: !0 })), this.preloadLazyTools();
2956
- }
2957
- disconnectedCallback() {
2958
- super.disconnectedCallback(), this.dragManager?.detach(), this.unsubscribe?.(), this.store.events.removeAllListeners(), this.removeEventListener("keydown", this._handleKeydown);
2959
- }
2960
- // ----------------------------------------------------------
2961
- // Public API — public API
2962
- // ----------------------------------------------------------
2963
- loadDesign(e) {
2964
- this.store.loadDesign(e);
2965
- }
2966
- saveDesign(e) {
2967
- e(structuredClone(this.store.getDesign()));
2968
- }
2969
- exportHtml(e, t) {
2970
- const o = this.store.getDesign(), n = /* @__PURE__ */ new Set();
2971
- for (const s of o.body.rows)
2972
- for (const r of s.columns)
2973
- for (const a of r.contents)
2974
- n.add(a.type);
2975
- const i = Array.from(n).filter((s) => !this.toolRegistry.isLoaded(s)).map((s) => this.toolRegistry.ensureLoaded(s));
2976
- i.length > 0 ? Promise.all(i).then(() => this.doExport(o, e, t)) : this.doExport(o, e, t);
2977
- }
2978
- doExport(e, t, o) {
2979
- const n = /* @__PURE__ */ new Map();
2980
- for (const i of this.toolRegistry.getAll())
2981
- n.set(i.name, (s, r) => i.renderer.renderHtml(s, r));
2982
- t(Wt(e, n, o));
2983
- }
2984
- async exportHtmlAsync(e) {
2985
- return new Promise((t) => this.exportHtml(t, e));
2986
- }
2987
- registerTool(e) {
2988
- this.toolRegistry.register(e), this.requestUpdate();
2989
- }
2990
- registerPropertyEditor(e, t) {
2991
- }
2992
- registerTab(e) {
2993
- }
2994
- registerCallback(e, t) {
2995
- this.callbacks.set(e, t);
2996
- }
2997
- setMergeTags(e) {
2998
- }
2999
- undo() {
3000
- this.store.undo();
3001
- }
3002
- redo() {
3003
- this.store.redo();
3004
- }
3005
- setBodyValues(e) {
3006
- this.store.updateBodyValues(e);
3007
- }
3008
- // ----------------------------------------------------------
3009
- // Internal
3010
- // ----------------------------------------------------------
3011
- /**
3012
- * Preload lazy tools during browser idle time.
3013
- * Uses requestIdleCallback to avoid blocking the main thread.
3014
- * Falls back to setTimeout(1000) for browsers without idle callback support.
3015
- */
3016
- preloadLazyTools() {
3017
- const e = window.requestIdleCallback ?? ((t) => setTimeout(t, 1e3));
3018
- for (const { meta: t } of rt)
3019
- e(() => {
3020
- this.toolRegistry.ensureLoaded(t.name);
3021
- });
3022
- }
3023
- registerBuiltInTools() {
3024
- for (const e of Et)
3025
- this.toolRegistry.register(e);
3026
- for (const { meta: e, loader: t } of rt)
3027
- this.toolRegistry.registerLazy(e, t);
3028
- }
3029
- applyOptions() {
3030
- this.options.design && this.store.loadDesign(this.options.design);
3031
- }
3032
- render() {
3033
- return p`
3034
- <me-editor-sidebar
3035
- .store=${this.store}
3036
- .toolRegistry=${this.toolRegistry}
3037
- ></me-editor-sidebar>
3038
- <me-editor-canvas
3039
- .store=${this.store}
3040
- .toolRegistry=${this.toolRegistry}
3041
- ></me-editor-canvas>
3042
- <me-property-panel
3043
- .store=${this.store}
3044
- .toolRegistry=${this.toolRegistry}
3045
- ></me-property-panel>
3046
- `;
3047
- }
3048
- };
3049
- B.styles = k`
3050
- :host {
3051
- display: flex;
3052
- width: 100%;
3053
- height: 100%;
3054
- flex: 1;
3055
- min-height: 500px;
3056
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3057
- color: #111827;
3058
- box-sizing: border-box;
3059
- overflow: hidden;
3060
- position: relative;
3061
- }
3062
- * { box-sizing: border-box; }
3063
- `;
3064
- pt([
3065
- b({ type: Object })
3066
- ], B.prototype, "options", 2);
3067
- B = pt([
3068
- C("mail-editor")
3069
- ], B);
3070
- function _(e, t) {
3071
- customElements.get(e) || customElements.define(e, t);
3072
- }
3073
- _("mail-editor", B);
3074
- _("me-editor-canvas", M);
3075
- _("me-row-renderer", R);
3076
- _("me-column-renderer", D);
3077
- _("me-content-renderer", S);
3078
- _("me-editor-sidebar", P);
3079
- _("me-body-settings", A);
3080
- _("me-property-panel", E);
3081
- _("me-inline-toolbar", V);
3082
- const $e = customElements.get("mail-editor") !== void 0;
3083
- export {
3084
- $e as E,
3085
- B as M,
3086
- kt as T,
3087
- $t as a,
3088
- z as e,
3089
- ve as j,
3090
- l as s
3091
- };
3092
- //# sourceMappingURL=index-CpMbWdgn.js.map