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.cjs
CHANGED
|
@@ -170,7 +170,8 @@ var I18N = {
|
|
|
170
170
|
blockPaletteGroupWidgets: "Widgets",
|
|
171
171
|
closePanel: "Close",
|
|
172
172
|
canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
|
|
173
|
-
emailContentSettings: "Email content"
|
|
173
|
+
emailContentSettings: "Email content",
|
|
174
|
+
loadingDesign: "Loading design\u2026"
|
|
174
175
|
},
|
|
175
176
|
fr: {
|
|
176
177
|
layouts: "Mises en page",
|
|
@@ -223,6 +224,7 @@ var I18N = {
|
|
|
223
224
|
blockPaletteGroupWidgets: "Widgets",
|
|
224
225
|
closePanel: "Fermer",
|
|
225
226
|
canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
|
|
227
|
+
loadingDesign: "Chargement du design\u2026",
|
|
226
228
|
emailContentSettings: "Contenu de l\u2019e-mail",
|
|
227
229
|
zoomIn: "Zoom +",
|
|
228
230
|
zoomOut: "Zoom -",
|
|
@@ -282,6 +284,7 @@ var I18N = {
|
|
|
282
284
|
blockPaletteGroupWidgets: "Widgets",
|
|
283
285
|
closePanel: "Schlie\xDFen",
|
|
284
286
|
canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
|
|
287
|
+
loadingDesign: "Design wird geladen\u2026",
|
|
285
288
|
emailContentSettings: "E-Mail-Inhalt",
|
|
286
289
|
zoomIn: "Zoom +",
|
|
287
290
|
zoomOut: "Zoom -",
|
|
@@ -342,6 +345,7 @@ var I18N = {
|
|
|
342
345
|
blockPaletteGroupWidgets: "Widgets",
|
|
343
346
|
closePanel: "Cerrar",
|
|
344
347
|
canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
|
|
348
|
+
loadingDesign: "Cargando dise\xF1o\u2026",
|
|
345
349
|
emailContentSettings: "Contenido del correo",
|
|
346
350
|
resizeSidebar: "Redimensionar panel"
|
|
347
351
|
}
|
|
@@ -692,6 +696,10 @@ function makeContentBlock(type) {
|
|
|
692
696
|
}
|
|
693
697
|
|
|
694
698
|
// src/lib/columnPath.ts
|
|
699
|
+
var MAX_NESTED_LAYOUT_DEPTH = 1;
|
|
700
|
+
function nestedLayoutDepth(nested) {
|
|
701
|
+
return nested != null && nested.parentBlockIdx != null && nested.innerCellIdx != null ? 1 : 0;
|
|
702
|
+
}
|
|
695
703
|
function isSplitLayoutBlock(b) {
|
|
696
704
|
return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
|
|
697
705
|
}
|
|
@@ -1283,8 +1291,18 @@ function normalizeEmailDocument(input) {
|
|
|
1283
1291
|
settings: doc.settings && typeof doc.settings === "object" && !Array.isArray(doc.settings) ? doc.settings : {},
|
|
1284
1292
|
rows: rows.map((r, ri) => {
|
|
1285
1293
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1286
|
-
const
|
|
1287
|
-
const
|
|
1294
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1295
|
+
const colCount = Math.max(1, columns.length, layoutColCount);
|
|
1296
|
+
const colsNormalized = (() => {
|
|
1297
|
+
if (columns.length >= colCount) return columns;
|
|
1298
|
+
if (columns.length > 0) {
|
|
1299
|
+
return [
|
|
1300
|
+
...columns,
|
|
1301
|
+
...Array.from({ length: colCount - columns.length }).map(() => ({}))
|
|
1302
|
+
];
|
|
1303
|
+
}
|
|
1304
|
+
return Array.from({ length: colCount }).map(() => ({}));
|
|
1305
|
+
})();
|
|
1288
1306
|
return {
|
|
1289
1307
|
id: ensureId(r.id, `row${ri + 1}`),
|
|
1290
1308
|
type: "row",
|
|
@@ -1295,6 +1313,7 @@ function normalizeEmailDocument(input) {
|
|
|
1295
1313
|
align: r.layout?.align || "center"
|
|
1296
1314
|
},
|
|
1297
1315
|
styles: r.styles && typeof r.styles === "object" && !Array.isArray(r.styles) ? r.styles : {},
|
|
1316
|
+
...typeof r._reactEmailStudio === "object" && r._reactEmailStudio !== null && !Array.isArray(r._reactEmailStudio) ? { _reactEmailStudio: r._reactEmailStudio } : {},
|
|
1298
1317
|
columns: colsNormalized.map((c, ci) => ({
|
|
1299
1318
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1300
1319
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
@@ -1305,7 +1324,8 @@ function normalizeEmailDocument(input) {
|
|
|
1305
1324
|
content: b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1306
1325
|
styles: b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles) ? b.styles : {},
|
|
1307
1326
|
behavior: b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? b.behavior : {},
|
|
1308
|
-
responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {}
|
|
1327
|
+
responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {},
|
|
1328
|
+
...b?.props && typeof b.props === "object" && !Array.isArray(b.props) ? { props: b.props } : {}
|
|
1309
1329
|
})) : []
|
|
1310
1330
|
}))
|
|
1311
1331
|
};
|
|
@@ -1316,6 +1336,14 @@ function normalizeEmailDocument(input) {
|
|
|
1316
1336
|
}
|
|
1317
1337
|
|
|
1318
1338
|
// src/lib/emailDesignJson.ts
|
|
1339
|
+
var MAX_LAYOUT_TREE_DEPTH = 32;
|
|
1340
|
+
function cloneJson(v) {
|
|
1341
|
+
try {
|
|
1342
|
+
return JSON.parse(JSON.stringify(v));
|
|
1343
|
+
} catch {
|
|
1344
|
+
return v;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1319
1347
|
function layoutColumnPaddingForDoc(p) {
|
|
1320
1348
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1321
1349
|
const n = Math.max(0, p);
|
|
@@ -1402,7 +1430,242 @@ function normalizeBoxStyles(props, t) {
|
|
|
1402
1430
|
}
|
|
1403
1431
|
props.padding = boxPad(props.padding, uniformPadDefault(def?.padding));
|
|
1404
1432
|
}
|
|
1405
|
-
function
|
|
1433
|
+
function internalBlockToEmailDoc(b, depth = 0) {
|
|
1434
|
+
const p = b?.props && typeof b.props === "object" ? b.props : {};
|
|
1435
|
+
const id = typeof b?.id === "string" ? b.id : uid();
|
|
1436
|
+
const type = b?.type === "nestedRow" ? "layout" : b?.type;
|
|
1437
|
+
if (!isKnownBlockType(type)) {
|
|
1438
|
+
return { id, type: String(type || "text"), content: {}, styles: {} };
|
|
1439
|
+
}
|
|
1440
|
+
switch (type) {
|
|
1441
|
+
case "heading": {
|
|
1442
|
+
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;
|
|
1443
|
+
const padUniform = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : uniformPadDefault(p.padding);
|
|
1444
|
+
return {
|
|
1445
|
+
id,
|
|
1446
|
+
type,
|
|
1447
|
+
content: { text: typeof p.content === "string" ? p.content : "", tag: p.level || "h2" },
|
|
1448
|
+
styles: {
|
|
1449
|
+
fontSize: p.fontSize,
|
|
1450
|
+
fontWeight: fw,
|
|
1451
|
+
color: p.color,
|
|
1452
|
+
lineHeight: p.lineHeight,
|
|
1453
|
+
textAlign: p.align,
|
|
1454
|
+
letterSpacing: p.letterSpacing,
|
|
1455
|
+
marginBottom: padUniform
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
case "text":
|
|
1460
|
+
return {
|
|
1461
|
+
id,
|
|
1462
|
+
type,
|
|
1463
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1464
|
+
styles: {
|
|
1465
|
+
fontSize: p.fontSize,
|
|
1466
|
+
color: p.color,
|
|
1467
|
+
lineHeight: p.lineHeight,
|
|
1468
|
+
textAlign: p.align,
|
|
1469
|
+
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
case "html":
|
|
1473
|
+
return {
|
|
1474
|
+
id,
|
|
1475
|
+
type,
|
|
1476
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1477
|
+
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1478
|
+
};
|
|
1479
|
+
case "image": {
|
|
1480
|
+
const br = p.borderRadius;
|
|
1481
|
+
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
1482
|
+
(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
|
|
1483
|
+
) : 0;
|
|
1484
|
+
return {
|
|
1485
|
+
id,
|
|
1486
|
+
type,
|
|
1487
|
+
content: {
|
|
1488
|
+
src: p.src || "",
|
|
1489
|
+
alt: p.alt || "",
|
|
1490
|
+
link: p.link || ""
|
|
1491
|
+
},
|
|
1492
|
+
styles: {
|
|
1493
|
+
width: p.width,
|
|
1494
|
+
align: p.align,
|
|
1495
|
+
borderRadius
|
|
1496
|
+
},
|
|
1497
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
case "button": {
|
|
1501
|
+
let borderRadiusOut;
|
|
1502
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1503
|
+
borderRadiusOut = p.borderRadius;
|
|
1504
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1505
|
+
const br = p.borderRadius;
|
|
1506
|
+
const tl = numOr(br.tl, 0);
|
|
1507
|
+
const tr2 = numOr(br.tr, tl);
|
|
1508
|
+
const brc = numOr(br.br, tl);
|
|
1509
|
+
const bl = numOr(br.bl, tr2);
|
|
1510
|
+
borderRadiusOut = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
id,
|
|
1514
|
+
type,
|
|
1515
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1516
|
+
styles: {
|
|
1517
|
+
backgroundColor: p.bgColor,
|
|
1518
|
+
color: p.textColor,
|
|
1519
|
+
fontSize: p.fontSize,
|
|
1520
|
+
fontWeight: p.fontWeight,
|
|
1521
|
+
...borderRadiusOut !== void 0 ? { borderRadius: borderRadiusOut } : {},
|
|
1522
|
+
textAlign: p.align,
|
|
1523
|
+
padding: {
|
|
1524
|
+
top: p.paddingV ?? 11,
|
|
1525
|
+
right: p.paddingH ?? 24,
|
|
1526
|
+
bottom: p.paddingV ?? 11,
|
|
1527
|
+
left: p.paddingH ?? 24
|
|
1528
|
+
}
|
|
1529
|
+
},
|
|
1530
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
case "divider": {
|
|
1534
|
+
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;
|
|
1535
|
+
return {
|
|
1536
|
+
id,
|
|
1537
|
+
type,
|
|
1538
|
+
content: {},
|
|
1539
|
+
styles: { color: p.color, thickness: p.thickness, paddingY: padY }
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
case "spacer":
|
|
1543
|
+
return { id, type, content: {}, styles: { height: p.height } };
|
|
1544
|
+
case "social": {
|
|
1545
|
+
const urls = p.socialUrls && typeof p.socialUrls === "object" && !Array.isArray(p.socialUrls) ? p.socialUrls : {};
|
|
1546
|
+
const networks = {};
|
|
1547
|
+
const ordered = Array.isArray(p.networks) ? p.networks.filter((n) => typeof n === "string" && !!n) : [];
|
|
1548
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1549
|
+
for (const n of ordered) {
|
|
1550
|
+
const u = urls[n];
|
|
1551
|
+
networks[n] = typeof u === "string" && u.trim() ? u : "";
|
|
1552
|
+
seen.add(n);
|
|
1553
|
+
}
|
|
1554
|
+
for (const key of Object.keys(urls)) {
|
|
1555
|
+
if (seen.has(key)) continue;
|
|
1556
|
+
const v = urls[key];
|
|
1557
|
+
if (typeof v === "string" && v.trim()) {
|
|
1558
|
+
networks[key] = v;
|
|
1559
|
+
seen.add(key);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
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) } : {} : {};
|
|
1563
|
+
return {
|
|
1564
|
+
id,
|
|
1565
|
+
type,
|
|
1566
|
+
content: { networks },
|
|
1567
|
+
styles: {
|
|
1568
|
+
iconSize: p.iconSize,
|
|
1569
|
+
gap: p.gap,
|
|
1570
|
+
shape: p.shape,
|
|
1571
|
+
align: p.align,
|
|
1572
|
+
...padStyles
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
case "table":
|
|
1577
|
+
return {
|
|
1578
|
+
id,
|
|
1579
|
+
type,
|
|
1580
|
+
content: { data: Array.isArray(p.data) ? p.data : [] },
|
|
1581
|
+
styles: {
|
|
1582
|
+
borderColor: p.cellBorder,
|
|
1583
|
+
cellPadding: p.cellPadding,
|
|
1584
|
+
headerBg: p.headerBg,
|
|
1585
|
+
striped: p.striped
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
case "video":
|
|
1589
|
+
return {
|
|
1590
|
+
id,
|
|
1591
|
+
type,
|
|
1592
|
+
content: { url: p.src || "" },
|
|
1593
|
+
styles: { height: p.height }
|
|
1594
|
+
};
|
|
1595
|
+
case "timer": {
|
|
1596
|
+
let timerBr;
|
|
1597
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1598
|
+
timerBr = p.borderRadius;
|
|
1599
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1600
|
+
const br = p.borderRadius;
|
|
1601
|
+
const tl = numOr(br.tl, 0);
|
|
1602
|
+
const tr2 = numOr(br.tr, tl);
|
|
1603
|
+
const brc = numOr(br.br, tl);
|
|
1604
|
+
const bl = numOr(br.bl, tr2);
|
|
1605
|
+
timerBr = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1606
|
+
}
|
|
1607
|
+
return {
|
|
1608
|
+
id,
|
|
1609
|
+
type,
|
|
1610
|
+
content: { endDate: p.endDate || "" },
|
|
1611
|
+
styles: {
|
|
1612
|
+
backgroundColor: p.bgColor,
|
|
1613
|
+
color: p.textColor,
|
|
1614
|
+
padding: typeof p.padding === "number" ? p.padding : uniformPadDefault(p.padding),
|
|
1615
|
+
...timerBr !== void 0 ? { borderRadius: timerBr } : {},
|
|
1616
|
+
textAlign: p.align
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
case "menu":
|
|
1621
|
+
return {
|
|
1622
|
+
id,
|
|
1623
|
+
type,
|
|
1624
|
+
content: { items: Array.isArray(p.items) ? p.items : [] },
|
|
1625
|
+
styles: { fontSize: p.fontSize, color: p.color }
|
|
1626
|
+
};
|
|
1627
|
+
case "link":
|
|
1628
|
+
return {
|
|
1629
|
+
id,
|
|
1630
|
+
type,
|
|
1631
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1632
|
+
styles: { color: p.color, fontSize: p.fontSize, textAlign: p.align },
|
|
1633
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1634
|
+
};
|
|
1635
|
+
case "layout": {
|
|
1636
|
+
const ratios = Array.isArray(p.ratios) ? [...p.ratios] : [1];
|
|
1637
|
+
const emptyCells = ratios.map(() => []);
|
|
1638
|
+
const rawCells = Array.isArray(p.cells) ? p.cells : [];
|
|
1639
|
+
const cellsOut = depth >= MAX_LAYOUT_TREE_DEPTH ? emptyCells : ratios.map((_, i) => {
|
|
1640
|
+
const col = rawCells[i];
|
|
1641
|
+
return Array.isArray(col) ? col.map((child) => internalBlockToEmailDoc(child, depth + 1)) : [];
|
|
1642
|
+
});
|
|
1643
|
+
return {
|
|
1644
|
+
id,
|
|
1645
|
+
type,
|
|
1646
|
+
content: {
|
|
1647
|
+
preset: p.preset,
|
|
1648
|
+
cols: p.cols,
|
|
1649
|
+
ratios,
|
|
1650
|
+
gap: p.gap,
|
|
1651
|
+
padding: p.padding,
|
|
1652
|
+
bgColor: p.bgColor,
|
|
1653
|
+
bgImage: p.bgImage,
|
|
1654
|
+
bgSize: p.bgSize,
|
|
1655
|
+
bgRepeat: p.bgRepeat,
|
|
1656
|
+
bgPosition: p.bgPosition,
|
|
1657
|
+
bgGradient: p.bgGradient ?? null,
|
|
1658
|
+
columnStyles: p.columnStyles,
|
|
1659
|
+
cells: cellsOut
|
|
1660
|
+
},
|
|
1661
|
+
styles: {}
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
default:
|
|
1665
|
+
return { id, type, content: {}, styles: {} };
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function mapBlockToInternal(b, layoutDepth = 0) {
|
|
1406
1669
|
const t = b.type;
|
|
1407
1670
|
if (!isKnownBlockType(t)) return null;
|
|
1408
1671
|
const block = makeContentBlock(t);
|
|
@@ -1473,8 +1736,28 @@ function mapBlockToInternal(b) {
|
|
|
1473
1736
|
if (asNum(s.height) != null) block.props.height = s.height;
|
|
1474
1737
|
break;
|
|
1475
1738
|
case "social": {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1739
|
+
const networksRaw = c.networks;
|
|
1740
|
+
let networksObj = {};
|
|
1741
|
+
if (Array.isArray(networksRaw)) {
|
|
1742
|
+
const urlMap = c.socialUrls && typeof c.socialUrls === "object" && !Array.isArray(c.socialUrls) ? c.socialUrls : {};
|
|
1743
|
+
for (const n of networksRaw) {
|
|
1744
|
+
if (typeof n !== "string" || !n) continue;
|
|
1745
|
+
const u = urlMap[n];
|
|
1746
|
+
networksObj[n] = typeof u === "string" ? u : "";
|
|
1747
|
+
}
|
|
1748
|
+
for (const key of Object.keys(urlMap)) {
|
|
1749
|
+
if (key in networksObj) continue;
|
|
1750
|
+
const u = urlMap[key];
|
|
1751
|
+
if (typeof u === "string" && u.trim()) networksObj[key] = u;
|
|
1752
|
+
}
|
|
1753
|
+
} else if (networksRaw && typeof networksRaw === "object" && !Array.isArray(networksRaw)) {
|
|
1754
|
+
networksObj = { ...networksRaw };
|
|
1755
|
+
for (const k of Object.keys(networksObj)) {
|
|
1756
|
+
const v = networksObj[k];
|
|
1757
|
+
networksObj[k] = typeof v === "string" ? v : "";
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const keys = Object.keys(networksObj);
|
|
1478
1761
|
if (keys.length) {
|
|
1479
1762
|
block.props.networks = keys;
|
|
1480
1763
|
block.props.socialUrls = networksObj;
|
|
@@ -1483,6 +1766,13 @@ function mapBlockToInternal(b) {
|
|
|
1483
1766
|
if (asNum(s.gap) != null) block.props.gap = s.gap;
|
|
1484
1767
|
if (asStr(s.shape)) block.props.shape = s.shape;
|
|
1485
1768
|
if (asStr(s.align)) block.props.align = s.align;
|
|
1769
|
+
if (s.padding != null) {
|
|
1770
|
+
if (typeof s.padding === "number" && Number.isFinite(s.padding)) {
|
|
1771
|
+
block.props.padding = s.padding;
|
|
1772
|
+
} else if (s.padding && typeof s.padding === "object" && !Array.isArray(s.padding)) {
|
|
1773
|
+
block.props.padding = layoutColumnPaddingForDoc(s.padding);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1486
1776
|
break;
|
|
1487
1777
|
}
|
|
1488
1778
|
case "table":
|
|
@@ -1523,15 +1813,81 @@ function mapBlockToInternal(b) {
|
|
|
1523
1813
|
if (asNum(s.fontSize) != null) block.props.fontSize = s.fontSize;
|
|
1524
1814
|
if (asStr(s.textAlign)) block.props.align = s.textAlign;
|
|
1525
1815
|
break;
|
|
1526
|
-
case "layout":
|
|
1816
|
+
case "layout": {
|
|
1817
|
+
if (Array.isArray(c.cells)) {
|
|
1818
|
+
const d = DEFAULT_BLOCK_PROPS.layout;
|
|
1819
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1820
|
+
if (typeof c.cols === "number") block.props.cols = c.cols;
|
|
1821
|
+
if (Array.isArray(c.ratios)) block.props.ratios = [...c.ratios];
|
|
1822
|
+
if (typeof c.gap === "number") block.props.gap = c.gap;
|
|
1823
|
+
if (c.padding !== void 0 && c.padding !== null) block.props.padding = c.padding;
|
|
1824
|
+
if (typeof c.bgColor === "string") block.props.bgColor = c.bgColor;
|
|
1825
|
+
if (typeof c.bgImage === "string") block.props.bgImage = c.bgImage;
|
|
1826
|
+
if (typeof c.bgSize === "string") block.props.bgSize = c.bgSize;
|
|
1827
|
+
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1828
|
+
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1829
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1830
|
+
if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
1831
|
+
block.props.columnStyles = c.columnStyles;
|
|
1832
|
+
}
|
|
1833
|
+
block.props.cells = c.cells.map(
|
|
1834
|
+
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
1835
|
+
);
|
|
1836
|
+
} else {
|
|
1837
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1838
|
+
}
|
|
1527
1839
|
break;
|
|
1840
|
+
}
|
|
1528
1841
|
default:
|
|
1529
1842
|
break;
|
|
1530
1843
|
}
|
|
1844
|
+
const styleObj = s && typeof s === "object" && !Array.isArray(s) ? s : {};
|
|
1845
|
+
const hasDocStyles = Object.keys(styleObj).some((k) => {
|
|
1846
|
+
const v = styleObj[k];
|
|
1847
|
+
return v !== void 0 && v !== null && v !== "";
|
|
1848
|
+
});
|
|
1849
|
+
const skipContentMergeKeys = {
|
|
1850
|
+
heading: /* @__PURE__ */ new Set(["text", "tag"]),
|
|
1851
|
+
text: /* @__PURE__ */ new Set(["html", "text"]),
|
|
1852
|
+
html: /* @__PURE__ */ new Set(["html"]),
|
|
1853
|
+
image: /* @__PURE__ */ new Set(["src", "alt", "link"]),
|
|
1854
|
+
button: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1855
|
+
link: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1856
|
+
social: /* @__PURE__ */ new Set(["networks", "socialUrls"]),
|
|
1857
|
+
table: /* @__PURE__ */ new Set(["data"]),
|
|
1858
|
+
menu: /* @__PURE__ */ new Set(["items"]),
|
|
1859
|
+
video: /* @__PURE__ */ new Set(["url"]),
|
|
1860
|
+
timer: /* @__PURE__ */ new Set(["endDate"])
|
|
1861
|
+
};
|
|
1862
|
+
if (!hasDocStyles && c && typeof c === "object" && t !== "layout") {
|
|
1863
|
+
const known = DEFAULT_BLOCK_PROPS[t];
|
|
1864
|
+
const skip = skipContentMergeKeys[t];
|
|
1865
|
+
if (known && typeof known === "object" && !Array.isArray(known)) {
|
|
1866
|
+
for (const key of Object.keys(known)) {
|
|
1867
|
+
if (skip?.has(key)) continue;
|
|
1868
|
+
if (key in c && c[key] !== void 0) {
|
|
1869
|
+
block.props[key] = c[key];
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
1531
1875
|
return block;
|
|
1532
1876
|
}
|
|
1533
1877
|
function rowToInternal(r) {
|
|
1534
|
-
const
|
|
1878
|
+
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1879
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1880
|
+
const cols = Math.max(1, columns.length, layoutColCount);
|
|
1881
|
+
const colList = (() => {
|
|
1882
|
+
if (columns.length >= cols) return columns;
|
|
1883
|
+
if (columns.length > 0) {
|
|
1884
|
+
return [
|
|
1885
|
+
...columns,
|
|
1886
|
+
...Array.from({ length: cols - columns.length }).map(() => ({}))
|
|
1887
|
+
];
|
|
1888
|
+
}
|
|
1889
|
+
return Array.from({ length: cols }).map(() => ({}));
|
|
1890
|
+
})();
|
|
1535
1891
|
const preset = pickPresetByColCount(cols) || LAYOUT_PRESETS[0];
|
|
1536
1892
|
const row = makeLayoutRow(preset);
|
|
1537
1893
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
@@ -1544,14 +1900,14 @@ function rowToInternal(r) {
|
|
|
1544
1900
|
row.bgSize = r.styles?.backgroundSize || row.bgSize;
|
|
1545
1901
|
row.bgPosition = r.styles?.backgroundPosition || row.bgPosition || "center";
|
|
1546
1902
|
row.bgGradient = r.styles?.backgroundGradient || row.bgGradient || null;
|
|
1547
|
-
|
|
1548
|
-
const colList = columns.length ? columns : Array.from({ length: cols }).map(() => ({}));
|
|
1549
|
-
row.cells = colList.slice(0, cols).map((c) => {
|
|
1903
|
+
row.cells = colList.map((c) => {
|
|
1550
1904
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1551
|
-
return blocks.map(mapBlockToInternal).filter((x) => x != null);
|
|
1905
|
+
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1552
1906
|
});
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1907
|
+
const studio = r._reactEmailStudio;
|
|
1908
|
+
const snap = studio?.row;
|
|
1909
|
+
const columnStylesFromDoc = {};
|
|
1910
|
+
colList.forEach((c, i) => {
|
|
1555
1911
|
const bgColor = typeof c.styles?.backgroundColor === "string" ? c.styles.backgroundColor : "";
|
|
1556
1912
|
const padding = layoutColumnPaddingForDoc(c.styles?.padding);
|
|
1557
1913
|
const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
|
|
@@ -1563,17 +1919,30 @@ function rowToInternal(r) {
|
|
|
1563
1919
|
const hasPad = columnPaddingNonZero(padding);
|
|
1564
1920
|
const hasBr = columnRadiusNonZero(borderRadius);
|
|
1565
1921
|
if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
|
|
1566
|
-
|
|
1922
|
+
columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
|
|
1567
1923
|
}
|
|
1568
1924
|
});
|
|
1569
|
-
if (
|
|
1925
|
+
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1926
|
+
const { cells: _omitCells, columnStyles: snapCs, ...rest } = snap;
|
|
1927
|
+
Object.assign(row, rest);
|
|
1928
|
+
const snapColumn = snapCs && typeof snapCs === "object" && !Array.isArray(snapCs) ? snapCs : {};
|
|
1929
|
+
row.columnStyles = { ...snapColumn, ...columnStylesFromDoc };
|
|
1930
|
+
if (!Object.keys(row.columnStyles).length) delete row.columnStyles;
|
|
1931
|
+
} else if (Object.keys(columnStylesFromDoc).length) {
|
|
1932
|
+
row.columnStyles = columnStylesFromDoc;
|
|
1933
|
+
}
|
|
1570
1934
|
return row;
|
|
1571
1935
|
}
|
|
1572
1936
|
function normalizeEmailDesignInput(input) {
|
|
1573
1937
|
const doc = normalizeEmailDocument(input);
|
|
1574
1938
|
if (!doc) return null;
|
|
1575
1939
|
const s = doc.settings || {};
|
|
1576
|
-
const
|
|
1940
|
+
const studioSettings = s._reactEmailStudio;
|
|
1941
|
+
const settings = {};
|
|
1942
|
+
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1943
|
+
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1944
|
+
}
|
|
1945
|
+
Object.assign(settings, {
|
|
1577
1946
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1578
1947
|
bgImage: s.backgroundImage || "",
|
|
1579
1948
|
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
@@ -1587,15 +1956,15 @@ function normalizeEmailDesignInput(input) {
|
|
|
1587
1956
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1588
1957
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1589
1958
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1590
|
-
padding: 24,
|
|
1591
|
-
borderRadius: 8,
|
|
1959
|
+
padding: typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : typeof settings.padding === "number" ? settings.padding : 24,
|
|
1960
|
+
borderRadius: typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : typeof settings.borderRadius === "number" ? settings.borderRadius : 8,
|
|
1592
1961
|
pageFontFamily: s.fontFamily,
|
|
1593
1962
|
pageTextColor: s.color,
|
|
1594
1963
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1595
1964
|
pageResponsive: s.responsive,
|
|
1596
1965
|
pageRtl: s.rtl,
|
|
1597
1966
|
fontFamily: s.fontFamily
|
|
1598
|
-
};
|
|
1967
|
+
});
|
|
1599
1968
|
const rows = (doc.rows || []).map(rowToInternal);
|
|
1600
1969
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1601
1970
|
}
|
|
@@ -1619,14 +1988,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1619
1988
|
fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
|
|
1620
1989
|
lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
|
|
1621
1990
|
color: settings.pageTextColor || "#111827",
|
|
1622
|
-
responsive: true,
|
|
1623
|
-
rtl:
|
|
1991
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
1992
|
+
rtl: !!settings.pageRtl,
|
|
1993
|
+
_reactEmailStudio: {
|
|
1994
|
+
contentPadding: settings.padding ?? 24,
|
|
1995
|
+
contentBorderRadius: settings.borderRadius ?? 8,
|
|
1996
|
+
editorSettings: cloneJson(settings)
|
|
1997
|
+
}
|
|
1624
1998
|
},
|
|
1625
1999
|
rows: rows.map((r, ri) => {
|
|
1626
|
-
const
|
|
2000
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
2001
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
|
|
2002
|
+
const cols = Math.max(1, cellArrays.length, declaredCols);
|
|
2003
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
2004
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
2005
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
2006
|
+
})();
|
|
2007
|
+
const { cells: _rowCells, ...rowEditorSnap } = r;
|
|
1627
2008
|
return {
|
|
1628
2009
|
id: r.id || `row_${ri + 1}`,
|
|
1629
2010
|
type: "row",
|
|
2011
|
+
_reactEmailStudio: {
|
|
2012
|
+
row: cloneJson(rowEditorSnap)
|
|
2013
|
+
},
|
|
1630
2014
|
layout: {
|
|
1631
2015
|
columns: cols,
|
|
1632
2016
|
gap: r.gap ?? 0,
|
|
@@ -1640,13 +2024,14 @@ function designToEmailDocument(rows, settings) {
|
|
|
1640
2024
|
backgroundSize: r.bgSize || "cover",
|
|
1641
2025
|
backgroundPosition: r.bgPosition || "center",
|
|
1642
2026
|
backgroundGradient: r.bgGradient || null,
|
|
1643
|
-
padding:
|
|
2027
|
+
padding: rowPad,
|
|
1644
2028
|
borderRadius: 0,
|
|
1645
2029
|
borderWidth: 0,
|
|
1646
2030
|
borderColor: "#e5e7eb",
|
|
1647
2031
|
textAlign: "left"
|
|
1648
2032
|
},
|
|
1649
|
-
columns: (
|
|
2033
|
+
columns: Array.from({ length: cols }, (_, ci) => {
|
|
2034
|
+
const cellBlocks = cellArrays[ci] ?? [];
|
|
1650
2035
|
const cs = r.columnStyles && r.columnStyles[ci] || {};
|
|
1651
2036
|
return {
|
|
1652
2037
|
id: `col_${ri + 1}_${ci + 1}`,
|
|
@@ -1661,12 +2046,7 @@ function designToEmailDocument(rows, settings) {
|
|
|
1661
2046
|
backgroundGradient: cs.bgGradient || null,
|
|
1662
2047
|
borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
|
|
1663
2048
|
},
|
|
1664
|
-
blocks: (cellBlocks || []).map((b) => (
|
|
1665
|
-
id: b.id,
|
|
1666
|
-
type: b.type,
|
|
1667
|
-
content: b.props,
|
|
1668
|
-
styles: {}
|
|
1669
|
-
}))
|
|
2049
|
+
blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
|
|
1670
2050
|
};
|
|
1671
2051
|
})
|
|
1672
2052
|
};
|
|
@@ -1674,14 +2054,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1674
2054
|
};
|
|
1675
2055
|
return doc;
|
|
1676
2056
|
}
|
|
1677
|
-
function hydrateBlock(b) {
|
|
2057
|
+
function hydrateBlock(b, depth = 0) {
|
|
1678
2058
|
if (!b || typeof b !== "object") return null;
|
|
1679
2059
|
const t = b.type === "nestedRow" ? "layout" : b.type;
|
|
1680
2060
|
if (!isKnownBlockType(t)) return null;
|
|
1681
2061
|
if (t === "layout" && b.props && Array.isArray(b.props.cells)) {
|
|
1682
2062
|
const defaults2 = DEFAULT_BLOCK_PROPS.layout;
|
|
2063
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2064
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? [...b.props.ratios] : [1];
|
|
2065
|
+
return {
|
|
2066
|
+
...b,
|
|
2067
|
+
type: "layout",
|
|
2068
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2069
|
+
props: {
|
|
2070
|
+
...defaults2,
|
|
2071
|
+
...b.props,
|
|
2072
|
+
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2073
|
+
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2074
|
+
cells: ratios.map(() => [])
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
1683
2078
|
const cells = b.props.cells.map(
|
|
1684
|
-
(col) => Array.isArray(col) ? col.map(hydrateBlock).filter((x) => x != null) : []
|
|
2079
|
+
(col) => Array.isArray(col) ? col.map((x) => hydrateBlock(x, depth + 1)).filter((x) => x != null) : []
|
|
1685
2080
|
);
|
|
1686
2081
|
return {
|
|
1687
2082
|
...b,
|
|
@@ -1712,6 +2107,78 @@ function hydrateBlock(b) {
|
|
|
1712
2107
|
props: merged
|
|
1713
2108
|
};
|
|
1714
2109
|
}
|
|
2110
|
+
function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
2111
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2112
|
+
const r = raw;
|
|
2113
|
+
if (r.props && typeof r.props === "object" && typeof r.type === "string" && !("styles" in r)) {
|
|
2114
|
+
return hydrateBlock(r, layoutDepth);
|
|
2115
|
+
}
|
|
2116
|
+
return mapBlockToInternal(r, layoutDepth);
|
|
2117
|
+
}
|
|
2118
|
+
function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
|
|
2119
|
+
const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
2120
|
+
if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
|
|
2121
|
+
const a = isObj(fromContent) ? fromContent : {};
|
|
2122
|
+
const b = isObj(fromHydrated) ? fromHydrated : {};
|
|
2123
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
2124
|
+
const out = {};
|
|
2125
|
+
for (const k of keys) {
|
|
2126
|
+
const va = a[k];
|
|
2127
|
+
const vb = b[k];
|
|
2128
|
+
if (isObj(va) && isObj(vb)) {
|
|
2129
|
+
out[k] = { ...va, ...vb };
|
|
2130
|
+
} else if (vb !== void 0) {
|
|
2131
|
+
out[k] = vb;
|
|
2132
|
+
} else {
|
|
2133
|
+
out[k] = va;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
return Object.keys(out).length ? out : void 0;
|
|
2137
|
+
}
|
|
2138
|
+
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2139
|
+
const exp = b.props;
|
|
2140
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
|
|
2141
|
+
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2142
|
+
const columnStylesFromContent = block.props.columnStyles;
|
|
2143
|
+
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2144
|
+
if (hb) {
|
|
2145
|
+
block.props = hb.props;
|
|
2146
|
+
const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
|
|
2147
|
+
if (merged) block.props.columnStyles = merged;
|
|
2148
|
+
else delete block.props.columnStyles;
|
|
2149
|
+
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2150
|
+
}
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
Object.assign(block.props, exp);
|
|
2154
|
+
normalizeBoxStyles(block.props, t);
|
|
2155
|
+
if (t === "html" || t === "text") {
|
|
2156
|
+
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
function exportEmailDocBlock(b, depth = 0) {
|
|
2160
|
+
if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
|
|
2161
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2162
|
+
const out = { ...doc };
|
|
2163
|
+
if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
2164
|
+
out.props = cloneJson(b.props);
|
|
2165
|
+
}
|
|
2166
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2167
|
+
const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
|
|
2168
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2169
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
|
|
2170
|
+
out.content = { ...baseContent, cells: ratios.map(() => []) };
|
|
2171
|
+
} else {
|
|
2172
|
+
out.content = {
|
|
2173
|
+
...baseContent,
|
|
2174
|
+
cells: b.props.cells.map(
|
|
2175
|
+
(col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
|
|
2176
|
+
)
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
return out;
|
|
2181
|
+
}
|
|
1715
2182
|
function hydrateLayoutRow(row) {
|
|
1716
2183
|
if (!row || typeof row !== "object") return row;
|
|
1717
2184
|
const cells = (row.cells || []).map(
|
|
@@ -4443,32 +4910,42 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4443
4910
|
] });
|
|
4444
4911
|
case "text":
|
|
4445
4912
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4446
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4447
|
-
|
|
4913
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4914
|
+
"textarea",
|
|
4448
4915
|
{
|
|
4916
|
+
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
4449
4917
|
value: normalizeRichHtmlForStorage(p.content),
|
|
4450
|
-
onChange: (
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4918
|
+
onChange: (e) => set("content", normalizeRichHtmlForStorage(e.target.value)),
|
|
4919
|
+
spellCheck: false,
|
|
4920
|
+
placeholder: "e.g. <p>Your message</p>"
|
|
4921
|
+
},
|
|
4922
|
+
block.id
|
|
4923
|
+
) }),
|
|
4924
|
+
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4925
|
+
"select",
|
|
4926
|
+
{
|
|
4927
|
+
style: IS,
|
|
4928
|
+
defaultValue: "",
|
|
4929
|
+
onChange: (e) => {
|
|
4930
|
+
const v = e.target.value;
|
|
4931
|
+
if (v) {
|
|
4932
|
+
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4933
|
+
e.target.value = "";
|
|
4934
|
+
}
|
|
4459
4935
|
},
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
C
|
|
4936
|
+
children: [
|
|
4937
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
|
|
4938
|
+
mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
|
|
4939
|
+
]
|
|
4465
4940
|
}
|
|
4466
|
-
) }
|
|
4941
|
+
) }) : null,
|
|
4467
4942
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
|
|
4468
4943
|
Col("color", "Color"),
|
|
4469
4944
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
4470
4945
|
Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
|
|
4471
4946
|
Sel("fontFamily", "Font", FONTS2),
|
|
4947
|
+
Tog("italic", "Italic"),
|
|
4948
|
+
Tog("underline", "Underline"),
|
|
4472
4949
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Line height", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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) }) }),
|
|
4473
4950
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
|
|
4474
4951
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -6039,6 +6516,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6039
6516
|
const [previewDevice, setPreviewDevice] = (0, import_react8.useState)("desktop");
|
|
6040
6517
|
const [activeTab, setActiveTab] = (0, import_react8.useState)("blocks");
|
|
6041
6518
|
const [templatesOpen, setTemplatesOpen] = (0, import_react8.useState)(false);
|
|
6519
|
+
const [jsonLoading, setJsonLoading] = (0, import_react8.useState)(false);
|
|
6042
6520
|
const [autoSaveMsg, setAutoSaveMsg] = (0, import_react8.useState)("");
|
|
6043
6521
|
const [locale, setLocale] = (0, import_react8.useState)(
|
|
6044
6522
|
typeof opt.locale === "string" ? opt.locale : "en"
|
|
@@ -6181,31 +6659,40 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6181
6659
|
}, [rows, history, future, selContentMeta]);
|
|
6182
6660
|
const buildApi = (0, import_react8.useCallback)(() => ({
|
|
6183
6661
|
loadJson(input) {
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6662
|
+
setJsonLoading(true);
|
|
6663
|
+
requestAnimationFrame(() => {
|
|
6664
|
+
requestAnimationFrame(() => {
|
|
6665
|
+
try {
|
|
6666
|
+
let parsed = input;
|
|
6667
|
+
if (typeof input === "string") {
|
|
6668
|
+
try {
|
|
6669
|
+
parsed = JSON.parse(input);
|
|
6670
|
+
} catch {
|
|
6671
|
+
return;
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
const norm = normalizeEmailDesignInput(parsed);
|
|
6675
|
+
if (!norm) return;
|
|
6676
|
+
setRows(norm.rows);
|
|
6677
|
+
const ns = norm.settings;
|
|
6678
|
+
setSettings((s) => ({ ...s, ...ns }));
|
|
6679
|
+
const inferPage = () => {
|
|
6680
|
+
if (ns?.bgGradient) return "gradient";
|
|
6681
|
+
if (String(ns?.bgImage ?? "").trim()) return "image";
|
|
6682
|
+
return "solid";
|
|
6683
|
+
};
|
|
6684
|
+
const inferContent = () => {
|
|
6685
|
+
if (ns?.contentBgGradient) return "gradient";
|
|
6686
|
+
if (String(ns?.contentBgImage ?? "").trim()) return "image";
|
|
6687
|
+
return "solid";
|
|
6688
|
+
};
|
|
6689
|
+
setPageBgUiStep(inferPage());
|
|
6690
|
+
setContentBgUiStep(inferContent());
|
|
6691
|
+
} finally {
|
|
6692
|
+
setJsonLoading(false);
|
|
6693
|
+
}
|
|
6694
|
+
});
|
|
6695
|
+
});
|
|
6209
6696
|
},
|
|
6210
6697
|
exportJson(cb, pretty) {
|
|
6211
6698
|
const design = designToEmailDocument(rowsRef.current, settingsRef.current);
|
|
@@ -6304,6 +6791,9 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6304
6791
|
const nested = action.nested;
|
|
6305
6792
|
const toLoc = toColumnLoc(rowId, cellIdx, nested);
|
|
6306
6793
|
if (action.kind === "new") {
|
|
6794
|
+
if ((action.contentType === "layout" || action.contentType === "nestedRow") && nestedLayoutDepth(nested) >= MAX_NESTED_LAYOUT_DEPTH) {
|
|
6795
|
+
return;
|
|
6796
|
+
}
|
|
6307
6797
|
let nb;
|
|
6308
6798
|
if (action.preset && (action.contentType === "layout" || action.contentType === "nestedRow")) {
|
|
6309
6799
|
nb = makeNestedRowBlock(action.preset);
|
|
@@ -6589,6 +7079,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6589
7079
|
::-webkit-scrollbar-track{background:transparent;}
|
|
6590
7080
|
::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
|
|
6591
7081
|
::-webkit-scrollbar-thumb:hover{background:${C.muted};}
|
|
7082
|
+
@keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
|
|
6592
7083
|
` }),
|
|
6593
7084
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("workspace"), style: {
|
|
6594
7085
|
display: "flex",
|
|
@@ -7517,7 +8008,47 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7517
8008
|
) })
|
|
7518
8009
|
]
|
|
7519
8010
|
}
|
|
7520
|
-
)
|
|
8011
|
+
),
|
|
8012
|
+
jsonLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
8013
|
+
"div",
|
|
8014
|
+
{
|
|
8015
|
+
id: eid("json-loading"),
|
|
8016
|
+
role: "status",
|
|
8017
|
+
"aria-live": "polite",
|
|
8018
|
+
"aria-busy": true,
|
|
8019
|
+
style: {
|
|
8020
|
+
position: "absolute",
|
|
8021
|
+
inset: 0,
|
|
8022
|
+
zIndex: 180,
|
|
8023
|
+
background: `${C.bg}f2`,
|
|
8024
|
+
backdropFilter: "blur(2px)",
|
|
8025
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
8026
|
+
display: "flex",
|
|
8027
|
+
flexDirection: "column",
|
|
8028
|
+
alignItems: "center",
|
|
8029
|
+
justifyContent: "center",
|
|
8030
|
+
gap: 14,
|
|
8031
|
+
pointerEvents: "auto"
|
|
8032
|
+
},
|
|
8033
|
+
children: [
|
|
8034
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
8035
|
+
"div",
|
|
8036
|
+
{
|
|
8037
|
+
"aria-hidden": true,
|
|
8038
|
+
style: {
|
|
8039
|
+
width: 40,
|
|
8040
|
+
height: 40,
|
|
8041
|
+
borderRadius: "50%",
|
|
8042
|
+
border: `3px solid ${C.border}`,
|
|
8043
|
+
borderTopColor: C.accent,
|
|
8044
|
+
animation: "email-editor-json-spin 0.65s linear infinite"
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
),
|
|
8048
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
|
|
8049
|
+
]
|
|
8050
|
+
}
|
|
8051
|
+
) : null
|
|
7521
8052
|
] }),
|
|
7522
8053
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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: [
|
|
7523
8054
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
|