react-email-studio 2.0.0 → 3.1.1
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/TUTORIAL.md +264 -264
- package/USER_README.md +28 -28
- package/dist/index.cjs +608 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +608 -77
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -138,7 +138,8 @@ var I18N = {
|
|
|
138
138
|
blockPaletteGroupWidgets: "Widgets",
|
|
139
139
|
closePanel: "Close",
|
|
140
140
|
canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
|
|
141
|
-
emailContentSettings: "Email content"
|
|
141
|
+
emailContentSettings: "Email content",
|
|
142
|
+
loadingDesign: "Loading design\u2026"
|
|
142
143
|
},
|
|
143
144
|
fr: {
|
|
144
145
|
layouts: "Mises en page",
|
|
@@ -191,6 +192,7 @@ var I18N = {
|
|
|
191
192
|
blockPaletteGroupWidgets: "Widgets",
|
|
192
193
|
closePanel: "Fermer",
|
|
193
194
|
canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
|
|
195
|
+
loadingDesign: "Chargement du design\u2026",
|
|
194
196
|
emailContentSettings: "Contenu de l\u2019e-mail",
|
|
195
197
|
zoomIn: "Zoom +",
|
|
196
198
|
zoomOut: "Zoom -",
|
|
@@ -250,6 +252,7 @@ var I18N = {
|
|
|
250
252
|
blockPaletteGroupWidgets: "Widgets",
|
|
251
253
|
closePanel: "Schlie\xDFen",
|
|
252
254
|
canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
|
|
255
|
+
loadingDesign: "Design wird geladen\u2026",
|
|
253
256
|
emailContentSettings: "E-Mail-Inhalt",
|
|
254
257
|
zoomIn: "Zoom +",
|
|
255
258
|
zoomOut: "Zoom -",
|
|
@@ -310,6 +313,7 @@ var I18N = {
|
|
|
310
313
|
blockPaletteGroupWidgets: "Widgets",
|
|
311
314
|
closePanel: "Cerrar",
|
|
312
315
|
canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
|
|
316
|
+
loadingDesign: "Cargando dise\xF1o\u2026",
|
|
313
317
|
emailContentSettings: "Contenido del correo",
|
|
314
318
|
resizeSidebar: "Redimensionar panel"
|
|
315
319
|
}
|
|
@@ -660,6 +664,10 @@ function makeContentBlock(type) {
|
|
|
660
664
|
}
|
|
661
665
|
|
|
662
666
|
// src/lib/columnPath.ts
|
|
667
|
+
var MAX_NESTED_LAYOUT_DEPTH = 1;
|
|
668
|
+
function nestedLayoutDepth(nested) {
|
|
669
|
+
return nested != null && nested.parentBlockIdx != null && nested.innerCellIdx != null ? 1 : 0;
|
|
670
|
+
}
|
|
663
671
|
function isSplitLayoutBlock(b) {
|
|
664
672
|
return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
|
|
665
673
|
}
|
|
@@ -1251,8 +1259,18 @@ function normalizeEmailDocument(input) {
|
|
|
1251
1259
|
settings: doc.settings && typeof doc.settings === "object" && !Array.isArray(doc.settings) ? doc.settings : {},
|
|
1252
1260
|
rows: rows.map((r, ri) => {
|
|
1253
1261
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1254
|
-
const
|
|
1255
|
-
const
|
|
1262
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1263
|
+
const colCount = Math.max(1, columns.length, layoutColCount);
|
|
1264
|
+
const colsNormalized = (() => {
|
|
1265
|
+
if (columns.length >= colCount) return columns;
|
|
1266
|
+
if (columns.length > 0) {
|
|
1267
|
+
return [
|
|
1268
|
+
...columns,
|
|
1269
|
+
...Array.from({ length: colCount - columns.length }).map(() => ({}))
|
|
1270
|
+
];
|
|
1271
|
+
}
|
|
1272
|
+
return Array.from({ length: colCount }).map(() => ({}));
|
|
1273
|
+
})();
|
|
1256
1274
|
return {
|
|
1257
1275
|
id: ensureId(r.id, `row${ri + 1}`),
|
|
1258
1276
|
type: "row",
|
|
@@ -1263,6 +1281,7 @@ function normalizeEmailDocument(input) {
|
|
|
1263
1281
|
align: r.layout?.align || "center"
|
|
1264
1282
|
},
|
|
1265
1283
|
styles: r.styles && typeof r.styles === "object" && !Array.isArray(r.styles) ? r.styles : {},
|
|
1284
|
+
...typeof r._reactEmailStudio === "object" && r._reactEmailStudio !== null && !Array.isArray(r._reactEmailStudio) ? { _reactEmailStudio: r._reactEmailStudio } : {},
|
|
1266
1285
|
columns: colsNormalized.map((c, ci) => ({
|
|
1267
1286
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1268
1287
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
@@ -1273,7 +1292,8 @@ function normalizeEmailDocument(input) {
|
|
|
1273
1292
|
content: b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1274
1293
|
styles: b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles) ? b.styles : {},
|
|
1275
1294
|
behavior: b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? b.behavior : {},
|
|
1276
|
-
responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {}
|
|
1295
|
+
responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {},
|
|
1296
|
+
...b?.props && typeof b.props === "object" && !Array.isArray(b.props) ? { props: b.props } : {}
|
|
1277
1297
|
})) : []
|
|
1278
1298
|
}))
|
|
1279
1299
|
};
|
|
@@ -1284,6 +1304,14 @@ function normalizeEmailDocument(input) {
|
|
|
1284
1304
|
}
|
|
1285
1305
|
|
|
1286
1306
|
// src/lib/emailDesignJson.ts
|
|
1307
|
+
var MAX_LAYOUT_TREE_DEPTH = 32;
|
|
1308
|
+
function cloneJson(v) {
|
|
1309
|
+
try {
|
|
1310
|
+
return JSON.parse(JSON.stringify(v));
|
|
1311
|
+
} catch {
|
|
1312
|
+
return v;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1287
1315
|
function layoutColumnPaddingForDoc(p) {
|
|
1288
1316
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1289
1317
|
const n = Math.max(0, p);
|
|
@@ -1370,7 +1398,242 @@ function normalizeBoxStyles(props, t) {
|
|
|
1370
1398
|
}
|
|
1371
1399
|
props.padding = boxPad(props.padding, uniformPadDefault(def?.padding));
|
|
1372
1400
|
}
|
|
1373
|
-
function
|
|
1401
|
+
function internalBlockToEmailDoc(b, depth = 0) {
|
|
1402
|
+
const p = b?.props && typeof b.props === "object" ? b.props : {};
|
|
1403
|
+
const id = typeof b?.id === "string" ? b.id : uid();
|
|
1404
|
+
const type = b?.type === "nestedRow" ? "layout" : b?.type;
|
|
1405
|
+
if (!isKnownBlockType(type)) {
|
|
1406
|
+
return { id, type: String(type || "text"), content: {}, styles: {} };
|
|
1407
|
+
}
|
|
1408
|
+
switch (type) {
|
|
1409
|
+
case "heading": {
|
|
1410
|
+
const fw = typeof p.fontWeight === "number" ? p.fontWeight : typeof p.fontWeight === "string" ? Number.parseInt(String(p.fontWeight), 10) || (p.bold ? 700 : 400) : p.bold ? 700 : 400;
|
|
1411
|
+
const padUniform = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : uniformPadDefault(p.padding);
|
|
1412
|
+
return {
|
|
1413
|
+
id,
|
|
1414
|
+
type,
|
|
1415
|
+
content: { text: typeof p.content === "string" ? p.content : "", tag: p.level || "h2" },
|
|
1416
|
+
styles: {
|
|
1417
|
+
fontSize: p.fontSize,
|
|
1418
|
+
fontWeight: fw,
|
|
1419
|
+
color: p.color,
|
|
1420
|
+
lineHeight: p.lineHeight,
|
|
1421
|
+
textAlign: p.align,
|
|
1422
|
+
letterSpacing: p.letterSpacing,
|
|
1423
|
+
marginBottom: padUniform
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
case "text":
|
|
1428
|
+
return {
|
|
1429
|
+
id,
|
|
1430
|
+
type,
|
|
1431
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1432
|
+
styles: {
|
|
1433
|
+
fontSize: p.fontSize,
|
|
1434
|
+
color: p.color,
|
|
1435
|
+
lineHeight: p.lineHeight,
|
|
1436
|
+
textAlign: p.align,
|
|
1437
|
+
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
case "html":
|
|
1441
|
+
return {
|
|
1442
|
+
id,
|
|
1443
|
+
type,
|
|
1444
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1445
|
+
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1446
|
+
};
|
|
1447
|
+
case "image": {
|
|
1448
|
+
const br = p.borderRadius;
|
|
1449
|
+
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
1450
|
+
(numOr(br.tl, 0) + numOr(br.tr, numOr(br.tl, 0)) + numOr(br.br, numOr(br.tl, 0)) + numOr(br.bl, numOr(br.tr, 0))) / 4
|
|
1451
|
+
) : 0;
|
|
1452
|
+
return {
|
|
1453
|
+
id,
|
|
1454
|
+
type,
|
|
1455
|
+
content: {
|
|
1456
|
+
src: p.src || "",
|
|
1457
|
+
alt: p.alt || "",
|
|
1458
|
+
link: p.link || ""
|
|
1459
|
+
},
|
|
1460
|
+
styles: {
|
|
1461
|
+
width: p.width,
|
|
1462
|
+
align: p.align,
|
|
1463
|
+
borderRadius
|
|
1464
|
+
},
|
|
1465
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
case "button": {
|
|
1469
|
+
let borderRadiusOut;
|
|
1470
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1471
|
+
borderRadiusOut = p.borderRadius;
|
|
1472
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1473
|
+
const br = p.borderRadius;
|
|
1474
|
+
const tl = numOr(br.tl, 0);
|
|
1475
|
+
const tr2 = numOr(br.tr, tl);
|
|
1476
|
+
const brc = numOr(br.br, tl);
|
|
1477
|
+
const bl = numOr(br.bl, tr2);
|
|
1478
|
+
borderRadiusOut = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1479
|
+
}
|
|
1480
|
+
return {
|
|
1481
|
+
id,
|
|
1482
|
+
type,
|
|
1483
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1484
|
+
styles: {
|
|
1485
|
+
backgroundColor: p.bgColor,
|
|
1486
|
+
color: p.textColor,
|
|
1487
|
+
fontSize: p.fontSize,
|
|
1488
|
+
fontWeight: p.fontWeight,
|
|
1489
|
+
...borderRadiusOut !== void 0 ? { borderRadius: borderRadiusOut } : {},
|
|
1490
|
+
textAlign: p.align,
|
|
1491
|
+
padding: {
|
|
1492
|
+
top: p.paddingV ?? 11,
|
|
1493
|
+
right: p.paddingH ?? 24,
|
|
1494
|
+
bottom: p.paddingV ?? 11,
|
|
1495
|
+
left: p.paddingH ?? 24
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
case "divider": {
|
|
1502
|
+
const padY = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? numOr(p.padding.top, 12) : 12;
|
|
1503
|
+
return {
|
|
1504
|
+
id,
|
|
1505
|
+
type,
|
|
1506
|
+
content: {},
|
|
1507
|
+
styles: { color: p.color, thickness: p.thickness, paddingY: padY }
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
case "spacer":
|
|
1511
|
+
return { id, type, content: {}, styles: { height: p.height } };
|
|
1512
|
+
case "social": {
|
|
1513
|
+
const urls = p.socialUrls && typeof p.socialUrls === "object" && !Array.isArray(p.socialUrls) ? p.socialUrls : {};
|
|
1514
|
+
const networks = {};
|
|
1515
|
+
const ordered = Array.isArray(p.networks) ? p.networks.filter((n) => typeof n === "string" && !!n) : [];
|
|
1516
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1517
|
+
for (const n of ordered) {
|
|
1518
|
+
const u = urls[n];
|
|
1519
|
+
networks[n] = typeof u === "string" && u.trim() ? u : "";
|
|
1520
|
+
seen.add(n);
|
|
1521
|
+
}
|
|
1522
|
+
for (const key of Object.keys(urls)) {
|
|
1523
|
+
if (seen.has(key)) continue;
|
|
1524
|
+
const v = urls[key];
|
|
1525
|
+
if (typeof v === "string" && v.trim()) {
|
|
1526
|
+
networks[key] = v;
|
|
1527
|
+
seen.add(key);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const padStyles = p.padding != null ? typeof p.padding === "number" && Number.isFinite(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {} : {};
|
|
1531
|
+
return {
|
|
1532
|
+
id,
|
|
1533
|
+
type,
|
|
1534
|
+
content: { networks },
|
|
1535
|
+
styles: {
|
|
1536
|
+
iconSize: p.iconSize,
|
|
1537
|
+
gap: p.gap,
|
|
1538
|
+
shape: p.shape,
|
|
1539
|
+
align: p.align,
|
|
1540
|
+
...padStyles
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
case "table":
|
|
1545
|
+
return {
|
|
1546
|
+
id,
|
|
1547
|
+
type,
|
|
1548
|
+
content: { data: Array.isArray(p.data) ? p.data : [] },
|
|
1549
|
+
styles: {
|
|
1550
|
+
borderColor: p.cellBorder,
|
|
1551
|
+
cellPadding: p.cellPadding,
|
|
1552
|
+
headerBg: p.headerBg,
|
|
1553
|
+
striped: p.striped
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
case "video":
|
|
1557
|
+
return {
|
|
1558
|
+
id,
|
|
1559
|
+
type,
|
|
1560
|
+
content: { url: p.src || "" },
|
|
1561
|
+
styles: { height: p.height }
|
|
1562
|
+
};
|
|
1563
|
+
case "timer": {
|
|
1564
|
+
let timerBr;
|
|
1565
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1566
|
+
timerBr = p.borderRadius;
|
|
1567
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1568
|
+
const br = p.borderRadius;
|
|
1569
|
+
const tl = numOr(br.tl, 0);
|
|
1570
|
+
const tr2 = numOr(br.tr, tl);
|
|
1571
|
+
const brc = numOr(br.br, tl);
|
|
1572
|
+
const bl = numOr(br.bl, tr2);
|
|
1573
|
+
timerBr = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1574
|
+
}
|
|
1575
|
+
return {
|
|
1576
|
+
id,
|
|
1577
|
+
type,
|
|
1578
|
+
content: { endDate: p.endDate || "" },
|
|
1579
|
+
styles: {
|
|
1580
|
+
backgroundColor: p.bgColor,
|
|
1581
|
+
color: p.textColor,
|
|
1582
|
+
padding: typeof p.padding === "number" ? p.padding : uniformPadDefault(p.padding),
|
|
1583
|
+
...timerBr !== void 0 ? { borderRadius: timerBr } : {},
|
|
1584
|
+
textAlign: p.align
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
case "menu":
|
|
1589
|
+
return {
|
|
1590
|
+
id,
|
|
1591
|
+
type,
|
|
1592
|
+
content: { items: Array.isArray(p.items) ? p.items : [] },
|
|
1593
|
+
styles: { fontSize: p.fontSize, color: p.color }
|
|
1594
|
+
};
|
|
1595
|
+
case "link":
|
|
1596
|
+
return {
|
|
1597
|
+
id,
|
|
1598
|
+
type,
|
|
1599
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1600
|
+
styles: { color: p.color, fontSize: p.fontSize, textAlign: p.align },
|
|
1601
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1602
|
+
};
|
|
1603
|
+
case "layout": {
|
|
1604
|
+
const ratios = Array.isArray(p.ratios) ? [...p.ratios] : [1];
|
|
1605
|
+
const emptyCells = ratios.map(() => []);
|
|
1606
|
+
const rawCells = Array.isArray(p.cells) ? p.cells : [];
|
|
1607
|
+
const cellsOut = depth >= MAX_LAYOUT_TREE_DEPTH ? emptyCells : ratios.map((_, i) => {
|
|
1608
|
+
const col = rawCells[i];
|
|
1609
|
+
return Array.isArray(col) ? col.map((child) => internalBlockToEmailDoc(child, depth + 1)) : [];
|
|
1610
|
+
});
|
|
1611
|
+
return {
|
|
1612
|
+
id,
|
|
1613
|
+
type,
|
|
1614
|
+
content: {
|
|
1615
|
+
preset: p.preset,
|
|
1616
|
+
cols: p.cols,
|
|
1617
|
+
ratios,
|
|
1618
|
+
gap: p.gap,
|
|
1619
|
+
padding: p.padding,
|
|
1620
|
+
bgColor: p.bgColor,
|
|
1621
|
+
bgImage: p.bgImage,
|
|
1622
|
+
bgSize: p.bgSize,
|
|
1623
|
+
bgRepeat: p.bgRepeat,
|
|
1624
|
+
bgPosition: p.bgPosition,
|
|
1625
|
+
bgGradient: p.bgGradient ?? null,
|
|
1626
|
+
columnStyles: p.columnStyles,
|
|
1627
|
+
cells: cellsOut
|
|
1628
|
+
},
|
|
1629
|
+
styles: {}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
default:
|
|
1633
|
+
return { id, type, content: {}, styles: {} };
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
function mapBlockToInternal(b, layoutDepth = 0) {
|
|
1374
1637
|
const t = b.type;
|
|
1375
1638
|
if (!isKnownBlockType(t)) return null;
|
|
1376
1639
|
const block = makeContentBlock(t);
|
|
@@ -1441,8 +1704,28 @@ function mapBlockToInternal(b) {
|
|
|
1441
1704
|
if (asNum(s.height) != null) block.props.height = s.height;
|
|
1442
1705
|
break;
|
|
1443
1706
|
case "social": {
|
|
1444
|
-
const
|
|
1445
|
-
|
|
1707
|
+
const networksRaw = c.networks;
|
|
1708
|
+
let networksObj = {};
|
|
1709
|
+
if (Array.isArray(networksRaw)) {
|
|
1710
|
+
const urlMap = c.socialUrls && typeof c.socialUrls === "object" && !Array.isArray(c.socialUrls) ? c.socialUrls : {};
|
|
1711
|
+
for (const n of networksRaw) {
|
|
1712
|
+
if (typeof n !== "string" || !n) continue;
|
|
1713
|
+
const u = urlMap[n];
|
|
1714
|
+
networksObj[n] = typeof u === "string" ? u : "";
|
|
1715
|
+
}
|
|
1716
|
+
for (const key of Object.keys(urlMap)) {
|
|
1717
|
+
if (key in networksObj) continue;
|
|
1718
|
+
const u = urlMap[key];
|
|
1719
|
+
if (typeof u === "string" && u.trim()) networksObj[key] = u;
|
|
1720
|
+
}
|
|
1721
|
+
} else if (networksRaw && typeof networksRaw === "object" && !Array.isArray(networksRaw)) {
|
|
1722
|
+
networksObj = { ...networksRaw };
|
|
1723
|
+
for (const k of Object.keys(networksObj)) {
|
|
1724
|
+
const v = networksObj[k];
|
|
1725
|
+
networksObj[k] = typeof v === "string" ? v : "";
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const keys = Object.keys(networksObj);
|
|
1446
1729
|
if (keys.length) {
|
|
1447
1730
|
block.props.networks = keys;
|
|
1448
1731
|
block.props.socialUrls = networksObj;
|
|
@@ -1451,6 +1734,13 @@ function mapBlockToInternal(b) {
|
|
|
1451
1734
|
if (asNum(s.gap) != null) block.props.gap = s.gap;
|
|
1452
1735
|
if (asStr(s.shape)) block.props.shape = s.shape;
|
|
1453
1736
|
if (asStr(s.align)) block.props.align = s.align;
|
|
1737
|
+
if (s.padding != null) {
|
|
1738
|
+
if (typeof s.padding === "number" && Number.isFinite(s.padding)) {
|
|
1739
|
+
block.props.padding = s.padding;
|
|
1740
|
+
} else if (s.padding && typeof s.padding === "object" && !Array.isArray(s.padding)) {
|
|
1741
|
+
block.props.padding = layoutColumnPaddingForDoc(s.padding);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1454
1744
|
break;
|
|
1455
1745
|
}
|
|
1456
1746
|
case "table":
|
|
@@ -1491,15 +1781,81 @@ function mapBlockToInternal(b) {
|
|
|
1491
1781
|
if (asNum(s.fontSize) != null) block.props.fontSize = s.fontSize;
|
|
1492
1782
|
if (asStr(s.textAlign)) block.props.align = s.textAlign;
|
|
1493
1783
|
break;
|
|
1494
|
-
case "layout":
|
|
1784
|
+
case "layout": {
|
|
1785
|
+
if (Array.isArray(c.cells)) {
|
|
1786
|
+
const d = DEFAULT_BLOCK_PROPS.layout;
|
|
1787
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1788
|
+
if (typeof c.cols === "number") block.props.cols = c.cols;
|
|
1789
|
+
if (Array.isArray(c.ratios)) block.props.ratios = [...c.ratios];
|
|
1790
|
+
if (typeof c.gap === "number") block.props.gap = c.gap;
|
|
1791
|
+
if (c.padding !== void 0 && c.padding !== null) block.props.padding = c.padding;
|
|
1792
|
+
if (typeof c.bgColor === "string") block.props.bgColor = c.bgColor;
|
|
1793
|
+
if (typeof c.bgImage === "string") block.props.bgImage = c.bgImage;
|
|
1794
|
+
if (typeof c.bgSize === "string") block.props.bgSize = c.bgSize;
|
|
1795
|
+
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1796
|
+
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1797
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1798
|
+
if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
1799
|
+
block.props.columnStyles = c.columnStyles;
|
|
1800
|
+
}
|
|
1801
|
+
block.props.cells = c.cells.map(
|
|
1802
|
+
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
1803
|
+
);
|
|
1804
|
+
} else {
|
|
1805
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1806
|
+
}
|
|
1495
1807
|
break;
|
|
1808
|
+
}
|
|
1496
1809
|
default:
|
|
1497
1810
|
break;
|
|
1498
1811
|
}
|
|
1812
|
+
const styleObj = s && typeof s === "object" && !Array.isArray(s) ? s : {};
|
|
1813
|
+
const hasDocStyles = Object.keys(styleObj).some((k) => {
|
|
1814
|
+
const v = styleObj[k];
|
|
1815
|
+
return v !== void 0 && v !== null && v !== "";
|
|
1816
|
+
});
|
|
1817
|
+
const skipContentMergeKeys = {
|
|
1818
|
+
heading: /* @__PURE__ */ new Set(["text", "tag"]),
|
|
1819
|
+
text: /* @__PURE__ */ new Set(["html", "text"]),
|
|
1820
|
+
html: /* @__PURE__ */ new Set(["html"]),
|
|
1821
|
+
image: /* @__PURE__ */ new Set(["src", "alt", "link"]),
|
|
1822
|
+
button: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1823
|
+
link: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1824
|
+
social: /* @__PURE__ */ new Set(["networks", "socialUrls"]),
|
|
1825
|
+
table: /* @__PURE__ */ new Set(["data"]),
|
|
1826
|
+
menu: /* @__PURE__ */ new Set(["items"]),
|
|
1827
|
+
video: /* @__PURE__ */ new Set(["url"]),
|
|
1828
|
+
timer: /* @__PURE__ */ new Set(["endDate"])
|
|
1829
|
+
};
|
|
1830
|
+
if (!hasDocStyles && c && typeof c === "object" && t !== "layout") {
|
|
1831
|
+
const known = DEFAULT_BLOCK_PROPS[t];
|
|
1832
|
+
const skip = skipContentMergeKeys[t];
|
|
1833
|
+
if (known && typeof known === "object" && !Array.isArray(known)) {
|
|
1834
|
+
for (const key of Object.keys(known)) {
|
|
1835
|
+
if (skip?.has(key)) continue;
|
|
1836
|
+
if (key in c && c[key] !== void 0) {
|
|
1837
|
+
block.props[key] = c[key];
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
1499
1843
|
return block;
|
|
1500
1844
|
}
|
|
1501
1845
|
function rowToInternal(r) {
|
|
1502
|
-
const
|
|
1846
|
+
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1847
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1848
|
+
const cols = Math.max(1, columns.length, layoutColCount);
|
|
1849
|
+
const colList = (() => {
|
|
1850
|
+
if (columns.length >= cols) return columns;
|
|
1851
|
+
if (columns.length > 0) {
|
|
1852
|
+
return [
|
|
1853
|
+
...columns,
|
|
1854
|
+
...Array.from({ length: cols - columns.length }).map(() => ({}))
|
|
1855
|
+
];
|
|
1856
|
+
}
|
|
1857
|
+
return Array.from({ length: cols }).map(() => ({}));
|
|
1858
|
+
})();
|
|
1503
1859
|
const preset = pickPresetByColCount(cols) || LAYOUT_PRESETS[0];
|
|
1504
1860
|
const row = makeLayoutRow(preset);
|
|
1505
1861
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
@@ -1512,14 +1868,14 @@ function rowToInternal(r) {
|
|
|
1512
1868
|
row.bgSize = r.styles?.backgroundSize || row.bgSize;
|
|
1513
1869
|
row.bgPosition = r.styles?.backgroundPosition || row.bgPosition || "center";
|
|
1514
1870
|
row.bgGradient = r.styles?.backgroundGradient || row.bgGradient || null;
|
|
1515
|
-
|
|
1516
|
-
const colList = columns.length ? columns : Array.from({ length: cols }).map(() => ({}));
|
|
1517
|
-
row.cells = colList.slice(0, cols).map((c) => {
|
|
1871
|
+
row.cells = colList.map((c) => {
|
|
1518
1872
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1519
|
-
return blocks.map(mapBlockToInternal).filter((x) => x != null);
|
|
1873
|
+
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1520
1874
|
});
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1875
|
+
const studio = r._reactEmailStudio;
|
|
1876
|
+
const snap = studio?.row;
|
|
1877
|
+
const columnStylesFromDoc = {};
|
|
1878
|
+
colList.forEach((c, i) => {
|
|
1523
1879
|
const bgColor = typeof c.styles?.backgroundColor === "string" ? c.styles.backgroundColor : "";
|
|
1524
1880
|
const padding = layoutColumnPaddingForDoc(c.styles?.padding);
|
|
1525
1881
|
const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
|
|
@@ -1531,17 +1887,30 @@ function rowToInternal(r) {
|
|
|
1531
1887
|
const hasPad = columnPaddingNonZero(padding);
|
|
1532
1888
|
const hasBr = columnRadiusNonZero(borderRadius);
|
|
1533
1889
|
if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
|
|
1534
|
-
|
|
1890
|
+
columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
|
|
1535
1891
|
}
|
|
1536
1892
|
});
|
|
1537
|
-
if (
|
|
1893
|
+
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1894
|
+
const { cells: _omitCells, columnStyles: snapCs, ...rest } = snap;
|
|
1895
|
+
Object.assign(row, rest);
|
|
1896
|
+
const snapColumn = snapCs && typeof snapCs === "object" && !Array.isArray(snapCs) ? snapCs : {};
|
|
1897
|
+
row.columnStyles = { ...snapColumn, ...columnStylesFromDoc };
|
|
1898
|
+
if (!Object.keys(row.columnStyles).length) delete row.columnStyles;
|
|
1899
|
+
} else if (Object.keys(columnStylesFromDoc).length) {
|
|
1900
|
+
row.columnStyles = columnStylesFromDoc;
|
|
1901
|
+
}
|
|
1538
1902
|
return row;
|
|
1539
1903
|
}
|
|
1540
1904
|
function normalizeEmailDesignInput(input) {
|
|
1541
1905
|
const doc = normalizeEmailDocument(input);
|
|
1542
1906
|
if (!doc) return null;
|
|
1543
1907
|
const s = doc.settings || {};
|
|
1544
|
-
const
|
|
1908
|
+
const studioSettings = s._reactEmailStudio;
|
|
1909
|
+
const settings = {};
|
|
1910
|
+
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1911
|
+
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1912
|
+
}
|
|
1913
|
+
Object.assign(settings, {
|
|
1545
1914
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1546
1915
|
bgImage: s.backgroundImage || "",
|
|
1547
1916
|
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
@@ -1555,15 +1924,15 @@ function normalizeEmailDesignInput(input) {
|
|
|
1555
1924
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1556
1925
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1557
1926
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1558
|
-
padding: 24,
|
|
1559
|
-
borderRadius: 8,
|
|
1927
|
+
padding: typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : typeof settings.padding === "number" ? settings.padding : 24,
|
|
1928
|
+
borderRadius: typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : typeof settings.borderRadius === "number" ? settings.borderRadius : 8,
|
|
1560
1929
|
pageFontFamily: s.fontFamily,
|
|
1561
1930
|
pageTextColor: s.color,
|
|
1562
1931
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1563
1932
|
pageResponsive: s.responsive,
|
|
1564
1933
|
pageRtl: s.rtl,
|
|
1565
1934
|
fontFamily: s.fontFamily
|
|
1566
|
-
};
|
|
1935
|
+
});
|
|
1567
1936
|
const rows = (doc.rows || []).map(rowToInternal);
|
|
1568
1937
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1569
1938
|
}
|
|
@@ -1587,14 +1956,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1587
1956
|
fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
|
|
1588
1957
|
lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
|
|
1589
1958
|
color: settings.pageTextColor || "#111827",
|
|
1590
|
-
responsive: true,
|
|
1591
|
-
rtl:
|
|
1959
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
1960
|
+
rtl: !!settings.pageRtl,
|
|
1961
|
+
_reactEmailStudio: {
|
|
1962
|
+
contentPadding: settings.padding ?? 24,
|
|
1963
|
+
contentBorderRadius: settings.borderRadius ?? 8,
|
|
1964
|
+
editorSettings: cloneJson(settings)
|
|
1965
|
+
}
|
|
1592
1966
|
},
|
|
1593
1967
|
rows: rows.map((r, ri) => {
|
|
1594
|
-
const
|
|
1968
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
1969
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
|
|
1970
|
+
const cols = Math.max(1, cellArrays.length, declaredCols);
|
|
1971
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1972
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1973
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
1974
|
+
})();
|
|
1975
|
+
const { cells: _rowCells, ...rowEditorSnap } = r;
|
|
1595
1976
|
return {
|
|
1596
1977
|
id: r.id || `row_${ri + 1}`,
|
|
1597
1978
|
type: "row",
|
|
1979
|
+
_reactEmailStudio: {
|
|
1980
|
+
row: cloneJson(rowEditorSnap)
|
|
1981
|
+
},
|
|
1598
1982
|
layout: {
|
|
1599
1983
|
columns: cols,
|
|
1600
1984
|
gap: r.gap ?? 0,
|
|
@@ -1608,13 +1992,14 @@ function designToEmailDocument(rows, settings) {
|
|
|
1608
1992
|
backgroundSize: r.bgSize || "cover",
|
|
1609
1993
|
backgroundPosition: r.bgPosition || "center",
|
|
1610
1994
|
backgroundGradient: r.bgGradient || null,
|
|
1611
|
-
padding:
|
|
1995
|
+
padding: rowPad,
|
|
1612
1996
|
borderRadius: 0,
|
|
1613
1997
|
borderWidth: 0,
|
|
1614
1998
|
borderColor: "#e5e7eb",
|
|
1615
1999
|
textAlign: "left"
|
|
1616
2000
|
},
|
|
1617
|
-
columns: (
|
|
2001
|
+
columns: Array.from({ length: cols }, (_, ci) => {
|
|
2002
|
+
const cellBlocks = cellArrays[ci] ?? [];
|
|
1618
2003
|
const cs = r.columnStyles && r.columnStyles[ci] || {};
|
|
1619
2004
|
return {
|
|
1620
2005
|
id: `col_${ri + 1}_${ci + 1}`,
|
|
@@ -1629,12 +2014,7 @@ function designToEmailDocument(rows, settings) {
|
|
|
1629
2014
|
backgroundGradient: cs.bgGradient || null,
|
|
1630
2015
|
borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
|
|
1631
2016
|
},
|
|
1632
|
-
blocks: (cellBlocks || []).map((b) => (
|
|
1633
|
-
id: b.id,
|
|
1634
|
-
type: b.type,
|
|
1635
|
-
content: b.props,
|
|
1636
|
-
styles: {}
|
|
1637
|
-
}))
|
|
2017
|
+
blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
|
|
1638
2018
|
};
|
|
1639
2019
|
})
|
|
1640
2020
|
};
|
|
@@ -1642,14 +2022,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1642
2022
|
};
|
|
1643
2023
|
return doc;
|
|
1644
2024
|
}
|
|
1645
|
-
function hydrateBlock(b) {
|
|
2025
|
+
function hydrateBlock(b, depth = 0) {
|
|
1646
2026
|
if (!b || typeof b !== "object") return null;
|
|
1647
2027
|
const t = b.type === "nestedRow" ? "layout" : b.type;
|
|
1648
2028
|
if (!isKnownBlockType(t)) return null;
|
|
1649
2029
|
if (t === "layout" && b.props && Array.isArray(b.props.cells)) {
|
|
1650
2030
|
const defaults2 = DEFAULT_BLOCK_PROPS.layout;
|
|
2031
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2032
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? [...b.props.ratios] : [1];
|
|
2033
|
+
return {
|
|
2034
|
+
...b,
|
|
2035
|
+
type: "layout",
|
|
2036
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2037
|
+
props: {
|
|
2038
|
+
...defaults2,
|
|
2039
|
+
...b.props,
|
|
2040
|
+
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2041
|
+
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2042
|
+
cells: ratios.map(() => [])
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
1651
2046
|
const cells = b.props.cells.map(
|
|
1652
|
-
(col) => Array.isArray(col) ? col.map(hydrateBlock).filter((x) => x != null) : []
|
|
2047
|
+
(col) => Array.isArray(col) ? col.map((x) => hydrateBlock(x, depth + 1)).filter((x) => x != null) : []
|
|
1653
2048
|
);
|
|
1654
2049
|
return {
|
|
1655
2050
|
...b,
|
|
@@ -1680,6 +2075,78 @@ function hydrateBlock(b) {
|
|
|
1680
2075
|
props: merged
|
|
1681
2076
|
};
|
|
1682
2077
|
}
|
|
2078
|
+
function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
2079
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2080
|
+
const r = raw;
|
|
2081
|
+
if (r.props && typeof r.props === "object" && typeof r.type === "string" && !("styles" in r)) {
|
|
2082
|
+
return hydrateBlock(r, layoutDepth);
|
|
2083
|
+
}
|
|
2084
|
+
return mapBlockToInternal(r, layoutDepth);
|
|
2085
|
+
}
|
|
2086
|
+
function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
|
|
2087
|
+
const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
2088
|
+
if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
|
|
2089
|
+
const a = isObj(fromContent) ? fromContent : {};
|
|
2090
|
+
const b = isObj(fromHydrated) ? fromHydrated : {};
|
|
2091
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
2092
|
+
const out = {};
|
|
2093
|
+
for (const k of keys) {
|
|
2094
|
+
const va = a[k];
|
|
2095
|
+
const vb = b[k];
|
|
2096
|
+
if (isObj(va) && isObj(vb)) {
|
|
2097
|
+
out[k] = { ...va, ...vb };
|
|
2098
|
+
} else if (vb !== void 0) {
|
|
2099
|
+
out[k] = vb;
|
|
2100
|
+
} else {
|
|
2101
|
+
out[k] = va;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
return Object.keys(out).length ? out : void 0;
|
|
2105
|
+
}
|
|
2106
|
+
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2107
|
+
const exp = b.props;
|
|
2108
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
|
|
2109
|
+
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2110
|
+
const columnStylesFromContent = block.props.columnStyles;
|
|
2111
|
+
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2112
|
+
if (hb) {
|
|
2113
|
+
block.props = hb.props;
|
|
2114
|
+
const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
|
|
2115
|
+
if (merged) block.props.columnStyles = merged;
|
|
2116
|
+
else delete block.props.columnStyles;
|
|
2117
|
+
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2118
|
+
}
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
Object.assign(block.props, exp);
|
|
2122
|
+
normalizeBoxStyles(block.props, t);
|
|
2123
|
+
if (t === "html" || t === "text") {
|
|
2124
|
+
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
function exportEmailDocBlock(b, depth = 0) {
|
|
2128
|
+
if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
|
|
2129
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2130
|
+
const out = { ...doc };
|
|
2131
|
+
if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
2132
|
+
out.props = cloneJson(b.props);
|
|
2133
|
+
}
|
|
2134
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2135
|
+
const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
|
|
2136
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2137
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
|
|
2138
|
+
out.content = { ...baseContent, cells: ratios.map(() => []) };
|
|
2139
|
+
} else {
|
|
2140
|
+
out.content = {
|
|
2141
|
+
...baseContent,
|
|
2142
|
+
cells: b.props.cells.map(
|
|
2143
|
+
(col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
|
|
2144
|
+
)
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
return out;
|
|
2149
|
+
}
|
|
1683
2150
|
function hydrateLayoutRow(row) {
|
|
1684
2151
|
if (!row || typeof row !== "object") return row;
|
|
1685
2152
|
const cells = (row.cells || []).map(
|
|
@@ -4452,32 +4919,42 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4452
4919
|
] });
|
|
4453
4920
|
case "text":
|
|
4454
4921
|
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
4455
|
-
/* @__PURE__ */ jsx6(
|
|
4456
|
-
|
|
4922
|
+
/* @__PURE__ */ jsx6(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ jsx6(
|
|
4923
|
+
"textarea",
|
|
4457
4924
|
{
|
|
4925
|
+
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
4458
4926
|
value: normalizeRichHtmlForStorage(p.content),
|
|
4459
|
-
onChange: (
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4927
|
+
onChange: (e) => set("content", normalizeRichHtmlForStorage(e.target.value)),
|
|
4928
|
+
spellCheck: false,
|
|
4929
|
+
placeholder: "e.g. <p>Your message</p>"
|
|
4930
|
+
},
|
|
4931
|
+
block.id
|
|
4932
|
+
) }),
|
|
4933
|
+
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ jsx6(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ jsxs4(
|
|
4934
|
+
"select",
|
|
4935
|
+
{
|
|
4936
|
+
style: IS,
|
|
4937
|
+
defaultValue: "",
|
|
4938
|
+
onChange: (e) => {
|
|
4939
|
+
const v = e.target.value;
|
|
4940
|
+
if (v) {
|
|
4941
|
+
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4942
|
+
e.target.value = "";
|
|
4943
|
+
}
|
|
4468
4944
|
},
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
C
|
|
4945
|
+
children: [
|
|
4946
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "Choose\u2026" }),
|
|
4947
|
+
mergeTags.map((t) => /* @__PURE__ */ jsx6("option", { value: t.value, children: t.name }, t.name))
|
|
4948
|
+
]
|
|
4474
4949
|
}
|
|
4475
|
-
) }
|
|
4950
|
+
) }) : null,
|
|
4476
4951
|
/* @__PURE__ */ jsx6(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
|
|
4477
4952
|
Col("color", "Color"),
|
|
4478
4953
|
/* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
4479
4954
|
Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
|
|
4480
4955
|
Sel("fontFamily", "Font", FONTS2),
|
|
4956
|
+
Tog("italic", "Italic"),
|
|
4957
|
+
Tog("underline", "Underline"),
|
|
4481
4958
|
/* @__PURE__ */ jsx6(PR, { label: "Line height", C, children: /* @__PURE__ */ jsx6("input", { type: "number", style: useIS(C).IS, min: 1, max: 4, step: 0.05, value: p.lineHeight ?? 1.65, onChange: (e) => set("lineHeight", +e.target.value) }) }),
|
|
4482
4959
|
/* @__PURE__ */ jsx6(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
|
|
4483
4960
|
/* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -6054,6 +6531,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
6054
6531
|
const [previewDevice, setPreviewDevice] = useState6("desktop");
|
|
6055
6532
|
const [activeTab, setActiveTab] = useState6("blocks");
|
|
6056
6533
|
const [templatesOpen, setTemplatesOpen] = useState6(false);
|
|
6534
|
+
const [jsonLoading, setJsonLoading] = useState6(false);
|
|
6057
6535
|
const [autoSaveMsg, setAutoSaveMsg] = useState6("");
|
|
6058
6536
|
const [locale, setLocale] = useState6(
|
|
6059
6537
|
typeof opt.locale === "string" ? opt.locale : "en"
|
|
@@ -6196,31 +6674,40 @@ var ReactEmailEditor = forwardRef(
|
|
|
6196
6674
|
}, [rows, history, future, selContentMeta]);
|
|
6197
6675
|
const buildApi = useCallback2(() => ({
|
|
6198
6676
|
loadJson(input) {
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6677
|
+
setJsonLoading(true);
|
|
6678
|
+
requestAnimationFrame(() => {
|
|
6679
|
+
requestAnimationFrame(() => {
|
|
6680
|
+
try {
|
|
6681
|
+
let parsed = input;
|
|
6682
|
+
if (typeof input === "string") {
|
|
6683
|
+
try {
|
|
6684
|
+
parsed = JSON.parse(input);
|
|
6685
|
+
} catch {
|
|
6686
|
+
return;
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
const norm = normalizeEmailDesignInput(parsed);
|
|
6690
|
+
if (!norm) return;
|
|
6691
|
+
setRows(norm.rows);
|
|
6692
|
+
const ns = norm.settings;
|
|
6693
|
+
setSettings((s) => ({ ...s, ...ns }));
|
|
6694
|
+
const inferPage = () => {
|
|
6695
|
+
if (ns?.bgGradient) return "gradient";
|
|
6696
|
+
if (String(ns?.bgImage ?? "").trim()) return "image";
|
|
6697
|
+
return "solid";
|
|
6698
|
+
};
|
|
6699
|
+
const inferContent = () => {
|
|
6700
|
+
if (ns?.contentBgGradient) return "gradient";
|
|
6701
|
+
if (String(ns?.contentBgImage ?? "").trim()) return "image";
|
|
6702
|
+
return "solid";
|
|
6703
|
+
};
|
|
6704
|
+
setPageBgUiStep(inferPage());
|
|
6705
|
+
setContentBgUiStep(inferContent());
|
|
6706
|
+
} finally {
|
|
6707
|
+
setJsonLoading(false);
|
|
6708
|
+
}
|
|
6709
|
+
});
|
|
6710
|
+
});
|
|
6224
6711
|
},
|
|
6225
6712
|
exportJson(cb, pretty) {
|
|
6226
6713
|
const design = designToEmailDocument(rowsRef.current, settingsRef.current);
|
|
@@ -6319,6 +6806,9 @@ var ReactEmailEditor = forwardRef(
|
|
|
6319
6806
|
const nested = action.nested;
|
|
6320
6807
|
const toLoc = toColumnLoc(rowId, cellIdx, nested);
|
|
6321
6808
|
if (action.kind === "new") {
|
|
6809
|
+
if ((action.contentType === "layout" || action.contentType === "nestedRow") && nestedLayoutDepth(nested) >= MAX_NESTED_LAYOUT_DEPTH) {
|
|
6810
|
+
return;
|
|
6811
|
+
}
|
|
6322
6812
|
let nb;
|
|
6323
6813
|
if (action.preset && (action.contentType === "layout" || action.contentType === "nestedRow")) {
|
|
6324
6814
|
nb = makeNestedRowBlock(action.preset);
|
|
@@ -6604,6 +7094,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
6604
7094
|
::-webkit-scrollbar-track{background:transparent;}
|
|
6605
7095
|
::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
|
|
6606
7096
|
::-webkit-scrollbar-thumb:hover{background:${C.muted};}
|
|
7097
|
+
@keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
|
|
6607
7098
|
` }),
|
|
6608
7099
|
/* @__PURE__ */ jsxs9("div", { id: eid("workspace"), style: {
|
|
6609
7100
|
display: "flex",
|
|
@@ -7532,7 +8023,47 @@ var ReactEmailEditor = forwardRef(
|
|
|
7532
8023
|
) })
|
|
7533
8024
|
]
|
|
7534
8025
|
}
|
|
7535
|
-
)
|
|
8026
|
+
),
|
|
8027
|
+
jsonLoading ? /* @__PURE__ */ jsxs9(
|
|
8028
|
+
"div",
|
|
8029
|
+
{
|
|
8030
|
+
id: eid("json-loading"),
|
|
8031
|
+
role: "status",
|
|
8032
|
+
"aria-live": "polite",
|
|
8033
|
+
"aria-busy": true,
|
|
8034
|
+
style: {
|
|
8035
|
+
position: "absolute",
|
|
8036
|
+
inset: 0,
|
|
8037
|
+
zIndex: 180,
|
|
8038
|
+
background: `${C.bg}f2`,
|
|
8039
|
+
backdropFilter: "blur(2px)",
|
|
8040
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
8041
|
+
display: "flex",
|
|
8042
|
+
flexDirection: "column",
|
|
8043
|
+
alignItems: "center",
|
|
8044
|
+
justifyContent: "center",
|
|
8045
|
+
gap: 14,
|
|
8046
|
+
pointerEvents: "auto"
|
|
8047
|
+
},
|
|
8048
|
+
children: [
|
|
8049
|
+
/* @__PURE__ */ jsx11(
|
|
8050
|
+
"div",
|
|
8051
|
+
{
|
|
8052
|
+
"aria-hidden": true,
|
|
8053
|
+
style: {
|
|
8054
|
+
width: 40,
|
|
8055
|
+
height: 40,
|
|
8056
|
+
borderRadius: "50%",
|
|
8057
|
+
border: `3px solid ${C.border}`,
|
|
8058
|
+
borderTopColor: C.accent,
|
|
8059
|
+
animation: "email-editor-json-spin 0.65s linear infinite"
|
|
8060
|
+
}
|
|
8061
|
+
}
|
|
8062
|
+
),
|
|
8063
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
|
|
8064
|
+
]
|
|
8065
|
+
}
|
|
8066
|
+
) : null
|
|
7536
8067
|
] }),
|
|
7537
8068
|
/* @__PURE__ */ jsxs9("div", { id: eid("status-bar"), style: { display: "flex", alignItems: "center", gap: 12, padding: "3px 14px", background: C.toolbarBg, borderTop: `1px solid ${C.border}`, fontSize: 10, color: C.muted, flexShrink: 0, position: "sticky", bottom: 0, zIndex: 100 }, children: [
|
|
7538
8069
|
/* @__PURE__ */ jsxs9("span", { children: [
|