@emabuild/core 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvas/row-renderer.d.ts +4 -0
- package/dist/canvas/row-renderer.d.ts.map +1 -1
- package/dist/dnd/drag-manager.d.ts +13 -14
- package/dist/dnd/drag-manager.d.ts.map +1 -1
- package/dist/dnd/drop-indicator.d.ts +24 -0
- package/dist/dnd/drop-indicator.d.ts.map +1 -0
- package/dist/dnd/shadow-dom-utils.d.ts +29 -0
- package/dist/dnd/shadow-dom-utils.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{mail-editor-DzI7SmSe.js → mail-editor-ClkIyPni.js} +830 -871
- package/dist/mail-editor-ClkIyPni.js.map +1 -0
- package/dist/mail-editor.d.ts +1 -0
- package/dist/mail-editor.d.ts.map +1 -1
- package/dist/mail-editor.js +1 -1
- package/dist/properties/property-panel.d.ts +4 -6
- package/dist/properties/property-panel.d.ts.map +1 -1
- package/dist/properties/widgets/alignment-widget.d.ts +4 -0
- package/dist/properties/widgets/alignment-widget.d.ts.map +1 -0
- package/dist/properties/widgets/color-picker-widget.d.ts +4 -0
- package/dist/properties/widgets/color-picker-widget.d.ts.map +1 -0
- package/dist/properties/widgets/dropdown-widget.d.ts +4 -0
- package/dist/properties/widgets/dropdown-widget.d.ts.map +1 -0
- package/dist/properties/widgets/index.d.ts +8 -0
- package/dist/properties/widgets/index.d.ts.map +1 -0
- package/dist/properties/widgets/padding-widget.d.ts +4 -0
- package/dist/properties/widgets/padding-widget.d.ts.map +1 -0
- package/dist/properties/widgets/text-input-widget.d.ts +4 -0
- package/dist/properties/widgets/text-input-widget.d.ts.map +1 -0
- package/dist/properties/widgets/textarea-widget.d.ts +4 -0
- package/dist/properties/widgets/textarea-widget.d.ts.map +1 -0
- package/dist/properties/widgets/toggle-widget.d.ts +4 -0
- package/dist/properties/widgets/toggle-widget.d.ts.map +1 -0
- package/dist/sidebar/body-settings.d.ts +15 -0
- package/dist/sidebar/body-settings.d.ts.map +1 -0
- package/dist/sidebar/editor-sidebar.d.ts.map +1 -1
- package/dist/state/design-factory.d.ts +9 -0
- package/dist/state/design-factory.d.ts.map +1 -0
- package/dist/state/design-lookup.d.ts +14 -0
- package/dist/state/design-lookup.d.ts.map +1 -0
- package/dist/state/editor-store.d.ts +26 -7
- package/dist/state/editor-store.d.ts.map +1 -1
- package/dist/state/history-manager.d.ts +20 -0
- package/dist/state/history-manager.d.ts.map +1 -0
- package/dist/tools/built-in/button-tool.d.ts.map +1 -1
- package/dist/tools/built-in/divider-tool.d.ts.map +1 -1
- package/dist/tools/built-in/form-tool.d.ts.map +1 -1
- package/dist/tools/built-in/heading-tool.d.ts.map +1 -1
- package/dist/tools/built-in/html-tool.d.ts.map +1 -1
- package/dist/tools/built-in/image-tool.d.ts.map +1 -1
- package/dist/tools/built-in/menu-tool.d.ts.map +1 -1
- package/dist/tools/built-in/paragraph-tool.d.ts.map +1 -1
- package/dist/tools/built-in/social-tool.d.ts.map +1 -1
- package/dist/tools/built-in/table-tool.d.ts.map +1 -1
- package/dist/tools/built-in/text-tool.d.ts.map +1 -1
- package/dist/tools/built-in/timer-tool.d.ts.map +1 -1
- package/dist/tools/built-in/video-tool.d.ts.map +1 -1
- package/dist/tools/helpers/email-html.d.ts +55 -0
- package/dist/tools/helpers/email-html.d.ts.map +1 -0
- package/dist/tools/helpers/index.d.ts +5 -0
- package/dist/tools/helpers/index.d.ts.map +1 -0
- package/dist/tools/helpers/types.d.ts +36 -0
- package/dist/tools/helpers/types.d.ts.map +1 -0
- package/dist/tools/helpers/value-extractor.d.ts +31 -0
- package/dist/tools/helpers/value-extractor.d.ts.map +1 -0
- package/dist/utils/event-emitter.d.ts +32 -4
- package/dist/utils/event-emitter.d.ts.map +1 -1
- package/dist/utils/id-generator.d.ts +17 -3
- package/dist/utils/id-generator.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/mail-editor-DzI7SmSe.js.map +0 -1
- package/dist/utils/deep-clone.d.ts +0 -2
- package/dist/utils/deep-clone.d.ts.map +0 -1
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { html, css, LitElement } from "lit";
|
|
2
2
|
import { property, customElement } from "lit/decorators.js";
|
|
3
3
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
4
|
-
function deepClone(obj) {
|
|
5
|
-
return structuredClone(obj);
|
|
6
|
-
}
|
|
7
4
|
function createCounterManager() {
|
|
8
5
|
const counters = {};
|
|
9
6
|
return {
|
|
@@ -11,7 +8,9 @@ function createCounterManager() {
|
|
|
11
8
|
return { ...counters };
|
|
12
9
|
},
|
|
13
10
|
setCounters(c) {
|
|
14
|
-
Object.keys(counters)
|
|
11
|
+
for (const key of Object.keys(counters)) {
|
|
12
|
+
delete counters[key];
|
|
13
|
+
}
|
|
15
14
|
Object.assign(counters, c);
|
|
16
15
|
},
|
|
17
16
|
next(prefix) {
|
|
@@ -25,24 +24,28 @@ class EventEmitter {
|
|
|
25
24
|
constructor() {
|
|
26
25
|
this.listeners = /* @__PURE__ */ new Map();
|
|
27
26
|
}
|
|
27
|
+
/** Register a listener for an event */
|
|
28
28
|
on(event, listener) {
|
|
29
29
|
if (!this.listeners.has(event)) {
|
|
30
30
|
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
31
31
|
}
|
|
32
32
|
this.listeners.get(event).add(listener);
|
|
33
33
|
}
|
|
34
|
+
/** Remove a specific listener */
|
|
34
35
|
off(event, listener) {
|
|
35
36
|
this.listeners.get(event)?.delete(listener);
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
/** Emit an event with a payload. Errors in listeners are caught and logged. */
|
|
39
|
+
emit(event, payload) {
|
|
38
40
|
this.listeners.get(event)?.forEach((fn) => {
|
|
39
41
|
try {
|
|
40
|
-
fn(
|
|
42
|
+
fn(payload);
|
|
41
43
|
} catch (e) {
|
|
42
|
-
console.error(`[
|
|
44
|
+
console.error(`[emabuild] Error in "${event}" listener:`, e);
|
|
43
45
|
}
|
|
44
46
|
});
|
|
45
47
|
}
|
|
48
|
+
/** Remove all listeners, optionally scoped to a single event */
|
|
46
49
|
removeAllListeners(event) {
|
|
47
50
|
if (event) {
|
|
48
51
|
this.listeners.delete(event);
|
|
@@ -51,6 +54,48 @@ class EventEmitter {
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
}
|
|
57
|
+
class HistoryManager {
|
|
58
|
+
constructor(maxHistory = 50) {
|
|
59
|
+
this.undoStack = [];
|
|
60
|
+
this.redoStack = [];
|
|
61
|
+
this.maxHistory = maxHistory;
|
|
62
|
+
}
|
|
63
|
+
/** Whether there are states to undo to */
|
|
64
|
+
get canUndo() {
|
|
65
|
+
return this.undoStack.length > 0;
|
|
66
|
+
}
|
|
67
|
+
/** Whether there are states to redo to */
|
|
68
|
+
get canRedo() {
|
|
69
|
+
return this.redoStack.length > 0;
|
|
70
|
+
}
|
|
71
|
+
/** Save current design to the undo stack before a mutation */
|
|
72
|
+
push(design) {
|
|
73
|
+
this.undoStack.push(structuredClone(design));
|
|
74
|
+
if (this.undoStack.length > this.maxHistory) {
|
|
75
|
+
this.undoStack.shift();
|
|
76
|
+
}
|
|
77
|
+
this.redoStack = [];
|
|
78
|
+
}
|
|
79
|
+
/** Restore the previous state, pushing current state to redo. Returns the restored design or undefined. */
|
|
80
|
+
undo(currentDesign) {
|
|
81
|
+
const prev = this.undoStack.pop();
|
|
82
|
+
if (!prev) return void 0;
|
|
83
|
+
this.redoStack.push(structuredClone(currentDesign));
|
|
84
|
+
return prev;
|
|
85
|
+
}
|
|
86
|
+
/** Restore the next state, pushing current state to undo. Returns the restored design or undefined. */
|
|
87
|
+
redo(currentDesign) {
|
|
88
|
+
const next = this.redoStack.pop();
|
|
89
|
+
if (!next) return void 0;
|
|
90
|
+
this.undoStack.push(structuredClone(currentDesign));
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
/** Clear all history (e.g. when loading a new design) */
|
|
94
|
+
clear() {
|
|
95
|
+
this.undoStack = [];
|
|
96
|
+
this.redoStack = [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
54
99
|
function createEmptyDesign() {
|
|
55
100
|
return {
|
|
56
101
|
counters: { u_row: 0, u_column: 0 },
|
|
@@ -96,13 +141,98 @@ function createEmptyDesign() {
|
|
|
96
141
|
schemaVersion: 16
|
|
97
142
|
};
|
|
98
143
|
}
|
|
144
|
+
function createRow(cm, cellProportions) {
|
|
145
|
+
const rowNum = cm.next("u_row");
|
|
146
|
+
const columns = cellProportions.map(() => {
|
|
147
|
+
const colNum = cm.next("u_column");
|
|
148
|
+
return {
|
|
149
|
+
id: `u_column_${colNum}`,
|
|
150
|
+
contents: [],
|
|
151
|
+
values: {
|
|
152
|
+
backgroundColor: "",
|
|
153
|
+
padding: "0px",
|
|
154
|
+
border: {},
|
|
155
|
+
borderRadius: "0px",
|
|
156
|
+
_meta: { htmlID: `u_column_${colNum}`, htmlClassNames: "u_column" }
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
id: `u_row_${rowNum}`,
|
|
162
|
+
cells: cellProportions,
|
|
163
|
+
columns,
|
|
164
|
+
values: {
|
|
165
|
+
displayCondition: null,
|
|
166
|
+
columns: false,
|
|
167
|
+
backgroundColor: "",
|
|
168
|
+
columnsBackgroundColor: "",
|
|
169
|
+
backgroundImage: { url: "", fullWidth: true, repeat: false, center: true, cover: false },
|
|
170
|
+
padding: "0px",
|
|
171
|
+
anchor: "",
|
|
172
|
+
hideDesktop: false,
|
|
173
|
+
hideMobile: false,
|
|
174
|
+
_meta: { htmlID: `u_row_${rowNum}`, htmlClassNames: "u_row" }
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function createContent(cm, type, values = {}) {
|
|
179
|
+
const counter = cm.next(`u_content_${type}`);
|
|
180
|
+
const id = `u_content_${type}_${counter}`;
|
|
181
|
+
return {
|
|
182
|
+
id,
|
|
183
|
+
type,
|
|
184
|
+
values: {
|
|
185
|
+
containerPadding: "10px",
|
|
186
|
+
anchor: "",
|
|
187
|
+
hideDesktop: false,
|
|
188
|
+
hideMobile: false,
|
|
189
|
+
displayCondition: null,
|
|
190
|
+
_meta: { htmlID: id, htmlClassNames: `u_content_${type}` },
|
|
191
|
+
...values
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function findRow(design, rowId) {
|
|
196
|
+
return design.body.rows.find((r) => r.id === rowId);
|
|
197
|
+
}
|
|
198
|
+
function findColumn(design, columnId) {
|
|
199
|
+
for (const row of design.body.rows) {
|
|
200
|
+
const col = row.columns.find((c) => c.id === columnId);
|
|
201
|
+
if (col) return col;
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
function findContent(design, contentId) {
|
|
206
|
+
for (const row of design.body.rows) {
|
|
207
|
+
for (const col of row.columns) {
|
|
208
|
+
const content = col.contents.find((c) => c.id === contentId);
|
|
209
|
+
if (content) return content;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
function findParentColumn(design, contentId) {
|
|
215
|
+
for (const row of design.body.rows) {
|
|
216
|
+
for (const col of row.columns) {
|
|
217
|
+
if (col.contents.some((c) => c.id === contentId)) return col;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
function findParentRow(design, columnId) {
|
|
223
|
+
for (const row of design.body.rows) {
|
|
224
|
+
if (row.columns.some((c) => c.id === columnId)) return row;
|
|
225
|
+
}
|
|
226
|
+
return void 0;
|
|
227
|
+
}
|
|
228
|
+
function getRowIndex(design, rowId) {
|
|
229
|
+
return design.body.rows.findIndex((r) => r.id === rowId);
|
|
230
|
+
}
|
|
99
231
|
class EditorStore {
|
|
100
232
|
constructor() {
|
|
101
|
-
this.
|
|
102
|
-
this.redoStack = [];
|
|
103
|
-
this.maxHistory = 50;
|
|
104
|
-
this.subscribers = /* @__PURE__ */ new Set();
|
|
233
|
+
this.history = new HistoryManager();
|
|
105
234
|
this.counterManager = createCounterManager();
|
|
235
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
106
236
|
this.events = new EventEmitter();
|
|
107
237
|
this._selectedId = null;
|
|
108
238
|
this._hoveredId = null;
|
|
@@ -110,9 +240,8 @@ class EditorStore {
|
|
|
110
240
|
this._activeTab = "content";
|
|
111
241
|
this.design = createEmptyDesign();
|
|
112
242
|
}
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
// ----------------------------------------------------------
|
|
243
|
+
// ── Subscriptions ──────────────────────────────────────────
|
|
244
|
+
/** Subscribe to all state changes. Returns an unsubscribe function. */
|
|
116
245
|
subscribe(fn) {
|
|
117
246
|
this.subscribers.add(fn);
|
|
118
247
|
return () => this.subscribers.delete(fn);
|
|
@@ -120,18 +249,20 @@ class EditorStore {
|
|
|
120
249
|
notify() {
|
|
121
250
|
this.subscribers.forEach((fn) => fn());
|
|
122
251
|
}
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
// ----------------------------------------------------------
|
|
252
|
+
// ── Getters ────────────────────────────────────────────────
|
|
253
|
+
/** Get the full design document */
|
|
126
254
|
getDesign() {
|
|
127
255
|
return this.design;
|
|
128
256
|
}
|
|
257
|
+
/** Get the design body */
|
|
129
258
|
getBody() {
|
|
130
259
|
return this.design.body;
|
|
131
260
|
}
|
|
261
|
+
/** Get all rows */
|
|
132
262
|
getRows() {
|
|
133
263
|
return this.design.body.rows;
|
|
134
264
|
}
|
|
265
|
+
/** Get body-level values (background, fonts, etc.) */
|
|
135
266
|
getBodyValues() {
|
|
136
267
|
return this.design.body.values;
|
|
137
268
|
}
|
|
@@ -148,54 +279,39 @@ class EditorStore {
|
|
|
148
279
|
return this._activeTab;
|
|
149
280
|
}
|
|
150
281
|
get canUndo() {
|
|
151
|
-
return this.
|
|
282
|
+
return this.history.canUndo;
|
|
152
283
|
}
|
|
153
284
|
get canRedo() {
|
|
154
|
-
return this.
|
|
285
|
+
return this.history.canRedo;
|
|
155
286
|
}
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
// ----------------------------------------------------------
|
|
287
|
+
// ── Design Loading ─────────────────────────────────────────
|
|
288
|
+
/** Load a design document, resetting history and selection */
|
|
159
289
|
loadDesign(design) {
|
|
160
|
-
this.design =
|
|
290
|
+
this.design = structuredClone(design);
|
|
161
291
|
this.counterManager.setCounters(this.design.counters);
|
|
162
|
-
this.
|
|
163
|
-
this.redoStack = [];
|
|
292
|
+
this.history.clear();
|
|
164
293
|
this._selectedId = null;
|
|
165
294
|
this.notify();
|
|
166
295
|
this.events.emit("design:loaded", { design: this.design });
|
|
167
296
|
}
|
|
168
|
-
//
|
|
169
|
-
// History (undo/redo)
|
|
170
|
-
// ----------------------------------------------------------
|
|
171
|
-
pushHistory() {
|
|
172
|
-
this.undoStack.push(deepClone(this.design));
|
|
173
|
-
if (this.undoStack.length > this.maxHistory) {
|
|
174
|
-
this.undoStack.shift();
|
|
175
|
-
}
|
|
176
|
-
this.redoStack = [];
|
|
177
|
-
}
|
|
297
|
+
// ── Undo / Redo ────────────────────────────────────────────
|
|
178
298
|
undo() {
|
|
179
|
-
const prev = this.
|
|
299
|
+
const prev = this.history.undo(this.design);
|
|
180
300
|
if (!prev) return;
|
|
181
|
-
this.redoStack.push(deepClone(this.design));
|
|
182
301
|
this.design = prev;
|
|
183
302
|
this.counterManager.setCounters(this.design.counters);
|
|
184
303
|
this.notify();
|
|
185
304
|
this.emitUpdate("content_updated");
|
|
186
305
|
}
|
|
187
306
|
redo() {
|
|
188
|
-
const next = this.
|
|
307
|
+
const next = this.history.redo(this.design);
|
|
189
308
|
if (!next) return;
|
|
190
|
-
this.undoStack.push(deepClone(this.design));
|
|
191
309
|
this.design = next;
|
|
192
310
|
this.counterManager.setCounters(this.design.counters);
|
|
193
311
|
this.notify();
|
|
194
312
|
this.emitUpdate("content_updated");
|
|
195
313
|
}
|
|
196
|
-
//
|
|
197
|
-
// Selection / UI state
|
|
198
|
-
// ----------------------------------------------------------
|
|
314
|
+
// ── Selection / UI State ───────────────────────────────────
|
|
199
315
|
select(id) {
|
|
200
316
|
this._selectedId = id;
|
|
201
317
|
this.notify();
|
|
@@ -212,67 +328,95 @@ class EditorStore {
|
|
|
212
328
|
this._activeTab = tab;
|
|
213
329
|
this.notify();
|
|
214
330
|
}
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
// ----------------------------------------------------------
|
|
331
|
+
// ── Row Operations ─────────────────────────────────────────
|
|
332
|
+
/** Add a row at the given index (or at the end) */
|
|
218
333
|
addRow(row, index) {
|
|
219
|
-
this.
|
|
334
|
+
this.history.push(this.design);
|
|
220
335
|
const rows = this.design.body.rows;
|
|
336
|
+
const cloned = structuredClone(row);
|
|
221
337
|
if (index !== void 0 && index >= 0 && index <= rows.length) {
|
|
222
|
-
rows.splice(index, 0,
|
|
338
|
+
rows.splice(index, 0, cloned);
|
|
223
339
|
} else {
|
|
224
|
-
rows.push(
|
|
340
|
+
rows.push(cloned);
|
|
225
341
|
}
|
|
226
342
|
this.syncCounters();
|
|
227
343
|
this.notify();
|
|
228
|
-
this.emitUpdate("row_added",
|
|
344
|
+
this.emitUpdate("row_added", cloned);
|
|
229
345
|
}
|
|
346
|
+
/** Remove a row by ID */
|
|
230
347
|
removeRow(rowId) {
|
|
231
|
-
this.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
this.emitUpdate("row_removed");
|
|
239
|
-
}
|
|
348
|
+
const idx = getRowIndex(this.design, rowId);
|
|
349
|
+
if (idx === -1) return;
|
|
350
|
+
this.history.push(this.design);
|
|
351
|
+
this.design.body.rows.splice(idx, 1);
|
|
352
|
+
if (this._selectedId === rowId) this._selectedId = null;
|
|
353
|
+
this.notify();
|
|
354
|
+
this.emitUpdate("row_removed");
|
|
240
355
|
}
|
|
356
|
+
/** Move a row from one index to another */
|
|
241
357
|
moveRow(fromIndex, toIndex) {
|
|
242
|
-
this.
|
|
358
|
+
this.history.push(this.design);
|
|
243
359
|
const rows = this.design.body.rows;
|
|
244
360
|
const [row] = rows.splice(fromIndex, 1);
|
|
245
361
|
rows.splice(toIndex, 0, row);
|
|
246
362
|
this.notify();
|
|
247
363
|
this.emitUpdate("row_reordered");
|
|
248
364
|
}
|
|
365
|
+
/** Duplicate a row, assigning fresh IDs to all nested elements */
|
|
366
|
+
duplicateRow(rowId) {
|
|
367
|
+
const row = findRow(this.design, rowId);
|
|
368
|
+
if (!row) return;
|
|
369
|
+
this.history.push(this.design);
|
|
370
|
+
const cloned = structuredClone(row);
|
|
371
|
+
const rowNum = this.counterManager.next("u_row");
|
|
372
|
+
cloned.id = `u_row_${rowNum}`;
|
|
373
|
+
cloned.values._meta = { htmlID: cloned.id, htmlClassNames: "u_row" };
|
|
374
|
+
for (const col of cloned.columns) {
|
|
375
|
+
const colNum = this.counterManager.next("u_column");
|
|
376
|
+
col.id = `u_column_${colNum}`;
|
|
377
|
+
col.values._meta = { htmlID: col.id, htmlClassNames: "u_column" };
|
|
378
|
+
for (const content of col.contents) {
|
|
379
|
+
const cNum = this.counterManager.next(`u_content_${content.type}`);
|
|
380
|
+
content.id = `u_content_${content.type}_${cNum}`;
|
|
381
|
+
content.values._meta = { htmlID: content.id, htmlClassNames: `u_content_${content.type}` };
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const idx = getRowIndex(this.design, rowId);
|
|
385
|
+
this.design.body.rows.splice(idx + 1, 0, cloned);
|
|
386
|
+
this.syncCounters();
|
|
387
|
+
this.notify();
|
|
388
|
+
this.emitUpdate("row_added", cloned);
|
|
389
|
+
}
|
|
390
|
+
/** Get the index of a row */
|
|
391
|
+
getRowIndex(rowId) {
|
|
392
|
+
return getRowIndex(this.design, rowId);
|
|
393
|
+
}
|
|
394
|
+
/** Update row-level values */
|
|
249
395
|
updateRowValues(rowId, patch) {
|
|
250
|
-
const row = this.
|
|
396
|
+
const row = findRow(this.design, rowId);
|
|
251
397
|
if (!row) return;
|
|
252
|
-
this.
|
|
398
|
+
this.history.push(this.design);
|
|
253
399
|
Object.assign(row.values, patch);
|
|
254
400
|
this.notify();
|
|
255
401
|
this.emitUpdate("content_updated");
|
|
256
402
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
// ----------------------------------------------------------
|
|
403
|
+
// ── Column Operations ──────────────────────────────────────
|
|
404
|
+
/** Update column-level values */
|
|
260
405
|
updateColumnValues(columnId, patch) {
|
|
261
|
-
const col = this.
|
|
406
|
+
const col = findColumn(this.design, columnId);
|
|
262
407
|
if (!col) return;
|
|
263
|
-
this.
|
|
408
|
+
this.history.push(this.design);
|
|
264
409
|
Object.assign(col.values, patch);
|
|
265
410
|
this.notify();
|
|
266
411
|
this.emitUpdate("content_updated");
|
|
267
412
|
}
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
// ----------------------------------------------------------
|
|
413
|
+
// ── Content Operations ─────────────────────────────────────
|
|
414
|
+
/** Add content to a column at the given index */
|
|
271
415
|
addContent(columnId, content, index) {
|
|
272
|
-
const col = this.
|
|
416
|
+
const col = findColumn(this.design, columnId);
|
|
273
417
|
if (!col) return;
|
|
274
|
-
this.
|
|
275
|
-
const cloned =
|
|
418
|
+
this.history.push(this.design);
|
|
419
|
+
const cloned = structuredClone(content);
|
|
276
420
|
if (index !== void 0 && index >= 0 && index <= col.contents.length) {
|
|
277
421
|
col.contents.splice(index, 0, cloned);
|
|
278
422
|
} else {
|
|
@@ -282,12 +426,13 @@ class EditorStore {
|
|
|
282
426
|
this.notify();
|
|
283
427
|
this.emitUpdate("content_added", cloned);
|
|
284
428
|
}
|
|
429
|
+
/** Remove a content block by ID */
|
|
285
430
|
removeContent(contentId) {
|
|
286
431
|
for (const row of this.design.body.rows) {
|
|
287
432
|
for (const col of row.columns) {
|
|
288
433
|
const idx = col.contents.findIndex((c) => c.id === contentId);
|
|
289
434
|
if (idx !== -1) {
|
|
290
|
-
this.
|
|
435
|
+
this.history.push(this.design);
|
|
291
436
|
col.contents.splice(idx, 1);
|
|
292
437
|
if (this._selectedId === contentId) this._selectedId = null;
|
|
293
438
|
this.notify();
|
|
@@ -297,49 +442,47 @@ class EditorStore {
|
|
|
297
442
|
}
|
|
298
443
|
}
|
|
299
444
|
}
|
|
445
|
+
/** Update content values by ID */
|
|
300
446
|
updateContentValues(contentId, patch) {
|
|
301
|
-
const content = this.
|
|
447
|
+
const content = findContent(this.design, contentId);
|
|
302
448
|
if (!content) return;
|
|
303
|
-
this.
|
|
449
|
+
this.history.push(this.design);
|
|
304
450
|
Object.assign(content.values, patch);
|
|
305
451
|
this.notify();
|
|
306
452
|
this.emitUpdate("content_updated");
|
|
307
453
|
}
|
|
454
|
+
/** Move a content block to a different column at a given index */
|
|
308
455
|
moveContent(contentId, targetColumnId, targetIndex) {
|
|
309
|
-
const content = this.
|
|
456
|
+
const content = findContent(this.design, contentId);
|
|
310
457
|
if (!content) return;
|
|
458
|
+
this.history.push(this.design);
|
|
311
459
|
for (const row of this.design.body.rows) {
|
|
312
460
|
for (const col of row.columns) {
|
|
313
461
|
const idx = col.contents.findIndex((c) => c.id === contentId);
|
|
314
462
|
if (idx !== -1) {
|
|
315
|
-
this.pushHistory();
|
|
316
463
|
col.contents.splice(idx, 1);
|
|
317
464
|
break;
|
|
318
465
|
}
|
|
319
466
|
}
|
|
320
467
|
}
|
|
321
|
-
const targetCol = this.
|
|
322
|
-
if (targetCol)
|
|
323
|
-
targetCol.contents.splice(targetIndex, 0, content);
|
|
324
|
-
}
|
|
468
|
+
const targetCol = findColumn(this.design, targetColumnId);
|
|
469
|
+
if (targetCol) targetCol.contents.splice(targetIndex, 0, content);
|
|
325
470
|
this.notify();
|
|
326
471
|
this.emitUpdate("content_reordered");
|
|
327
472
|
}
|
|
473
|
+
/** Duplicate a content block, inserting the copy right after the original */
|
|
328
474
|
duplicateContent(contentId) {
|
|
329
|
-
const content = this.
|
|
475
|
+
const content = findContent(this.design, contentId);
|
|
330
476
|
if (!content) return;
|
|
331
477
|
for (const row of this.design.body.rows) {
|
|
332
478
|
for (const col of row.columns) {
|
|
333
479
|
const idx = col.contents.findIndex((c) => c.id === contentId);
|
|
334
480
|
if (idx !== -1) {
|
|
335
|
-
|
|
481
|
+
this.history.push(this.design);
|
|
482
|
+
const cloned = structuredClone(content);
|
|
336
483
|
const counter = this.counterManager.next(`u_content_${content.type}`);
|
|
337
484
|
cloned.id = `u_content_${content.type}_${counter}`;
|
|
338
|
-
cloned.values._meta = {
|
|
339
|
-
htmlID: cloned.id,
|
|
340
|
-
htmlClassNames: `u_content_${content.type}`
|
|
341
|
-
};
|
|
342
|
-
this.pushHistory();
|
|
485
|
+
cloned.values._meta = { htmlID: cloned.id, htmlClassNames: `u_content_${content.type}` };
|
|
343
486
|
col.contents.splice(idx + 1, 0, cloned);
|
|
344
487
|
this.syncCounters();
|
|
345
488
|
this.notify();
|
|
@@ -349,109 +492,44 @@ class EditorStore {
|
|
|
349
492
|
}
|
|
350
493
|
}
|
|
351
494
|
}
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
// ----------------------------------------------------------
|
|
495
|
+
// ── Body Values ────────────────────────────────────────────
|
|
496
|
+
/** Update body-level values (background, fonts, etc.) */
|
|
355
497
|
updateBodyValues(patch) {
|
|
356
|
-
this.
|
|
498
|
+
this.history.push(this.design);
|
|
357
499
|
Object.assign(this.design.body.values, patch);
|
|
358
500
|
this.notify();
|
|
359
501
|
this.emitUpdate("body_updated");
|
|
360
502
|
}
|
|
361
|
-
//
|
|
362
|
-
// Lookup helpers
|
|
363
|
-
// ----------------------------------------------------------
|
|
503
|
+
// ── Lookups (delegate to design-lookup) ────────────────────
|
|
364
504
|
findRow(rowId) {
|
|
365
|
-
return this.design
|
|
505
|
+
return findRow(this.design, rowId);
|
|
366
506
|
}
|
|
367
507
|
findColumn(columnId) {
|
|
368
|
-
|
|
369
|
-
const col = row.columns.find((c) => c.id === columnId);
|
|
370
|
-
if (col) return col;
|
|
371
|
-
}
|
|
372
|
-
return void 0;
|
|
508
|
+
return findColumn(this.design, columnId);
|
|
373
509
|
}
|
|
374
510
|
findContent(contentId) {
|
|
375
|
-
|
|
376
|
-
for (const col of row.columns) {
|
|
377
|
-
const content = col.contents.find((c) => c.id === contentId);
|
|
378
|
-
if (content) return content;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return void 0;
|
|
511
|
+
return findContent(this.design, contentId);
|
|
382
512
|
}
|
|
383
513
|
findParentColumn(contentId) {
|
|
384
|
-
|
|
385
|
-
for (const col of row.columns) {
|
|
386
|
-
if (col.contents.some((c) => c.id === contentId)) return col;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return void 0;
|
|
514
|
+
return findParentColumn(this.design, contentId);
|
|
390
515
|
}
|
|
391
516
|
findParentRow(columnId) {
|
|
392
|
-
|
|
393
|
-
if (row.columns.some((c) => c.id === columnId)) return row;
|
|
394
|
-
}
|
|
395
|
-
return void 0;
|
|
517
|
+
return findParentRow(this.design, columnId);
|
|
396
518
|
}
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
// ----------------------------------------------------------
|
|
400
|
-
/** Creates a new row with the given column layout */
|
|
519
|
+
// ── Factory Methods (delegate to design-factory) ───────────
|
|
520
|
+
/** Create a new row with the given column layout */
|
|
401
521
|
createRow(cellProportions) {
|
|
402
|
-
const
|
|
403
|
-
const columns = cellProportions.map((_, i) => {
|
|
404
|
-
const colNum = this.counterManager.next("u_column");
|
|
405
|
-
return {
|
|
406
|
-
id: `u_column_${colNum}`,
|
|
407
|
-
contents: [],
|
|
408
|
-
values: {
|
|
409
|
-
backgroundColor: "",
|
|
410
|
-
padding: "0px",
|
|
411
|
-
border: {},
|
|
412
|
-
borderRadius: "0px",
|
|
413
|
-
_meta: { htmlID: `u_column_${colNum}`, htmlClassNames: "u_column" }
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
});
|
|
522
|
+
const row = createRow(this.counterManager, cellProportions);
|
|
417
523
|
this.syncCounters();
|
|
418
|
-
return
|
|
419
|
-
id: `u_row_${rowNum}`,
|
|
420
|
-
cells: cellProportions,
|
|
421
|
-
columns,
|
|
422
|
-
values: {
|
|
423
|
-
displayCondition: null,
|
|
424
|
-
columns: false,
|
|
425
|
-
backgroundColor: "",
|
|
426
|
-
columnsBackgroundColor: "",
|
|
427
|
-
backgroundImage: { url: "", fullWidth: true, repeat: false, center: true, cover: false },
|
|
428
|
-
padding: "0px",
|
|
429
|
-
anchor: "",
|
|
430
|
-
hideDesktop: false,
|
|
431
|
-
hideMobile: false,
|
|
432
|
-
_meta: { htmlID: `u_row_${rowNum}`, htmlClassNames: "u_row" }
|
|
433
|
-
}
|
|
434
|
-
};
|
|
524
|
+
return row;
|
|
435
525
|
}
|
|
436
|
-
/**
|
|
526
|
+
/** Create a new content block for the given tool type */
|
|
437
527
|
createContent(type, values = {}) {
|
|
438
|
-
const
|
|
439
|
-
const id = `u_content_${type}_${counter}`;
|
|
528
|
+
const content = createContent(this.counterManager, type, values);
|
|
440
529
|
this.syncCounters();
|
|
441
|
-
return
|
|
442
|
-
id,
|
|
443
|
-
type,
|
|
444
|
-
values: {
|
|
445
|
-
containerPadding: "10px",
|
|
446
|
-
anchor: "",
|
|
447
|
-
hideDesktop: false,
|
|
448
|
-
hideMobile: false,
|
|
449
|
-
displayCondition: null,
|
|
450
|
-
_meta: { htmlID: id, htmlClassNames: `u_content_${type}` },
|
|
451
|
-
...values
|
|
452
|
-
}
|
|
453
|
-
};
|
|
530
|
+
return content;
|
|
454
531
|
}
|
|
532
|
+
// ── Private Helpers ────────────────────────────────────────
|
|
455
533
|
syncCounters() {
|
|
456
534
|
this.design.counters = this.counterManager.getCounters();
|
|
457
535
|
}
|
|
@@ -502,14 +580,72 @@ const dragState = {
|
|
|
502
580
|
this.draggingContentId = null;
|
|
503
581
|
}
|
|
504
582
|
};
|
|
583
|
+
function createDropIndicator(color) {
|
|
584
|
+
const el = document.createElement("div");
|
|
585
|
+
Object.assign(el.style, {
|
|
586
|
+
position: "absolute",
|
|
587
|
+
left: "0",
|
|
588
|
+
right: "0",
|
|
589
|
+
height: "3px",
|
|
590
|
+
background: color,
|
|
591
|
+
borderRadius: "2px",
|
|
592
|
+
pointerEvents: "none",
|
|
593
|
+
zIndex: "1000",
|
|
594
|
+
display: "none",
|
|
595
|
+
boxShadow: `0 0 6px ${color}80`
|
|
596
|
+
});
|
|
597
|
+
return el;
|
|
598
|
+
}
|
|
599
|
+
function positionIndicator(indicator, container, items, index, insetX = "4px") {
|
|
600
|
+
if (indicator.parentNode !== container) {
|
|
601
|
+
indicator.remove();
|
|
602
|
+
container.appendChild(indicator);
|
|
603
|
+
}
|
|
604
|
+
const containerEl = container instanceof ShadowRoot ? container.host : container;
|
|
605
|
+
const containerRect = containerEl.getBoundingClientRect();
|
|
606
|
+
let indicatorY;
|
|
607
|
+
if (items.length === 0 || index === 0) {
|
|
608
|
+
indicatorY = items.length === 0 ? 0 : items[0].getBoundingClientRect().top - containerRect.top;
|
|
609
|
+
} else if (index >= items.length) {
|
|
610
|
+
const lastRect = items[items.length - 1].getBoundingClientRect();
|
|
611
|
+
indicatorY = lastRect.bottom - containerRect.top;
|
|
612
|
+
} else {
|
|
613
|
+
const elRect = items[index].getBoundingClientRect();
|
|
614
|
+
indicatorY = elRect.top - containerRect.top;
|
|
615
|
+
}
|
|
616
|
+
Object.assign(indicator.style, {
|
|
617
|
+
display: "block",
|
|
618
|
+
top: `${indicatorY}px`,
|
|
619
|
+
left: insetX,
|
|
620
|
+
right: insetX,
|
|
621
|
+
width: "auto"
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
function hideIndicator(indicator) {
|
|
625
|
+
if (indicator) indicator.style.display = "none";
|
|
626
|
+
}
|
|
627
|
+
function walkShadowDom(root, callback) {
|
|
628
|
+
const children = root instanceof ShadowRoot ? root.children : root.children;
|
|
629
|
+
for (const child of Array.from(children)) {
|
|
630
|
+
const el = child;
|
|
631
|
+
callback(el);
|
|
632
|
+
if (el.shadowRoot) walkShadowDom(el.shadowRoot, callback);
|
|
633
|
+
if (el.children?.length) walkShadowDom(el, callback);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function queryShadowAll(root, selector) {
|
|
637
|
+
const results = [];
|
|
638
|
+
walkShadowDom(root, (el) => {
|
|
639
|
+
if (el.matches?.(selector)) results.push(el);
|
|
640
|
+
});
|
|
641
|
+
return results;
|
|
642
|
+
}
|
|
505
643
|
class DragManager {
|
|
506
644
|
constructor(store, toolRegistry, root) {
|
|
507
645
|
this.currentDrop = null;
|
|
508
|
-
this.
|
|
646
|
+
this.contentIndicator = null;
|
|
509
647
|
this.rowIndicator = null;
|
|
510
|
-
this.
|
|
511
|
-
};
|
|
512
|
-
this.handleDragOver = (e) => {
|
|
648
|
+
this.onDragOver = (e) => {
|
|
513
649
|
const types = e.dataTransfer?.types || [];
|
|
514
650
|
const isToolDrag = types.includes("application/maileditor-tool");
|
|
515
651
|
const isLayoutDrag = types.includes("application/maileditor-layout");
|
|
@@ -518,76 +654,42 @@ class DragManager {
|
|
|
518
654
|
e.preventDefault();
|
|
519
655
|
e.dataTransfer.dropEffect = isToolDrag || isLayoutDrag ? "copy" : "move";
|
|
520
656
|
if (isLayoutDrag) {
|
|
521
|
-
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
this.currentDrop = drop;
|
|
525
|
-
this.showRowIndicator(drop);
|
|
526
|
-
} else {
|
|
527
|
-
this.hideRowIndicator();
|
|
528
|
-
this.currentDrop = null;
|
|
529
|
-
}
|
|
657
|
+
this.currentDrop = this.findRowDropTarget(e.clientY);
|
|
658
|
+
hideIndicator(this.contentIndicator);
|
|
659
|
+
this.showRowIndicator();
|
|
530
660
|
} else {
|
|
531
|
-
|
|
532
|
-
this.
|
|
533
|
-
|
|
534
|
-
this.currentDrop = drop;
|
|
535
|
-
this.showContentIndicator(drop);
|
|
536
|
-
} else {
|
|
537
|
-
this.hideIndicator();
|
|
538
|
-
this.currentDrop = null;
|
|
539
|
-
}
|
|
661
|
+
this.currentDrop = this.findContentDropTarget(e.clientX, e.clientY);
|
|
662
|
+
hideIndicator(this.rowIndicator);
|
|
663
|
+
this.showContentIndicator();
|
|
540
664
|
}
|
|
541
665
|
};
|
|
542
|
-
this.
|
|
666
|
+
this.onDrop = (e) => {
|
|
543
667
|
e.preventDefault();
|
|
544
|
-
this.
|
|
545
|
-
this.hideRowIndicator();
|
|
668
|
+
this.hideAllIndicators();
|
|
546
669
|
const layoutData = e.dataTransfer?.getData("application/maileditor-layout");
|
|
547
670
|
if (layoutData) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const rowIndex = this.currentDrop?.type === "row" ? this.currentDrop.rowIndex : void 0;
|
|
551
|
-
this.store.addRow(row, rowIndex);
|
|
552
|
-
this.reset();
|
|
553
|
-
return;
|
|
671
|
+
this.handleLayoutDrop(JSON.parse(layoutData));
|
|
672
|
+
return this.reset();
|
|
554
673
|
}
|
|
555
674
|
const toolName = e.dataTransfer?.getData("application/maileditor-tool");
|
|
556
675
|
if (toolName) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const content = this.store.createContent(toolName, defaults);
|
|
560
|
-
this.store.addContent(this.currentDrop.columnId, content, this.currentDrop.contentIndex);
|
|
561
|
-
this.store.select(content.id);
|
|
562
|
-
} else {
|
|
563
|
-
const row = this.store.createRow([1]);
|
|
564
|
-
this.store.addRow(row);
|
|
565
|
-
const defaults = this.toolRegistry.getDefaultValues(toolName);
|
|
566
|
-
const content = this.store.createContent(toolName, defaults);
|
|
567
|
-
this.store.addContent(row.columns[0].id, content);
|
|
568
|
-
this.store.select(content.id);
|
|
569
|
-
}
|
|
570
|
-
this.reset();
|
|
571
|
-
return;
|
|
676
|
+
this.handleToolDrop(toolName);
|
|
677
|
+
return this.reset();
|
|
572
678
|
}
|
|
573
679
|
const contentId = e.dataTransfer?.getData("application/maileditor-content") || dragState.draggingContentId;
|
|
574
|
-
if (contentId
|
|
575
|
-
this.
|
|
576
|
-
this.store.select(contentId);
|
|
680
|
+
if (contentId) {
|
|
681
|
+
this.handleContentDrop(contentId);
|
|
577
682
|
}
|
|
578
683
|
this.reset();
|
|
579
|
-
dragState.reset();
|
|
580
684
|
};
|
|
581
|
-
this.
|
|
582
|
-
this.
|
|
583
|
-
this.hideRowIndicator();
|
|
685
|
+
this.onDragEnd = () => {
|
|
686
|
+
this.hideAllIndicators();
|
|
584
687
|
this.reset();
|
|
585
688
|
};
|
|
586
|
-
this.
|
|
689
|
+
this.onDragLeave = (e) => {
|
|
587
690
|
const related = e.relatedTarget;
|
|
588
691
|
if (!related || !this.root.contains(related)) {
|
|
589
|
-
this.
|
|
590
|
-
this.hideRowIndicator();
|
|
692
|
+
this.hideAllIndicators();
|
|
591
693
|
this.currentDrop = null;
|
|
592
694
|
}
|
|
593
695
|
};
|
|
@@ -595,56 +697,61 @@ class DragManager {
|
|
|
595
697
|
this.toolRegistry = toolRegistry;
|
|
596
698
|
this.root = root;
|
|
597
699
|
}
|
|
700
|
+
/** Attach all drag event listeners to the shadow root */
|
|
598
701
|
attach() {
|
|
599
|
-
this.root.addEventListener("dragover", this.
|
|
600
|
-
this.root.addEventListener("drop", this.
|
|
601
|
-
this.root.addEventListener("dragend", this.
|
|
602
|
-
this.root.addEventListener("dragleave", this.
|
|
603
|
-
this.
|
|
604
|
-
this.
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
createIndicator(color) {
|
|
608
|
-
const el = document.createElement("div");
|
|
609
|
-
Object.assign(el.style, {
|
|
610
|
-
position: "absolute",
|
|
611
|
-
left: "0",
|
|
612
|
-
right: "0",
|
|
613
|
-
height: "3px",
|
|
614
|
-
background: color,
|
|
615
|
-
borderRadius: "2px",
|
|
616
|
-
pointerEvents: "none",
|
|
617
|
-
zIndex: "1000",
|
|
618
|
-
display: "none",
|
|
619
|
-
boxShadow: `0 0 6px ${color}80`
|
|
620
|
-
});
|
|
621
|
-
return el;
|
|
622
|
-
}
|
|
702
|
+
this.root.addEventListener("dragover", this.onDragOver);
|
|
703
|
+
this.root.addEventListener("drop", this.onDrop);
|
|
704
|
+
this.root.addEventListener("dragend", this.onDragEnd);
|
|
705
|
+
this.root.addEventListener("dragleave", this.onDragLeave);
|
|
706
|
+
this.contentIndicator = createDropIndicator("#3b82f6");
|
|
707
|
+
this.rowIndicator = createDropIndicator("#8b5cf6");
|
|
708
|
+
}
|
|
709
|
+
/** Remove all event listeners and clean up indicators */
|
|
623
710
|
detach() {
|
|
624
|
-
this.root.removeEventListener("dragover", this.
|
|
625
|
-
this.root.removeEventListener("drop", this.
|
|
626
|
-
this.root.removeEventListener("dragend", this.
|
|
627
|
-
this.root.removeEventListener("dragleave", this.
|
|
628
|
-
this.
|
|
629
|
-
this.indicator?.remove();
|
|
711
|
+
this.root.removeEventListener("dragover", this.onDragOver);
|
|
712
|
+
this.root.removeEventListener("drop", this.onDrop);
|
|
713
|
+
this.root.removeEventListener("dragend", this.onDragEnd);
|
|
714
|
+
this.root.removeEventListener("dragleave", this.onDragLeave);
|
|
715
|
+
this.contentIndicator?.remove();
|
|
630
716
|
this.rowIndicator?.remove();
|
|
631
717
|
}
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
718
|
+
// ── Drop Handlers ──────────────────────────────────────────
|
|
719
|
+
handleLayoutDrop(cells) {
|
|
720
|
+
const row = this.store.createRow(cells);
|
|
721
|
+
const rowIndex = this.currentDrop?.type === "row" ? this.currentDrop.rowIndex : void 0;
|
|
722
|
+
this.store.addRow(row, rowIndex);
|
|
723
|
+
}
|
|
724
|
+
handleToolDrop(toolName) {
|
|
725
|
+
if (this.currentDrop?.type === "content" && this.currentDrop.columnId) {
|
|
726
|
+
const defaults = this.toolRegistry.getDefaultValues(toolName);
|
|
727
|
+
const content = this.store.createContent(toolName, defaults);
|
|
728
|
+
this.store.addContent(this.currentDrop.columnId, content, this.currentDrop.contentIndex);
|
|
729
|
+
this.store.select(content.id);
|
|
730
|
+
} else {
|
|
731
|
+
const row = this.store.createRow([1]);
|
|
732
|
+
this.store.addRow(row);
|
|
733
|
+
const defaults = this.toolRegistry.getDefaultValues(toolName);
|
|
734
|
+
const content = this.store.createContent(toolName, defaults);
|
|
735
|
+
this.store.addContent(row.columns[0].id, content);
|
|
736
|
+
this.store.select(content.id);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
handleContentDrop(contentId) {
|
|
740
|
+
if (this.currentDrop?.type === "content" && this.currentDrop.columnId) {
|
|
741
|
+
this.store.moveContent(contentId, this.currentDrop.columnId, this.currentDrop.contentIndex);
|
|
742
|
+
this.store.select(contentId);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// ── Drop Target Detection ─────────────────────────────────
|
|
746
|
+
findRowDropTarget(clientY) {
|
|
636
747
|
const canvas = this.root.querySelector("me-editor-canvas");
|
|
637
748
|
if (!canvas?.shadowRoot) return null;
|
|
638
749
|
const rows = Array.from(canvas.shadowRoot.querySelectorAll("me-row-renderer"));
|
|
639
|
-
if (rows.length === 0) {
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
const firstRect = rows[0].getBoundingClientRect();
|
|
643
|
-
let bestDist = Math.abs(clientY - firstRect.top);
|
|
644
|
-
let bestTarget = { type: "row", rowIndex: 0, y: firstRect.top };
|
|
750
|
+
if (rows.length === 0) return { type: "row", rowIndex: 0, y: 0 };
|
|
751
|
+
let bestDist = Math.abs(clientY - rows[0].getBoundingClientRect().top);
|
|
752
|
+
let bestTarget = { type: "row", rowIndex: 0, y: rows[0].getBoundingClientRect().top };
|
|
645
753
|
for (let i = 0; i < rows.length; i++) {
|
|
646
|
-
const
|
|
647
|
-
const y = rect.bottom;
|
|
754
|
+
const y = rows[i].getBoundingClientRect().bottom;
|
|
648
755
|
const dist = Math.abs(clientY - y);
|
|
649
756
|
if (dist < bestDist) {
|
|
650
757
|
bestDist = dist;
|
|
@@ -653,21 +760,16 @@ class DragManager {
|
|
|
653
760
|
}
|
|
654
761
|
return bestTarget;
|
|
655
762
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
// ----------------------------------------------------------
|
|
659
|
-
findContentDropPosition(clientX, clientY) {
|
|
660
|
-
const columns = this.queryShadowAll(this.root, "me-column-renderer");
|
|
763
|
+
findContentDropTarget(clientX, clientY) {
|
|
764
|
+
const columns = queryShadowAll(this.root, "me-column-renderer");
|
|
661
765
|
let bestTarget = null;
|
|
662
766
|
let bestDist = Infinity;
|
|
663
767
|
for (const colEl of columns) {
|
|
664
768
|
const columnId = colEl.dataset.columnId;
|
|
665
|
-
if (!columnId) continue;
|
|
666
|
-
const colShadow = colEl.shadowRoot;
|
|
667
|
-
if (!colShadow) continue;
|
|
769
|
+
if (!columnId || !colEl.shadowRoot) continue;
|
|
668
770
|
const colRect = colEl.getBoundingClientRect();
|
|
669
771
|
if (clientX < colRect.left || clientX > colRect.right) continue;
|
|
670
|
-
const contentEls = Array.from(
|
|
772
|
+
const contentEls = Array.from(colEl.shadowRoot.querySelectorAll("me-content-renderer"));
|
|
671
773
|
if (contentEls.length === 0) {
|
|
672
774
|
const dist2 = Math.abs(clientY - (colRect.top + colRect.height / 2));
|
|
673
775
|
if (dist2 < bestDist) {
|
|
@@ -676,11 +778,11 @@ class DragManager {
|
|
|
676
778
|
}
|
|
677
779
|
continue;
|
|
678
780
|
}
|
|
679
|
-
const
|
|
680
|
-
let dist = Math.abs(clientY -
|
|
781
|
+
const firstY = contentEls[0].getBoundingClientRect().top;
|
|
782
|
+
let dist = Math.abs(clientY - firstY);
|
|
681
783
|
if (dist < bestDist) {
|
|
682
784
|
bestDist = dist;
|
|
683
|
-
bestTarget = { type: "content", columnId, contentIndex: 0, y:
|
|
785
|
+
bestTarget = { type: "content", columnId, contentIndex: 0, y: firstY };
|
|
684
786
|
}
|
|
685
787
|
for (let i = 0; i < contentEls.length; i++) {
|
|
686
788
|
const rect = contentEls[i].getBoundingClientRect();
|
|
@@ -695,113 +797,72 @@ class DragManager {
|
|
|
695
797
|
}
|
|
696
798
|
return bestTarget;
|
|
697
799
|
}
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const columns = this.queryShadowAll(this.root, "me-column-renderer");
|
|
704
|
-
const colEl = columns.find((c) => c.dataset.columnId === drop.columnId);
|
|
705
|
-
if (!colEl?.shadowRoot) return;
|
|
706
|
-
if (this.indicator.parentNode !== colEl.shadowRoot) {
|
|
707
|
-
this.indicator.remove();
|
|
708
|
-
colEl.shadowRoot.appendChild(this.indicator);
|
|
800
|
+
// ── Indicator Positioning ──────────────────────────────────
|
|
801
|
+
showContentIndicator() {
|
|
802
|
+
if (!this.contentIndicator || !this.currentDrop?.columnId) {
|
|
803
|
+
hideIndicator(this.contentIndicator);
|
|
804
|
+
return;
|
|
709
805
|
}
|
|
806
|
+
const columns = queryShadowAll(this.root, "me-column-renderer");
|
|
807
|
+
const colEl = columns.find((c) => c.dataset.columnId === this.currentDrop.columnId);
|
|
808
|
+
if (!colEl?.shadowRoot) return;
|
|
710
809
|
const contentEls = Array.from(colEl.shadowRoot.querySelectorAll("me-content-renderer"));
|
|
711
|
-
|
|
712
|
-
let indicatorY;
|
|
713
|
-
if (contentEls.length === 0 || drop.contentIndex === 0) {
|
|
714
|
-
indicatorY = 0;
|
|
715
|
-
} else if (drop.contentIndex >= contentEls.length) {
|
|
716
|
-
const lastRect = contentEls[contentEls.length - 1].getBoundingClientRect();
|
|
717
|
-
indicatorY = lastRect.bottom - colRect.top;
|
|
718
|
-
} else {
|
|
719
|
-
const elRect = contentEls[drop.contentIndex].getBoundingClientRect();
|
|
720
|
-
indicatorY = elRect.top - colRect.top;
|
|
721
|
-
}
|
|
722
|
-
Object.assign(this.indicator.style, {
|
|
723
|
-
display: "block",
|
|
724
|
-
top: `${indicatorY}px`,
|
|
725
|
-
left: "4px",
|
|
726
|
-
right: "4px",
|
|
727
|
-
width: "auto"
|
|
728
|
-
});
|
|
810
|
+
positionIndicator(this.contentIndicator, colEl.shadowRoot, contentEls, this.currentDrop.contentIndex ?? 0, "4px");
|
|
729
811
|
}
|
|
730
|
-
showRowIndicator(
|
|
731
|
-
if (!this.rowIndicator)
|
|
812
|
+
showRowIndicator() {
|
|
813
|
+
if (!this.rowIndicator || !this.currentDrop) {
|
|
814
|
+
hideIndicator(this.rowIndicator);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
732
817
|
const canvas = this.root.querySelector("me-editor-canvas");
|
|
733
|
-
|
|
734
|
-
const canvasBody = canvas.shadowRoot.querySelector(".canvas-body");
|
|
818
|
+
const canvasBody = canvas?.shadowRoot?.querySelector(".canvas-body");
|
|
735
819
|
if (!canvasBody) return;
|
|
736
|
-
if (this.rowIndicator.parentNode !== canvasBody) {
|
|
737
|
-
this.rowIndicator.remove();
|
|
738
|
-
canvasBody.appendChild(this.rowIndicator);
|
|
739
|
-
}
|
|
740
820
|
const rows = Array.from(canvas.shadowRoot.querySelectorAll("me-row-renderer"));
|
|
741
|
-
|
|
742
|
-
let indicatorY;
|
|
743
|
-
if (rows.length === 0 || drop.rowIndex === 0) {
|
|
744
|
-
indicatorY = 0;
|
|
745
|
-
} else if (drop.rowIndex >= rows.length) {
|
|
746
|
-
const lastRect = rows[rows.length - 1].getBoundingClientRect();
|
|
747
|
-
indicatorY = lastRect.bottom - bodyRect.top;
|
|
748
|
-
} else {
|
|
749
|
-
const elRect = rows[drop.rowIndex].getBoundingClientRect();
|
|
750
|
-
indicatorY = elRect.top - bodyRect.top;
|
|
751
|
-
}
|
|
752
|
-
Object.assign(this.rowIndicator.style, {
|
|
753
|
-
display: "block",
|
|
754
|
-
top: `${indicatorY}px`,
|
|
755
|
-
left: "0",
|
|
756
|
-
right: "0",
|
|
757
|
-
width: "auto"
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
hideIndicator() {
|
|
761
|
-
if (this.indicator) this.indicator.style.display = "none";
|
|
821
|
+
positionIndicator(this.rowIndicator, canvasBody, rows, this.currentDrop.rowIndex ?? 0, "0");
|
|
762
822
|
}
|
|
763
|
-
|
|
764
|
-
|
|
823
|
+
hideAllIndicators() {
|
|
824
|
+
hideIndicator(this.contentIndicator);
|
|
825
|
+
hideIndicator(this.rowIndicator);
|
|
765
826
|
}
|
|
766
827
|
reset() {
|
|
767
828
|
this.currentDrop = null;
|
|
768
|
-
|
|
769
|
-
this.hideRowIndicator();
|
|
829
|
+
dragState.reset();
|
|
770
830
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
for (const child of Array.from(children)) {
|
|
784
|
-
const el = child;
|
|
785
|
-
callback(el);
|
|
786
|
-
if (el.shadowRoot) this.walkShadowDom(el.shadowRoot, callback);
|
|
787
|
-
if (el.children?.length) this.walkShadowDom(el, callback);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
findContentElement(el) {
|
|
791
|
-
while (el) {
|
|
792
|
-
if (el.dataset?.contentId) return el;
|
|
793
|
-
if (el.tagName?.toLowerCase() === "me-content-renderer") return el;
|
|
794
|
-
if (el.parentElement) {
|
|
795
|
-
el = el.parentElement;
|
|
796
|
-
} else if (el.getRootNode().host) {
|
|
797
|
-
el = el.getRootNode().host;
|
|
798
|
-
} else {
|
|
799
|
-
break;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
return null;
|
|
831
|
+
}
|
|
832
|
+
function str(values, key, fallback = "") {
|
|
833
|
+
const v = values[key];
|
|
834
|
+
if (typeof v === "string" && v !== "") return v;
|
|
835
|
+
return fallback;
|
|
836
|
+
}
|
|
837
|
+
function jsonParse(value, fallback) {
|
|
838
|
+
if (typeof value !== "string") return fallback;
|
|
839
|
+
try {
|
|
840
|
+
return JSON.parse(value);
|
|
841
|
+
} catch {
|
|
842
|
+
return fallback;
|
|
803
843
|
}
|
|
804
844
|
}
|
|
845
|
+
function emailTableCell(content, options) {
|
|
846
|
+
const { padding, align = "left", extraTdStyle = "" } = options;
|
|
847
|
+
const tdStyle = `overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;${extraTdStyle}`;
|
|
848
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
849
|
+
<tbody><tr><td style="${tdStyle}" align="${align}">
|
|
850
|
+
${content}
|
|
851
|
+
</td></tr></tbody>
|
|
852
|
+
</table>`;
|
|
853
|
+
}
|
|
854
|
+
function vmlRoundrectButton(text, href, options) {
|
|
855
|
+
const { bgColor, textColor, fontSize, fontWeight, borderRadius } = options;
|
|
856
|
+
const radiusPx = parseInt(borderRadius) || 0;
|
|
857
|
+
if (radiusPx <= 0) return "";
|
|
858
|
+
const arcsize = Math.round(radiusPx / 20 * 100);
|
|
859
|
+
return `<!--[if mso]>
|
|
860
|
+
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:auto;v-text-anchor:middle;width:auto;" arcsize="${arcsize}%" stroke="f" fillcolor="${bgColor}">
|
|
861
|
+
<w:anchorlock/>
|
|
862
|
+
<center style="color:${textColor};font-family:arial,helvetica,sans-serif;font-size:${fontSize};font-weight:${fontWeight};">${text}</center>
|
|
863
|
+
</v:roundrect>
|
|
864
|
+
<![endif]-->`;
|
|
865
|
+
}
|
|
805
866
|
const textTool = {
|
|
806
867
|
name: "text",
|
|
807
868
|
label: "Text",
|
|
@@ -813,7 +874,7 @@ const textTool = {
|
|
|
813
874
|
title: "Text",
|
|
814
875
|
options: {
|
|
815
876
|
text: {
|
|
816
|
-
label: "Text",
|
|
877
|
+
label: "Text Content",
|
|
817
878
|
defaultValue: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
|
|
818
879
|
widget: "rich_text"
|
|
819
880
|
}
|
|
@@ -824,8 +885,20 @@ const textTool = {
|
|
|
824
885
|
options: {
|
|
825
886
|
color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
|
|
826
887
|
backgroundColor: { label: "Background Color", defaultValue: "", widget: "color_picker" },
|
|
827
|
-
|
|
828
|
-
|
|
888
|
+
textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
|
|
889
|
+
lineHeight: {
|
|
890
|
+
label: "Line Height",
|
|
891
|
+
defaultValue: "140%",
|
|
892
|
+
widget: "dropdown",
|
|
893
|
+
widgetParams: { options: [
|
|
894
|
+
{ label: "100%", value: "100%" },
|
|
895
|
+
{ label: "120%", value: "120%" },
|
|
896
|
+
{ label: "140%", value: "140%" },
|
|
897
|
+
{ label: "160%", value: "160%" },
|
|
898
|
+
{ label: "180%", value: "180%" },
|
|
899
|
+
{ label: "200%", value: "200%" }
|
|
900
|
+
] }
|
|
901
|
+
}
|
|
829
902
|
}
|
|
830
903
|
},
|
|
831
904
|
spacing: {
|
|
@@ -852,37 +925,28 @@ const textTool = {
|
|
|
852
925
|
textAlign: "left"
|
|
853
926
|
},
|
|
854
927
|
renderer: {
|
|
855
|
-
renderEditor(values
|
|
856
|
-
const padding = values
|
|
857
|
-
const bgColor = values
|
|
858
|
-
const color = values
|
|
859
|
-
const lineHeight = values
|
|
860
|
-
const textContent = values
|
|
928
|
+
renderEditor(values) {
|
|
929
|
+
const padding = str(values, "containerPadding", "10px");
|
|
930
|
+
const bgColor = str(values, "backgroundColor", "transparent");
|
|
931
|
+
const color = str(values, "color", "inherit");
|
|
932
|
+
const lineHeight = str(values, "lineHeight", "140%");
|
|
933
|
+
const textContent = str(values, "text");
|
|
861
934
|
return html`
|
|
862
935
|
<div style="padding:${padding};background-color:${bgColor};color:${color};line-height:${lineHeight};word-break:break-word;">
|
|
863
936
|
${unsafeHTML(textContent)}
|
|
864
937
|
</div>
|
|
865
938
|
`;
|
|
866
939
|
},
|
|
867
|
-
renderHtml(values
|
|
868
|
-
const padding = values
|
|
869
|
-
const bgColor = values
|
|
870
|
-
const color = values
|
|
871
|
-
const lineHeight = values
|
|
872
|
-
const
|
|
873
|
-
const
|
|
940
|
+
renderHtml(values) {
|
|
941
|
+
const padding = str(values, "containerPadding", "10px");
|
|
942
|
+
const bgColor = str(values, "backgroundColor");
|
|
943
|
+
const color = str(values, "color", "#000000");
|
|
944
|
+
const lineHeight = str(values, "lineHeight", "140%");
|
|
945
|
+
const textAlign = str(values, "textAlign", "left");
|
|
946
|
+
const textContent = str(values, "text");
|
|
874
947
|
const bgStyle = bgColor ? `background-color:${bgColor};` : "";
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
<tr>
|
|
878
|
-
<td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};${bgStyle}font-family:arial,helvetica,sans-serif;" align="left">
|
|
879
|
-
<div style="font-size:14px;color:${color};line-height:${lineHeight};text-align:${textAlign};word-wrap:break-word;">
|
|
880
|
-
${textContent}
|
|
881
|
-
</div>
|
|
882
|
-
</td>
|
|
883
|
-
</tr>
|
|
884
|
-
</tbody>
|
|
885
|
-
</table>`;
|
|
948
|
+
const inner = `<div style="font-size:14px;color:${color};line-height:${lineHeight};text-align:${textAlign};word-wrap:break-word;">${textContent}</div>`;
|
|
949
|
+
return emailTableCell(inner, { padding, extraTdStyle: bgStyle });
|
|
886
950
|
}
|
|
887
951
|
}
|
|
888
952
|
};
|
|
@@ -896,41 +960,74 @@ const headingTool = {
|
|
|
896
960
|
text: {
|
|
897
961
|
title: "Heading",
|
|
898
962
|
options: {
|
|
899
|
-
text: {
|
|
900
|
-
label: "Text",
|
|
901
|
-
defaultValue: "Heading",
|
|
902
|
-
widget: "text"
|
|
903
|
-
},
|
|
963
|
+
text: { label: "Text", defaultValue: "Heading", widget: "text" },
|
|
904
964
|
headingType: {
|
|
905
965
|
label: "Heading Type",
|
|
906
966
|
defaultValue: "h1",
|
|
907
967
|
widget: "dropdown",
|
|
908
|
-
widgetParams: {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
]
|
|
915
|
-
}
|
|
968
|
+
widgetParams: { options: [
|
|
969
|
+
{ label: "H1", value: "h1" },
|
|
970
|
+
{ label: "H2", value: "h2" },
|
|
971
|
+
{ label: "H3", value: "h3" },
|
|
972
|
+
{ label: "H4", value: "h4" }
|
|
973
|
+
] }
|
|
916
974
|
}
|
|
917
975
|
}
|
|
918
976
|
},
|
|
919
977
|
style: {
|
|
920
978
|
title: "Style",
|
|
921
979
|
options: {
|
|
922
|
-
fontSize: {
|
|
980
|
+
fontSize: {
|
|
981
|
+
label: "Font Size",
|
|
982
|
+
defaultValue: "22px",
|
|
983
|
+
widget: "dropdown",
|
|
984
|
+
widgetParams: { options: [
|
|
985
|
+
{ label: "14px", value: "14px" },
|
|
986
|
+
{ label: "16px", value: "16px" },
|
|
987
|
+
{ label: "18px", value: "18px" },
|
|
988
|
+
{ label: "20px", value: "20px" },
|
|
989
|
+
{ label: "22px", value: "22px" },
|
|
990
|
+
{ label: "26px", value: "26px" },
|
|
991
|
+
{ label: "30px", value: "30px" },
|
|
992
|
+
{ label: "36px", value: "36px" },
|
|
993
|
+
{ label: "48px", value: "48px" },
|
|
994
|
+
{ label: "60px", value: "60px" }
|
|
995
|
+
] }
|
|
996
|
+
},
|
|
923
997
|
color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
|
|
924
|
-
textAlign: { label: "Text Align", defaultValue: "left", widget: "
|
|
925
|
-
fontWeight: {
|
|
926
|
-
|
|
998
|
+
textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
|
|
999
|
+
fontWeight: {
|
|
1000
|
+
label: "Font Weight",
|
|
1001
|
+
defaultValue: "700",
|
|
1002
|
+
widget: "dropdown",
|
|
1003
|
+
widgetParams: { options: [
|
|
1004
|
+
{ label: "Normal", value: "400" },
|
|
1005
|
+
{ label: "Medium", value: "500" },
|
|
1006
|
+
{ label: "Semi Bold", value: "600" },
|
|
1007
|
+
{ label: "Bold", value: "700" },
|
|
1008
|
+
{ label: "Extra Bold", value: "800" }
|
|
1009
|
+
] }
|
|
1010
|
+
},
|
|
1011
|
+
lineHeight: {
|
|
1012
|
+
label: "Line Height",
|
|
1013
|
+
defaultValue: "140%",
|
|
1014
|
+
widget: "dropdown",
|
|
1015
|
+
widgetParams: { options: [
|
|
1016
|
+
{ label: "100%", value: "100%" },
|
|
1017
|
+
{ label: "120%", value: "120%" },
|
|
1018
|
+
{ label: "140%", value: "140%" },
|
|
1019
|
+
{ label: "160%", value: "160%" },
|
|
1020
|
+
{ label: "180%", value: "180%" },
|
|
1021
|
+
{ label: "200%", value: "200%" }
|
|
1022
|
+
] }
|
|
1023
|
+
},
|
|
927
1024
|
letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
|
|
928
1025
|
}
|
|
929
1026
|
},
|
|
930
1027
|
spacing: {
|
|
931
1028
|
title: "Spacing",
|
|
932
1029
|
options: {
|
|
933
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "
|
|
1030
|
+
containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
|
|
934
1031
|
}
|
|
935
1032
|
},
|
|
936
1033
|
general: {
|
|
@@ -954,35 +1051,32 @@ const headingTool = {
|
|
|
954
1051
|
containerPadding: "10px"
|
|
955
1052
|
},
|
|
956
1053
|
renderer: {
|
|
957
|
-
renderEditor(values
|
|
958
|
-
const padding = values
|
|
959
|
-
const fontSize = values
|
|
960
|
-
const color = values
|
|
961
|
-
const textAlign = values
|
|
962
|
-
const fontWeight = values
|
|
963
|
-
const lineHeight = values
|
|
964
|
-
const text = values
|
|
1054
|
+
renderEditor(values) {
|
|
1055
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1056
|
+
const fontSize = str(values, "fontSize", "22px");
|
|
1057
|
+
const color = str(values, "color", "#000000");
|
|
1058
|
+
const textAlign = str(values, "textAlign", "left");
|
|
1059
|
+
const fontWeight = str(values, "fontWeight", "700");
|
|
1060
|
+
const lineHeight = str(values, "lineHeight", "140%");
|
|
1061
|
+
const text = str(values, "text", "Heading");
|
|
965
1062
|
return html`
|
|
966
1063
|
<div style="padding:${padding};font-size:${fontSize};color:${color};text-align:${textAlign};font-weight:${fontWeight};line-height:${lineHeight};">
|
|
967
1064
|
${text}
|
|
968
1065
|
</div>
|
|
969
1066
|
`;
|
|
970
1067
|
},
|
|
971
|
-
renderHtml(values
|
|
972
|
-
const padding = values
|
|
973
|
-
const fontSize = values
|
|
974
|
-
const color = values
|
|
975
|
-
const textAlign = values
|
|
976
|
-
const fontWeight = values
|
|
977
|
-
const lineHeight = values
|
|
978
|
-
const letterSpacing = values
|
|
979
|
-
const tag = values
|
|
980
|
-
const text = values
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
<${tag} style="margin:0;font-size:${fontSize};color:${color};text-align:${textAlign};font-weight:${fontWeight};line-height:${lineHeight};letter-spacing:${letterSpacing};">${text}</${tag}>
|
|
984
|
-
</td></tr></tbody>
|
|
985
|
-
</table>`;
|
|
1068
|
+
renderHtml(values) {
|
|
1069
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1070
|
+
const fontSize = str(values, "fontSize", "22px");
|
|
1071
|
+
const color = str(values, "color", "#000000");
|
|
1072
|
+
const textAlign = str(values, "textAlign", "left");
|
|
1073
|
+
const fontWeight = str(values, "fontWeight", "700");
|
|
1074
|
+
const lineHeight = str(values, "lineHeight", "140%");
|
|
1075
|
+
const letterSpacing = str(values, "letterSpacing", "normal");
|
|
1076
|
+
const tag = str(values, "headingType", "h1");
|
|
1077
|
+
const text = str(values, "text", "Heading");
|
|
1078
|
+
const inner = `<${tag} style="margin:0;font-size:${fontSize};color:${color};text-align:${textAlign};font-weight:${fontWeight};line-height:${lineHeight};letter-spacing:${letterSpacing};">${text}</${tag}>`;
|
|
1079
|
+
return emailTableCell(inner, { padding });
|
|
986
1080
|
}
|
|
987
1081
|
}
|
|
988
1082
|
};
|
|
@@ -1007,20 +1101,20 @@ const paragraphTool = {
|
|
|
1007
1101
|
title: "Style",
|
|
1008
1102
|
options: {
|
|
1009
1103
|
color: { label: "Text Color", defaultValue: "#374151", widget: "color_picker" },
|
|
1104
|
+
textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
|
|
1010
1105
|
lineHeight: { label: "Line Height", defaultValue: "160%", widget: "text" },
|
|
1011
|
-
letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
|
|
1012
|
-
textAlign: { label: "Text Align", defaultValue: "left", widget: "text" }
|
|
1106
|
+
letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
|
|
1013
1107
|
}
|
|
1014
1108
|
},
|
|
1015
1109
|
spacing: {
|
|
1016
1110
|
title: "Spacing",
|
|
1017
1111
|
options: {
|
|
1018
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "
|
|
1112
|
+
containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
|
|
1019
1113
|
}
|
|
1020
1114
|
}
|
|
1021
1115
|
},
|
|
1022
1116
|
defaultValues: {
|
|
1023
|
-
text: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
1117
|
+
text: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>',
|
|
1024
1118
|
color: "#374151",
|
|
1025
1119
|
lineHeight: "160%",
|
|
1026
1120
|
letterSpacing: "normal",
|
|
@@ -1029,24 +1123,19 @@ const paragraphTool = {
|
|
|
1029
1123
|
},
|
|
1030
1124
|
renderer: {
|
|
1031
1125
|
renderEditor(values) {
|
|
1032
|
-
const padding = values
|
|
1033
|
-
const color = values
|
|
1034
|
-
const lineHeight = values
|
|
1035
|
-
|
|
1036
|
-
return html`<div style="padding:${padding};color:${color};line-height:${lineHeight};word-break:break-word;">${unsafeHTML(text)}</div>`;
|
|
1126
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1127
|
+
const color = str(values, "color", "#374151");
|
|
1128
|
+
const lineHeight = str(values, "lineHeight", "160%");
|
|
1129
|
+
return html`<div style="padding:${padding};color:${color};line-height:${lineHeight};word-break:break-word;">${unsafeHTML(str(values, "text"))}</div>`;
|
|
1037
1130
|
},
|
|
1038
1131
|
renderHtml(values) {
|
|
1039
|
-
const padding = values
|
|
1040
|
-
const color = values
|
|
1041
|
-
const lineHeight = values
|
|
1042
|
-
const
|
|
1043
|
-
const
|
|
1044
|
-
const
|
|
1045
|
-
return
|
|
1046
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="left">
|
|
1047
|
-
<div style="font-size:14px;color:${color};line-height:${lineHeight};text-align:${textAlign};letter-spacing:${letterSpacing};word-wrap:break-word;">${text}</div>
|
|
1048
|
-
</td></tr></tbody>
|
|
1049
|
-
</table>`;
|
|
1132
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1133
|
+
const color = str(values, "color", "#374151");
|
|
1134
|
+
const lineHeight = str(values, "lineHeight", "160%");
|
|
1135
|
+
const textAlign = str(values, "textAlign", "left");
|
|
1136
|
+
const letterSpacing = str(values, "letterSpacing", "normal");
|
|
1137
|
+
const inner = `<div style="font-size:14px;color:${color};line-height:${lineHeight};text-align:${textAlign};letter-spacing:${letterSpacing};word-wrap:break-word;">${str(values, "text")}</div>`;
|
|
1138
|
+
return emailTableCell(inner, { padding });
|
|
1050
1139
|
}
|
|
1051
1140
|
}
|
|
1052
1141
|
};
|
|
@@ -1069,17 +1158,25 @@ const imageTool = {
|
|
|
1069
1158
|
style: {
|
|
1070
1159
|
title: "Style",
|
|
1071
1160
|
options: {
|
|
1072
|
-
width: {
|
|
1073
|
-
|
|
1074
|
-
|
|
1161
|
+
width: {
|
|
1162
|
+
label: "Width",
|
|
1163
|
+
defaultValue: "100%",
|
|
1164
|
+
widget: "dropdown",
|
|
1165
|
+
widgetParams: { options: [
|
|
1166
|
+
{ label: "Auto", value: "auto" },
|
|
1167
|
+
{ label: "25%", value: "25%" },
|
|
1168
|
+
{ label: "50%", value: "50%" },
|
|
1169
|
+
{ label: "75%", value: "75%" },
|
|
1170
|
+
{ label: "100%", value: "100%" }
|
|
1171
|
+
] }
|
|
1172
|
+
},
|
|
1173
|
+
align: { label: "Align", defaultValue: "center", widget: "alignment" },
|
|
1075
1174
|
borderRadius: { label: "Border Radius", defaultValue: "0px", widget: "text" }
|
|
1076
1175
|
}
|
|
1077
1176
|
},
|
|
1078
1177
|
spacing: {
|
|
1079
1178
|
title: "Spacing",
|
|
1080
|
-
options: {
|
|
1081
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1082
|
-
}
|
|
1179
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1083
1180
|
},
|
|
1084
1181
|
general: {
|
|
1085
1182
|
title: "General",
|
|
@@ -1102,46 +1199,32 @@ const imageTool = {
|
|
|
1102
1199
|
containerPadding: "10px"
|
|
1103
1200
|
},
|
|
1104
1201
|
renderer: {
|
|
1105
|
-
renderEditor(values
|
|
1106
|
-
const padding = values
|
|
1107
|
-
const src = values
|
|
1108
|
-
const alt = values
|
|
1109
|
-
const width = values
|
|
1110
|
-
const borderRadius = values
|
|
1111
|
-
const align = values
|
|
1202
|
+
renderEditor(values) {
|
|
1203
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1204
|
+
const src = str(values, "src");
|
|
1205
|
+
const alt = str(values, "alt");
|
|
1206
|
+
const width = str(values, "width", "100%");
|
|
1207
|
+
const borderRadius = str(values, "borderRadius", "0px");
|
|
1208
|
+
const align = str(values, "align", "center");
|
|
1112
1209
|
if (!src) {
|
|
1113
|
-
return html
|
|
1114
|
-
<div style="padding:${padding};text-align:${align};">
|
|
1115
|
-
<div style="background:#f1f5f9;border:2px dashed #cbd5e1;border-radius:8px;padding:40px 20px;text-align:center;color:#94a3b8;font-family:sans-serif;font-size:13px;">
|
|
1116
|
-
No image set. Enter a URL in the property panel.
|
|
1117
|
-
</div>
|
|
1118
|
-
</div>
|
|
1119
|
-
`;
|
|
1210
|
+
return html`<div style="padding:${padding};text-align:${align};"><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>`;
|
|
1120
1211
|
}
|
|
1121
|
-
return html
|
|
1122
|
-
<div style="padding:${padding};text-align:${align};">
|
|
1123
|
-
<img src=${src} alt=${alt} style="display:inline-block;max-width:100%;width:${width};border-radius:${borderRadius};border:0;" />
|
|
1124
|
-
</div>
|
|
1125
|
-
`;
|
|
1212
|
+
return html`<div style="padding:${padding};text-align:${align};"><img src=${src} alt=${alt} style="display:inline-block;max-width:100%;width:${width};border-radius:${borderRadius};border:0;" /></div>`;
|
|
1126
1213
|
},
|
|
1127
1214
|
renderHtml(values, ctx) {
|
|
1128
|
-
const padding = values
|
|
1129
|
-
const src = values
|
|
1130
|
-
const alt = values
|
|
1131
|
-
const href = values
|
|
1132
|
-
const target = values
|
|
1133
|
-
const width = values
|
|
1134
|
-
const borderRadius = values
|
|
1135
|
-
const align = values
|
|
1215
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1216
|
+
const src = str(values, "src");
|
|
1217
|
+
const alt = str(values, "alt");
|
|
1218
|
+
const href = str(values, "href");
|
|
1219
|
+
const target = str(values, "target", "_blank");
|
|
1220
|
+
const width = str(values, "width", "100%");
|
|
1221
|
+
const borderRadius = str(values, "borderRadius", "0px");
|
|
1222
|
+
const align = str(values, "align", "center");
|
|
1136
1223
|
const widthPx = width === "100%" ? ctx.columnWidth : parseInt(width);
|
|
1137
|
-
const
|
|
1138
|
-
const imgTag = `<img align="${align}" border="0" src="${src}" alt="${alt}" title="${alt}" style="outline:none;text-decoration:none
|
|
1224
|
+
const brStyle = borderRadius !== "0px" ? `border-radius:${borderRadius};` : "";
|
|
1225
|
+
const imgTag = `<img align="${align}" border="0" src="${src}" alt="${alt}" title="${alt}" style="outline:none;text-decoration:none;clear:both;display:inline-block!important;border:none;height:auto;float:none;width:${width};max-width:${widthPx}px;${brStyle}" width="${widthPx}" />`;
|
|
1139
1226
|
const content = href ? `<a href="${href}" target="${target}" style="text-decoration:none;">${imgTag}</a>` : imgTag;
|
|
1140
|
-
return
|
|
1141
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="${align}">
|
|
1142
|
-
${content}
|
|
1143
|
-
</td></tr></tbody>
|
|
1144
|
-
</table>`;
|
|
1227
|
+
return emailTableCell(content, { padding, align });
|
|
1145
1228
|
}
|
|
1146
1229
|
}
|
|
1147
1230
|
};
|
|
@@ -1165,21 +1248,45 @@ const buttonTool = {
|
|
|
1165
1248
|
options: {
|
|
1166
1249
|
backgroundColor: { label: "Button Color", defaultValue: "#3b82f6", widget: "color_picker" },
|
|
1167
1250
|
textColor: { label: "Text Color", defaultValue: "#ffffff", widget: "color_picker" },
|
|
1168
|
-
fontSize: {
|
|
1169
|
-
|
|
1251
|
+
fontSize: {
|
|
1252
|
+
label: "Font Size",
|
|
1253
|
+
defaultValue: "14px",
|
|
1254
|
+
widget: "dropdown",
|
|
1255
|
+
widgetParams: { options: [
|
|
1256
|
+
{ label: "12px", value: "12px" },
|
|
1257
|
+
{ label: "13px", value: "13px" },
|
|
1258
|
+
{ label: "14px", value: "14px" },
|
|
1259
|
+
{ label: "16px", value: "16px" },
|
|
1260
|
+
{ label: "18px", value: "18px" },
|
|
1261
|
+
{ label: "20px", value: "20px" }
|
|
1262
|
+
] }
|
|
1263
|
+
},
|
|
1264
|
+
fontWeight: {
|
|
1265
|
+
label: "Font Weight",
|
|
1266
|
+
defaultValue: "700",
|
|
1267
|
+
widget: "dropdown",
|
|
1268
|
+
widgetParams: { options: [{ label: "Normal", value: "400" }, { label: "Bold", value: "700" }] }
|
|
1269
|
+
},
|
|
1170
1270
|
borderRadius: { label: "Border Radius", defaultValue: "4px", widget: "text" },
|
|
1171
|
-
buttonWidth: {
|
|
1172
|
-
|
|
1173
|
-
|
|
1271
|
+
buttonWidth: {
|
|
1272
|
+
label: "Width",
|
|
1273
|
+
defaultValue: "auto",
|
|
1274
|
+
widget: "dropdown",
|
|
1275
|
+
widgetParams: { options: [
|
|
1276
|
+
{ label: "Auto", value: "auto" },
|
|
1277
|
+
{ label: "100%", value: "100%" },
|
|
1278
|
+
{ label: "50%", value: "50%" }
|
|
1279
|
+
] }
|
|
1280
|
+
},
|
|
1281
|
+
textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
|
|
1282
|
+
buttonPadding: { label: "Button Padding", defaultValue: "10px 20px", widget: "padding" },
|
|
1174
1283
|
borderColor: { label: "Border Color", defaultValue: "", widget: "color_picker" },
|
|
1175
1284
|
borderWidth: { label: "Border Width", defaultValue: "0px", widget: "text" }
|
|
1176
1285
|
}
|
|
1177
1286
|
},
|
|
1178
1287
|
spacing: {
|
|
1179
1288
|
title: "Spacing",
|
|
1180
|
-
options: {
|
|
1181
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1182
|
-
}
|
|
1289
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1183
1290
|
},
|
|
1184
1291
|
general: {
|
|
1185
1292
|
title: "General",
|
|
@@ -1207,64 +1314,52 @@ const buttonTool = {
|
|
|
1207
1314
|
containerPadding: "10px"
|
|
1208
1315
|
},
|
|
1209
1316
|
renderer: {
|
|
1210
|
-
renderEditor(values
|
|
1211
|
-
const padding = values
|
|
1212
|
-
const
|
|
1213
|
-
const
|
|
1214
|
-
const fontSize = values
|
|
1215
|
-
const fontWeight = values
|
|
1216
|
-
const
|
|
1217
|
-
const
|
|
1218
|
-
const text = values
|
|
1219
|
-
const
|
|
1220
|
-
const
|
|
1221
|
-
const
|
|
1222
|
-
const
|
|
1223
|
-
const borderStyle =
|
|
1224
|
-
const widthStyle =
|
|
1317
|
+
renderEditor(values) {
|
|
1318
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1319
|
+
const bg = str(values, "backgroundColor", "#3b82f6");
|
|
1320
|
+
const color = str(values, "textColor", "#ffffff");
|
|
1321
|
+
const fontSize = str(values, "fontSize", "14px");
|
|
1322
|
+
const fontWeight = str(values, "fontWeight", "700");
|
|
1323
|
+
const radius = str(values, "borderRadius", "4px");
|
|
1324
|
+
const btnPad = str(values, "buttonPadding", "10px 20px");
|
|
1325
|
+
const text = str(values, "text", "Click Me");
|
|
1326
|
+
const align = str(values, "textAlign", "center");
|
|
1327
|
+
const btnWidth = str(values, "buttonWidth", "auto");
|
|
1328
|
+
const bw = str(values, "borderWidth", "0px");
|
|
1329
|
+
const bc = str(values, "borderColor", bg);
|
|
1330
|
+
const borderStyle = bw !== "0px" ? `border:${bw} solid ${bc};` : "border:none;";
|
|
1331
|
+
const widthStyle = btnWidth === "auto" ? "display:inline-block;" : `display:block;width:${btnWidth};`;
|
|
1225
1332
|
return html`
|
|
1226
|
-
<div style="padding:${padding};text-align:${
|
|
1227
|
-
<a style="${widthStyle}background-color:${
|
|
1228
|
-
${text}
|
|
1229
|
-
</a>
|
|
1333
|
+
<div style="padding:${padding};text-align:${align};">
|
|
1334
|
+
<a style="${widthStyle}background-color:${bg};color:${color};font-size:${fontSize};font-weight:${fontWeight};border-radius:${radius};padding:${btnPad};text-decoration:none;text-align:center;${borderStyle}cursor:pointer;font-family:arial,helvetica,sans-serif;box-sizing:border-box;">${text}</a>
|
|
1230
1335
|
</div>
|
|
1231
1336
|
`;
|
|
1232
1337
|
},
|
|
1233
|
-
renderHtml(values
|
|
1234
|
-
const padding = values
|
|
1235
|
-
const
|
|
1236
|
-
const
|
|
1237
|
-
const fontSize = values
|
|
1238
|
-
const fontWeight = values
|
|
1239
|
-
const
|
|
1240
|
-
const
|
|
1241
|
-
const text = values
|
|
1242
|
-
const
|
|
1243
|
-
const href = values
|
|
1244
|
-
const target = values
|
|
1245
|
-
const
|
|
1246
|
-
const
|
|
1247
|
-
const borderStyle =
|
|
1248
|
-
const vml =
|
|
1249
|
-
|
|
1250
|
-
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:auto;v-text-anchor:middle;width:auto;" arcsize="${Math.round(parseInt(borderRadius) / 20 * 100)}%" stroke="f" fillcolor="${bgColor}">
|
|
1251
|
-
<w:anchorlock/>
|
|
1252
|
-
<center style="color:${textColor};font-family:arial,helvetica,sans-serif;font-size:${fontSize};font-weight:${fontWeight};">${text}</center>
|
|
1253
|
-
</v:roundrect>
|
|
1254
|
-
<![endif]-->
|
|
1338
|
+
renderHtml(values) {
|
|
1339
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1340
|
+
const bg = str(values, "backgroundColor", "#3b82f6");
|
|
1341
|
+
const color = str(values, "textColor", "#ffffff");
|
|
1342
|
+
const fontSize = str(values, "fontSize", "14px");
|
|
1343
|
+
const fontWeight = str(values, "fontWeight", "700");
|
|
1344
|
+
const radius = str(values, "borderRadius", "4px");
|
|
1345
|
+
const btnPad = str(values, "buttonPadding", "10px 20px");
|
|
1346
|
+
const text = str(values, "text", "Click Me");
|
|
1347
|
+
const align = str(values, "textAlign", "center");
|
|
1348
|
+
const href = str(values, "href", "#");
|
|
1349
|
+
const target = str(values, "target", "_blank");
|
|
1350
|
+
const bw = str(values, "borderWidth", "0px");
|
|
1351
|
+
const bc = str(values, "borderColor", bg);
|
|
1352
|
+
const borderStyle = bw !== "0px" ? `border:${bw} solid ${bc};` : "border:none;";
|
|
1353
|
+
const vml = vmlRoundrectButton(text, href, { bgColor: bg, textColor: color, fontSize, fontWeight, borderRadius: radius });
|
|
1354
|
+
const vmlOpen = vml ? `${vml}
|
|
1255
1355
|
<!--[if !mso]><!-->` : "<!--[if !mso]><!-->";
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
${
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
</a>
|
|
1264
|
-
${vmlEnd}
|
|
1265
|
-
</div>
|
|
1266
|
-
</td></tr></tbody>
|
|
1267
|
-
</table>`;
|
|
1356
|
+
const vmlClose = "<!--<![endif]-->";
|
|
1357
|
+
const inner = `<div align="${align}">
|
|
1358
|
+
${vmlOpen}
|
|
1359
|
+
<a href="${href}" target="${target}" style="box-sizing:border-box;display:inline-block;text-decoration:none;text-align:center;color:${color};background-color:${bg};border-radius:${radius};font-size:${fontSize};font-weight:${fontWeight};padding:${btnPad};font-family:arial,helvetica,sans-serif;${borderStyle}mso-border-alt:none;word-break:keep-all;"><span style="line-height:120%;">${text}</span></a>
|
|
1360
|
+
${vmlClose}
|
|
1361
|
+
</div>`;
|
|
1362
|
+
return emailTableCell(inner, { padding, align });
|
|
1268
1363
|
}
|
|
1269
1364
|
}
|
|
1270
1365
|
};
|
|
@@ -1279,16 +1374,24 @@ const dividerTool = {
|
|
|
1279
1374
|
title: "Style",
|
|
1280
1375
|
options: {
|
|
1281
1376
|
borderTopWidth: { label: "Width", defaultValue: "1px", widget: "text" },
|
|
1282
|
-
borderTopStyle: {
|
|
1377
|
+
borderTopStyle: {
|
|
1378
|
+
label: "Style",
|
|
1379
|
+
defaultValue: "solid",
|
|
1380
|
+
widget: "dropdown",
|
|
1381
|
+
widgetParams: { options: [
|
|
1382
|
+
{ label: "Solid", value: "solid" },
|
|
1383
|
+
{ label: "Dashed", value: "dashed" },
|
|
1384
|
+
{ label: "Dotted", value: "dotted" },
|
|
1385
|
+
{ label: "Double", value: "double" }
|
|
1386
|
+
] }
|
|
1387
|
+
},
|
|
1283
1388
|
borderTopColor: { label: "Color", defaultValue: "#cccccc", widget: "color_picker" },
|
|
1284
1389
|
width: { label: "Line Width", defaultValue: "100%", widget: "text" }
|
|
1285
1390
|
}
|
|
1286
1391
|
},
|
|
1287
1392
|
spacing: {
|
|
1288
1393
|
title: "Spacing",
|
|
1289
|
-
options: {
|
|
1290
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1291
|
-
}
|
|
1394
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1292
1395
|
},
|
|
1293
1396
|
general: {
|
|
1294
1397
|
title: "General",
|
|
@@ -1307,30 +1410,17 @@ const dividerTool = {
|
|
|
1307
1410
|
},
|
|
1308
1411
|
renderer: {
|
|
1309
1412
|
renderEditor(values) {
|
|
1310
|
-
const padding = values
|
|
1311
|
-
const width = values
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
const bc = values.borderTopColor || "#cccccc";
|
|
1315
|
-
return html`
|
|
1316
|
-
<div style="padding:${padding};">
|
|
1317
|
-
<div style="border-top:${bw} ${bs} ${bc};width:${width};margin:0 auto;"></div>
|
|
1318
|
-
</div>
|
|
1319
|
-
`;
|
|
1413
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1414
|
+
const width = str(values, "width", "100%");
|
|
1415
|
+
const border = `${str(values, "borderTopWidth", "1px")} ${str(values, "borderTopStyle", "solid")} ${str(values, "borderTopColor", "#cccccc")}`;
|
|
1416
|
+
return html`<div style="padding:${padding};"><div style="border-top:${border};width:${width};margin:0 auto;"></div></div>`;
|
|
1320
1417
|
},
|
|
1321
1418
|
renderHtml(values) {
|
|
1322
|
-
const padding = values
|
|
1323
|
-
const width = values
|
|
1324
|
-
const
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1327
|
-
return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
1328
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="center">
|
|
1329
|
-
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${width}" style="border-collapse:collapse;table-layout:fixed;border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;vertical-align:top;border-top:${bw} ${bs} ${bc};-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;">
|
|
1330
|
-
<tbody><tr><td style="font-size:0;line-height:0;"> </td></tr></tbody>
|
|
1331
|
-
</table>
|
|
1332
|
-
</td></tr></tbody>
|
|
1333
|
-
</table>`;
|
|
1419
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1420
|
+
const width = str(values, "width", "100%");
|
|
1421
|
+
const border = `${str(values, "borderTopWidth", "1px")} ${str(values, "borderTopStyle", "solid")} ${str(values, "borderTopColor", "#cccccc")}`;
|
|
1422
|
+
const inner = `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${width}" style="border-collapse:collapse;border-top:${border};"><tbody><tr><td style="font-size:0;line-height:0;"> </td></tr></tbody></table>`;
|
|
1423
|
+
return emailTableCell(inner, { padding, align: "center" });
|
|
1334
1424
|
}
|
|
1335
1425
|
}
|
|
1336
1426
|
};
|
|
@@ -1353,9 +1443,7 @@ const htmlTool = {
|
|
|
1353
1443
|
},
|
|
1354
1444
|
spacing: {
|
|
1355
1445
|
title: "Spacing",
|
|
1356
|
-
options: {
|
|
1357
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1358
|
-
}
|
|
1446
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" } }
|
|
1359
1447
|
},
|
|
1360
1448
|
general: {
|
|
1361
1449
|
title: "General",
|
|
@@ -1371,22 +1459,14 @@ const htmlTool = {
|
|
|
1371
1459
|
},
|
|
1372
1460
|
renderer: {
|
|
1373
1461
|
renderEditor(values) {
|
|
1374
|
-
|
|
1375
|
-
const content = values.html || "";
|
|
1376
|
-
return html`<div style="padding:${padding};">${unsafeHTML(content)}</div>`;
|
|
1462
|
+
return html`<div style="padding:${str(values, "containerPadding", "10px")};">${unsafeHTML(str(values, "html"))}</div>`;
|
|
1377
1463
|
},
|
|
1378
1464
|
renderHtml(values) {
|
|
1379
|
-
|
|
1380
|
-
const content = values.html || "";
|
|
1381
|
-
return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
1382
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="left">
|
|
1383
|
-
${content}
|
|
1384
|
-
</td></tr></tbody>
|
|
1385
|
-
</table>`;
|
|
1465
|
+
return emailTableCell(str(values, "html"), { padding: str(values, "containerPadding", "10px") });
|
|
1386
1466
|
}
|
|
1387
1467
|
}
|
|
1388
1468
|
};
|
|
1389
|
-
const
|
|
1469
|
+
const DEFAULT_ICONS = [
|
|
1390
1470
|
{ name: "Facebook", url: "https://facebook.com/", icon: "f", color: "#1877F2" },
|
|
1391
1471
|
{ name: "Twitter", url: "https://twitter.com/", icon: "𝕏", color: "#000000" },
|
|
1392
1472
|
{ name: "Instagram", url: "https://instagram.com/", icon: "📷", color: "#E4405F" },
|
|
@@ -1402,26 +1482,22 @@ const socialTool = {
|
|
|
1402
1482
|
icons: {
|
|
1403
1483
|
title: "Social Icons",
|
|
1404
1484
|
options: {
|
|
1405
|
-
icons: { label: "Icons (JSON)", defaultValue: JSON.stringify(
|
|
1485
|
+
icons: { label: "Icons (JSON)", defaultValue: JSON.stringify(DEFAULT_ICONS), widget: "rich_text" },
|
|
1406
1486
|
iconSize: { label: "Icon Size", defaultValue: "32px", widget: "text" },
|
|
1407
1487
|
iconSpacing: { label: "Spacing", defaultValue: "8px", widget: "text" }
|
|
1408
1488
|
}
|
|
1409
1489
|
},
|
|
1410
1490
|
style: {
|
|
1411
1491
|
title: "Style",
|
|
1412
|
-
options: {
|
|
1413
|
-
textAlign: { label: "Align", defaultValue: "center", widget: "text" }
|
|
1414
|
-
}
|
|
1492
|
+
options: { textAlign: { label: "Align", defaultValue: "center", widget: "alignment" } }
|
|
1415
1493
|
},
|
|
1416
1494
|
spacing: {
|
|
1417
1495
|
title: "Spacing",
|
|
1418
|
-
options: {
|
|
1419
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1420
|
-
}
|
|
1496
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1421
1497
|
}
|
|
1422
1498
|
},
|
|
1423
1499
|
defaultValues: {
|
|
1424
|
-
icons: JSON.stringify(
|
|
1500
|
+
icons: JSON.stringify(DEFAULT_ICONS),
|
|
1425
1501
|
iconSize: "32px",
|
|
1426
1502
|
iconSpacing: "8px",
|
|
1427
1503
|
textAlign: "center",
|
|
@@ -1429,55 +1505,34 @@ const socialTool = {
|
|
|
1429
1505
|
},
|
|
1430
1506
|
renderer: {
|
|
1431
1507
|
renderEditor(values) {
|
|
1432
|
-
const padding = values
|
|
1433
|
-
const
|
|
1434
|
-
const iconSize = values
|
|
1435
|
-
const spacing = values
|
|
1436
|
-
|
|
1437
|
-
try {
|
|
1438
|
-
icons = JSON.parse(values.icons);
|
|
1439
|
-
} catch {
|
|
1440
|
-
}
|
|
1508
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1509
|
+
const align = str(values, "textAlign", "center");
|
|
1510
|
+
const iconSize = str(values, "iconSize", "32px");
|
|
1511
|
+
const spacing = str(values, "iconSpacing", "8px");
|
|
1512
|
+
const icons = jsonParse(values.icons, DEFAULT_ICONS);
|
|
1441
1513
|
return html`
|
|
1442
|
-
<div style="padding:${padding};text-align:${
|
|
1443
|
-
${icons.map(
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
`
|
|
1447
|
-
)}
|
|
1514
|
+
<div style="padding:${padding};text-align:${align};">
|
|
1515
|
+
${icons.map((s) => html`
|
|
1516
|
+
<a href=${s.url} target="_blank" style="display:inline-block;width:${iconSize};height:${iconSize};line-height:${iconSize};text-align:center;background:${s.color};color:white;border-radius:50%;text-decoration:none;font-size:14px;font-weight:bold;margin:0 ${spacing};font-family:arial,sans-serif;vertical-align:middle;">${s.icon}</a>
|
|
1517
|
+
`)}
|
|
1448
1518
|
</div>
|
|
1449
1519
|
`;
|
|
1450
1520
|
},
|
|
1451
1521
|
renderHtml(values) {
|
|
1452
|
-
const padding = values
|
|
1453
|
-
const
|
|
1454
|
-
const iconSize =
|
|
1455
|
-
const spacing = values
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
} catch {
|
|
1460
|
-
}
|
|
1461
|
-
const iconsHtml = icons.map(
|
|
1462
|
-
(s) => `<td align="center" valign="middle" style="padding:0 ${spacing};">
|
|
1463
|
-
<a href="${s.url}" target="_blank" style="text-decoration:none;">
|
|
1464
|
-
<table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr>
|
|
1465
|
-
<td width="${iconSize}" height="${iconSize}" align="center" valign="middle" style="width:${iconSize}px;height:${iconSize}px;background:${s.color};border-radius:50%;color:#ffffff;font-size:14px;font-weight:bold;font-family:arial,sans-serif;">
|
|
1466
|
-
${s.icon}
|
|
1467
|
-
</td>
|
|
1468
|
-
</tr></table>
|
|
1469
|
-
</a>
|
|
1470
|
-
</td>`
|
|
1522
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1523
|
+
const align = str(values, "textAlign", "center");
|
|
1524
|
+
const iconSize = str(values, "iconSize", "32");
|
|
1525
|
+
const spacing = str(values, "iconSpacing", "8px");
|
|
1526
|
+
const icons = jsonParse(values.icons, DEFAULT_ICONS);
|
|
1527
|
+
const cells = icons.map(
|
|
1528
|
+
(s) => `<td align="center" valign="middle" style="padding:0 ${spacing};"><a href="${s.url}" target="_blank" style="text-decoration:none;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td width="${iconSize}" height="${iconSize}" align="center" valign="middle" style="width:${iconSize}px;height:${iconSize}px;background:${s.color};border-radius:50%;color:#fff;font-size:14px;font-weight:bold;font-family:arial,sans-serif;">${s.icon}</td></tr></table></a></td>`
|
|
1471
1529
|
).join("\n");
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
<table role="presentation" cellpadding="0" cellspacing="0" border="0" align="${textAlign}"><tr>${iconsHtml}</tr></table>
|
|
1475
|
-
</td></tr></tbody>
|
|
1476
|
-
</table>`;
|
|
1530
|
+
const inner = `<table role="presentation" cellpadding="0" cellspacing="0" border="0" align="${align}"><tr>${cells}</tr></table>`;
|
|
1531
|
+
return emailTableCell(inner, { padding, align });
|
|
1477
1532
|
}
|
|
1478
1533
|
}
|
|
1479
1534
|
};
|
|
1480
|
-
const
|
|
1535
|
+
const DEFAULT_ITEMS = [
|
|
1481
1536
|
{ text: "Home", href: "#" },
|
|
1482
1537
|
{ text: "About", href: "#" },
|
|
1483
1538
|
{ text: "Contact", href: "#" }
|
|
@@ -1491,14 +1546,12 @@ const menuTool = {
|
|
|
1491
1546
|
options: {
|
|
1492
1547
|
menu: {
|
|
1493
1548
|
title: "Menu",
|
|
1494
|
-
options: {
|
|
1495
|
-
items: { label: "Items (JSON)", defaultValue: JSON.stringify(defaultItems), widget: "rich_text" }
|
|
1496
|
-
}
|
|
1549
|
+
options: { items: { label: "Items (JSON)", defaultValue: JSON.stringify(DEFAULT_ITEMS), widget: "rich_text" } }
|
|
1497
1550
|
},
|
|
1498
1551
|
style: {
|
|
1499
1552
|
title: "Style",
|
|
1500
1553
|
options: {
|
|
1501
|
-
textAlign: { label: "Align", defaultValue: "center", widget: "
|
|
1554
|
+
textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
|
|
1502
1555
|
fontSize: { label: "Font Size", defaultValue: "14px", widget: "text" },
|
|
1503
1556
|
color: { label: "Text Color", defaultValue: "#333333", widget: "color_picker" },
|
|
1504
1557
|
separator: { label: "Separator", defaultValue: "|", widget: "text" },
|
|
@@ -1507,13 +1560,11 @@ const menuTool = {
|
|
|
1507
1560
|
},
|
|
1508
1561
|
spacing: {
|
|
1509
1562
|
title: "Spacing",
|
|
1510
|
-
options: {
|
|
1511
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1512
|
-
}
|
|
1563
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1513
1564
|
}
|
|
1514
1565
|
},
|
|
1515
1566
|
defaultValues: {
|
|
1516
|
-
items: JSON.stringify(
|
|
1567
|
+
items: JSON.stringify(DEFAULT_ITEMS),
|
|
1517
1568
|
textAlign: "center",
|
|
1518
1569
|
fontSize: "14px",
|
|
1519
1570
|
color: "#333333",
|
|
@@ -1523,47 +1574,33 @@ const menuTool = {
|
|
|
1523
1574
|
},
|
|
1524
1575
|
renderer: {
|
|
1525
1576
|
renderEditor(values) {
|
|
1526
|
-
const padding = values
|
|
1527
|
-
const
|
|
1528
|
-
const fontSize = values
|
|
1529
|
-
const color = values
|
|
1530
|
-
const sep = values
|
|
1531
|
-
const sepColor = values
|
|
1532
|
-
|
|
1533
|
-
try {
|
|
1534
|
-
items = JSON.parse(values.items);
|
|
1535
|
-
} catch {
|
|
1536
|
-
}
|
|
1577
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1578
|
+
const align = str(values, "textAlign", "center");
|
|
1579
|
+
const fontSize = str(values, "fontSize", "14px");
|
|
1580
|
+
const color = str(values, "color", "#333333");
|
|
1581
|
+
const sep = str(values, "separator", "|");
|
|
1582
|
+
const sepColor = str(values, "separatorColor", "#cccccc");
|
|
1583
|
+
const items = jsonParse(values.items, DEFAULT_ITEMS);
|
|
1537
1584
|
return html`
|
|
1538
|
-
<div style="padding:${padding};text-align:${
|
|
1539
|
-
${items.map(
|
|
1540
|
-
|
|
1541
|
-
<a href=${item.href} style="color:${color};text-decoration:none;">${item.text}</a>`
|
|
1542
|
-
)}
|
|
1585
|
+
<div style="padding:${padding};text-align:${align};font-size:${fontSize};font-family:arial,sans-serif;">
|
|
1586
|
+
${items.map((item, i) => html`${i > 0 ? html`<span style="color:${sepColor};padding:0 8px;">${sep}</span>` : ""}
|
|
1587
|
+
<a href=${item.href} style="color:${color};text-decoration:none;">${item.text}</a>`)}
|
|
1543
1588
|
</div>
|
|
1544
1589
|
`;
|
|
1545
1590
|
},
|
|
1546
1591
|
renderHtml(values) {
|
|
1547
|
-
const padding = values
|
|
1548
|
-
const
|
|
1549
|
-
const fontSize = values
|
|
1550
|
-
const color = values
|
|
1551
|
-
const sep = values
|
|
1552
|
-
const sepColor = values
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
items = JSON.parse(values.items);
|
|
1556
|
-
} catch {
|
|
1557
|
-
}
|
|
1558
|
-
const linksHtml = items.map((item, i) => {
|
|
1592
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1593
|
+
const align = str(values, "textAlign", "center");
|
|
1594
|
+
const fontSize = str(values, "fontSize", "14px");
|
|
1595
|
+
const color = str(values, "color", "#333333");
|
|
1596
|
+
const sep = str(values, "separator", "|");
|
|
1597
|
+
const sepColor = str(values, "separatorColor", "#cccccc");
|
|
1598
|
+
const items = jsonParse(values.items, DEFAULT_ITEMS);
|
|
1599
|
+
const links = items.map((item, i) => {
|
|
1559
1600
|
const prefix = i > 0 ? `<span style="color:${sepColor};padding:0 8px;">${sep}</span>` : "";
|
|
1560
1601
|
return `${prefix}<a href="${item.href}" target="_blank" style="color:${color};text-decoration:none;font-family:arial,helvetica,sans-serif;font-size:${fontSize};">${item.text}</a>`;
|
|
1561
1602
|
}).join("");
|
|
1562
|
-
return `<
|
|
1563
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="${textAlign}">
|
|
1564
|
-
<div style="text-align:${textAlign};">${linksHtml}</div>
|
|
1565
|
-
</td></tr></tbody>
|
|
1566
|
-
</table>`;
|
|
1603
|
+
return emailTableCell(`<div style="text-align:${align};">${links}</div>`, { padding, align });
|
|
1567
1604
|
}
|
|
1568
1605
|
}
|
|
1569
1606
|
};
|
|
@@ -1588,15 +1625,11 @@ const videoTool = {
|
|
|
1588
1625
|
},
|
|
1589
1626
|
style: {
|
|
1590
1627
|
title: "Style",
|
|
1591
|
-
options: {
|
|
1592
|
-
textAlign: { label: "Align", defaultValue: "center", widget: "text" }
|
|
1593
|
-
}
|
|
1628
|
+
options: { textAlign: { label: "Align", defaultValue: "center", widget: "alignment" } }
|
|
1594
1629
|
},
|
|
1595
1630
|
spacing: {
|
|
1596
1631
|
title: "Spacing",
|
|
1597
|
-
options: {
|
|
1598
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1599
|
-
}
|
|
1632
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1600
1633
|
}
|
|
1601
1634
|
},
|
|
1602
1635
|
defaultValues: {
|
|
@@ -1608,46 +1641,23 @@ const videoTool = {
|
|
|
1608
1641
|
},
|
|
1609
1642
|
renderer: {
|
|
1610
1643
|
renderEditor(values) {
|
|
1611
|
-
const padding = values
|
|
1612
|
-
const url = values
|
|
1613
|
-
const thumbnail = values
|
|
1614
|
-
const
|
|
1644
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1645
|
+
const url = str(values, "url");
|
|
1646
|
+
const thumbnail = str(values, "thumbnailUrl") || getYouTubeThumbnail(url) || "";
|
|
1647
|
+
const align = str(values, "textAlign", "center");
|
|
1615
1648
|
if (!thumbnail) {
|
|
1616
|
-
return html
|
|
1617
|
-
<div style="padding:${padding};text-align:${textAlign};">
|
|
1618
|
-
<div style="background:#0f172a;border-radius:8px;padding:40px;text-align:center;color:white;font-family:sans-serif;position:relative;">
|
|
1619
|
-
<div style="font-size:48px;opacity:0.8;">▶</div>
|
|
1620
|
-
<div style="font-size:12px;margin-top:8px;opacity:0.6;">${url || "Enter video URL"}</div>
|
|
1621
|
-
</div>
|
|
1622
|
-
</div>
|
|
1623
|
-
`;
|
|
1649
|
+
return html`<div style="padding:${padding};text-align:${align};"><div style="background:#0f172a;border-radius:8px;padding:40px;text-align:center;color:white;font-family:sans-serif;"><div style="font-size:48px;opacity:0.8;">▶</div><div style="font-size:12px;margin-top:8px;opacity:0.6;">${url || "Enter video URL"}</div></div></div>`;
|
|
1624
1650
|
}
|
|
1625
|
-
return html
|
|
1626
|
-
<div style="padding:${padding};text-align:${textAlign};">
|
|
1627
|
-
<div style="position:relative;display:inline-block;max-width:100%;cursor:pointer;">
|
|
1628
|
-
<img src=${thumbnail} alt="Video thumbnail" style="display:block;max-width:100%;border-radius:4px;" />
|
|
1629
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;">
|
|
1630
|
-
<div style="width:60px;height:60px;background:rgba(0,0,0,0.7);border-radius:50%;display:flex;align-items:center;justify-content:center;color:white;font-size:24px;">▶</div>
|
|
1631
|
-
</div>
|
|
1632
|
-
</div>
|
|
1633
|
-
</div>
|
|
1634
|
-
`;
|
|
1651
|
+
return html`<div style="padding:${padding};text-align:${align};"><div style="position:relative;display:inline-block;max-width:100%;cursor:pointer;"><img src=${thumbnail} alt="Video thumbnail" style="display:block;max-width:100%;border-radius:4px;" /><div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;"><div style="width:60px;height:60px;background:rgba(0,0,0,0.7);border-radius:50%;display:flex;align-items:center;justify-content:center;color:white;font-size:24px;">▶</div></div></div></div>`;
|
|
1635
1652
|
},
|
|
1636
1653
|
renderHtml(values, ctx) {
|
|
1637
|
-
const padding = values
|
|
1638
|
-
const url = values
|
|
1639
|
-
const thumbnail = values
|
|
1640
|
-
const alt = values
|
|
1641
|
-
const
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1644
|
-
return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
1645
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="${textAlign}">
|
|
1646
|
-
<a href="${url}" target="_blank" style="text-decoration:none;">
|
|
1647
|
-
${imgTag}
|
|
1648
|
-
</a>
|
|
1649
|
-
</td></tr></tbody>
|
|
1650
|
-
</table>`;
|
|
1654
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1655
|
+
const url = str(values, "url", "#");
|
|
1656
|
+
const thumbnail = str(values, "thumbnailUrl") || getYouTubeThumbnail(url) || "";
|
|
1657
|
+
const alt = str(values, "alt", "Video");
|
|
1658
|
+
const align = str(values, "textAlign", "center");
|
|
1659
|
+
const imgTag = thumbnail ? `<img src="${thumbnail}" alt="${alt}" width="${ctx.columnWidth}" style="display:block;max-width:100%;width:${ctx.columnWidth}px;border:0;" />` : `<div style="background:#0f172a;padding:40px;text-align:center;color:white;font-family:arial,sans-serif;font-size:16px;">▶ Watch Video</div>`;
|
|
1660
|
+
return emailTableCell(`<a href="${url}" target="_blank" style="text-decoration:none;">${imgTag}</a>`, { padding, align });
|
|
1651
1661
|
}
|
|
1652
1662
|
}
|
|
1653
1663
|
};
|
|
@@ -1668,7 +1678,7 @@ const timerTool = {
|
|
|
1668
1678
|
style: {
|
|
1669
1679
|
title: "Style",
|
|
1670
1680
|
options: {
|
|
1671
|
-
textAlign: { label: "Align", defaultValue: "center", widget: "
|
|
1681
|
+
textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
|
|
1672
1682
|
backgroundColor: { label: "Background", defaultValue: "#1f2937", widget: "color_picker" },
|
|
1673
1683
|
textColor: { label: "Text Color", defaultValue: "#ffffff", widget: "color_picker" },
|
|
1674
1684
|
fontSize: { label: "Font Size", defaultValue: "32px", widget: "text" }
|
|
@@ -1676,9 +1686,7 @@ const timerTool = {
|
|
|
1676
1686
|
},
|
|
1677
1687
|
spacing: {
|
|
1678
1688
|
title: "Spacing",
|
|
1679
|
-
options: {
|
|
1680
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1681
|
-
}
|
|
1689
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1682
1690
|
}
|
|
1683
1691
|
},
|
|
1684
1692
|
defaultValues: {
|
|
@@ -1692,39 +1700,25 @@ const timerTool = {
|
|
|
1692
1700
|
},
|
|
1693
1701
|
renderer: {
|
|
1694
1702
|
renderEditor(values) {
|
|
1695
|
-
const padding = values
|
|
1696
|
-
const bg = values
|
|
1697
|
-
const color = values
|
|
1698
|
-
const fontSize = values
|
|
1699
|
-
const
|
|
1700
|
-
return html
|
|
1701
|
-
<div style="padding:${padding};">
|
|
1702
|
-
<div style="background:${bg};color:${color};font-size:${fontSize};text-align:${textAlign};padding:20px;border-radius:4px;font-family:monospace;font-weight:bold;letter-spacing:4px;">
|
|
1703
|
-
00 : 00 : 00 : 00
|
|
1704
|
-
<div style="font-size:11px;letter-spacing:8px;opacity:0.6;margin-top:4px;">DAYS HRS MIN SEC</div>
|
|
1705
|
-
</div>
|
|
1706
|
-
</div>
|
|
1707
|
-
`;
|
|
1703
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1704
|
+
const bg = str(values, "backgroundColor", "#1f2937");
|
|
1705
|
+
const color = str(values, "textColor", "#ffffff");
|
|
1706
|
+
const fontSize = str(values, "fontSize", "32px");
|
|
1707
|
+
const align = str(values, "textAlign", "center");
|
|
1708
|
+
return html`<div style="padding:${padding};"><div style="background:${bg};color:${color};font-size:${fontSize};text-align:${align};padding:20px;border-radius:4px;font-family:monospace;font-weight:bold;letter-spacing:4px;">00 : 00 : 00 : 00<div style="font-size:11px;letter-spacing:8px;opacity:0.6;margin-top:4px;">DAYS HRS MIN SEC</div></div></div>`;
|
|
1708
1709
|
},
|
|
1709
1710
|
renderHtml(values) {
|
|
1710
|
-
const padding = values
|
|
1711
|
-
const bg = values
|
|
1712
|
-
const color = values
|
|
1713
|
-
const fontSize = values
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1716
|
-
return
|
|
1717
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="${textAlign}">
|
|
1718
|
-
<div style="background-color:${bg};color:${color};font-size:${fontSize};text-align:${textAlign};padding:20px;border-radius:4px;font-family:'Courier New',monospace;font-weight:bold;letter-spacing:4px;">
|
|
1719
|
-
<div>00 : 00 : 00 : 00</div>
|
|
1720
|
-
<div style="font-size:11px;letter-spacing:8px;opacity:0.6;margin-top:4px;">DAYS HRS MIN SEC</div>
|
|
1721
|
-
</div>
|
|
1722
|
-
</td></tr></tbody>
|
|
1723
|
-
</table>`;
|
|
1711
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1712
|
+
const bg = str(values, "backgroundColor", "#1f2937");
|
|
1713
|
+
const color = str(values, "textColor", "#ffffff");
|
|
1714
|
+
const fontSize = str(values, "fontSize", "32px");
|
|
1715
|
+
const align = str(values, "textAlign", "center");
|
|
1716
|
+
const inner = `<div style="background-color:${bg};color:${color};font-size:${fontSize};text-align:${align};padding:20px;border-radius:4px;font-family:'Courier New',monospace;font-weight:bold;letter-spacing:4px;"><div>00 : 00 : 00 : 00</div><div style="font-size:11px;letter-spacing:8px;opacity:0.6;margin-top:4px;">DAYS HRS MIN SEC</div></div>`;
|
|
1717
|
+
return emailTableCell(inner, { padding, align });
|
|
1724
1718
|
}
|
|
1725
1719
|
}
|
|
1726
1720
|
};
|
|
1727
|
-
const
|
|
1721
|
+
const DEFAULT_DATA = [
|
|
1728
1722
|
["Header 1", "Header 2", "Header 3"],
|
|
1729
1723
|
["Cell 1", "Cell 2", "Cell 3"],
|
|
1730
1724
|
["Cell 4", "Cell 5", "Cell 6"]
|
|
@@ -1738,9 +1732,7 @@ const tableTool = {
|
|
|
1738
1732
|
options: {
|
|
1739
1733
|
table: {
|
|
1740
1734
|
title: "Table",
|
|
1741
|
-
options: {
|
|
1742
|
-
tableData: { label: "Table Data (JSON)", defaultValue: JSON.stringify(defaultTableData), widget: "rich_text" }
|
|
1743
|
-
}
|
|
1735
|
+
options: { tableData: { label: "Table Data (JSON)", defaultValue: JSON.stringify(DEFAULT_DATA), widget: "rich_text" } }
|
|
1744
1736
|
},
|
|
1745
1737
|
style: {
|
|
1746
1738
|
title: "Style",
|
|
@@ -1754,13 +1746,11 @@ const tableTool = {
|
|
|
1754
1746
|
},
|
|
1755
1747
|
spacing: {
|
|
1756
1748
|
title: "Spacing",
|
|
1757
|
-
options: {
|
|
1758
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1759
|
-
}
|
|
1749
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1760
1750
|
}
|
|
1761
1751
|
},
|
|
1762
1752
|
defaultValues: {
|
|
1763
|
-
tableData: JSON.stringify(
|
|
1753
|
+
tableData: JSON.stringify(DEFAULT_DATA),
|
|
1764
1754
|
headerBg: "#f3f4f6",
|
|
1765
1755
|
headerColor: "#111827",
|
|
1766
1756
|
borderColor: "#e5e7eb",
|
|
@@ -1770,69 +1760,42 @@ const tableTool = {
|
|
|
1770
1760
|
},
|
|
1771
1761
|
renderer: {
|
|
1772
1762
|
renderEditor(values) {
|
|
1773
|
-
const padding = values
|
|
1774
|
-
const
|
|
1775
|
-
const
|
|
1776
|
-
const
|
|
1777
|
-
const
|
|
1778
|
-
const
|
|
1779
|
-
|
|
1780
|
-
try {
|
|
1781
|
-
data = JSON.parse(values.tableData);
|
|
1782
|
-
} catch {
|
|
1783
|
-
}
|
|
1784
|
-
const headerRow = data[0] || [];
|
|
1785
|
-
const bodyRows = data.slice(1);
|
|
1763
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1764
|
+
const hBg = str(values, "headerBg", "#f3f4f6");
|
|
1765
|
+
const hColor = str(values, "headerColor", "#111827");
|
|
1766
|
+
const bColor = str(values, "borderColor", "#e5e7eb");
|
|
1767
|
+
const cPad = str(values, "cellPadding", "8px 12px");
|
|
1768
|
+
const fSize = str(values, "fontSize", "14px");
|
|
1769
|
+
const data = jsonParse(values.tableData, DEFAULT_DATA);
|
|
1786
1770
|
return html`
|
|
1787
1771
|
<div style="padding:${padding};overflow-x:auto;">
|
|
1788
|
-
<table style="width:100%;border-collapse:collapse;font-size:${
|
|
1789
|
-
<thead>
|
|
1790
|
-
|
|
1791
|
-
${headerRow.map((cell) => html`<th style="padding:${cellPadding};background:${headerBg};color:${headerColor};border:1px solid ${borderColor};text-align:left;font-weight:600;">${cell}</th>`)}
|
|
1792
|
-
</tr>
|
|
1793
|
-
</thead>
|
|
1794
|
-
<tbody>
|
|
1795
|
-
${bodyRows.map((row) => html`
|
|
1796
|
-
<tr>
|
|
1797
|
-
${row.map((cell) => html`<td style="padding:${cellPadding};border:1px solid ${borderColor};">${cell}</td>`)}
|
|
1798
|
-
</tr>
|
|
1799
|
-
`)}
|
|
1800
|
-
</tbody>
|
|
1772
|
+
<table style="width:100%;border-collapse:collapse;font-size:${fSize};font-family:arial,sans-serif;">
|
|
1773
|
+
<thead><tr>${data[0]?.map((c) => html`<th style="padding:${cPad};background:${hBg};color:${hColor};border:1px solid ${bColor};text-align:left;font-weight:600;">${c}</th>`)}</tr></thead>
|
|
1774
|
+
<tbody>${data.slice(1).map((row) => html`<tr>${row.map((c) => html`<td style="padding:${cPad};border:1px solid ${bColor};">${c}</td>`)}</tr>`)}</tbody>
|
|
1801
1775
|
</table>
|
|
1802
1776
|
</div>
|
|
1803
1777
|
`;
|
|
1804
1778
|
},
|
|
1805
1779
|
renderHtml(values) {
|
|
1806
|
-
const padding = values
|
|
1807
|
-
const
|
|
1808
|
-
const
|
|
1809
|
-
const
|
|
1810
|
-
const
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
const bodyRows = data.slice(1);
|
|
1819
|
-
const headerHtml = headerRow.map(
|
|
1820
|
-
(c) => `<th style="padding:${cellPadding};background-color:${headerBg};color:${headerColor};border:1px solid ${borderColor};text-align:left;font-weight:600;font-family:arial,helvetica,sans-serif;font-size:${fontSize};">${c}</th>`
|
|
1821
|
-
).join("");
|
|
1822
|
-
const bodyHtml = bodyRows.map(
|
|
1823
|
-
(row) => `<tr>${row.map((c) => `<td style="padding:${cellPadding};border:1px solid ${borderColor};font-family:arial,helvetica,sans-serif;font-size:${fontSize};">${c}</td>`).join("")}</tr>`
|
|
1824
|
-
).join("");
|
|
1825
|
-
return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
1826
|
-
<tbody><tr><td style="overflow-wrap:break-word;word-break:break-word;padding:${padding};font-family:arial,helvetica,sans-serif;" align="left">
|
|
1827
|
-
<table cellpadding="0" cellspacing="0" width="100%" border="0" style="border-collapse:collapse;">
|
|
1828
|
-
<thead><tr>${headerHtml}</tr></thead>
|
|
1829
|
-
<tbody>${bodyHtml}</tbody>
|
|
1830
|
-
</table>
|
|
1831
|
-
</td></tr></tbody>
|
|
1832
|
-
</table>`;
|
|
1780
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1781
|
+
const hBg = str(values, "headerBg", "#f3f4f6");
|
|
1782
|
+
const hColor = str(values, "headerColor", "#111827");
|
|
1783
|
+
const bColor = str(values, "borderColor", "#e5e7eb");
|
|
1784
|
+
const cPad = str(values, "cellPadding", "8px 12px");
|
|
1785
|
+
const fSize = str(values, "fontSize", "14px");
|
|
1786
|
+
const data = jsonParse(values.tableData, DEFAULT_DATA);
|
|
1787
|
+
const font = "font-family:arial,helvetica,sans-serif;";
|
|
1788
|
+
const hCells = (data[0] || []).map((c) => `<th style="padding:${cPad};background-color:${hBg};color:${hColor};border:1px solid ${bColor};text-align:left;font-weight:600;${font}font-size:${fSize};">${c}</th>`).join("");
|
|
1789
|
+
const bRows = data.slice(1).map((row) => `<tr>${row.map((c) => `<td style="padding:${cPad};border:1px solid ${bColor};${font}font-size:${fSize};">${c}</td>`).join("")}</tr>`).join("");
|
|
1790
|
+
const inner = `<table cellpadding="0" cellspacing="0" width="100%" border="0" style="border-collapse:collapse;"><thead><tr>${hCells}</tr></thead><tbody>${bRows}</tbody></table>`;
|
|
1791
|
+
return emailTableCell(inner, { padding });
|
|
1833
1792
|
}
|
|
1834
1793
|
}
|
|
1835
1794
|
};
|
|
1795
|
+
const DEFAULT_FIELDS = [
|
|
1796
|
+
{ label: "Name", name: "name", type: "text", placeholder: "Your name" },
|
|
1797
|
+
{ label: "Email", name: "email", type: "email", placeholder: "your@email.com" }
|
|
1798
|
+
];
|
|
1836
1799
|
const formTool = {
|
|
1837
1800
|
name: "form",
|
|
1838
1801
|
label: "Form",
|
|
@@ -1846,10 +1809,7 @@ const formTool = {
|
|
|
1846
1809
|
actionUrl: { label: "Action URL", defaultValue: "#", widget: "text" },
|
|
1847
1810
|
method: { label: "Method", defaultValue: "POST", widget: "text" },
|
|
1848
1811
|
submitText: { label: "Submit Text", defaultValue: "Submit", widget: "text" },
|
|
1849
|
-
fields: { label: "Fields (JSON)", defaultValue: JSON.stringify(
|
|
1850
|
-
{ label: "Name", name: "name", type: "text", placeholder: "Your name" },
|
|
1851
|
-
{ label: "Email", name: "email", type: "email", placeholder: "your@email.com" }
|
|
1852
|
-
]), widget: "rich_text" }
|
|
1812
|
+
fields: { label: "Fields (JSON)", defaultValue: JSON.stringify(DEFAULT_FIELDS), widget: "rich_text" }
|
|
1853
1813
|
}
|
|
1854
1814
|
},
|
|
1855
1815
|
style: {
|
|
@@ -1861,34 +1821,25 @@ const formTool = {
|
|
|
1861
1821
|
},
|
|
1862
1822
|
spacing: {
|
|
1863
1823
|
title: "Spacing",
|
|
1864
|
-
options: {
|
|
1865
|
-
containerPadding: { label: "Padding", defaultValue: "10px", widget: "text" }
|
|
1866
|
-
}
|
|
1824
|
+
options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
|
|
1867
1825
|
}
|
|
1868
1826
|
},
|
|
1869
1827
|
defaultValues: {
|
|
1870
1828
|
actionUrl: "#",
|
|
1871
1829
|
method: "POST",
|
|
1872
1830
|
submitText: "Submit",
|
|
1873
|
-
fields: JSON.stringify(
|
|
1874
|
-
{ label: "Name", name: "name", type: "text", placeholder: "Your name" },
|
|
1875
|
-
{ label: "Email", name: "email", type: "email", placeholder: "your@email.com" }
|
|
1876
|
-
]),
|
|
1831
|
+
fields: JSON.stringify(DEFAULT_FIELDS),
|
|
1877
1832
|
buttonBg: "#3b82f6",
|
|
1878
1833
|
buttonColor: "#ffffff",
|
|
1879
1834
|
containerPadding: "10px"
|
|
1880
1835
|
},
|
|
1881
1836
|
renderer: {
|
|
1882
1837
|
renderEditor(values) {
|
|
1883
|
-
const padding = values
|
|
1884
|
-
const submitText = values
|
|
1885
|
-
const
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
try {
|
|
1889
|
-
fields = JSON.parse(values.fields);
|
|
1890
|
-
} catch {
|
|
1891
|
-
}
|
|
1838
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1839
|
+
const submitText = str(values, "submitText", "Submit");
|
|
1840
|
+
const btnBg = str(values, "buttonBg", "#3b82f6");
|
|
1841
|
+
const btnColor = str(values, "buttonColor", "#ffffff");
|
|
1842
|
+
const fields = jsonParse(values.fields, DEFAULT_FIELDS);
|
|
1892
1843
|
return html`
|
|
1893
1844
|
<div style="padding:${padding};font-family:arial,sans-serif;">
|
|
1894
1845
|
${fields.map((f) => html`
|
|
@@ -1897,36 +1848,24 @@ const formTool = {
|
|
|
1897
1848
|
<input type=${f.type || "text"} placeholder=${f.placeholder || ""} style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:4px;font-size:14px;box-sizing:border-box;" />
|
|
1898
1849
|
</div>
|
|
1899
1850
|
`)}
|
|
1900
|
-
<button style="background:${
|
|
1851
|
+
<button style="background:${btnBg};color:${btnColor};border:none;padding:10px 24px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;">${submitText}</button>
|
|
1901
1852
|
</div>
|
|
1902
1853
|
`;
|
|
1903
1854
|
},
|
|
1904
1855
|
renderHtml(values) {
|
|
1905
|
-
const padding = values
|
|
1906
|
-
const actionUrl = values
|
|
1907
|
-
const method = values
|
|
1908
|
-
const submitText = values
|
|
1909
|
-
const
|
|
1910
|
-
const
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
fields = JSON.parse(values.fields);
|
|
1914
|
-
} catch {
|
|
1915
|
-
}
|
|
1856
|
+
const padding = str(values, "containerPadding", "10px");
|
|
1857
|
+
const actionUrl = str(values, "actionUrl", "#");
|
|
1858
|
+
const method = str(values, "method", "POST");
|
|
1859
|
+
const submitText = str(values, "submitText", "Submit");
|
|
1860
|
+
const btnBg = str(values, "buttonBg", "#3b82f6");
|
|
1861
|
+
const btnColor = str(values, "buttonColor", "#ffffff");
|
|
1862
|
+
const fields = jsonParse(values.fields, DEFAULT_FIELDS);
|
|
1863
|
+
const font = "font-family:arial,helvetica,sans-serif;";
|
|
1916
1864
|
const fieldsHtml = fields.map(
|
|
1917
|
-
(f) => `<div style="margin-bottom:12px;"
|
|
1918
|
-
<label style="display:block;font-size:13px;color:#374151;margin-bottom:4px;font-weight:500;font-family:arial,helvetica,sans-serif;">${f.label}</label>
|
|
1919
|
-
<input type="${f.type || "text"}" name="${f.name}" placeholder="${f.placeholder || ""}" style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:4px;font-size:14px;box-sizing:border-box;font-family:arial,helvetica,sans-serif;" />
|
|
1920
|
-
</div>`
|
|
1865
|
+
(f) => `<div style="margin-bottom:12px;"><label style="display:block;font-size:13px;color:#374151;margin-bottom:4px;font-weight:500;${font}">${f.label}</label><input type="${f.type || "text"}" name="${f.name}" placeholder="${f.placeholder || ""}" style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:4px;font-size:14px;box-sizing:border-box;${font}" /></div>`
|
|
1921
1866
|
).join("");
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
<form action="${actionUrl}" method="${method}">
|
|
1925
|
-
${fieldsHtml}
|
|
1926
|
-
<button type="submit" style="background-color:${buttonBg};color:${buttonColor};border:none;padding:10px 24px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;font-family:arial,helvetica,sans-serif;">${submitText}</button>
|
|
1927
|
-
</form>
|
|
1928
|
-
</td></tr></tbody>
|
|
1929
|
-
</table>`;
|
|
1867
|
+
const inner = `<form action="${actionUrl}" method="${method}">${fieldsHtml}<button type="submit" style="background-color:${btnBg};color:${btnColor};border:none;padding:10px 24px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;${font}">${submitText}</button></form>`;
|
|
1868
|
+
return emailTableCell(inner, { padding });
|
|
1930
1869
|
}
|
|
1931
1870
|
}
|
|
1932
1871
|
};
|
|
@@ -2151,11 +2090,30 @@ let MailEditorElement = class extends LitElement {
|
|
|
2151
2090
|
this.dragManager = null;
|
|
2152
2091
|
this.callbacks = /* @__PURE__ */ new Map();
|
|
2153
2092
|
this.unsubscribe = null;
|
|
2093
|
+
this._handleKeydown = (e) => {
|
|
2094
|
+
const mod = e.metaKey || e.ctrlKey;
|
|
2095
|
+
const tag = e.target?.tagName;
|
|
2096
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
2097
|
+
if (mod && e.key === "z" && !e.shiftKey) {
|
|
2098
|
+
e.preventDefault();
|
|
2099
|
+
this.store.undo();
|
|
2100
|
+
} else if (mod && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
2101
|
+
e.preventDefault();
|
|
2102
|
+
this.store.redo();
|
|
2103
|
+
} else if ((e.key === "Delete" || e.key === "Backspace") && this.store.selectedId) {
|
|
2104
|
+
e.preventDefault();
|
|
2105
|
+
this.store.removeContent(this.store.selectedId);
|
|
2106
|
+
} else if (e.key === "Escape") {
|
|
2107
|
+
this.store.select(null);
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2154
2110
|
}
|
|
2155
2111
|
connectedCallback() {
|
|
2156
2112
|
super.connectedCallback();
|
|
2157
2113
|
this.registerBuiltInTools();
|
|
2158
2114
|
this.applyOptions();
|
|
2115
|
+
this.setAttribute("tabindex", "0");
|
|
2116
|
+
this.addEventListener("keydown", this._handleKeydown);
|
|
2159
2117
|
}
|
|
2160
2118
|
firstUpdated() {
|
|
2161
2119
|
this.dragManager = new DragManager(this.store, this.toolRegistry, this.shadowRoot);
|
|
@@ -2174,6 +2132,7 @@ let MailEditorElement = class extends LitElement {
|
|
|
2174
2132
|
this.dragManager?.detach();
|
|
2175
2133
|
this.unsubscribe?.();
|
|
2176
2134
|
this.store.events.removeAllListeners();
|
|
2135
|
+
this.removeEventListener("keydown", this._handleKeydown);
|
|
2177
2136
|
}
|
|
2178
2137
|
// ----------------------------------------------------------
|
|
2179
2138
|
// Public API — mirrors Unlayer
|
|
@@ -2283,4 +2242,4 @@ export {
|
|
|
2283
2242
|
MailEditorElement as M,
|
|
2284
2243
|
ToolRegistry as T
|
|
2285
2244
|
};
|
|
2286
|
-
//# sourceMappingURL=mail-editor-
|
|
2245
|
+
//# sourceMappingURL=mail-editor-ClkIyPni.js.map
|