@marlinjai/email-editor-core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/MjmlBuilder-DsiOyhiL.d.mts +667 -0
- package/dist/MjmlBuilder-DsiOyhiL.d.ts +667 -0
- package/dist/{TemplateModel-v__r5Nvi.d.mts → TemplateModel-CmpvrapI.d.ts} +2 -433
- package/dist/{TemplateModel-v__r5Nvi.d.ts → TemplateModel-PcCoyBdo.d.mts} +2 -433
- package/dist/browser.d.mts +13 -0
- package/dist/browser.d.ts +13 -0
- package/dist/browser.js +959 -0
- package/dist/browser.mjs +30 -0
- package/dist/chunk-NQPMHQ5G.mjs +490 -0
- package/dist/chunk-OOCYGZMB.mjs +1406 -0
- package/dist/index.d.mts +7 -30
- package/dist/index.d.ts +7 -30
- package/dist/index.js +881 -33
- package/dist/index.mjs +46 -51
- package/dist/server.d.mts +9 -167
- package/dist/server.d.ts +9 -167
- package/dist/server.js +172 -79
- package/dist/server.mjs +55 -817
- package/package.json +13 -7
- package/dist/TemplateModel-Dq4GoWpl.d.mts +0 -14743
- package/dist/TemplateModel-Dq4GoWpl.d.ts +0 -14743
- package/dist/chunk-2ZL7A2I6.mjs +0 -1029
- package/dist/chunk-MBTNU2JZ.mjs +0 -395
package/dist/chunk-2ZL7A2I6.mjs
DELETED
|
@@ -1,1029 +0,0 @@
|
|
|
1
|
-
// src/schema/gradient.ts
|
|
2
|
-
function buildGradientCSS(gradient) {
|
|
3
|
-
if (gradient.stops.length === 0) return void 0;
|
|
4
|
-
const stopList = gradient.stops.map((s) => `${s.color} ${s.position}%`).join(", ");
|
|
5
|
-
if (gradient.type === "radial") {
|
|
6
|
-
return `radial-gradient(circle, ${stopList})`;
|
|
7
|
-
}
|
|
8
|
-
return `linear-gradient(${gradient.angle}deg, ${stopList})`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// src/schema/types.ts
|
|
12
|
-
function isWrapper(item) {
|
|
13
|
-
return item.type === "wrapper";
|
|
14
|
-
}
|
|
15
|
-
function allSections(items) {
|
|
16
|
-
return items.flatMap((item) => isWrapper(item) ? item.sections : [item]);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// src/schema/validation.ts
|
|
20
|
-
import { z } from "zod";
|
|
21
|
-
var SpacingSchema = z.object({
|
|
22
|
-
top: z.string().optional(),
|
|
23
|
-
right: z.string().optional(),
|
|
24
|
-
bottom: z.string().optional(),
|
|
25
|
-
left: z.string().optional()
|
|
26
|
-
});
|
|
27
|
-
var GradientStopSchema = z.object({
|
|
28
|
-
color: z.string().min(1),
|
|
29
|
-
position: z.number().min(0).max(100)
|
|
30
|
-
});
|
|
31
|
-
var BackgroundGradientSchema = z.object({
|
|
32
|
-
type: z.enum(["linear", "radial"]),
|
|
33
|
-
angle: z.number().min(0).max(360),
|
|
34
|
-
stops: z.array(GradientStopSchema).min(1)
|
|
35
|
-
});
|
|
36
|
-
var ExtraAttributesSchema = z.record(z.string().regex(/^[a-z][a-z0-9-]*$/), z.string());
|
|
37
|
-
var MjmlHeadSchema = z.object({
|
|
38
|
-
attributes: z.string().optional(),
|
|
39
|
-
bodyAttributes: ExtraAttributesSchema.optional(),
|
|
40
|
-
headRaw: z.string().optional()
|
|
41
|
-
});
|
|
42
|
-
var CustomFontSchema = z.object({
|
|
43
|
-
name: z.string(),
|
|
44
|
-
href: z.string()
|
|
45
|
-
});
|
|
46
|
-
var TemplateMetadataSchema = z.object({
|
|
47
|
-
name: z.string().optional(),
|
|
48
|
-
subject: z.string().optional(),
|
|
49
|
-
previewText: z.string().optional(),
|
|
50
|
-
title: z.string().optional(),
|
|
51
|
-
// Epoch milliseconds (what the editor's store emits) or an ISO 8601 string.
|
|
52
|
-
createdAt: z.union([z.string(), z.number()]).optional(),
|
|
53
|
-
updatedAt: z.union([z.string(), z.number()]).optional(),
|
|
54
|
-
// Head component settings
|
|
55
|
-
fonts: z.array(CustomFontSchema).optional(),
|
|
56
|
-
breakpoint: z.string().optional(),
|
|
57
|
-
customCSS: z.string().optional(),
|
|
58
|
-
inlineCSS: z.string().optional(),
|
|
59
|
-
mjmlHead: MjmlHeadSchema.optional()
|
|
60
|
-
});
|
|
61
|
-
var TextBlockSchema = z.object({
|
|
62
|
-
id: z.string(),
|
|
63
|
-
type: z.literal("text"),
|
|
64
|
-
hidden: z.boolean().optional(),
|
|
65
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
66
|
-
content: z.string(),
|
|
67
|
-
align: z.enum(["left", "center", "right", "justify"]).optional(),
|
|
68
|
-
color: z.string().optional(),
|
|
69
|
-
fontSize: z.string().optional(),
|
|
70
|
-
fontFamily: z.string().optional(),
|
|
71
|
-
padding: SpacingSchema.optional(),
|
|
72
|
-
lineHeight: z.string().optional()
|
|
73
|
-
});
|
|
74
|
-
var ImageBlockSchema = z.object({
|
|
75
|
-
id: z.string(),
|
|
76
|
-
type: z.literal("image"),
|
|
77
|
-
hidden: z.boolean().optional(),
|
|
78
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
79
|
-
src: z.string().min(1),
|
|
80
|
-
// Allow any non-empty string, not just URLs
|
|
81
|
-
alt: z.string().optional(),
|
|
82
|
-
width: z.string().optional(),
|
|
83
|
-
height: z.string().optional(),
|
|
84
|
-
align: z.enum(["left", "center", "right"]).optional(),
|
|
85
|
-
href: z.string().optional(),
|
|
86
|
-
// Allow any string for link
|
|
87
|
-
padding: SpacingSchema.optional(),
|
|
88
|
-
borderRadius: z.string().optional()
|
|
89
|
-
// Rounded corners
|
|
90
|
-
});
|
|
91
|
-
var ButtonBlockSchema = z.object({
|
|
92
|
-
id: z.string(),
|
|
93
|
-
type: z.literal("button"),
|
|
94
|
-
hidden: z.boolean().optional(),
|
|
95
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
96
|
-
label: z.string().min(1),
|
|
97
|
-
href: z.string().min(1),
|
|
98
|
-
// Allow any non-empty string
|
|
99
|
-
align: z.enum(["left", "center", "right"]).optional(),
|
|
100
|
-
backgroundColor: z.string().optional(),
|
|
101
|
-
color: z.string().optional(),
|
|
102
|
-
borderRadius: z.string().optional(),
|
|
103
|
-
border: z.string().optional(),
|
|
104
|
-
// CSS border shorthand
|
|
105
|
-
padding: SpacingSchema.optional(),
|
|
106
|
-
innerPadding: z.string().optional()
|
|
107
|
-
});
|
|
108
|
-
var DividerBlockSchema = z.object({
|
|
109
|
-
id: z.string(),
|
|
110
|
-
type: z.literal("divider"),
|
|
111
|
-
hidden: z.boolean().optional(),
|
|
112
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
113
|
-
borderColor: z.string().optional(),
|
|
114
|
-
borderWidth: z.string().optional(),
|
|
115
|
-
borderStyle: z.enum(["solid", "dashed", "dotted"]).optional(),
|
|
116
|
-
width: z.string().optional(),
|
|
117
|
-
// Width of the divider line
|
|
118
|
-
padding: SpacingSchema.optional()
|
|
119
|
-
});
|
|
120
|
-
var SpacerBlockSchema = z.object({
|
|
121
|
-
id: z.string(),
|
|
122
|
-
type: z.literal("spacer"),
|
|
123
|
-
hidden: z.boolean().optional(),
|
|
124
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
125
|
-
height: z.string()
|
|
126
|
-
});
|
|
127
|
-
var HeaderBlockSchema = z.object({
|
|
128
|
-
id: z.string(),
|
|
129
|
-
type: z.literal("header"),
|
|
130
|
-
hidden: z.boolean().optional(),
|
|
131
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
132
|
-
locked: z.literal(true)
|
|
133
|
-
});
|
|
134
|
-
var FooterBlockSchema = z.object({
|
|
135
|
-
id: z.string(),
|
|
136
|
-
type: z.literal("footer"),
|
|
137
|
-
hidden: z.boolean().optional(),
|
|
138
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
139
|
-
locked: z.literal(true)
|
|
140
|
-
});
|
|
141
|
-
var SocialBlockSchema = z.object({
|
|
142
|
-
id: z.string(),
|
|
143
|
-
type: z.literal("social"),
|
|
144
|
-
hidden: z.boolean().optional(),
|
|
145
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
146
|
-
mode: z.enum(["horizontal", "vertical"]).optional(),
|
|
147
|
-
align: z.enum(["left", "center", "right"]).optional(),
|
|
148
|
-
iconSize: z.string().optional(),
|
|
149
|
-
iconPadding: z.string().optional(),
|
|
150
|
-
borderRadius: z.string().optional(),
|
|
151
|
-
// For round icons
|
|
152
|
-
links: z.array(
|
|
153
|
-
z.object({
|
|
154
|
-
platform: z.enum(["facebook", "twitter", "instagram", "linkedin", "youtube", "pinterest", "github"]),
|
|
155
|
-
url: z.string(),
|
|
156
|
-
color: z.string().optional()
|
|
157
|
-
// Per-icon color override (uses platform default if not set)
|
|
158
|
-
})
|
|
159
|
-
)
|
|
160
|
-
});
|
|
161
|
-
var HeroBlockSchema = z.object({
|
|
162
|
-
id: z.string(),
|
|
163
|
-
type: z.literal("hero"),
|
|
164
|
-
hidden: z.boolean().optional(),
|
|
165
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
166
|
-
backgroundImage: z.string().min(1),
|
|
167
|
-
backgroundHeight: z.string().optional(),
|
|
168
|
-
backgroundWidth: z.string().optional(),
|
|
169
|
-
backgroundColor: z.string().optional(),
|
|
170
|
-
verticalAlign: z.enum(["top", "middle", "bottom"]).optional(),
|
|
171
|
-
mode: z.enum(["fluid-height", "fixed-height"]).optional()
|
|
172
|
-
});
|
|
173
|
-
var AccordionBlockSchema = z.object({
|
|
174
|
-
id: z.string(),
|
|
175
|
-
type: z.literal("accordion"),
|
|
176
|
-
hidden: z.boolean().optional(),
|
|
177
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
178
|
-
items: z.array(
|
|
179
|
-
z.object({
|
|
180
|
-
title: z.string(),
|
|
181
|
-
content: z.string()
|
|
182
|
-
})
|
|
183
|
-
),
|
|
184
|
-
iconPosition: z.enum(["left", "right"]).optional(),
|
|
185
|
-
borderColor: z.string().optional(),
|
|
186
|
-
fontFamily: z.string().optional()
|
|
187
|
-
});
|
|
188
|
-
var RawBlockSchema = z.object({
|
|
189
|
-
id: z.string(),
|
|
190
|
-
type: z.literal("raw"),
|
|
191
|
-
hidden: z.boolean().optional(),
|
|
192
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
193
|
-
html: z.string()
|
|
194
|
-
});
|
|
195
|
-
var NavbarBlockSchema = z.object({
|
|
196
|
-
id: z.string(),
|
|
197
|
-
type: z.literal("navbar"),
|
|
198
|
-
hidden: z.boolean().optional(),
|
|
199
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
200
|
-
links: z.array(
|
|
201
|
-
z.object({
|
|
202
|
-
label: z.string(),
|
|
203
|
-
href: z.string(),
|
|
204
|
-
color: z.string().optional()
|
|
205
|
-
})
|
|
206
|
-
),
|
|
207
|
-
hamburger: z.boolean().optional(),
|
|
208
|
-
baseUrl: z.string().optional(),
|
|
209
|
-
align: z.enum(["left", "center", "right"]).optional(),
|
|
210
|
-
icoColor: z.string().optional(),
|
|
211
|
-
padding: SpacingSchema.optional()
|
|
212
|
-
});
|
|
213
|
-
var CarouselBlockSchema = z.object({
|
|
214
|
-
id: z.string(),
|
|
215
|
-
type: z.literal("carousel"),
|
|
216
|
-
hidden: z.boolean().optional(),
|
|
217
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
218
|
-
images: z.array(
|
|
219
|
-
z.object({
|
|
220
|
-
src: z.string(),
|
|
221
|
-
alt: z.string().optional(),
|
|
222
|
-
href: z.string().optional(),
|
|
223
|
-
thumbnailSrc: z.string().optional()
|
|
224
|
-
})
|
|
225
|
-
),
|
|
226
|
-
thumbnails: z.enum(["visible", "hidden"]).optional(),
|
|
227
|
-
borderRadius: z.string().optional(),
|
|
228
|
-
iconWidth: z.string().optional(),
|
|
229
|
-
tbBorderRadius: z.string().optional(),
|
|
230
|
-
padding: SpacingSchema.optional()
|
|
231
|
-
});
|
|
232
|
-
var TableBlockSchema = z.object({
|
|
233
|
-
id: z.string(),
|
|
234
|
-
type: z.literal("table"),
|
|
235
|
-
hidden: z.boolean().optional(),
|
|
236
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
237
|
-
headers: z.array(z.string()),
|
|
238
|
-
rows: z.array(z.array(z.string())),
|
|
239
|
-
align: z.enum(["left", "center", "right"]).optional(),
|
|
240
|
-
color: z.string().optional(),
|
|
241
|
-
fontFamily: z.string().optional(),
|
|
242
|
-
fontSize: z.string().optional(),
|
|
243
|
-
cellpadding: z.string().optional(),
|
|
244
|
-
cellspacing: z.string().optional(),
|
|
245
|
-
border: z.string().optional(),
|
|
246
|
-
padding: SpacingSchema.optional()
|
|
247
|
-
});
|
|
248
|
-
var BlockSchema = z.discriminatedUnion("type", [
|
|
249
|
-
TextBlockSchema,
|
|
250
|
-
ImageBlockSchema,
|
|
251
|
-
ButtonBlockSchema,
|
|
252
|
-
DividerBlockSchema,
|
|
253
|
-
SpacerBlockSchema,
|
|
254
|
-
HeaderBlockSchema,
|
|
255
|
-
FooterBlockSchema,
|
|
256
|
-
SocialBlockSchema,
|
|
257
|
-
HeroBlockSchema,
|
|
258
|
-
AccordionBlockSchema,
|
|
259
|
-
RawBlockSchema,
|
|
260
|
-
NavbarBlockSchema,
|
|
261
|
-
CarouselBlockSchema,
|
|
262
|
-
TableBlockSchema
|
|
263
|
-
]);
|
|
264
|
-
var ColumnSchema = z.object({
|
|
265
|
-
id: z.string(),
|
|
266
|
-
width: z.number().min(0).max(100).optional(),
|
|
267
|
-
backgroundColor: z.string().optional(),
|
|
268
|
-
backgroundGradient: BackgroundGradientSchema.optional(),
|
|
269
|
-
padding: SpacingSchema.optional(),
|
|
270
|
-
verticalAlign: z.enum(["top", "middle", "bottom"]).optional(),
|
|
271
|
-
// Vertical content alignment
|
|
272
|
-
hidden: z.boolean().optional(),
|
|
273
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
274
|
-
blocks: z.array(BlockSchema)
|
|
275
|
-
});
|
|
276
|
-
var sectionFields = {
|
|
277
|
-
id: z.string(),
|
|
278
|
-
type: z.literal("section"),
|
|
279
|
-
backgroundColor: z.string().optional(),
|
|
280
|
-
backgroundImage: z.string().optional(),
|
|
281
|
-
backgroundPosition: z.string().optional(),
|
|
282
|
-
backgroundRepeat: z.enum(["repeat", "no-repeat"]).optional(),
|
|
283
|
-
backgroundSize: z.string().optional(),
|
|
284
|
-
backgroundGradient: BackgroundGradientSchema.optional(),
|
|
285
|
-
padding: SpacingSchema.optional(),
|
|
286
|
-
noStack: z.boolean().optional(),
|
|
287
|
-
fullWidth: z.boolean().optional(),
|
|
288
|
-
hidden: z.boolean().optional(),
|
|
289
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
290
|
-
bodyRaw: z.boolean().optional(),
|
|
291
|
-
columns: z.array(ColumnSchema).min(1)
|
|
292
|
-
};
|
|
293
|
-
var SectionSchema = z.object({
|
|
294
|
-
...sectionFields,
|
|
295
|
-
isWrapper: z.undefined({ invalid_type_error: "isWrapper is a schema 1.0 field; a 1.1 document holds a wrapper instead" }).optional()
|
|
296
|
-
});
|
|
297
|
-
var SectionSchemaV1_0 = z.object({
|
|
298
|
-
...sectionFields,
|
|
299
|
-
isWrapper: z.boolean().optional()
|
|
300
|
-
});
|
|
301
|
-
var WrapperSchema = z.object({
|
|
302
|
-
id: z.string(),
|
|
303
|
-
type: z.literal("wrapper"),
|
|
304
|
-
hidden: z.boolean().optional(),
|
|
305
|
-
backgroundColor: z.string().optional(),
|
|
306
|
-
backgroundImage: z.string().optional(),
|
|
307
|
-
backgroundGradient: BackgroundGradientSchema.optional(),
|
|
308
|
-
backgroundPosition: z.string().optional(),
|
|
309
|
-
backgroundRepeat: z.enum(["repeat", "no-repeat"]).optional(),
|
|
310
|
-
backgroundSize: z.string().optional(),
|
|
311
|
-
border: z.string().optional(),
|
|
312
|
-
borderTop: z.string().optional(),
|
|
313
|
-
borderRight: z.string().optional(),
|
|
314
|
-
borderBottom: z.string().optional(),
|
|
315
|
-
borderLeft: z.string().optional(),
|
|
316
|
-
borderRadius: z.string().optional(),
|
|
317
|
-
padding: SpacingSchema.optional(),
|
|
318
|
-
fullWidth: z.boolean().optional(),
|
|
319
|
-
cssClass: z.string().optional(),
|
|
320
|
-
gap: z.string().regex(/^[0-9]+(\.[0-9]+)?px$/, "gap is a length in px, e.g. 16px").optional(),
|
|
321
|
-
textAlign: z.enum(["left", "center", "right"]).optional(),
|
|
322
|
-
extraAttributes: ExtraAttributesSchema.optional(),
|
|
323
|
-
sections: z.array(SectionSchema)
|
|
324
|
-
});
|
|
325
|
-
var TopLevelItemSchema = z.discriminatedUnion("type", [SectionSchema, WrapperSchema]);
|
|
326
|
-
var EmailTemplateSchemaV1_0 = z.object({
|
|
327
|
-
id: z.string().optional(),
|
|
328
|
-
version: z.literal("1.0"),
|
|
329
|
-
metadata: TemplateMetadataSchema,
|
|
330
|
-
sections: z.array(SectionSchemaV1_0)
|
|
331
|
-
});
|
|
332
|
-
var EmailTemplateSchemaV1_1 = z.object({
|
|
333
|
-
id: z.string().optional(),
|
|
334
|
-
version: z.literal("1.1"),
|
|
335
|
-
metadata: TemplateMetadataSchema,
|
|
336
|
-
sections: z.array(TopLevelItemSchema)
|
|
337
|
-
});
|
|
338
|
-
var EmailTemplateSchema = z.discriminatedUnion("version", [EmailTemplateSchemaV1_0, EmailTemplateSchemaV1_1]);
|
|
339
|
-
function validateTemplate(template) {
|
|
340
|
-
return EmailTemplateSchema.safeParse(template);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/schema/migrate.ts
|
|
344
|
-
import { nanoid } from "nanoid";
|
|
345
|
-
var CURRENT_TEMPLATE_VERSION = "1.1";
|
|
346
|
-
var SUPPORTED_TEMPLATE_VERSIONS = ["1.0", CURRENT_TEMPLATE_VERSION];
|
|
347
|
-
var TemplateMigrationError = class extends Error {
|
|
348
|
-
constructor(code, message, options = {}) {
|
|
349
|
-
super(message);
|
|
350
|
-
this.name = "TemplateMigrationError";
|
|
351
|
-
this.code = code;
|
|
352
|
-
this.version = options.version;
|
|
353
|
-
this.issues = options.issues ?? [];
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
function isTemplateMigrationError(error) {
|
|
357
|
-
return error instanceof TemplateMigrationError;
|
|
358
|
-
}
|
|
359
|
-
function parseVersion(version) {
|
|
360
|
-
const match = /^(\d+)\.(\d+)$/.exec(version);
|
|
361
|
-
if (!match) return null;
|
|
362
|
-
return [Number(match[1]), Number(match[2])];
|
|
363
|
-
}
|
|
364
|
-
function compareVersions(a, b) {
|
|
365
|
-
return a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1];
|
|
366
|
-
}
|
|
367
|
-
function migrateTemplate(doc) {
|
|
368
|
-
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
|
|
369
|
-
throw new TemplateMigrationError(
|
|
370
|
-
"INVALID_INPUT",
|
|
371
|
-
`Expected a template document object, received ${doc === null ? "null" : Array.isArray(doc) ? "an array" : typeof doc}.`
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
const version = doc.version;
|
|
375
|
-
if (typeof version !== "string" || version.length === 0) {
|
|
376
|
-
throw new TemplateMigrationError(
|
|
377
|
-
"MISSING_VERSION",
|
|
378
|
-
'The template document has no "version" field, so its schema cannot be determined.'
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
const parsed = parseVersion(version);
|
|
382
|
-
if (!parsed) {
|
|
383
|
-
throw new TemplateMigrationError(
|
|
384
|
-
"UNSUPPORTED_VERSION",
|
|
385
|
-
`Template schema version "${version}" is not of the form "major.minor".`,
|
|
386
|
-
{ version }
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
const current = parseVersion(CURRENT_TEMPLATE_VERSION);
|
|
390
|
-
if (compareVersions(parsed, current) > 0) {
|
|
391
|
-
throw new TemplateMigrationError(
|
|
392
|
-
"NEWER_VERSION",
|
|
393
|
-
`Template schema version "${version}" is newer than this editor supports (${CURRENT_TEMPLATE_VERSION}). Upgrade the @marlinjai/email-editor packages to open it.`,
|
|
394
|
-
{ version }
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
if (!SUPPORTED_TEMPLATE_VERSIONS.includes(version)) {
|
|
398
|
-
throw new TemplateMigrationError(
|
|
399
|
-
"UNSUPPORTED_VERSION",
|
|
400
|
-
`Template schema version "${version}" is not supported and has no migration to ${CURRENT_TEMPLATE_VERSION}.`,
|
|
401
|
-
{ version }
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
let next = doc;
|
|
405
|
-
if (version === "1.0") {
|
|
406
|
-
assertValid(EmailTemplateSchemaV1_0, next, version);
|
|
407
|
-
next = migrateV1_0ToV1_1(next);
|
|
408
|
-
}
|
|
409
|
-
assertValid(EmailTemplateSchemaV1_1, next, version);
|
|
410
|
-
return next;
|
|
411
|
-
}
|
|
412
|
-
function assertValid(schema, doc, version) {
|
|
413
|
-
const result = schema.safeParse(doc);
|
|
414
|
-
if (result.success) return;
|
|
415
|
-
const issues = result.error.issues.map((issue) => ({ path: issue.path, message: issue.message }));
|
|
416
|
-
const first = issues[0];
|
|
417
|
-
throw new TemplateMigrationError(
|
|
418
|
-
"INVALID_DOCUMENT",
|
|
419
|
-
`Template document does not match schema version ${version}: ${first ? `${first.path.join(".") || "(root)"}: ${first.message}` : "unknown validation error"}${issues.length > 1 ? ` (and ${issues.length - 1} more)` : ""}.`,
|
|
420
|
-
{ version, issues }
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
function migrateV1_0ToV1_1(doc) {
|
|
424
|
-
if (!doc.sections.some((s) => s.isWrapper === true && !s.bodyRaw)) {
|
|
425
|
-
return { ...doc, version: "1.1", sections: doc.sections.map(stripIsWrapper) };
|
|
426
|
-
}
|
|
427
|
-
const ids = /* @__PURE__ */ new Set();
|
|
428
|
-
for (const s of doc.sections) {
|
|
429
|
-
ids.add(s.id);
|
|
430
|
-
for (const c of s.columns ?? []) {
|
|
431
|
-
ids.add(c.id);
|
|
432
|
-
for (const b of c.blocks ?? []) ids.add(b.id);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
const freshId = (base) => {
|
|
436
|
-
let candidate = `${base}-inner`;
|
|
437
|
-
for (let n = 2; ids.has(candidate); n++) candidate = `${base}-inner-${n}`;
|
|
438
|
-
ids.add(candidate);
|
|
439
|
-
return candidate;
|
|
440
|
-
};
|
|
441
|
-
const sections = doc.sections.map((s) => {
|
|
442
|
-
if (s.isWrapper !== true || s.bodyRaw) return stripIsWrapper(s);
|
|
443
|
-
const { isWrapper: _flag, noStack: _noStack, ...rest } = s;
|
|
444
|
-
const wrapper = { id: s.id, type: "wrapper", sections: [{ id: freshId(s.id), type: "section", columns: s.columns }] };
|
|
445
|
-
if (rest.hidden !== void 0) wrapper.hidden = rest.hidden;
|
|
446
|
-
if (rest.backgroundColor !== void 0) wrapper.backgroundColor = rest.backgroundColor;
|
|
447
|
-
if (rest.backgroundImage !== void 0) wrapper.backgroundImage = rest.backgroundImage;
|
|
448
|
-
if (rest.backgroundGradient !== void 0) wrapper.backgroundGradient = rest.backgroundGradient;
|
|
449
|
-
if (rest.backgroundPosition !== void 0) wrapper.backgroundPosition = rest.backgroundPosition;
|
|
450
|
-
if (rest.backgroundRepeat !== void 0) wrapper.backgroundRepeat = rest.backgroundRepeat;
|
|
451
|
-
if (rest.backgroundSize !== void 0) wrapper.backgroundSize = rest.backgroundSize;
|
|
452
|
-
if (rest.fullWidth !== void 0) wrapper.fullWidth = rest.fullWidth;
|
|
453
|
-
if (rest.padding !== void 0) wrapper.padding = rest.padding;
|
|
454
|
-
if (rest.extraAttributes !== void 0) wrapper.extraAttributes = rest.extraAttributes;
|
|
455
|
-
return wrapper;
|
|
456
|
-
});
|
|
457
|
-
return { ...doc, version: "1.1", sections };
|
|
458
|
-
}
|
|
459
|
-
function stripIsWrapper(s) {
|
|
460
|
-
if (!("isWrapper" in s)) return s;
|
|
461
|
-
const { isWrapper: _flag, ...rest } = s;
|
|
462
|
-
return rest;
|
|
463
|
-
}
|
|
464
|
-
function withTemplateId(doc) {
|
|
465
|
-
if (typeof doc.id === "string" && doc.id.length > 0) return doc;
|
|
466
|
-
return { ...doc, id: nanoid() };
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// src/store/mst/models/BlockModel.ts
|
|
470
|
-
import { types } from "mobx-state-tree";
|
|
471
|
-
|
|
472
|
-
// src/store/mst/models/spacingSnapshot.ts
|
|
473
|
-
var SIDES = [
|
|
474
|
-
["top", "paddingTop"],
|
|
475
|
-
["right", "paddingRight"],
|
|
476
|
-
["bottom", "paddingBottom"],
|
|
477
|
-
["left", "paddingLeft"]
|
|
478
|
-
];
|
|
479
|
-
function paddingIn(snapshot) {
|
|
480
|
-
if (!snapshot || typeof snapshot !== "object") return snapshot;
|
|
481
|
-
const { padding, ...rest } = snapshot;
|
|
482
|
-
if (padding === void 0) return snapshot;
|
|
483
|
-
const flat = rest;
|
|
484
|
-
if (padding && typeof padding === "object") {
|
|
485
|
-
for (const [side, field] of SIDES) {
|
|
486
|
-
const value = padding[side];
|
|
487
|
-
if (flat[field] === void 0 && typeof value === "string" && value !== "") flat[field] = value;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return flat;
|
|
491
|
-
}
|
|
492
|
-
function paddingOut(snapshot) {
|
|
493
|
-
if (!snapshot || typeof snapshot !== "object") return snapshot;
|
|
494
|
-
const { paddingTop, paddingRight, paddingBottom, paddingLeft, ...rest } = snapshot;
|
|
495
|
-
const values = {
|
|
496
|
-
top: paddingTop,
|
|
497
|
-
right: paddingRight,
|
|
498
|
-
bottom: paddingBottom,
|
|
499
|
-
left: paddingLeft
|
|
500
|
-
};
|
|
501
|
-
const padding = {};
|
|
502
|
-
for (const [side] of SIDES) {
|
|
503
|
-
const value = values[side];
|
|
504
|
-
if (typeof value === "string" && value !== "") padding[side] = value;
|
|
505
|
-
}
|
|
506
|
-
return Object.keys(padding).length > 0 ? { ...rest, padding } : rest;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// src/store/mst/models/filledDefaults.ts
|
|
510
|
-
var same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
511
|
-
function fillDefaults(snapshot, defaults) {
|
|
512
|
-
if (!snapshot || typeof snapshot !== "object") return snapshot;
|
|
513
|
-
const s = { ...snapshot };
|
|
514
|
-
const filled = { ...s.filled ?? {} };
|
|
515
|
-
for (const [key, make] of Object.entries(defaults)) {
|
|
516
|
-
if (s[key] !== void 0) continue;
|
|
517
|
-
const value = make();
|
|
518
|
-
s[key] = value;
|
|
519
|
-
filled[key] = value;
|
|
520
|
-
}
|
|
521
|
-
if (Object.keys(filled).length > 0) s.filled = filled;
|
|
522
|
-
return s;
|
|
523
|
-
}
|
|
524
|
-
function dropFilled(snapshot) {
|
|
525
|
-
if (!snapshot || typeof snapshot !== "object") return snapshot;
|
|
526
|
-
const { filled, ...rest } = snapshot;
|
|
527
|
-
if (filled) {
|
|
528
|
-
for (const [key, value] of Object.entries(filled)) {
|
|
529
|
-
if (same(rest[key], value)) delete rest[key];
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
for (const key of Object.keys(rest)) if (rest[key] === void 0) delete rest[key];
|
|
533
|
-
return rest;
|
|
534
|
-
}
|
|
535
|
-
var empty = () => [];
|
|
536
|
-
var no = () => false;
|
|
537
|
-
var BLOCK_DEFAULTS = {
|
|
538
|
-
hidden: no,
|
|
539
|
-
content: () => "",
|
|
540
|
-
links: empty,
|
|
541
|
-
items: empty,
|
|
542
|
-
navLinks: empty,
|
|
543
|
-
hamburger: no,
|
|
544
|
-
images: empty,
|
|
545
|
-
headers: empty,
|
|
546
|
-
rows: empty,
|
|
547
|
-
locked: no
|
|
548
|
-
};
|
|
549
|
-
var COLUMN_DEFAULTS = {
|
|
550
|
-
width: () => 100,
|
|
551
|
-
hidden: no,
|
|
552
|
-
subColumns: empty
|
|
553
|
-
};
|
|
554
|
-
var SECTION_DEFAULTS = {
|
|
555
|
-
type: () => "section",
|
|
556
|
-
fullWidth: no,
|
|
557
|
-
noStack: no,
|
|
558
|
-
hidden: no
|
|
559
|
-
};
|
|
560
|
-
var WRAPPER_DEFAULTS = {
|
|
561
|
-
hidden: no,
|
|
562
|
-
fullWidth: no
|
|
563
|
-
};
|
|
564
|
-
function fillColumnWidths(section) {
|
|
565
|
-
if (!section || typeof section !== "object") return section;
|
|
566
|
-
const s = section;
|
|
567
|
-
if (!Array.isArray(s.columns) || s.columns.length === 0) return section;
|
|
568
|
-
if (s.columns.every((c) => c?.width !== void 0)) return section;
|
|
569
|
-
const share = 100 / s.columns.length;
|
|
570
|
-
return {
|
|
571
|
-
...s,
|
|
572
|
-
columns: s.columns.map((c) => {
|
|
573
|
-
const col = c;
|
|
574
|
-
if (!col || typeof col !== "object" || col.width !== void 0) return c;
|
|
575
|
-
return { ...col, width: share, filled: { ...col.filled ?? {}, width: share } };
|
|
576
|
-
})
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// src/store/mst/models/BlockModel.ts
|
|
581
|
-
var BlockType = /* @__PURE__ */ ((BlockType2) => {
|
|
582
|
-
BlockType2["TEXT"] = "text";
|
|
583
|
-
BlockType2["IMAGE"] = "image";
|
|
584
|
-
BlockType2["BUTTON"] = "button";
|
|
585
|
-
BlockType2["DIVIDER"] = "divider";
|
|
586
|
-
BlockType2["SPACER"] = "spacer";
|
|
587
|
-
BlockType2["SOCIAL"] = "social";
|
|
588
|
-
BlockType2["HERO"] = "hero";
|
|
589
|
-
BlockType2["ACCORDION"] = "accordion";
|
|
590
|
-
BlockType2["RAW"] = "raw";
|
|
591
|
-
BlockType2["NAVBAR"] = "navbar";
|
|
592
|
-
BlockType2["CAROUSEL"] = "carousel";
|
|
593
|
-
BlockType2["TABLE"] = "table";
|
|
594
|
-
BlockType2["HEADER"] = "header";
|
|
595
|
-
BlockType2["FOOTER"] = "footer";
|
|
596
|
-
return BlockType2;
|
|
597
|
-
})(BlockType || {});
|
|
598
|
-
var SocialLinkModel = types.model("SocialLink", {
|
|
599
|
-
platform: types.string,
|
|
600
|
-
url: types.string,
|
|
601
|
-
color: types.maybe(types.string)
|
|
602
|
-
});
|
|
603
|
-
var NavbarLinkModel = types.model("NavbarLink", {
|
|
604
|
-
href: types.string,
|
|
605
|
-
label: types.string,
|
|
606
|
-
color: types.maybe(types.string)
|
|
607
|
-
});
|
|
608
|
-
var CarouselImageModel = types.model("CarouselImage", {
|
|
609
|
-
src: types.string,
|
|
610
|
-
alt: types.maybe(types.string),
|
|
611
|
-
href: types.maybe(types.string),
|
|
612
|
-
thumbnailSrc: types.maybe(types.string)
|
|
613
|
-
});
|
|
614
|
-
var AccordionItemModel = types.model("AccordionItem", {
|
|
615
|
-
title: types.string,
|
|
616
|
-
content: types.string
|
|
617
|
-
});
|
|
618
|
-
var SpacingModel = types.model("Spacing", {
|
|
619
|
-
top: types.maybe(types.string),
|
|
620
|
-
right: types.maybe(types.string),
|
|
621
|
-
bottom: types.maybe(types.string),
|
|
622
|
-
left: types.maybe(types.string)
|
|
623
|
-
});
|
|
624
|
-
var BlockModelBase = types.model("Block", {
|
|
625
|
-
id: types.identifier,
|
|
626
|
-
type: types.enumeration("BlockType", Object.values(BlockType)),
|
|
627
|
-
hidden: types.optional(types.boolean, false),
|
|
628
|
-
// === Common Text Properties ===
|
|
629
|
-
content: types.optional(types.string, ""),
|
|
630
|
-
color: types.maybe(types.string),
|
|
631
|
-
backgroundColor: types.maybe(types.string),
|
|
632
|
-
fontSize: types.maybe(types.string),
|
|
633
|
-
fontFamily: types.maybe(types.string),
|
|
634
|
-
align: types.maybe(types.enumeration(["left", "center", "right", "justify"])),
|
|
635
|
-
lineHeight: types.maybe(types.string),
|
|
636
|
-
// === Spacing ===
|
|
637
|
-
paddingTop: types.maybe(types.string),
|
|
638
|
-
paddingRight: types.maybe(types.string),
|
|
639
|
-
paddingBottom: types.maybe(types.string),
|
|
640
|
-
paddingLeft: types.maybe(types.string),
|
|
641
|
-
// === Image Properties ===
|
|
642
|
-
src: types.maybe(types.string),
|
|
643
|
-
alt: types.maybe(types.string),
|
|
644
|
-
width: types.maybe(types.string),
|
|
645
|
-
height: types.maybe(types.string),
|
|
646
|
-
// === Button Properties ===
|
|
647
|
-
href: types.maybe(types.string),
|
|
648
|
-
label: types.maybe(types.string),
|
|
649
|
-
borderRadius: types.maybe(types.string),
|
|
650
|
-
border: types.maybe(types.string),
|
|
651
|
-
innerPadding: types.maybe(types.string),
|
|
652
|
-
// === Divider Properties ===
|
|
653
|
-
borderColor: types.maybe(types.string),
|
|
654
|
-
borderWidth: types.maybe(types.string),
|
|
655
|
-
borderStyle: types.maybe(types.enumeration(["solid", "dashed", "dotted"])),
|
|
656
|
-
// === Spacer Properties ===
|
|
657
|
-
// (uses height from image properties)
|
|
658
|
-
// === Social Properties ===
|
|
659
|
-
links: types.optional(types.array(SocialLinkModel), []),
|
|
660
|
-
iconSize: types.maybe(types.string),
|
|
661
|
-
iconPadding: types.maybe(types.string),
|
|
662
|
-
mode: types.maybe(types.enumeration(["horizontal", "vertical", "fixed-height", "fluid-height"])),
|
|
663
|
-
// === Hero Properties ===
|
|
664
|
-
backgroundImage: types.maybe(types.string),
|
|
665
|
-
backgroundHeight: types.maybe(types.string),
|
|
666
|
-
backgroundWidth: types.maybe(types.string),
|
|
667
|
-
verticalAlign: types.maybe(types.enumeration(["top", "middle", "bottom"])),
|
|
668
|
-
// === Accordion Properties ===
|
|
669
|
-
items: types.optional(types.array(AccordionItemModel), []),
|
|
670
|
-
iconPosition: types.maybe(types.enumeration(["left", "right"])),
|
|
671
|
-
// === Raw Properties ===
|
|
672
|
-
html: types.maybe(types.string),
|
|
673
|
-
// === Navbar Properties ===
|
|
674
|
-
navLinks: types.optional(types.array(NavbarLinkModel), []),
|
|
675
|
-
hamburger: types.optional(types.boolean, false),
|
|
676
|
-
baseUrl: types.maybe(types.string),
|
|
677
|
-
icoColor: types.maybe(types.string),
|
|
678
|
-
// === Carousel Properties ===
|
|
679
|
-
images: types.optional(types.array(CarouselImageModel), []),
|
|
680
|
-
thumbnails: types.maybe(types.enumeration(["visible", "hidden"])),
|
|
681
|
-
iconWidth: types.maybe(types.string),
|
|
682
|
-
tbBorderRadius: types.maybe(types.string),
|
|
683
|
-
// === Table Properties ===
|
|
684
|
-
headers: types.optional(types.array(types.string), []),
|
|
685
|
-
rows: types.optional(types.array(types.array(types.string)), []),
|
|
686
|
-
cellpadding: types.maybe(types.string),
|
|
687
|
-
cellspacing: types.maybe(types.string),
|
|
688
|
-
// === Locked Blocks (Header/Footer) ===
|
|
689
|
-
locked: types.optional(types.boolean, false),
|
|
690
|
-
// === MJML attributes the inspector has no control for (kept from an import) ===
|
|
691
|
-
extraAttributes: types.maybe(types.frozen()),
|
|
692
|
-
/** Defaults the store filled when it opened the node; they go back out only if changed (see `filledDefaults.ts`). */
|
|
693
|
-
filled: types.maybe(types.frozen())
|
|
694
|
-
}).actions((self) => ({
|
|
695
|
-
/**
|
|
696
|
-
* Update any style property by name
|
|
697
|
-
*/
|
|
698
|
-
updateStyle(property, value) {
|
|
699
|
-
if (property in self) {
|
|
700
|
-
self[property] = value;
|
|
701
|
-
}
|
|
702
|
-
},
|
|
703
|
-
/**
|
|
704
|
-
* Update multiple properties at once
|
|
705
|
-
*/
|
|
706
|
-
updateProperties(updates) {
|
|
707
|
-
Object.entries(updates).forEach(([key, value]) => {
|
|
708
|
-
if (key in self) {
|
|
709
|
-
self[key] = value;
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
},
|
|
713
|
-
/**
|
|
714
|
-
* Set block content (for text blocks)
|
|
715
|
-
*/
|
|
716
|
-
setContent(content) {
|
|
717
|
-
self.content = content;
|
|
718
|
-
},
|
|
719
|
-
/**
|
|
720
|
-
* Toggle block visibility
|
|
721
|
-
*/
|
|
722
|
-
toggleHidden() {
|
|
723
|
-
self.hidden = !self.hidden;
|
|
724
|
-
},
|
|
725
|
-
/**
|
|
726
|
-
* Set padding values
|
|
727
|
-
*/
|
|
728
|
-
setPadding(padding) {
|
|
729
|
-
if (padding.top !== void 0) self.paddingTop = padding.top;
|
|
730
|
-
if (padding.right !== void 0) self.paddingRight = padding.right;
|
|
731
|
-
if (padding.bottom !== void 0) self.paddingBottom = padding.bottom;
|
|
732
|
-
if (padding.left !== void 0) self.paddingLeft = padding.left;
|
|
733
|
-
},
|
|
734
|
-
/**
|
|
735
|
-
* Add a social link
|
|
736
|
-
*/
|
|
737
|
-
addSocialLink(platform, url, color) {
|
|
738
|
-
self.links.push(SocialLinkModel.create({ platform, url, color }));
|
|
739
|
-
},
|
|
740
|
-
/**
|
|
741
|
-
* Remove a social link by index
|
|
742
|
-
*/
|
|
743
|
-
removeSocialLink(index) {
|
|
744
|
-
self.links.splice(index, 1);
|
|
745
|
-
},
|
|
746
|
-
/**
|
|
747
|
-
* Add a navbar link
|
|
748
|
-
*/
|
|
749
|
-
addNavbarLink(href, label, color) {
|
|
750
|
-
self.navLinks.push(NavbarLinkModel.create({ href, label, color }));
|
|
751
|
-
},
|
|
752
|
-
/**
|
|
753
|
-
* Remove a navbar link by index
|
|
754
|
-
*/
|
|
755
|
-
removeNavbarLink(index) {
|
|
756
|
-
self.navLinks.splice(index, 1);
|
|
757
|
-
},
|
|
758
|
-
/**
|
|
759
|
-
* Add a carousel image
|
|
760
|
-
*/
|
|
761
|
-
addCarouselImage(src, alt, href, thumbnailSrc) {
|
|
762
|
-
self.images.push(CarouselImageModel.create({ src, alt, href, thumbnailSrc }));
|
|
763
|
-
},
|
|
764
|
-
/**
|
|
765
|
-
* Remove a carousel image by index
|
|
766
|
-
*/
|
|
767
|
-
removeCarouselImage(index) {
|
|
768
|
-
self.images.splice(index, 1);
|
|
769
|
-
},
|
|
770
|
-
/**
|
|
771
|
-
* Add an accordion item
|
|
772
|
-
*/
|
|
773
|
-
addAccordionItem(title, content) {
|
|
774
|
-
self.items.push(AccordionItemModel.create({ title, content }));
|
|
775
|
-
},
|
|
776
|
-
/**
|
|
777
|
-
* Remove an accordion item by index
|
|
778
|
-
*/
|
|
779
|
-
removeAccordionItem(index) {
|
|
780
|
-
self.items.splice(index, 1);
|
|
781
|
-
},
|
|
782
|
-
/**
|
|
783
|
-
* Add a table row
|
|
784
|
-
*/
|
|
785
|
-
addTableRow(cells) {
|
|
786
|
-
self.rows.push(cells);
|
|
787
|
-
},
|
|
788
|
-
/**
|
|
789
|
-
* Remove a table row by index
|
|
790
|
-
*/
|
|
791
|
-
removeTableRow(index) {
|
|
792
|
-
self.rows.splice(index, 1);
|
|
793
|
-
}
|
|
794
|
-
})).views((self) => ({
|
|
795
|
-
/**
|
|
796
|
-
* Computed style object for React rendering
|
|
797
|
-
*/
|
|
798
|
-
get computedStyle() {
|
|
799
|
-
const style = {};
|
|
800
|
-
if (self.color) style.color = self.color;
|
|
801
|
-
if (self.backgroundColor) style.backgroundColor = self.backgroundColor;
|
|
802
|
-
if (self.fontSize) style.fontSize = self.fontSize;
|
|
803
|
-
if (self.fontFamily) style.fontFamily = self.fontFamily;
|
|
804
|
-
if (self.align) style.textAlign = self.align;
|
|
805
|
-
if (self.lineHeight) style.lineHeight = self.lineHeight;
|
|
806
|
-
if (self.paddingTop) style.paddingTop = self.paddingTop;
|
|
807
|
-
if (self.paddingRight) style.paddingRight = self.paddingRight;
|
|
808
|
-
if (self.paddingBottom) style.paddingBottom = self.paddingBottom;
|
|
809
|
-
if (self.paddingLeft) style.paddingLeft = self.paddingLeft;
|
|
810
|
-
if (self.borderRadius) style.borderRadius = self.borderRadius;
|
|
811
|
-
if (self.border) style.border = self.border;
|
|
812
|
-
return style;
|
|
813
|
-
},
|
|
814
|
-
/**
|
|
815
|
-
* Get padding as a single object
|
|
816
|
-
*/
|
|
817
|
-
get padding() {
|
|
818
|
-
return {
|
|
819
|
-
top: self.paddingTop || void 0,
|
|
820
|
-
right: self.paddingRight || void 0,
|
|
821
|
-
bottom: self.paddingBottom || void 0,
|
|
822
|
-
left: self.paddingLeft || void 0
|
|
823
|
-
};
|
|
824
|
-
},
|
|
825
|
-
/**
|
|
826
|
-
* Get padding as a CSS string (e.g., "10px 20px 10px 20px")
|
|
827
|
-
*/
|
|
828
|
-
get paddingString() {
|
|
829
|
-
const { paddingTop, paddingRight, paddingBottom, paddingLeft } = self;
|
|
830
|
-
if (!paddingTop && !paddingRight && !paddingBottom && !paddingLeft) {
|
|
831
|
-
return void 0;
|
|
832
|
-
}
|
|
833
|
-
return `${paddingTop || "0"} ${paddingRight || "0"} ${paddingBottom || "0"} ${paddingLeft || "0"}`;
|
|
834
|
-
},
|
|
835
|
-
/**
|
|
836
|
-
* MJML attributes for export
|
|
837
|
-
*/
|
|
838
|
-
get mjmlAttributes() {
|
|
839
|
-
const attrs = {};
|
|
840
|
-
if (self.color) attrs.color = self.color;
|
|
841
|
-
if (self.backgroundColor) attrs["background-color"] = self.backgroundColor;
|
|
842
|
-
if (self.fontSize) attrs["font-size"] = self.fontSize;
|
|
843
|
-
if (self.fontFamily) attrs["font-family"] = self.fontFamily;
|
|
844
|
-
if (self.align) attrs.align = self.align;
|
|
845
|
-
if (self.lineHeight) attrs["line-height"] = self.lineHeight;
|
|
846
|
-
const paddingParts = [];
|
|
847
|
-
if (self.paddingTop) paddingParts.push(`top:${self.paddingTop}`);
|
|
848
|
-
if (self.paddingRight) paddingParts.push(`right:${self.paddingRight}`);
|
|
849
|
-
if (self.paddingBottom) paddingParts.push(`bottom:${self.paddingBottom}`);
|
|
850
|
-
if (self.paddingLeft) paddingParts.push(`left:${self.paddingLeft}`);
|
|
851
|
-
if (paddingParts.length > 0) {
|
|
852
|
-
const padding = `${self.paddingTop || "0"} ${self.paddingRight || "0"} ${self.paddingBottom || "0"} ${self.paddingLeft || "0"}`;
|
|
853
|
-
attrs.padding = padding;
|
|
854
|
-
}
|
|
855
|
-
if (self.src) attrs.src = self.src;
|
|
856
|
-
if (self.alt) attrs.alt = self.alt;
|
|
857
|
-
if (self.width) attrs.width = self.width;
|
|
858
|
-
if (self.height) attrs.height = self.height;
|
|
859
|
-
if (self.href) attrs.href = self.href;
|
|
860
|
-
if (self.borderRadius) attrs["border-radius"] = self.borderRadius;
|
|
861
|
-
if (self.border) attrs.border = self.border;
|
|
862
|
-
if (self.innerPadding) attrs["inner-padding"] = self.innerPadding;
|
|
863
|
-
if (self.borderColor) attrs["border-color"] = self.borderColor;
|
|
864
|
-
if (self.borderWidth) attrs["border-width"] = self.borderWidth;
|
|
865
|
-
if (self.borderStyle) attrs["border-style"] = self.borderStyle;
|
|
866
|
-
return attrs;
|
|
867
|
-
},
|
|
868
|
-
/**
|
|
869
|
-
* Check if this is a text-based block
|
|
870
|
-
*/
|
|
871
|
-
get isTextBlock() {
|
|
872
|
-
return self.type === "text" /* TEXT */;
|
|
873
|
-
},
|
|
874
|
-
/**
|
|
875
|
-
* Check if this is an image block
|
|
876
|
-
*/
|
|
877
|
-
get isImageBlock() {
|
|
878
|
-
return self.type === "image" /* IMAGE */;
|
|
879
|
-
},
|
|
880
|
-
/**
|
|
881
|
-
* Check if this is a button block
|
|
882
|
-
*/
|
|
883
|
-
get isButtonBlock() {
|
|
884
|
-
return self.type === "button" /* BUTTON */;
|
|
885
|
-
},
|
|
886
|
-
/**
|
|
887
|
-
* Check if this block is locked (header/footer)
|
|
888
|
-
*/
|
|
889
|
-
get isLocked() {
|
|
890
|
-
return self.locked || self.type === "header" /* HEADER */ || self.type === "footer" /* FOOTER */;
|
|
891
|
-
},
|
|
892
|
-
/**
|
|
893
|
-
* Get display name for layers panel
|
|
894
|
-
*/
|
|
895
|
-
get displayName() {
|
|
896
|
-
switch (self.type) {
|
|
897
|
-
case "text" /* TEXT */:
|
|
898
|
-
const text = self.content.replace(/<[^>]*>/g, "").trim();
|
|
899
|
-
return text.length > 20 ? text.substring(0, 20) + "..." : text || "Text";
|
|
900
|
-
case "image" /* IMAGE */:
|
|
901
|
-
return self.alt || "Image";
|
|
902
|
-
case "button" /* BUTTON */:
|
|
903
|
-
return self.label || "Button";
|
|
904
|
-
case "divider" /* DIVIDER */:
|
|
905
|
-
return "Divider";
|
|
906
|
-
case "spacer" /* SPACER */:
|
|
907
|
-
return `Spacer (${self.height || "20px"})`;
|
|
908
|
-
case "social" /* SOCIAL */:
|
|
909
|
-
return "Social Links";
|
|
910
|
-
case "hero" /* HERO */:
|
|
911
|
-
return "Hero";
|
|
912
|
-
case "accordion" /* ACCORDION */:
|
|
913
|
-
return "Accordion";
|
|
914
|
-
case "raw" /* RAW */:
|
|
915
|
-
return "Custom HTML";
|
|
916
|
-
case "navbar" /* NAVBAR */:
|
|
917
|
-
return "Navigation";
|
|
918
|
-
case "carousel" /* CAROUSEL */:
|
|
919
|
-
return "Carousel";
|
|
920
|
-
case "table" /* TABLE */:
|
|
921
|
-
return "Table";
|
|
922
|
-
case "header" /* HEADER */:
|
|
923
|
-
return "Header";
|
|
924
|
-
case "footer" /* FOOTER */:
|
|
925
|
-
return "Footer";
|
|
926
|
-
default:
|
|
927
|
-
return "Block";
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}));
|
|
931
|
-
var BlockModel = BlockModelBase.preProcessSnapshot((raw) => {
|
|
932
|
-
let snapshot = paddingIn(raw);
|
|
933
|
-
if (snapshot && snapshot.type === "navbar") {
|
|
934
|
-
const { links, navLinks, ...rest } = snapshot;
|
|
935
|
-
const stored = navLinks && navLinks.length > 0 ? navLinks : links ?? [];
|
|
936
|
-
snapshot = { ...rest, navLinks: stored };
|
|
937
|
-
}
|
|
938
|
-
return fillDefaults(snapshot, BLOCK_DEFAULTS);
|
|
939
|
-
}).postProcessSnapshot((raw) => {
|
|
940
|
-
const snapshot = paddingOut(dropFilled(raw));
|
|
941
|
-
if (snapshot.type !== "navbar") return snapshot;
|
|
942
|
-
const { navLinks, links: _socialLinks, ...rest } = snapshot;
|
|
943
|
-
return { ...rest, links: navLinks ?? [] };
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
// src/registry/blockCategories.ts
|
|
947
|
-
var LEAF_BLOCK_TYPES = [
|
|
948
|
-
"text" /* TEXT */,
|
|
949
|
-
"image" /* IMAGE */,
|
|
950
|
-
"button" /* BUTTON */,
|
|
951
|
-
"divider" /* DIVIDER */,
|
|
952
|
-
"spacer" /* SPACER */,
|
|
953
|
-
"social" /* SOCIAL */
|
|
954
|
-
];
|
|
955
|
-
var CONTAINER_BLOCK_TYPES = [
|
|
956
|
-
"hero" /* HERO */,
|
|
957
|
-
"accordion" /* ACCORDION */,
|
|
958
|
-
"raw" /* RAW */,
|
|
959
|
-
"navbar" /* NAVBAR */,
|
|
960
|
-
"carousel" /* CAROUSEL */,
|
|
961
|
-
"table" /* TABLE */,
|
|
962
|
-
"header" /* HEADER */,
|
|
963
|
-
"footer" /* FOOTER */
|
|
964
|
-
];
|
|
965
|
-
function isLeafBlockType(t) {
|
|
966
|
-
return LEAF_BLOCK_TYPES.includes(t);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
export {
|
|
970
|
-
buildGradientCSS,
|
|
971
|
-
isWrapper,
|
|
972
|
-
allSections,
|
|
973
|
-
SpacingSchema,
|
|
974
|
-
GradientStopSchema,
|
|
975
|
-
BackgroundGradientSchema,
|
|
976
|
-
ExtraAttributesSchema,
|
|
977
|
-
MjmlHeadSchema,
|
|
978
|
-
CustomFontSchema,
|
|
979
|
-
TemplateMetadataSchema,
|
|
980
|
-
TextBlockSchema,
|
|
981
|
-
ImageBlockSchema,
|
|
982
|
-
ButtonBlockSchema,
|
|
983
|
-
DividerBlockSchema,
|
|
984
|
-
SpacerBlockSchema,
|
|
985
|
-
HeaderBlockSchema,
|
|
986
|
-
FooterBlockSchema,
|
|
987
|
-
SocialBlockSchema,
|
|
988
|
-
HeroBlockSchema,
|
|
989
|
-
AccordionBlockSchema,
|
|
990
|
-
RawBlockSchema,
|
|
991
|
-
NavbarBlockSchema,
|
|
992
|
-
CarouselBlockSchema,
|
|
993
|
-
TableBlockSchema,
|
|
994
|
-
BlockSchema,
|
|
995
|
-
ColumnSchema,
|
|
996
|
-
SectionSchema,
|
|
997
|
-
SectionSchemaV1_0,
|
|
998
|
-
WrapperSchema,
|
|
999
|
-
TopLevelItemSchema,
|
|
1000
|
-
EmailTemplateSchemaV1_0,
|
|
1001
|
-
EmailTemplateSchemaV1_1,
|
|
1002
|
-
EmailTemplateSchema,
|
|
1003
|
-
validateTemplate,
|
|
1004
|
-
CURRENT_TEMPLATE_VERSION,
|
|
1005
|
-
SUPPORTED_TEMPLATE_VERSIONS,
|
|
1006
|
-
TemplateMigrationError,
|
|
1007
|
-
isTemplateMigrationError,
|
|
1008
|
-
migrateTemplate,
|
|
1009
|
-
migrateV1_0ToV1_1,
|
|
1010
|
-
withTemplateId,
|
|
1011
|
-
paddingIn,
|
|
1012
|
-
paddingOut,
|
|
1013
|
-
fillDefaults,
|
|
1014
|
-
dropFilled,
|
|
1015
|
-
COLUMN_DEFAULTS,
|
|
1016
|
-
SECTION_DEFAULTS,
|
|
1017
|
-
WRAPPER_DEFAULTS,
|
|
1018
|
-
fillColumnWidths,
|
|
1019
|
-
BlockType,
|
|
1020
|
-
SocialLinkModel,
|
|
1021
|
-
NavbarLinkModel,
|
|
1022
|
-
CarouselImageModel,
|
|
1023
|
-
AccordionItemModel,
|
|
1024
|
-
SpacingModel,
|
|
1025
|
-
BlockModel,
|
|
1026
|
-
LEAF_BLOCK_TYPES,
|
|
1027
|
-
CONTAINER_BLOCK_TYPES,
|
|
1028
|
-
isLeafBlockType
|
|
1029
|
-
};
|