react-email-studio 2.0.0 → 3.0.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/TUTORIAL.md +264 -264
- package/USER_README.md +28 -28
- package/dist/index.cjs +502 -50
- 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 +502 -50
- package/dist/index.js.map +1 -1
- package/package.json +62 -58
package/dist/index.cjs
CHANGED
|
@@ -692,6 +692,10 @@ function makeContentBlock(type) {
|
|
|
692
692
|
}
|
|
693
693
|
|
|
694
694
|
// src/lib/columnPath.ts
|
|
695
|
+
var MAX_NESTED_LAYOUT_DEPTH = 1;
|
|
696
|
+
function nestedLayoutDepth(nested) {
|
|
697
|
+
return nested != null && nested.parentBlockIdx != null && nested.innerCellIdx != null ? 1 : 0;
|
|
698
|
+
}
|
|
695
699
|
function isSplitLayoutBlock(b) {
|
|
696
700
|
return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
|
|
697
701
|
}
|
|
@@ -1283,8 +1287,18 @@ function normalizeEmailDocument(input) {
|
|
|
1283
1287
|
settings: doc.settings && typeof doc.settings === "object" && !Array.isArray(doc.settings) ? doc.settings : {},
|
|
1284
1288
|
rows: rows.map((r, ri) => {
|
|
1285
1289
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1286
|
-
const
|
|
1287
|
-
const
|
|
1290
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1291
|
+
const colCount = Math.max(1, columns.length, layoutColCount);
|
|
1292
|
+
const colsNormalized = (() => {
|
|
1293
|
+
if (columns.length >= colCount) return columns;
|
|
1294
|
+
if (columns.length > 0) {
|
|
1295
|
+
return [
|
|
1296
|
+
...columns,
|
|
1297
|
+
...Array.from({ length: colCount - columns.length }).map(() => ({}))
|
|
1298
|
+
];
|
|
1299
|
+
}
|
|
1300
|
+
return Array.from({ length: colCount }).map(() => ({}));
|
|
1301
|
+
})();
|
|
1288
1302
|
return {
|
|
1289
1303
|
id: ensureId(r.id, `row${ri + 1}`),
|
|
1290
1304
|
type: "row",
|
|
@@ -1295,6 +1309,7 @@ function normalizeEmailDocument(input) {
|
|
|
1295
1309
|
align: r.layout?.align || "center"
|
|
1296
1310
|
},
|
|
1297
1311
|
styles: r.styles && typeof r.styles === "object" && !Array.isArray(r.styles) ? r.styles : {},
|
|
1312
|
+
...typeof r._reactEmailStudio === "object" && r._reactEmailStudio !== null && !Array.isArray(r._reactEmailStudio) ? { _reactEmailStudio: r._reactEmailStudio } : {},
|
|
1298
1313
|
columns: colsNormalized.map((c, ci) => ({
|
|
1299
1314
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1300
1315
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
@@ -1305,7 +1320,8 @@ function normalizeEmailDocument(input) {
|
|
|
1305
1320
|
content: b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1306
1321
|
styles: b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles) ? b.styles : {},
|
|
1307
1322
|
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 : {}
|
|
1323
|
+
responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {},
|
|
1324
|
+
...b?.props && typeof b.props === "object" && !Array.isArray(b.props) ? { props: b.props } : {}
|
|
1309
1325
|
})) : []
|
|
1310
1326
|
}))
|
|
1311
1327
|
};
|
|
@@ -1316,6 +1332,14 @@ function normalizeEmailDocument(input) {
|
|
|
1316
1332
|
}
|
|
1317
1333
|
|
|
1318
1334
|
// src/lib/emailDesignJson.ts
|
|
1335
|
+
var MAX_LAYOUT_TREE_DEPTH = 32;
|
|
1336
|
+
function cloneJson(v) {
|
|
1337
|
+
try {
|
|
1338
|
+
return JSON.parse(JSON.stringify(v));
|
|
1339
|
+
} catch {
|
|
1340
|
+
return v;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1319
1343
|
function layoutColumnPaddingForDoc(p) {
|
|
1320
1344
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1321
1345
|
const n = Math.max(0, p);
|
|
@@ -1402,7 +1426,242 @@ function normalizeBoxStyles(props, t) {
|
|
|
1402
1426
|
}
|
|
1403
1427
|
props.padding = boxPad(props.padding, uniformPadDefault(def?.padding));
|
|
1404
1428
|
}
|
|
1405
|
-
function
|
|
1429
|
+
function internalBlockToEmailDoc(b, depth = 0) {
|
|
1430
|
+
const p = b?.props && typeof b.props === "object" ? b.props : {};
|
|
1431
|
+
const id = typeof b?.id === "string" ? b.id : uid();
|
|
1432
|
+
const type = b?.type === "nestedRow" ? "layout" : b?.type;
|
|
1433
|
+
if (!isKnownBlockType(type)) {
|
|
1434
|
+
return { id, type: String(type || "text"), content: {}, styles: {} };
|
|
1435
|
+
}
|
|
1436
|
+
switch (type) {
|
|
1437
|
+
case "heading": {
|
|
1438
|
+
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;
|
|
1439
|
+
const padUniform = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : uniformPadDefault(p.padding);
|
|
1440
|
+
return {
|
|
1441
|
+
id,
|
|
1442
|
+
type,
|
|
1443
|
+
content: { text: typeof p.content === "string" ? p.content : "", tag: p.level || "h2" },
|
|
1444
|
+
styles: {
|
|
1445
|
+
fontSize: p.fontSize,
|
|
1446
|
+
fontWeight: fw,
|
|
1447
|
+
color: p.color,
|
|
1448
|
+
lineHeight: p.lineHeight,
|
|
1449
|
+
textAlign: p.align,
|
|
1450
|
+
letterSpacing: p.letterSpacing,
|
|
1451
|
+
marginBottom: padUniform
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
case "text":
|
|
1456
|
+
return {
|
|
1457
|
+
id,
|
|
1458
|
+
type,
|
|
1459
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1460
|
+
styles: {
|
|
1461
|
+
fontSize: p.fontSize,
|
|
1462
|
+
color: p.color,
|
|
1463
|
+
lineHeight: p.lineHeight,
|
|
1464
|
+
textAlign: p.align,
|
|
1465
|
+
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
case "html":
|
|
1469
|
+
return {
|
|
1470
|
+
id,
|
|
1471
|
+
type,
|
|
1472
|
+
content: { html: typeof p.content === "string" ? p.content : "" },
|
|
1473
|
+
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1474
|
+
};
|
|
1475
|
+
case "image": {
|
|
1476
|
+
const br = p.borderRadius;
|
|
1477
|
+
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
1478
|
+
(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
|
|
1479
|
+
) : 0;
|
|
1480
|
+
return {
|
|
1481
|
+
id,
|
|
1482
|
+
type,
|
|
1483
|
+
content: {
|
|
1484
|
+
src: p.src || "",
|
|
1485
|
+
alt: p.alt || "",
|
|
1486
|
+
link: p.link || ""
|
|
1487
|
+
},
|
|
1488
|
+
styles: {
|
|
1489
|
+
width: p.width,
|
|
1490
|
+
align: p.align,
|
|
1491
|
+
borderRadius
|
|
1492
|
+
},
|
|
1493
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
case "button": {
|
|
1497
|
+
let borderRadiusOut;
|
|
1498
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1499
|
+
borderRadiusOut = p.borderRadius;
|
|
1500
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1501
|
+
const br = p.borderRadius;
|
|
1502
|
+
const tl = numOr(br.tl, 0);
|
|
1503
|
+
const tr2 = numOr(br.tr, tl);
|
|
1504
|
+
const brc = numOr(br.br, tl);
|
|
1505
|
+
const bl = numOr(br.bl, tr2);
|
|
1506
|
+
borderRadiusOut = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1507
|
+
}
|
|
1508
|
+
return {
|
|
1509
|
+
id,
|
|
1510
|
+
type,
|
|
1511
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1512
|
+
styles: {
|
|
1513
|
+
backgroundColor: p.bgColor,
|
|
1514
|
+
color: p.textColor,
|
|
1515
|
+
fontSize: p.fontSize,
|
|
1516
|
+
fontWeight: p.fontWeight,
|
|
1517
|
+
...borderRadiusOut !== void 0 ? { borderRadius: borderRadiusOut } : {},
|
|
1518
|
+
textAlign: p.align,
|
|
1519
|
+
padding: {
|
|
1520
|
+
top: p.paddingV ?? 11,
|
|
1521
|
+
right: p.paddingH ?? 24,
|
|
1522
|
+
bottom: p.paddingV ?? 11,
|
|
1523
|
+
left: p.paddingH ?? 24
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
case "divider": {
|
|
1530
|
+
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;
|
|
1531
|
+
return {
|
|
1532
|
+
id,
|
|
1533
|
+
type,
|
|
1534
|
+
content: {},
|
|
1535
|
+
styles: { color: p.color, thickness: p.thickness, paddingY: padY }
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
case "spacer":
|
|
1539
|
+
return { id, type, content: {}, styles: { height: p.height } };
|
|
1540
|
+
case "social": {
|
|
1541
|
+
const urls = p.socialUrls && typeof p.socialUrls === "object" && !Array.isArray(p.socialUrls) ? p.socialUrls : {};
|
|
1542
|
+
const networks = {};
|
|
1543
|
+
const ordered = Array.isArray(p.networks) ? p.networks.filter((n) => typeof n === "string" && !!n) : [];
|
|
1544
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1545
|
+
for (const n of ordered) {
|
|
1546
|
+
const u = urls[n];
|
|
1547
|
+
networks[n] = typeof u === "string" && u.trim() ? u : "";
|
|
1548
|
+
seen.add(n);
|
|
1549
|
+
}
|
|
1550
|
+
for (const key of Object.keys(urls)) {
|
|
1551
|
+
if (seen.has(key)) continue;
|
|
1552
|
+
const v = urls[key];
|
|
1553
|
+
if (typeof v === "string" && v.trim()) {
|
|
1554
|
+
networks[key] = v;
|
|
1555
|
+
seen.add(key);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
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) } : {} : {};
|
|
1559
|
+
return {
|
|
1560
|
+
id,
|
|
1561
|
+
type,
|
|
1562
|
+
content: { networks },
|
|
1563
|
+
styles: {
|
|
1564
|
+
iconSize: p.iconSize,
|
|
1565
|
+
gap: p.gap,
|
|
1566
|
+
shape: p.shape,
|
|
1567
|
+
align: p.align,
|
|
1568
|
+
...padStyles
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
case "table":
|
|
1573
|
+
return {
|
|
1574
|
+
id,
|
|
1575
|
+
type,
|
|
1576
|
+
content: { data: Array.isArray(p.data) ? p.data : [] },
|
|
1577
|
+
styles: {
|
|
1578
|
+
borderColor: p.cellBorder,
|
|
1579
|
+
cellPadding: p.cellPadding,
|
|
1580
|
+
headerBg: p.headerBg,
|
|
1581
|
+
striped: p.striped
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
case "video":
|
|
1585
|
+
return {
|
|
1586
|
+
id,
|
|
1587
|
+
type,
|
|
1588
|
+
content: { url: p.src || "" },
|
|
1589
|
+
styles: { height: p.height }
|
|
1590
|
+
};
|
|
1591
|
+
case "timer": {
|
|
1592
|
+
let timerBr;
|
|
1593
|
+
if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
|
|
1594
|
+
timerBr = p.borderRadius;
|
|
1595
|
+
} else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
|
|
1596
|
+
const br = p.borderRadius;
|
|
1597
|
+
const tl = numOr(br.tl, 0);
|
|
1598
|
+
const tr2 = numOr(br.tr, tl);
|
|
1599
|
+
const brc = numOr(br.br, tl);
|
|
1600
|
+
const bl = numOr(br.bl, tr2);
|
|
1601
|
+
timerBr = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
|
|
1602
|
+
}
|
|
1603
|
+
return {
|
|
1604
|
+
id,
|
|
1605
|
+
type,
|
|
1606
|
+
content: { endDate: p.endDate || "" },
|
|
1607
|
+
styles: {
|
|
1608
|
+
backgroundColor: p.bgColor,
|
|
1609
|
+
color: p.textColor,
|
|
1610
|
+
padding: typeof p.padding === "number" ? p.padding : uniformPadDefault(p.padding),
|
|
1611
|
+
...timerBr !== void 0 ? { borderRadius: timerBr } : {},
|
|
1612
|
+
textAlign: p.align
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
case "menu":
|
|
1617
|
+
return {
|
|
1618
|
+
id,
|
|
1619
|
+
type,
|
|
1620
|
+
content: { items: Array.isArray(p.items) ? p.items : [] },
|
|
1621
|
+
styles: { fontSize: p.fontSize, color: p.color }
|
|
1622
|
+
};
|
|
1623
|
+
case "link":
|
|
1624
|
+
return {
|
|
1625
|
+
id,
|
|
1626
|
+
type,
|
|
1627
|
+
content: { text: p.label || "", href: p.href || "#" },
|
|
1628
|
+
styles: { color: p.color, fontSize: p.fontSize, textAlign: p.align },
|
|
1629
|
+
...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
|
|
1630
|
+
};
|
|
1631
|
+
case "layout": {
|
|
1632
|
+
const ratios = Array.isArray(p.ratios) ? [...p.ratios] : [1];
|
|
1633
|
+
const emptyCells = ratios.map(() => []);
|
|
1634
|
+
const rawCells = Array.isArray(p.cells) ? p.cells : [];
|
|
1635
|
+
const cellsOut = depth >= MAX_LAYOUT_TREE_DEPTH ? emptyCells : ratios.map((_, i) => {
|
|
1636
|
+
const col = rawCells[i];
|
|
1637
|
+
return Array.isArray(col) ? col.map((child) => internalBlockToEmailDoc(child, depth + 1)) : [];
|
|
1638
|
+
});
|
|
1639
|
+
return {
|
|
1640
|
+
id,
|
|
1641
|
+
type,
|
|
1642
|
+
content: {
|
|
1643
|
+
preset: p.preset,
|
|
1644
|
+
cols: p.cols,
|
|
1645
|
+
ratios,
|
|
1646
|
+
gap: p.gap,
|
|
1647
|
+
padding: p.padding,
|
|
1648
|
+
bgColor: p.bgColor,
|
|
1649
|
+
bgImage: p.bgImage,
|
|
1650
|
+
bgSize: p.bgSize,
|
|
1651
|
+
bgRepeat: p.bgRepeat,
|
|
1652
|
+
bgPosition: p.bgPosition,
|
|
1653
|
+
bgGradient: p.bgGradient ?? null,
|
|
1654
|
+
columnStyles: p.columnStyles,
|
|
1655
|
+
cells: cellsOut
|
|
1656
|
+
},
|
|
1657
|
+
styles: {}
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
default:
|
|
1661
|
+
return { id, type, content: {}, styles: {} };
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function mapBlockToInternal(b, layoutDepth = 0) {
|
|
1406
1665
|
const t = b.type;
|
|
1407
1666
|
if (!isKnownBlockType(t)) return null;
|
|
1408
1667
|
const block = makeContentBlock(t);
|
|
@@ -1473,8 +1732,28 @@ function mapBlockToInternal(b) {
|
|
|
1473
1732
|
if (asNum(s.height) != null) block.props.height = s.height;
|
|
1474
1733
|
break;
|
|
1475
1734
|
case "social": {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1735
|
+
const networksRaw = c.networks;
|
|
1736
|
+
let networksObj = {};
|
|
1737
|
+
if (Array.isArray(networksRaw)) {
|
|
1738
|
+
const urlMap = c.socialUrls && typeof c.socialUrls === "object" && !Array.isArray(c.socialUrls) ? c.socialUrls : {};
|
|
1739
|
+
for (const n of networksRaw) {
|
|
1740
|
+
if (typeof n !== "string" || !n) continue;
|
|
1741
|
+
const u = urlMap[n];
|
|
1742
|
+
networksObj[n] = typeof u === "string" ? u : "";
|
|
1743
|
+
}
|
|
1744
|
+
for (const key of Object.keys(urlMap)) {
|
|
1745
|
+
if (key in networksObj) continue;
|
|
1746
|
+
const u = urlMap[key];
|
|
1747
|
+
if (typeof u === "string" && u.trim()) networksObj[key] = u;
|
|
1748
|
+
}
|
|
1749
|
+
} else if (networksRaw && typeof networksRaw === "object" && !Array.isArray(networksRaw)) {
|
|
1750
|
+
networksObj = { ...networksRaw };
|
|
1751
|
+
for (const k of Object.keys(networksObj)) {
|
|
1752
|
+
const v = networksObj[k];
|
|
1753
|
+
networksObj[k] = typeof v === "string" ? v : "";
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
const keys = Object.keys(networksObj);
|
|
1478
1757
|
if (keys.length) {
|
|
1479
1758
|
block.props.networks = keys;
|
|
1480
1759
|
block.props.socialUrls = networksObj;
|
|
@@ -1483,6 +1762,13 @@ function mapBlockToInternal(b) {
|
|
|
1483
1762
|
if (asNum(s.gap) != null) block.props.gap = s.gap;
|
|
1484
1763
|
if (asStr(s.shape)) block.props.shape = s.shape;
|
|
1485
1764
|
if (asStr(s.align)) block.props.align = s.align;
|
|
1765
|
+
if (s.padding != null) {
|
|
1766
|
+
if (typeof s.padding === "number" && Number.isFinite(s.padding)) {
|
|
1767
|
+
block.props.padding = s.padding;
|
|
1768
|
+
} else if (s.padding && typeof s.padding === "object" && !Array.isArray(s.padding)) {
|
|
1769
|
+
block.props.padding = layoutColumnPaddingForDoc(s.padding);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1486
1772
|
break;
|
|
1487
1773
|
}
|
|
1488
1774
|
case "table":
|
|
@@ -1523,15 +1809,81 @@ function mapBlockToInternal(b) {
|
|
|
1523
1809
|
if (asNum(s.fontSize) != null) block.props.fontSize = s.fontSize;
|
|
1524
1810
|
if (asStr(s.textAlign)) block.props.align = s.textAlign;
|
|
1525
1811
|
break;
|
|
1526
|
-
case "layout":
|
|
1812
|
+
case "layout": {
|
|
1813
|
+
if (Array.isArray(c.cells)) {
|
|
1814
|
+
const d = DEFAULT_BLOCK_PROPS.layout;
|
|
1815
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1816
|
+
if (typeof c.cols === "number") block.props.cols = c.cols;
|
|
1817
|
+
if (Array.isArray(c.ratios)) block.props.ratios = [...c.ratios];
|
|
1818
|
+
if (typeof c.gap === "number") block.props.gap = c.gap;
|
|
1819
|
+
if (c.padding !== void 0 && c.padding !== null) block.props.padding = c.padding;
|
|
1820
|
+
if (typeof c.bgColor === "string") block.props.bgColor = c.bgColor;
|
|
1821
|
+
if (typeof c.bgImage === "string") block.props.bgImage = c.bgImage;
|
|
1822
|
+
if (typeof c.bgSize === "string") block.props.bgSize = c.bgSize;
|
|
1823
|
+
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1824
|
+
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1825
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1826
|
+
if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
1827
|
+
block.props.columnStyles = c.columnStyles;
|
|
1828
|
+
}
|
|
1829
|
+
block.props.cells = c.cells.map(
|
|
1830
|
+
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
1831
|
+
);
|
|
1832
|
+
} else {
|
|
1833
|
+
block.props.preset = asStr(c.preset) ?? block.props.preset;
|
|
1834
|
+
}
|
|
1527
1835
|
break;
|
|
1836
|
+
}
|
|
1528
1837
|
default:
|
|
1529
1838
|
break;
|
|
1530
1839
|
}
|
|
1840
|
+
const styleObj = s && typeof s === "object" && !Array.isArray(s) ? s : {};
|
|
1841
|
+
const hasDocStyles = Object.keys(styleObj).some((k) => {
|
|
1842
|
+
const v = styleObj[k];
|
|
1843
|
+
return v !== void 0 && v !== null && v !== "";
|
|
1844
|
+
});
|
|
1845
|
+
const skipContentMergeKeys = {
|
|
1846
|
+
heading: /* @__PURE__ */ new Set(["text", "tag"]),
|
|
1847
|
+
text: /* @__PURE__ */ new Set(["html", "text"]),
|
|
1848
|
+
html: /* @__PURE__ */ new Set(["html"]),
|
|
1849
|
+
image: /* @__PURE__ */ new Set(["src", "alt", "link"]),
|
|
1850
|
+
button: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1851
|
+
link: /* @__PURE__ */ new Set(["text", "href"]),
|
|
1852
|
+
social: /* @__PURE__ */ new Set(["networks", "socialUrls"]),
|
|
1853
|
+
table: /* @__PURE__ */ new Set(["data"]),
|
|
1854
|
+
menu: /* @__PURE__ */ new Set(["items"]),
|
|
1855
|
+
video: /* @__PURE__ */ new Set(["url"]),
|
|
1856
|
+
timer: /* @__PURE__ */ new Set(["endDate"])
|
|
1857
|
+
};
|
|
1858
|
+
if (!hasDocStyles && c && typeof c === "object" && t !== "layout") {
|
|
1859
|
+
const known = DEFAULT_BLOCK_PROPS[t];
|
|
1860
|
+
const skip = skipContentMergeKeys[t];
|
|
1861
|
+
if (known && typeof known === "object" && !Array.isArray(known)) {
|
|
1862
|
+
for (const key of Object.keys(known)) {
|
|
1863
|
+
if (skip?.has(key)) continue;
|
|
1864
|
+
if (key in c && c[key] !== void 0) {
|
|
1865
|
+
block.props[key] = c[key];
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
1531
1871
|
return block;
|
|
1532
1872
|
}
|
|
1533
1873
|
function rowToInternal(r) {
|
|
1534
|
-
const
|
|
1874
|
+
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1875
|
+
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
1876
|
+
const cols = Math.max(1, columns.length, layoutColCount);
|
|
1877
|
+
const colList = (() => {
|
|
1878
|
+
if (columns.length >= cols) return columns;
|
|
1879
|
+
if (columns.length > 0) {
|
|
1880
|
+
return [
|
|
1881
|
+
...columns,
|
|
1882
|
+
...Array.from({ length: cols - columns.length }).map(() => ({}))
|
|
1883
|
+
];
|
|
1884
|
+
}
|
|
1885
|
+
return Array.from({ length: cols }).map(() => ({}));
|
|
1886
|
+
})();
|
|
1535
1887
|
const preset = pickPresetByColCount(cols) || LAYOUT_PRESETS[0];
|
|
1536
1888
|
const row = makeLayoutRow(preset);
|
|
1537
1889
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
@@ -1544,14 +1896,14 @@ function rowToInternal(r) {
|
|
|
1544
1896
|
row.bgSize = r.styles?.backgroundSize || row.bgSize;
|
|
1545
1897
|
row.bgPosition = r.styles?.backgroundPosition || row.bgPosition || "center";
|
|
1546
1898
|
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) => {
|
|
1899
|
+
row.cells = colList.map((c) => {
|
|
1550
1900
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1551
|
-
return blocks.map(mapBlockToInternal).filter((x) => x != null);
|
|
1901
|
+
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1552
1902
|
});
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1903
|
+
const studio = r._reactEmailStudio;
|
|
1904
|
+
const snap = studio?.row;
|
|
1905
|
+
const columnStylesFromDoc = {};
|
|
1906
|
+
colList.forEach((c, i) => {
|
|
1555
1907
|
const bgColor = typeof c.styles?.backgroundColor === "string" ? c.styles.backgroundColor : "";
|
|
1556
1908
|
const padding = layoutColumnPaddingForDoc(c.styles?.padding);
|
|
1557
1909
|
const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
|
|
@@ -1563,17 +1915,30 @@ function rowToInternal(r) {
|
|
|
1563
1915
|
const hasPad = columnPaddingNonZero(padding);
|
|
1564
1916
|
const hasBr = columnRadiusNonZero(borderRadius);
|
|
1565
1917
|
if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
|
|
1566
|
-
|
|
1918
|
+
columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
|
|
1567
1919
|
}
|
|
1568
1920
|
});
|
|
1569
|
-
if (
|
|
1921
|
+
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1922
|
+
const { cells: _omitCells, columnStyles: snapCs, ...rest } = snap;
|
|
1923
|
+
Object.assign(row, rest);
|
|
1924
|
+
const snapColumn = snapCs && typeof snapCs === "object" && !Array.isArray(snapCs) ? snapCs : {};
|
|
1925
|
+
row.columnStyles = { ...snapColumn, ...columnStylesFromDoc };
|
|
1926
|
+
if (!Object.keys(row.columnStyles).length) delete row.columnStyles;
|
|
1927
|
+
} else if (Object.keys(columnStylesFromDoc).length) {
|
|
1928
|
+
row.columnStyles = columnStylesFromDoc;
|
|
1929
|
+
}
|
|
1570
1930
|
return row;
|
|
1571
1931
|
}
|
|
1572
1932
|
function normalizeEmailDesignInput(input) {
|
|
1573
1933
|
const doc = normalizeEmailDocument(input);
|
|
1574
1934
|
if (!doc) return null;
|
|
1575
1935
|
const s = doc.settings || {};
|
|
1576
|
-
const
|
|
1936
|
+
const studioSettings = s._reactEmailStudio;
|
|
1937
|
+
const settings = {};
|
|
1938
|
+
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1939
|
+
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1940
|
+
}
|
|
1941
|
+
Object.assign(settings, {
|
|
1577
1942
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1578
1943
|
bgImage: s.backgroundImage || "",
|
|
1579
1944
|
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
@@ -1587,15 +1952,15 @@ function normalizeEmailDesignInput(input) {
|
|
|
1587
1952
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1588
1953
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1589
1954
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1590
|
-
padding: 24,
|
|
1591
|
-
borderRadius: 8,
|
|
1955
|
+
padding: typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : typeof settings.padding === "number" ? settings.padding : 24,
|
|
1956
|
+
borderRadius: typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : typeof settings.borderRadius === "number" ? settings.borderRadius : 8,
|
|
1592
1957
|
pageFontFamily: s.fontFamily,
|
|
1593
1958
|
pageTextColor: s.color,
|
|
1594
1959
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1595
1960
|
pageResponsive: s.responsive,
|
|
1596
1961
|
pageRtl: s.rtl,
|
|
1597
1962
|
fontFamily: s.fontFamily
|
|
1598
|
-
};
|
|
1963
|
+
});
|
|
1599
1964
|
const rows = (doc.rows || []).map(rowToInternal);
|
|
1600
1965
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1601
1966
|
}
|
|
@@ -1619,14 +1984,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1619
1984
|
fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
|
|
1620
1985
|
lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
|
|
1621
1986
|
color: settings.pageTextColor || "#111827",
|
|
1622
|
-
responsive: true,
|
|
1623
|
-
rtl:
|
|
1987
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
1988
|
+
rtl: !!settings.pageRtl,
|
|
1989
|
+
_reactEmailStudio: {
|
|
1990
|
+
contentPadding: settings.padding ?? 24,
|
|
1991
|
+
contentBorderRadius: settings.borderRadius ?? 8,
|
|
1992
|
+
editorSettings: cloneJson(settings)
|
|
1993
|
+
}
|
|
1624
1994
|
},
|
|
1625
1995
|
rows: rows.map((r, ri) => {
|
|
1626
|
-
const
|
|
1996
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
1997
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
|
|
1998
|
+
const cols = Math.max(1, cellArrays.length, declaredCols);
|
|
1999
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
2000
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
2001
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
2002
|
+
})();
|
|
2003
|
+
const { cells: _rowCells, ...rowEditorSnap } = r;
|
|
1627
2004
|
return {
|
|
1628
2005
|
id: r.id || `row_${ri + 1}`,
|
|
1629
2006
|
type: "row",
|
|
2007
|
+
_reactEmailStudio: {
|
|
2008
|
+
row: cloneJson(rowEditorSnap)
|
|
2009
|
+
},
|
|
1630
2010
|
layout: {
|
|
1631
2011
|
columns: cols,
|
|
1632
2012
|
gap: r.gap ?? 0,
|
|
@@ -1640,13 +2020,14 @@ function designToEmailDocument(rows, settings) {
|
|
|
1640
2020
|
backgroundSize: r.bgSize || "cover",
|
|
1641
2021
|
backgroundPosition: r.bgPosition || "center",
|
|
1642
2022
|
backgroundGradient: r.bgGradient || null,
|
|
1643
|
-
padding:
|
|
2023
|
+
padding: rowPad,
|
|
1644
2024
|
borderRadius: 0,
|
|
1645
2025
|
borderWidth: 0,
|
|
1646
2026
|
borderColor: "#e5e7eb",
|
|
1647
2027
|
textAlign: "left"
|
|
1648
2028
|
},
|
|
1649
|
-
columns: (
|
|
2029
|
+
columns: Array.from({ length: cols }, (_, ci) => {
|
|
2030
|
+
const cellBlocks = cellArrays[ci] ?? [];
|
|
1650
2031
|
const cs = r.columnStyles && r.columnStyles[ci] || {};
|
|
1651
2032
|
return {
|
|
1652
2033
|
id: `col_${ri + 1}_${ci + 1}`,
|
|
@@ -1661,12 +2042,7 @@ function designToEmailDocument(rows, settings) {
|
|
|
1661
2042
|
backgroundGradient: cs.bgGradient || null,
|
|
1662
2043
|
borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
|
|
1663
2044
|
},
|
|
1664
|
-
blocks: (cellBlocks || []).map((b) => (
|
|
1665
|
-
id: b.id,
|
|
1666
|
-
type: b.type,
|
|
1667
|
-
content: b.props,
|
|
1668
|
-
styles: {}
|
|
1669
|
-
}))
|
|
2045
|
+
blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
|
|
1670
2046
|
};
|
|
1671
2047
|
})
|
|
1672
2048
|
};
|
|
@@ -1674,14 +2050,29 @@ function designToEmailDocument(rows, settings) {
|
|
|
1674
2050
|
};
|
|
1675
2051
|
return doc;
|
|
1676
2052
|
}
|
|
1677
|
-
function hydrateBlock(b) {
|
|
2053
|
+
function hydrateBlock(b, depth = 0) {
|
|
1678
2054
|
if (!b || typeof b !== "object") return null;
|
|
1679
2055
|
const t = b.type === "nestedRow" ? "layout" : b.type;
|
|
1680
2056
|
if (!isKnownBlockType(t)) return null;
|
|
1681
2057
|
if (t === "layout" && b.props && Array.isArray(b.props.cells)) {
|
|
1682
2058
|
const defaults2 = DEFAULT_BLOCK_PROPS.layout;
|
|
2059
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2060
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? [...b.props.ratios] : [1];
|
|
2061
|
+
return {
|
|
2062
|
+
...b,
|
|
2063
|
+
type: "layout",
|
|
2064
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2065
|
+
props: {
|
|
2066
|
+
...defaults2,
|
|
2067
|
+
...b.props,
|
|
2068
|
+
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2069
|
+
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2070
|
+
cells: ratios.map(() => [])
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
1683
2074
|
const cells = b.props.cells.map(
|
|
1684
|
-
(col) => Array.isArray(col) ? col.map(hydrateBlock).filter((x) => x != null) : []
|
|
2075
|
+
(col) => Array.isArray(col) ? col.map((x) => hydrateBlock(x, depth + 1)).filter((x) => x != null) : []
|
|
1685
2076
|
);
|
|
1686
2077
|
return {
|
|
1687
2078
|
...b,
|
|
@@ -1712,6 +2103,54 @@ function hydrateBlock(b) {
|
|
|
1712
2103
|
props: merged
|
|
1713
2104
|
};
|
|
1714
2105
|
}
|
|
2106
|
+
function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
2107
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2108
|
+
const r = raw;
|
|
2109
|
+
if (r.props && typeof r.props === "object" && typeof r.type === "string" && !("styles" in r)) {
|
|
2110
|
+
return hydrateBlock(r, layoutDepth);
|
|
2111
|
+
}
|
|
2112
|
+
return mapBlockToInternal(r, layoutDepth);
|
|
2113
|
+
}
|
|
2114
|
+
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2115
|
+
const exp = b.props;
|
|
2116
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
|
|
2117
|
+
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2118
|
+
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2119
|
+
if (hb) {
|
|
2120
|
+
block.props = hb.props;
|
|
2121
|
+
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2122
|
+
}
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
Object.assign(block.props, exp);
|
|
2126
|
+
normalizeBoxStyles(block.props, t);
|
|
2127
|
+
if (t === "html" || t === "text") {
|
|
2128
|
+
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function exportEmailDocBlock(b, depth = 0) {
|
|
2132
|
+
if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
|
|
2133
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2134
|
+
const out = { ...doc };
|
|
2135
|
+
if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
2136
|
+
out.props = cloneJson(b.props);
|
|
2137
|
+
}
|
|
2138
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2139
|
+
const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
|
|
2140
|
+
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2141
|
+
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
|
|
2142
|
+
out.content = { ...baseContent, cells: ratios.map(() => []) };
|
|
2143
|
+
} else {
|
|
2144
|
+
out.content = {
|
|
2145
|
+
...baseContent,
|
|
2146
|
+
cells: b.props.cells.map(
|
|
2147
|
+
(col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
|
|
2148
|
+
)
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return out;
|
|
2153
|
+
}
|
|
1715
2154
|
function hydrateLayoutRow(row) {
|
|
1716
2155
|
if (!row || typeof row !== "object") return row;
|
|
1717
2156
|
const cells = (row.cells || []).map(
|
|
@@ -4443,32 +4882,42 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4443
4882
|
] });
|
|
4444
4883
|
case "text":
|
|
4445
4884
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4446
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4447
|
-
|
|
4885
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4886
|
+
"textarea",
|
|
4448
4887
|
{
|
|
4888
|
+
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
4449
4889
|
value: normalizeRichHtmlForStorage(p.content),
|
|
4450
|
-
onChange: (
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4890
|
+
onChange: (e) => set("content", normalizeRichHtmlForStorage(e.target.value)),
|
|
4891
|
+
spellCheck: false,
|
|
4892
|
+
placeholder: "e.g. <p>Your message</p>"
|
|
4893
|
+
},
|
|
4894
|
+
block.id
|
|
4895
|
+
) }),
|
|
4896
|
+
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4897
|
+
"select",
|
|
4898
|
+
{
|
|
4899
|
+
style: IS,
|
|
4900
|
+
defaultValue: "",
|
|
4901
|
+
onChange: (e) => {
|
|
4902
|
+
const v = e.target.value;
|
|
4903
|
+
if (v) {
|
|
4904
|
+
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4905
|
+
e.target.value = "";
|
|
4906
|
+
}
|
|
4459
4907
|
},
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
C
|
|
4908
|
+
children: [
|
|
4909
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
|
|
4910
|
+
mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
|
|
4911
|
+
]
|
|
4465
4912
|
}
|
|
4466
|
-
) }
|
|
4913
|
+
) }) : null,
|
|
4467
4914
|
/* @__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
4915
|
Col("color", "Color"),
|
|
4469
4916
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
4470
4917
|
Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
|
|
4471
4918
|
Sel("fontFamily", "Font", FONTS2),
|
|
4919
|
+
Tog("italic", "Italic"),
|
|
4920
|
+
Tog("underline", "Underline"),
|
|
4472
4921
|
/* @__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
4922
|
/* @__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
4923
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -6304,6 +6753,9 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6304
6753
|
const nested = action.nested;
|
|
6305
6754
|
const toLoc = toColumnLoc(rowId, cellIdx, nested);
|
|
6306
6755
|
if (action.kind === "new") {
|
|
6756
|
+
if ((action.contentType === "layout" || action.contentType === "nestedRow") && nestedLayoutDepth(nested) >= MAX_NESTED_LAYOUT_DEPTH) {
|
|
6757
|
+
return;
|
|
6758
|
+
}
|
|
6307
6759
|
let nb;
|
|
6308
6760
|
if (action.preset && (action.contentType === "layout" || action.contentType === "nestedRow")) {
|
|
6309
6761
|
nb = makeNestedRowBlock(action.preset);
|