react-email-studio 3.4.0 → 3.8.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/CHANGELOG.md +70 -2
- package/README.md +12 -0
- package/TUTORIAL.md +56 -2
- package/USER_README.md +17 -0
- package/dist/index.cjs +1560 -491
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -33
- package/dist/index.d.ts +84 -33
- package/dist/index.js +1566 -494
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,12 @@ __export(index_exports, {
|
|
|
33
33
|
EmailPreviewModal: () => PreviewModal,
|
|
34
34
|
ReactEmailEditor: () => ReactEmailEditor2,
|
|
35
35
|
base64ToUtf8: () => base64ToUtf8,
|
|
36
|
+
canonicalizeEmailDocument: () => canonicalizeEmailDocument,
|
|
37
|
+
coerceEmailDocumentInput: () => coerceEmailDocumentInput,
|
|
36
38
|
emailPreviewDevices: () => DEVICES,
|
|
37
39
|
extractHtmlForDesign: () => extractHtmlForDesign,
|
|
38
40
|
htmlToEmailDesignTemplate: () => htmlToEmailDesignTemplate,
|
|
41
|
+
htmlToJson: () => htmlToJson,
|
|
39
42
|
jsonToHtml: () => jsonToHtml,
|
|
40
43
|
utf8ToBase64: () => utf8ToBase64
|
|
41
44
|
});
|
|
@@ -498,9 +501,9 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
498
501
|
fontFamily: "Georgia,serif",
|
|
499
502
|
margin: { ...BOX0 }
|
|
500
503
|
},
|
|
504
|
+
/** Rich HTML body lives on the block root as `content` (string), not in `props`. */
|
|
501
505
|
html: {
|
|
502
506
|
...BLOCK_BG,
|
|
503
|
-
content: "<p>Rich HTML content. Edit with the rich editor.</p>",
|
|
504
507
|
fontSize: 15,
|
|
505
508
|
color: "#1e293b",
|
|
506
509
|
align: "left",
|
|
@@ -520,6 +523,7 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
520
523
|
align: "center",
|
|
521
524
|
padding: boxAll(8),
|
|
522
525
|
link: "",
|
|
526
|
+
linkEnabled: false,
|
|
523
527
|
linkTarget: "_blank",
|
|
524
528
|
borderRadius: { tl: 0, tr: 0, br: 0, bl: 0 },
|
|
525
529
|
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
|
@@ -641,10 +645,82 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
641
645
|
margin: { ...BOX0 }
|
|
642
646
|
}
|
|
643
647
|
};
|
|
648
|
+
var DEFAULT_HTML_BLOCK_MARKUP = "<p>Rich HTML content. Edit with the rich editor.</p>";
|
|
649
|
+
function imageLinkActive(p) {
|
|
650
|
+
const url = typeof p.link === "string" ? p.link.trim() : "";
|
|
651
|
+
const enabled = p.linkEnabled ?? !!url;
|
|
652
|
+
return enabled && !!url;
|
|
653
|
+
}
|
|
644
654
|
function isKnownBlockType(type) {
|
|
645
655
|
return typeof type === "string" && type in DEFAULT_BLOCK_PROPS;
|
|
646
656
|
}
|
|
647
657
|
|
|
658
|
+
// src/lib/columnProps.ts
|
|
659
|
+
var BOX02 = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
660
|
+
var DEFAULT_COLUMN_PROPS = {
|
|
661
|
+
bgColor: "",
|
|
662
|
+
bgImage: "",
|
|
663
|
+
bgGradient: null,
|
|
664
|
+
bgSize: "cover",
|
|
665
|
+
bgRepeat: "no-repeat",
|
|
666
|
+
bgPosition: "center",
|
|
667
|
+
padding: { ...BOX02 },
|
|
668
|
+
borderRadius: 0
|
|
669
|
+
};
|
|
670
|
+
function isObj(x) {
|
|
671
|
+
return x !== null && typeof x === "object" && !Array.isArray(x);
|
|
672
|
+
}
|
|
673
|
+
function colCountFromContainer(c) {
|
|
674
|
+
const cells = Array.isArray(c.cells) ? c.cells.length : 0;
|
|
675
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios.length : 0;
|
|
676
|
+
const cols = typeof c.cols === "number" && c.cols > 0 ? c.cols : 0;
|
|
677
|
+
return Math.max(1, cells, ratios, cols);
|
|
678
|
+
}
|
|
679
|
+
function columnsFromLegacyMap(map, count) {
|
|
680
|
+
const n = Math.max(1, count);
|
|
681
|
+
const src = isObj(map) ? map : {};
|
|
682
|
+
return Array.from({ length: n }, (_, i) => {
|
|
683
|
+
const raw = src[i] ?? src[String(i)];
|
|
684
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...isObj(raw) ? raw : {} } };
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function getColumns(container) {
|
|
688
|
+
const count = colCountFromContainer(container);
|
|
689
|
+
if (Array.isArray(container.columns)) {
|
|
690
|
+
const cols = container.columns;
|
|
691
|
+
return Array.from({ length: count }, (_, i) => {
|
|
692
|
+
const entry = cols[i];
|
|
693
|
+
if (isObj(entry) && isObj(entry.props)) {
|
|
694
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...entry.props } };
|
|
695
|
+
}
|
|
696
|
+
if (isObj(entry)) {
|
|
697
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...entry } };
|
|
698
|
+
}
|
|
699
|
+
return { props: { ...DEFAULT_COLUMN_PROPS } };
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
if (container.columnStyles) {
|
|
703
|
+
return columnsFromLegacyMap(container.columnStyles, count);
|
|
704
|
+
}
|
|
705
|
+
return columnsFromLegacyMap({}, count);
|
|
706
|
+
}
|
|
707
|
+
function getColumnPropsAt(container, index) {
|
|
708
|
+
const cols = getColumns(container);
|
|
709
|
+
const i = Math.max(0, Math.min(index, cols.length - 1));
|
|
710
|
+
return cols[i]?.props ?? { ...DEFAULT_COLUMN_PROPS };
|
|
711
|
+
}
|
|
712
|
+
function patchColumnPropsAt(container, index, patch) {
|
|
713
|
+
const cols = getColumns(container);
|
|
714
|
+
const i = Math.max(0, index);
|
|
715
|
+
while (cols.length <= i) cols.push({ props: { ...DEFAULT_COLUMN_PROPS } });
|
|
716
|
+
cols[i] = { props: { ...cols[i].props, ...patch } };
|
|
717
|
+
return cols;
|
|
718
|
+
}
|
|
719
|
+
function withColumnsOnly(row, columns) {
|
|
720
|
+
const { columnStyles: _drop, ...rest } = row;
|
|
721
|
+
return { ...rest, columns };
|
|
722
|
+
}
|
|
723
|
+
|
|
648
724
|
// src/lib/factories.ts
|
|
649
725
|
function uid() {
|
|
650
726
|
return Math.random().toString(36).slice(2, 10);
|
|
@@ -684,14 +760,30 @@ function makeNestedRowBlock(preset) {
|
|
|
684
760
|
bgRepeat: row.bgRepeat ?? "no-repeat",
|
|
685
761
|
bgPosition: row.bgPosition ?? "center",
|
|
686
762
|
bgGradient: row.bgGradient ?? null,
|
|
687
|
-
|
|
763
|
+
columns: row.columns ?? row.cells?.map(() => ({ props: { ...DEFAULT_COLUMN_PROPS } })),
|
|
688
764
|
cells: row.cells
|
|
689
765
|
}
|
|
690
766
|
};
|
|
691
767
|
}
|
|
692
768
|
function makeContentBlock(type) {
|
|
769
|
+
if (type === "html") {
|
|
770
|
+
return {
|
|
771
|
+
id: uid(),
|
|
772
|
+
type,
|
|
773
|
+
content: DEFAULT_HTML_BLOCK_MARKUP,
|
|
774
|
+
props: { ...DEFAULT_BLOCK_PROPS.html, content: DEFAULT_HTML_BLOCK_MARKUP }
|
|
775
|
+
};
|
|
776
|
+
}
|
|
693
777
|
return { id: uid(), type, props: { ...DEFAULT_BLOCK_PROPS[type] } };
|
|
694
778
|
}
|
|
779
|
+
function makeRootContentRow(contentType, preset) {
|
|
780
|
+
const row = makeLayoutRow(preset);
|
|
781
|
+
row.gap = 0;
|
|
782
|
+
row.padding = 0;
|
|
783
|
+
const block = makeContentBlock(contentType);
|
|
784
|
+
row.cells[0] = [block];
|
|
785
|
+
return { row, block };
|
|
786
|
+
}
|
|
695
787
|
|
|
696
788
|
// src/lib/columnPath.ts
|
|
697
789
|
var MAX_NESTED_LAYOUT_DEPTH = 1;
|
|
@@ -701,6 +793,13 @@ function nestedLayoutDepth(nested) {
|
|
|
701
793
|
function isSplitLayoutBlock(b) {
|
|
702
794
|
return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
|
|
703
795
|
}
|
|
796
|
+
function isRootContentRow(row) {
|
|
797
|
+
if (!row?.cells || row.cells.length !== 1) return false;
|
|
798
|
+
const col = row.cells[0];
|
|
799
|
+
if (!Array.isArray(col) || col.length !== 1) return false;
|
|
800
|
+
const b = col[0];
|
|
801
|
+
return !!b && typeof b.type === "string" && b.type !== "layout" && b.type !== "nestedRow";
|
|
802
|
+
}
|
|
704
803
|
function getColumnBlocks(rows, loc) {
|
|
705
804
|
const r = rows.find((x) => x.id === loc.rowId);
|
|
706
805
|
if (!r) return [];
|
|
@@ -766,56 +865,6 @@ function countBlocksInDesign(rows) {
|
|
|
766
865
|
return rows.reduce((sum, r) => sum + r.cells.reduce((s, c) => s + colBlocks(c), 0), 0);
|
|
767
866
|
}
|
|
768
867
|
|
|
769
|
-
// src/socialIcons.tsx
|
|
770
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
771
|
-
var GLYPHS = {
|
|
772
|
-
facebook: "M24 12.073C24 5.446 18.627 0 12 0S0 5.446 0 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
|
|
773
|
-
/** X (formerly Twitter) — Simple Icons “X” */
|
|
774
|
-
twitter: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
|
|
775
|
-
instagram: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.98-6.98.072-1.28.07-1.689.07-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z",
|
|
776
|
-
linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
|
|
777
|
-
youtube: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
778
|
-
pinterest: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.966-2.901 2.165-2.901 1.021 0 1.512.765 1.512 1.682 0 1.025-.653 2.555-.99 3.978-.281 1.189.597 2.159 1.775 2.159 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.698-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z",
|
|
779
|
-
tiktok: "M19.59 6.69a4.83 4.83 0 01-3.77-4.245V2h-3.45v13.672a2.896 2.896 0 01-5.201 1.743 2.895 2.895 0 012.31-4.629 2.89 2.89 0 01.878.14V9.4a6.84 6.84 0 00-1-.05A6.33 6.33 0 005 20.1a6.34 6.34 0 0010.86-4.43v-7.36a8.16 8.16 0 004.77 1.52v-3.4a4.85 4.85 0 01-1-.1z",
|
|
780
|
-
snapchat: "M12 0C5.373 0 0 5.372 0 12c0 4.991 3.657 9.128 8.438 9.879-.118-.949-.227-2.422.045-3.461.222-.95 1.444-6.037 1.444-6.037s-.367-.744-.367-1.844c0-1.726.999-3.012 2.243-3.012 1.058 0 1.569.795 1.569 1.748 0 1.065-.679 2.653-1.029 4.13-.292 1.237.618 2.246 1.835 2.246 2.203 0 3.895-2.323 3.895-5.67 0-2.965-2.129-5.038-5.167-5.038-3.52 0-5.585 2.643-5.585 5.589 0 1.106.425 2.295.956 2.943a.77.77 0 01.194.569c-.21.997-.679 3.135-.769 3.568-.121.588-.402.712-.929.431-3.485-1.624-5.667-6.731-5.667-10.839C2.5 6.506 6.847 2.344 12.001 2.344c4.391 0 7.809 2.776 7.809 6.586 0 4.308-2.694 7.635-6.369 7.635-1.271 0-2.464-.677-2.871-1.475 0 0-.669 2.562-.828 3.193-.301 1.301-1.12 2.929-1.668 3.922.125.047.256.07.395.07 1 0 1.826-.827 2.13-1.856.169-.617.982-3.857.982-3.857z",
|
|
781
|
-
github: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",
|
|
782
|
-
discord: "M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
|
|
783
|
-
};
|
|
784
|
-
function iconFillForNetwork(network) {
|
|
785
|
-
return network === "snapchat" ? "#000000" : "#ffffff";
|
|
786
|
-
}
|
|
787
|
-
function glyphPath(network) {
|
|
788
|
-
return GLYPHS[network] || null;
|
|
789
|
-
}
|
|
790
|
-
function socialIconSvgString(network, pixelSize) {
|
|
791
|
-
const d = glyphPath(network);
|
|
792
|
-
const fill = iconFillForNetwork(network);
|
|
793
|
-
const s = Math.max(8, Math.round(pixelSize));
|
|
794
|
-
if (!d) {
|
|
795
|
-
const letter = (network && network[0] ? network[0] : "?").toUpperCase();
|
|
796
|
-
return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
|
|
797
|
-
}
|
|
798
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${s}" height="${s}" style="display:block;flex-shrink:0;" aria-hidden="true"><path fill="${fill}" d="${d}"/></svg>`;
|
|
799
|
-
}
|
|
800
|
-
function SocialGlyph({ network, size }) {
|
|
801
|
-
const d = glyphPath(network);
|
|
802
|
-
const s = Math.max(8, Math.round(size));
|
|
803
|
-
if (!d) {
|
|
804
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
|
|
805
|
-
}
|
|
806
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
807
|
-
"svg",
|
|
808
|
-
{
|
|
809
|
-
width: s,
|
|
810
|
-
height: s,
|
|
811
|
-
viewBox: "0 0 24 24",
|
|
812
|
-
style: { display: "block", flexShrink: 0 },
|
|
813
|
-
"aria-hidden": true,
|
|
814
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { fill: "currentColor", d })
|
|
815
|
-
}
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
868
|
// src/lib/htmlUtils.ts
|
|
820
869
|
function escHtmlAttr(s) {
|
|
821
870
|
return String(s ?? "").replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -884,6 +933,161 @@ function normalizeRichHtmlForStorage(html) {
|
|
|
884
933
|
function isEffectivelyEmptyRichHtml(html) {
|
|
885
934
|
return normalizeRichHtmlForStorage(html) === EMPTY_RICH_HTML;
|
|
886
935
|
}
|
|
936
|
+
var RICH_HTML_CONTENT_CSS = `
|
|
937
|
+
.email-rich-html-content ul,
|
|
938
|
+
.email-rich-html-content ol {
|
|
939
|
+
margin: 0.35em 0;
|
|
940
|
+
padding-left: 1.35rem;
|
|
941
|
+
list-style-position: outside;
|
|
942
|
+
}
|
|
943
|
+
.email-rich-html-content ul { list-style-type: disc; }
|
|
944
|
+
.email-rich-html-content ol { list-style-type: decimal; }
|
|
945
|
+
.email-rich-html-content li { margin: 0.2em 0; }
|
|
946
|
+
.email-rich-html-content li > p { margin: 0; }
|
|
947
|
+
.email-rich-html-content p { margin: 0.35em 0; }
|
|
948
|
+
.email-rich-html-content h2,
|
|
949
|
+
.email-rich-html-content h3,
|
|
950
|
+
.email-rich-html-content h4 {
|
|
951
|
+
margin: 0.5em 0 0.25em;
|
|
952
|
+
line-height: 1.25;
|
|
953
|
+
font-weight: 700;
|
|
954
|
+
}
|
|
955
|
+
.email-rich-html-content blockquote {
|
|
956
|
+
margin: 0.35em 0;
|
|
957
|
+
padding-left: 0.75rem;
|
|
958
|
+
border-left: 3px solid #cbd5e1;
|
|
959
|
+
}
|
|
960
|
+
.email-rich-html-content hr {
|
|
961
|
+
border: none;
|
|
962
|
+
border-top: 1px solid #e2e8f0;
|
|
963
|
+
margin: 0.75em 0;
|
|
964
|
+
}
|
|
965
|
+
.email-rich-html-content code {
|
|
966
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
967
|
+
font-size: 0.92em;
|
|
968
|
+
background: rgba(0, 0, 0, 0.06);
|
|
969
|
+
padding: 0.1em 0.35em;
|
|
970
|
+
border-radius: 3px;
|
|
971
|
+
}
|
|
972
|
+
.email-rich-html-inner ul,
|
|
973
|
+
.email-rich-html-inner ol {
|
|
974
|
+
margin: 0.35em 0;
|
|
975
|
+
padding-left: 1.35rem;
|
|
976
|
+
list-style-position: outside;
|
|
977
|
+
}
|
|
978
|
+
.email-rich-html-inner ul { list-style-type: disc; }
|
|
979
|
+
.email-rich-html-inner ol { list-style-type: decimal; }
|
|
980
|
+
.email-rich-html-inner li { margin: 0.2em 0; }
|
|
981
|
+
.email-rich-html-inner li > p { margin: 0; }
|
|
982
|
+
.email-rich-html-inner p { margin: 0.35em 0; }
|
|
983
|
+
.email-rich-html-inner h2,
|
|
984
|
+
.email-rich-html-inner h3,
|
|
985
|
+
.email-rich-html-inner h4 {
|
|
986
|
+
margin: 0.5em 0 0.25em;
|
|
987
|
+
line-height: 1.25;
|
|
988
|
+
font-weight: 700;
|
|
989
|
+
}
|
|
990
|
+
.email-rich-html-inner blockquote {
|
|
991
|
+
margin: 0.35em 0;
|
|
992
|
+
padding-left: 0.75rem;
|
|
993
|
+
border-left: 3px solid #cbd5e1;
|
|
994
|
+
}
|
|
995
|
+
.email-rich-html-inner hr {
|
|
996
|
+
border: none;
|
|
997
|
+
border-top: 1px solid #e2e8f0;
|
|
998
|
+
margin: 0.75em 0;
|
|
999
|
+
}
|
|
1000
|
+
`.trim();
|
|
1001
|
+
function applyInlineListStyles(root) {
|
|
1002
|
+
root.querySelectorAll("ul").forEach((ul) => {
|
|
1003
|
+
const el = ul;
|
|
1004
|
+
el.style.margin = el.style.margin || "0.35em 0";
|
|
1005
|
+
el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
|
|
1006
|
+
el.style.listStyleType = el.style.listStyleType || "disc";
|
|
1007
|
+
el.style.listStylePosition = el.style.listStylePosition || "outside";
|
|
1008
|
+
});
|
|
1009
|
+
root.querySelectorAll("ol").forEach((ol) => {
|
|
1010
|
+
const el = ol;
|
|
1011
|
+
el.style.margin = el.style.margin || "0.35em 0";
|
|
1012
|
+
el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
|
|
1013
|
+
el.style.listStyleType = el.style.listStyleType || "decimal";
|
|
1014
|
+
el.style.listStylePosition = el.style.listStylePosition || "outside";
|
|
1015
|
+
});
|
|
1016
|
+
root.querySelectorAll("li").forEach((li) => {
|
|
1017
|
+
const el = li;
|
|
1018
|
+
if (!el.style.margin) el.style.margin = "0.2em 0";
|
|
1019
|
+
});
|
|
1020
|
+
root.querySelectorAll("li > p").forEach((p) => {
|
|
1021
|
+
const el = p;
|
|
1022
|
+
if (!el.style.margin) el.style.margin = "0";
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
function enhanceRichHtmlForEmail(html) {
|
|
1026
|
+
const raw = String(html ?? "").trim();
|
|
1027
|
+
if (!raw || raw === EMPTY_RICH_HTML) return raw || EMPTY_RICH_HTML;
|
|
1028
|
+
if (typeof document === "undefined") return raw;
|
|
1029
|
+
const tmp = document.createElement("div");
|
|
1030
|
+
tmp.innerHTML = raw;
|
|
1031
|
+
if (!tmp.querySelector("ul,ol,blockquote,h2,h3,h4,hr")) return raw;
|
|
1032
|
+
applyInlineListStyles(tmp);
|
|
1033
|
+
tmp.querySelectorAll("blockquote").forEach((bq) => {
|
|
1034
|
+
const el = bq;
|
|
1035
|
+
if (!el.style.margin) el.style.margin = "0.35em 0";
|
|
1036
|
+
if (!el.style.paddingLeft) el.style.paddingLeft = "0.75rem";
|
|
1037
|
+
if (!el.style.borderLeft) el.style.borderLeft = "3px solid #cbd5e1";
|
|
1038
|
+
});
|
|
1039
|
+
return tmp.innerHTML.trim();
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// src/socialIcons.tsx
|
|
1043
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1044
|
+
var GLYPHS = {
|
|
1045
|
+
facebook: "M24 12.073C24 5.446 18.627 0 12 0S0 5.446 0 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
|
|
1046
|
+
/** X (formerly Twitter) — Simple Icons “X” */
|
|
1047
|
+
twitter: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
|
|
1048
|
+
instagram: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.98-6.98.072-1.28.07-1.689.07-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z",
|
|
1049
|
+
linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
|
|
1050
|
+
youtube: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
1051
|
+
pinterest: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.966-2.901 2.165-2.901 1.021 0 1.512.765 1.512 1.682 0 1.025-.653 2.555-.99 3.978-.281 1.189.597 2.159 1.775 2.159 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.698-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z",
|
|
1052
|
+
tiktok: "M19.59 6.69a4.83 4.83 0 01-3.77-4.245V2h-3.45v13.672a2.896 2.896 0 01-5.201 1.743 2.895 2.895 0 012.31-4.629 2.89 2.89 0 01.878.14V9.4a6.84 6.84 0 00-1-.05A6.33 6.33 0 005 20.1a6.34 6.34 0 0010.86-4.43v-7.36a8.16 8.16 0 004.77 1.52v-3.4a4.85 4.85 0 01-1-.1z",
|
|
1053
|
+
snapchat: "M12 0C5.373 0 0 5.372 0 12c0 4.991 3.657 9.128 8.438 9.879-.118-.949-.227-2.422.045-3.461.222-.95 1.444-6.037 1.444-6.037s-.367-.744-.367-1.844c0-1.726.999-3.012 2.243-3.012 1.058 0 1.569.795 1.569 1.748 0 1.065-.679 2.653-1.029 4.13-.292 1.237.618 2.246 1.835 2.246 2.203 0 3.895-2.323 3.895-5.67 0-2.965-2.129-5.038-5.167-5.038-3.52 0-5.585 2.643-5.585 5.589 0 1.106.425 2.295.956 2.943a.77.77 0 01.194.569c-.21.997-.679 3.135-.769 3.568-.121.588-.402.712-.929.431-3.485-1.624-5.667-6.731-5.667-10.839C2.5 6.506 6.847 2.344 12.001 2.344c4.391 0 7.809 2.776 7.809 6.586 0 4.308-2.694 7.635-6.369 7.635-1.271 0-2.464-.677-2.871-1.475 0 0-.669 2.562-.828 3.193-.301 1.301-1.12 2.929-1.668 3.922.125.047.256.07.395.07 1 0 1.826-.827 2.13-1.856.169-.617.982-3.857.982-3.857z",
|
|
1054
|
+
github: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",
|
|
1055
|
+
discord: "M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
|
|
1056
|
+
};
|
|
1057
|
+
function iconFillForNetwork(network) {
|
|
1058
|
+
return network === "snapchat" ? "#000000" : "#ffffff";
|
|
1059
|
+
}
|
|
1060
|
+
function glyphPath(network) {
|
|
1061
|
+
return GLYPHS[network] || null;
|
|
1062
|
+
}
|
|
1063
|
+
function socialIconSvgString(network, pixelSize) {
|
|
1064
|
+
const d = glyphPath(network);
|
|
1065
|
+
const fill = iconFillForNetwork(network);
|
|
1066
|
+
const s = Math.max(8, Math.round(pixelSize));
|
|
1067
|
+
if (!d) {
|
|
1068
|
+
const letter = (network && network[0] ? network[0] : "?").toUpperCase();
|
|
1069
|
+
return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
|
|
1070
|
+
}
|
|
1071
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${s}" height="${s}" style="display:block;flex-shrink:0;" aria-hidden="true"><path fill="${fill}" d="${d}"/></svg>`;
|
|
1072
|
+
}
|
|
1073
|
+
function SocialGlyph({ network, size }) {
|
|
1074
|
+
const d = glyphPath(network);
|
|
1075
|
+
const s = Math.max(8, Math.round(size));
|
|
1076
|
+
if (!d) {
|
|
1077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
|
|
1078
|
+
}
|
|
1079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1080
|
+
"svg",
|
|
1081
|
+
{
|
|
1082
|
+
width: s,
|
|
1083
|
+
height: s,
|
|
1084
|
+
viewBox: "0 0 24 24",
|
|
1085
|
+
style: { display: "block", flexShrink: 0 },
|
|
1086
|
+
"aria-hidden": true,
|
|
1087
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { fill: "currentColor", d })
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
887
1091
|
|
|
888
1092
|
// src/lib/blockBackground.ts
|
|
889
1093
|
function linearGradientCssInner(g) {
|
|
@@ -1013,14 +1217,16 @@ function blockToHtml(cb) {
|
|
|
1013
1217
|
case "html": {
|
|
1014
1218
|
const shell = emailSurfaceBgCss(p);
|
|
1015
1219
|
const inner = `font-size:${lenPx(p.fontSize)};color:${p.color};text-align:${p.align};line-height:${lh(p.lineHeight)};letter-spacing:${lenPx(p.letterSpacing)};font-family:${p.fontFamily || "Georgia,serif"}`;
|
|
1016
|
-
|
|
1220
|
+
const bodyRaw = typeof cb.content === "string" ? cb.content : typeof p.content === "string" ? p.content : "";
|
|
1221
|
+
const body = enhanceRichHtmlForEmail(bodyRaw || "<p></p>");
|
|
1222
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div class="email-rich-html-inner" style="${inner}">${body}</div></div>`;
|
|
1017
1223
|
}
|
|
1018
1224
|
case "image": {
|
|
1019
1225
|
const of = typeof p.objectFit === "string" && p.objectFit.trim() ? p.objectFit.trim() : "cover";
|
|
1020
1226
|
const op = typeof p.objectPosition === "string" && p.objectPosition.trim() ? p.objectPosition.trim() : "center";
|
|
1021
1227
|
const img = `<img src="${p.src}" alt="${p.alt || ""}" style="max-width:${p.width};width:${p.width || "100%"};display:inline-block;border-radius:${radiusPx(p.borderRadius)};object-fit:${of};object-position:${op};"/>`;
|
|
1022
1228
|
const shell = emailSurfaceBgCss(p);
|
|
1023
|
-
return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${p
|
|
1229
|
+
return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${imageLinkActive(p) ? `<a href="${escHtmlAttr(p.link)}" target="${escHtmlAttr(p.linkTarget || "_blank")}">${img}</a>` : img}</div>`;
|
|
1024
1230
|
}
|
|
1025
1231
|
case "button": {
|
|
1026
1232
|
const bd = emailBackdropBgCss(p);
|
|
@@ -1109,7 +1315,7 @@ function blockToHtml(cb) {
|
|
|
1109
1315
|
bgRepeat: p.bgRepeat || "no-repeat",
|
|
1110
1316
|
bgPosition: typeof p.bgPosition === "string" ? p.bgPosition : "center",
|
|
1111
1317
|
bgGradient: p.bgGradient ?? null,
|
|
1112
|
-
|
|
1318
|
+
columns: getColumns(p),
|
|
1113
1319
|
cells: p.cells || []
|
|
1114
1320
|
};
|
|
1115
1321
|
return rowToHtml(pseudo);
|
|
@@ -1123,7 +1329,7 @@ function rowToHtml(row) {
|
|
|
1123
1329
|
const shellBg = emailSurfaceBgCss(row);
|
|
1124
1330
|
const inner = row.cells.length === 1 ? row.cells[0].map(blockToHtml).join("") : `<div class="email-row-flex" style="display:flex;flex-direction:row;flex-wrap:nowrap;align-items:flex-start;gap:${row.gap ?? 12}px;width:100%;max-width:100%;box-sizing:border-box;">${row.cells.map((cell, i) => {
|
|
1125
1331
|
const r = row.ratios[i] ?? 1;
|
|
1126
|
-
const cs = row
|
|
1332
|
+
const cs = getColumnPropsAt(row, i);
|
|
1127
1333
|
const colShell = emailSurfaceBgCss(cs);
|
|
1128
1334
|
const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
|
|
1129
1335
|
const o = cs.padding;
|
|
@@ -1171,6 +1377,7 @@ table[role="presentation"] td{min-width:0!important;word-break:break-word;}
|
|
|
1171
1377
|
@media screen and (max-width:480px){
|
|
1172
1378
|
.email-content-td{padding-left:max(10px,env(safe-area-inset-left))!important;padding-right:max(10px,env(safe-area-inset-right))!important;}
|
|
1173
1379
|
}
|
|
1380
|
+
${RICH_HTML_CONTENT_CSS}
|
|
1174
1381
|
</style>`;
|
|
1175
1382
|
function previewEmailSrcDoc(html, viewportWidthPx) {
|
|
1176
1383
|
const viewportTag = `<meta name="viewport" content="width=${viewportWidthPx},initial-scale=1"/>`;
|
|
@@ -1222,6 +1429,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1222
1429
|
return `background-image:linear-gradient(${angle}deg, ${pairs.join(", ")});background-repeat:no-repeat;background-position:${pagePos};background-size:cover;`;
|
|
1223
1430
|
})();
|
|
1224
1431
|
const pageBgImgRaw = typeof settings.bgImage === "string" ? String(settings.bgImage).trim() : "";
|
|
1432
|
+
const pageBgColorRaw = typeof settings.bgColor === "string" ? String(settings.bgColor).trim() : "";
|
|
1433
|
+
const pageBgColor = pageBgColorRaw ? pageBgColorRaw : "transparent";
|
|
1225
1434
|
const pageBgImgCss = !pageGradCss && pageBgImgRaw ? `background-image:url('${pageBgImgRaw.replace(/'/g, "%27")}');background-size:${settings.bgSize || "cover"};background-repeat:${settings.bgRepeat || "no-repeat"};background-position:${pagePos};` : "";
|
|
1226
1435
|
const contentPos = typeof settings.contentBgPosition === "string" && settings.contentBgPosition.trim() ? settings.contentBgPosition.trim() : "center";
|
|
1227
1436
|
const contentGrad = settings.contentBgGradient && typeof settings.contentBgGradient === "object" ? settings.contentBgGradient : null;
|
|
@@ -1244,8 +1453,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1244
1453
|
<head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
|
1245
1454
|
<title>Email</title>${RESPONSIVE_EMAIL_CSS}${css}
|
|
1246
1455
|
</head>
|
|
1247
|
-
<body style="margin:0;padding:0;background:${
|
|
1248
|
-
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${
|
|
1456
|
+
<body style="margin:0;padding:0;background:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1457
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1249
1458
|
<tr><td align="center" style="padding:0;max-width:100%;">
|
|
1250
1459
|
<table class="email-shell-table" role="presentation" width="100%" cellpadding="0" cellspacing="0"
|
|
1251
1460
|
style="${contentShellStyle.join(";")}">
|
|
@@ -1279,14 +1488,49 @@ function paddingToUniform(p, fallback = 0) {
|
|
|
1279
1488
|
function ensureId(id, prefix) {
|
|
1280
1489
|
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
1281
1490
|
}
|
|
1491
|
+
function normalizeDocBlock(b, prefix) {
|
|
1492
|
+
const raw = b && typeof b === "object" && !Array.isArray(b) ? b : {};
|
|
1493
|
+
const base = {
|
|
1494
|
+
id: ensureId(raw.id, prefix),
|
|
1495
|
+
type: typeof raw.type === "string" ? raw.type : "text",
|
|
1496
|
+
content: typeof raw.content === "string" ? raw.content : raw.content && typeof raw.content === "object" && !Array.isArray(raw.content) ? raw.content : {},
|
|
1497
|
+
...raw.behavior && typeof raw.behavior === "object" && !Array.isArray(raw.behavior) ? { behavior: raw.behavior } : {},
|
|
1498
|
+
...raw.responsive && typeof raw.responsive === "object" && !Array.isArray(raw.responsive) ? { responsive: raw.responsive } : {}
|
|
1499
|
+
};
|
|
1500
|
+
if (raw.props && typeof raw.props === "object" && !Array.isArray(raw.props)) {
|
|
1501
|
+
base.props = { ...raw.props };
|
|
1502
|
+
} else if (raw.styles && typeof raw.styles === "object" && !Array.isArray(raw.styles)) {
|
|
1503
|
+
base.styles = { ...raw.styles };
|
|
1504
|
+
}
|
|
1505
|
+
return base;
|
|
1506
|
+
}
|
|
1507
|
+
function normalizeDocSettings(settings) {
|
|
1508
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return {};
|
|
1509
|
+
const s = { ...settings };
|
|
1510
|
+
delete s._reactEmailStudio;
|
|
1511
|
+
return s;
|
|
1512
|
+
}
|
|
1282
1513
|
function normalizeEmailDocument(input) {
|
|
1283
1514
|
if (input == null || typeof input !== "object" || Array.isArray(input)) return null;
|
|
1284
1515
|
const doc = input;
|
|
1285
1516
|
if (doc.type !== "email_document") return null;
|
|
1517
|
+
const rawBlocks = Array.isArray(doc.blocks) ? doc.blocks : [];
|
|
1518
|
+
const useBlocks = rawBlocks.length > 0;
|
|
1286
1519
|
const rows = Array.isArray(doc.rows) ? doc.rows : [];
|
|
1287
|
-
|
|
1520
|
+
const base = {
|
|
1288
1521
|
type: "email_document",
|
|
1289
|
-
settings:
|
|
1522
|
+
settings: normalizeDocSettings(doc.settings),
|
|
1523
|
+
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1524
|
+
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1525
|
+
};
|
|
1526
|
+
if (useBlocks) {
|
|
1527
|
+
return {
|
|
1528
|
+
...base,
|
|
1529
|
+
blocks: rawBlocks.map((b, bi) => normalizeDocBlock(b, `block${bi + 1}`))
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
return {
|
|
1533
|
+
...base,
|
|
1290
1534
|
rows: rows.map((r, ri) => {
|
|
1291
1535
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1292
1536
|
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
@@ -1308,28 +1552,76 @@ function normalizeEmailDocument(input) {
|
|
|
1308
1552
|
columns: colCount,
|
|
1309
1553
|
gap: typeof r.layout?.gap === "number" ? r.layout.gap : 0,
|
|
1310
1554
|
stackOnMobile: r.layout?.stackOnMobile !== false,
|
|
1311
|
-
align: r.layout?.align || "center"
|
|
1555
|
+
align: r.layout?.align || "center",
|
|
1556
|
+
...typeof r.layout?.preset === "string" && r.layout.preset.trim() ? { preset: r.layout.preset.trim() } : {},
|
|
1557
|
+
...Array.isArray(r.layout?.ratios) && r.layout.ratios.length ? {
|
|
1558
|
+
ratios: r.layout.ratios.filter(
|
|
1559
|
+
(x) => typeof x === "number" && Number.isFinite(x)
|
|
1560
|
+
)
|
|
1561
|
+
} : {}
|
|
1312
1562
|
},
|
|
1313
|
-
|
|
1314
|
-
|
|
1563
|
+
props: (() => {
|
|
1564
|
+
const p = r.props;
|
|
1565
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { ...p };
|
|
1566
|
+
const s = r.styles;
|
|
1567
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1568
|
+
return {
|
|
1569
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1570
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1571
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1572
|
+
bgSize: s.backgroundSize || "cover",
|
|
1573
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1574
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1575
|
+
padding: s.padding,
|
|
1576
|
+
borderRadius: s.borderRadius,
|
|
1577
|
+
borderWidth: s.borderWidth,
|
|
1578
|
+
borderColor: s.borderColor,
|
|
1579
|
+
textAlign: s.textAlign
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
return {};
|
|
1583
|
+
})(),
|
|
1315
1584
|
columns: colsNormalized.map((c, ci) => ({
|
|
1316
1585
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1317
1586
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
...
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1587
|
+
...(() => {
|
|
1588
|
+
const p = c.props;
|
|
1589
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { props: { ...p } };
|
|
1590
|
+
const s = c.styles;
|
|
1591
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1592
|
+
return {
|
|
1593
|
+
props: {
|
|
1594
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1595
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1596
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1597
|
+
bgSize: s.backgroundSize || "cover",
|
|
1598
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1599
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1600
|
+
padding: s.padding,
|
|
1601
|
+
borderRadius: s.borderRadius
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
return {};
|
|
1606
|
+
})(),
|
|
1607
|
+
blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => {
|
|
1608
|
+
const base2 = {
|
|
1609
|
+
id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
|
|
1610
|
+
type: typeof b?.type === "string" ? b.type : "text",
|
|
1611
|
+
content: typeof b?.content === "string" ? b.content : b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1612
|
+
...b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? { behavior: b.behavior } : {},
|
|
1613
|
+
...b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? { responsive: b.responsive } : {}
|
|
1614
|
+
};
|
|
1615
|
+
if (b?.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
1616
|
+
base2.props = { ...b.props };
|
|
1617
|
+
} else if (b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles)) {
|
|
1618
|
+
base2.styles = { ...b.styles };
|
|
1619
|
+
}
|
|
1620
|
+
return base2;
|
|
1621
|
+
}) : []
|
|
1328
1622
|
}))
|
|
1329
1623
|
};
|
|
1330
|
-
})
|
|
1331
|
-
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1332
|
-
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1624
|
+
})
|
|
1333
1625
|
};
|
|
1334
1626
|
}
|
|
1335
1627
|
|
|
@@ -1342,6 +1634,60 @@ function cloneJson(v) {
|
|
|
1342
1634
|
return v;
|
|
1343
1635
|
}
|
|
1344
1636
|
}
|
|
1637
|
+
function legacyRowStylesToProps(s) {
|
|
1638
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return {};
|
|
1639
|
+
return {
|
|
1640
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1641
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1642
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1643
|
+
bgSize: s.backgroundSize || "cover",
|
|
1644
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1645
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1646
|
+
padding: s.padding,
|
|
1647
|
+
borderRadius: s.borderRadius,
|
|
1648
|
+
borderWidth: s.borderWidth,
|
|
1649
|
+
borderColor: s.borderColor,
|
|
1650
|
+
textAlign: s.textAlign
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
function rowPropsFromDocRow(r) {
|
|
1654
|
+
const p = r.props;
|
|
1655
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1656
|
+
return { ...p };
|
|
1657
|
+
}
|
|
1658
|
+
return legacyRowStylesToProps(r.styles);
|
|
1659
|
+
}
|
|
1660
|
+
function applyRowPropsToEditorRow(row, r) {
|
|
1661
|
+
const p = rowPropsFromDocRow(r);
|
|
1662
|
+
if (p.padding !== void 0 && p.padding !== null) {
|
|
1663
|
+
row.padding = paddingToUniform(p.padding, row.padding);
|
|
1664
|
+
}
|
|
1665
|
+
if (typeof p.bgColor === "string") row.bgColor = p.bgColor.trim();
|
|
1666
|
+
if (typeof p.bgImage === "string") row.bgImage = p.bgImage.trim();
|
|
1667
|
+
if (typeof p.bgRepeat === "string") row.bgRepeat = p.bgRepeat;
|
|
1668
|
+
if (typeof p.bgSize === "string") row.bgSize = p.bgSize;
|
|
1669
|
+
if (typeof p.bgPosition === "string") row.bgPosition = p.bgPosition;
|
|
1670
|
+
if (p.bgGradient !== void 0) row.bgGradient = p.bgGradient;
|
|
1671
|
+
}
|
|
1672
|
+
function editorRowToDocProps(r) {
|
|
1673
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1674
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1675
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
1676
|
+
})();
|
|
1677
|
+
return {
|
|
1678
|
+
bgColor: typeof r.bgColor === "string" ? r.bgColor : "",
|
|
1679
|
+
bgImage: typeof r.bgImage === "string" ? r.bgImage : "",
|
|
1680
|
+
bgRepeat: r.bgRepeat || "no-repeat",
|
|
1681
|
+
bgSize: r.bgSize || "cover",
|
|
1682
|
+
bgPosition: r.bgPosition || "center",
|
|
1683
|
+
bgGradient: r.bgGradient ?? null,
|
|
1684
|
+
padding: rowPad,
|
|
1685
|
+
borderRadius: 0,
|
|
1686
|
+
borderWidth: 0,
|
|
1687
|
+
borderColor: "#e5e7eb",
|
|
1688
|
+
textAlign: "left"
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1345
1691
|
function layoutColumnPaddingForDoc(p) {
|
|
1346
1692
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1347
1693
|
const n = Math.max(0, p);
|
|
@@ -1361,16 +1707,58 @@ function layoutColumnBorderRadiusForDoc(r) {
|
|
|
1361
1707
|
}
|
|
1362
1708
|
return 0;
|
|
1363
1709
|
}
|
|
1364
|
-
function
|
|
1365
|
-
|
|
1710
|
+
function normalizeLayoutContainerProps(props) {
|
|
1711
|
+
const columns = getColumns(props);
|
|
1712
|
+
const { columnStyles: _drop, ...rest } = props;
|
|
1713
|
+
return { ...rest, columns };
|
|
1714
|
+
}
|
|
1715
|
+
function mergeLayoutColumns(fromContent, fromHydrated) {
|
|
1716
|
+
const norm = (x) => {
|
|
1717
|
+
if (Array.isArray(x)) {
|
|
1718
|
+
return x.map((entry) => {
|
|
1719
|
+
const e = entry;
|
|
1720
|
+
const raw = e?.props && typeof e.props === "object" ? e.props : entry;
|
|
1721
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...raw } };
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
if (x && typeof x === "object" && !Array.isArray(x)) {
|
|
1725
|
+
return columnsFromLegacyMap(x, Object.keys(x).length);
|
|
1726
|
+
}
|
|
1727
|
+
return [];
|
|
1728
|
+
};
|
|
1729
|
+
const a = norm(fromContent);
|
|
1730
|
+
const b = norm(fromHydrated);
|
|
1731
|
+
const len = Math.max(a.length, b.length, 1);
|
|
1732
|
+
const out = [];
|
|
1733
|
+
for (let i = 0; i < len; i++) {
|
|
1734
|
+
out.push({
|
|
1735
|
+
props: { ...DEFAULT_COLUMN_PROPS, ...a[i]?.props ?? {}, ...b[i]?.props ?? {} }
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
return out.length ? out : void 0;
|
|
1366
1739
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
if (
|
|
1370
|
-
|
|
1371
|
-
return !!(o.tl || o.tr || o.br || o.bl);
|
|
1740
|
+
function columnPropsFromDocColumn(c) {
|
|
1741
|
+
const p = c?.props;
|
|
1742
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1743
|
+
return { ...DEFAULT_COLUMN_PROPS, ...p };
|
|
1372
1744
|
}
|
|
1373
|
-
|
|
1745
|
+
const s = c?.styles;
|
|
1746
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) {
|
|
1747
|
+
return { ...DEFAULT_COLUMN_PROPS };
|
|
1748
|
+
}
|
|
1749
|
+
const padding = layoutColumnPaddingForDoc(s.padding);
|
|
1750
|
+
const borderRadius = layoutColumnBorderRadiusForDoc(s.borderRadius);
|
|
1751
|
+
return {
|
|
1752
|
+
...DEFAULT_COLUMN_PROPS,
|
|
1753
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1754
|
+
bgImage: typeof s.backgroundImage === "string" ? String(s.backgroundImage).trim() : "",
|
|
1755
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1756
|
+
bgSize: s.backgroundSize || "cover",
|
|
1757
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1758
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1759
|
+
padding,
|
|
1760
|
+
borderRadius
|
|
1761
|
+
};
|
|
1374
1762
|
}
|
|
1375
1763
|
function asNum(v) {
|
|
1376
1764
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
@@ -1467,13 +1855,15 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1467
1855
|
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1468
1856
|
}
|
|
1469
1857
|
};
|
|
1470
|
-
case "html":
|
|
1858
|
+
case "html": {
|
|
1859
|
+
const body = typeof b.content === "string" ? b.content : typeof p.content === "string" ? p.content : "";
|
|
1471
1860
|
return {
|
|
1472
1861
|
id,
|
|
1473
1862
|
type,
|
|
1474
|
-
content: { html:
|
|
1863
|
+
content: { html: normalizeRichHtmlForStorage(body) },
|
|
1475
1864
|
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1476
1865
|
};
|
|
1866
|
+
}
|
|
1477
1867
|
case "image": {
|
|
1478
1868
|
const br = p.borderRadius;
|
|
1479
1869
|
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
@@ -1653,7 +2043,7 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1653
2043
|
bgRepeat: p.bgRepeat,
|
|
1654
2044
|
bgPosition: p.bgPosition,
|
|
1655
2045
|
bgGradient: p.bgGradient ?? null,
|
|
1656
|
-
|
|
2046
|
+
columns: getColumns(p).map((col) => ({ props: cloneJson(col.props) })),
|
|
1657
2047
|
cells: cellsOut
|
|
1658
2048
|
},
|
|
1659
2049
|
styles: {}
|
|
@@ -1693,17 +2083,26 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1693
2083
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1694
2084
|
}
|
|
1695
2085
|
break;
|
|
1696
|
-
case "html":
|
|
1697
|
-
|
|
1698
|
-
|
|
2086
|
+
case "html": {
|
|
2087
|
+
const br = b;
|
|
2088
|
+
const fromDoc = asStr(c.html);
|
|
2089
|
+
const fromRoot = typeof br.content === "string" ? String(br.content) : void 0;
|
|
2090
|
+
const fromLegacyProps = br.props && typeof br.props.content === "string" ? String(br.props.content) : void 0;
|
|
2091
|
+
const htmlNorm = normalizeRichHtmlForStorage(fromDoc ?? fromRoot ?? fromLegacyProps ?? "");
|
|
2092
|
+
block.content = htmlNorm;
|
|
2093
|
+
block.props.content = htmlNorm;
|
|
1699
2094
|
if (s.padding && typeof s.padding === "object") {
|
|
1700
2095
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1701
2096
|
}
|
|
1702
2097
|
break;
|
|
2098
|
+
}
|
|
1703
2099
|
case "image":
|
|
1704
2100
|
block.props.src = asStr(c.src) ?? block.props.src;
|
|
1705
2101
|
if (asStr(c.alt)) block.props.alt = c.alt;
|
|
1706
|
-
if (asStr(c.link))
|
|
2102
|
+
if (asStr(c.link)) {
|
|
2103
|
+
block.props.link = c.link;
|
|
2104
|
+
block.props.linkEnabled = true;
|
|
2105
|
+
}
|
|
1707
2106
|
if (beh.openInNewTab === true) block.props.linkTarget = "_blank";
|
|
1708
2107
|
if (asStr(s.width)) block.props.width = s.width;
|
|
1709
2108
|
if (asStr(s.align)) block.props.align = s.align;
|
|
@@ -1825,8 +2224,17 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1825
2224
|
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1826
2225
|
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1827
2226
|
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1828
|
-
if (c.
|
|
1829
|
-
|
|
2227
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
2228
|
+
if (Array.isArray(c.columns)) {
|
|
2229
|
+
block.props.columns = c.columns.map((col) => ({
|
|
2230
|
+
props: {
|
|
2231
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2232
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2233
|
+
}
|
|
2234
|
+
}));
|
|
2235
|
+
} else if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
2236
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios : [1];
|
|
2237
|
+
block.props.columns = columnsFromLegacyMap(c.columnStyles, ratios.length);
|
|
1830
2238
|
}
|
|
1831
2239
|
block.props.cells = c.cells.map(
|
|
1832
2240
|
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
@@ -1870,6 +2278,10 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1870
2278
|
}
|
|
1871
2279
|
}
|
|
1872
2280
|
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
2281
|
+
if (t === "image") {
|
|
2282
|
+
const link = asStr(block.props.link);
|
|
2283
|
+
if (link && block.props.linkEnabled == null) block.props.linkEnabled = true;
|
|
2284
|
+
}
|
|
1873
2285
|
return block;
|
|
1874
2286
|
}
|
|
1875
2287
|
function rowToInternal(r) {
|
|
@@ -1891,55 +2303,315 @@ function rowToInternal(r) {
|
|
|
1891
2303
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
1892
2304
|
row.cols = cols;
|
|
1893
2305
|
row.gap = typeof r.layout?.gap === "number" ? r.layout.gap : row.gap;
|
|
1894
|
-
row
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2306
|
+
applyRowPropsToEditorRow(row, r);
|
|
2307
|
+
const lay = r.layout;
|
|
2308
|
+
if (lay && typeof lay === "object" && !Array.isArray(lay)) {
|
|
2309
|
+
if (typeof lay.preset === "string" && lay.preset.trim()) row.preset = lay.preset.trim();
|
|
2310
|
+
if (Array.isArray(lay.ratios) && lay.ratios.length) {
|
|
2311
|
+
const rr = lay.ratios.filter((x) => typeof x === "number" && Number.isFinite(x));
|
|
2312
|
+
if (rr.length) row.ratios = rr;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
1901
2315
|
row.cells = colList.map((c) => {
|
|
1902
2316
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1903
2317
|
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1904
2318
|
});
|
|
1905
2319
|
const studio = r._reactEmailStudio;
|
|
1906
2320
|
const snap = studio?.row;
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
const padding = layoutColumnPaddingForDoc(c.styles?.padding);
|
|
1911
|
-
const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
|
|
1912
|
-
const bgImage = typeof c.styles?.backgroundImage === "string" ? String(c.styles.backgroundImage).trim() : "";
|
|
1913
|
-
const bgRepeat = typeof c.styles?.backgroundRepeat === "string" ? c.styles.backgroundRepeat : void 0;
|
|
1914
|
-
const bgSize = typeof c.styles?.backgroundSize === "string" ? c.styles.backgroundSize : void 0;
|
|
1915
|
-
const bgPosition = typeof c.styles?.backgroundPosition === "string" ? c.styles.backgroundPosition : void 0;
|
|
1916
|
-
const bgGradient = c.styles?.backgroundGradient || null;
|
|
1917
|
-
const hasPad = columnPaddingNonZero(padding);
|
|
1918
|
-
const hasBr = columnRadiusNonZero(borderRadius);
|
|
1919
|
-
if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
|
|
1920
|
-
columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
|
|
1921
|
-
}
|
|
1922
|
-
});
|
|
2321
|
+
const columnsFromDoc = colList.map((c) => ({
|
|
2322
|
+
props: columnPropsFromDocColumn(c)
|
|
2323
|
+
}));
|
|
1923
2324
|
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1924
|
-
const {
|
|
2325
|
+
const {
|
|
2326
|
+
cells: _omitCells,
|
|
2327
|
+
columnStyles: legacyCs,
|
|
2328
|
+
columns: snapCols,
|
|
2329
|
+
...rest
|
|
2330
|
+
} = snap;
|
|
1925
2331
|
Object.assign(row, rest);
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2332
|
+
const fromSnap = Array.isArray(snapCols) ? snapCols.map((col) => ({
|
|
2333
|
+
props: {
|
|
2334
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2335
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2336
|
+
}
|
|
2337
|
+
})) : columnsFromLegacyMap(legacyCs, cols);
|
|
2338
|
+
row.columns = mergeLayoutColumns(fromSnap, columnsFromDoc) ?? columnsFromDoc;
|
|
2339
|
+
delete row.columnStyles;
|
|
2340
|
+
} else {
|
|
2341
|
+
row.columns = columnsFromDoc;
|
|
1931
2342
|
}
|
|
1932
2343
|
return row;
|
|
1933
2344
|
}
|
|
1934
|
-
function
|
|
1935
|
-
|
|
1936
|
-
if (
|
|
1937
|
-
|
|
1938
|
-
|
|
2345
|
+
function coerceEmailDocumentInput(input) {
|
|
2346
|
+
if (input == null) return input;
|
|
2347
|
+
if (Array.isArray(input)) {
|
|
2348
|
+
if (input.length === 0) return input;
|
|
2349
|
+
return { type: "email_document", settings: {}, blocks: input };
|
|
2350
|
+
}
|
|
2351
|
+
if (typeof input !== "object") return input;
|
|
2352
|
+
const o = input;
|
|
2353
|
+
if (o.type === "email_document") return input;
|
|
2354
|
+
if (Array.isArray(o.blocks)) {
|
|
2355
|
+
return {
|
|
2356
|
+
type: "email_document",
|
|
2357
|
+
settings: o.settings && typeof o.settings === "object" ? o.settings : {},
|
|
2358
|
+
blocks: o.blocks
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
return input;
|
|
2362
|
+
}
|
|
2363
|
+
function rootLayoutBlockToDocRow(block) {
|
|
2364
|
+
const p = block.props && typeof block.props === "object" ? block.props : {};
|
|
2365
|
+
const c = block.content && typeof block.content === "object" && !Array.isArray(block.content) ? block.content : {};
|
|
2366
|
+
const cols = Math.max(
|
|
2367
|
+
1,
|
|
2368
|
+
typeof p.cols === "number" && p.cols > 0 ? Math.floor(p.cols) : 0,
|
|
2369
|
+
typeof c.cols === "number" && c.cols > 0 ? Math.floor(c.cols) : 0,
|
|
2370
|
+
Array.isArray(p.cells) ? p.cells.length : 0,
|
|
2371
|
+
Array.isArray(c.cells) ? c.cells.length : 0
|
|
2372
|
+
);
|
|
2373
|
+
const rawCells = Array.isArray(c.cells) ? c.cells : Array.isArray(p.cells) ? p.cells : [];
|
|
2374
|
+
const contentColumns = Array.isArray(c.columns) ? c.columns : [];
|
|
2375
|
+
const propsColumns = getColumns(p);
|
|
2376
|
+
const columns = Array.from({ length: cols }, (_, ci) => {
|
|
2377
|
+
const cellRaw = rawCells[ci];
|
|
2378
|
+
const blocks = Array.isArray(cellRaw) ? cellRaw.filter((x) => x && typeof x === "object").map((x, bi) => {
|
|
2379
|
+
const blk = x;
|
|
2380
|
+
return {
|
|
2381
|
+
id: ensureBlockId(blk.id, `b_${ci}_${bi}`),
|
|
2382
|
+
type: typeof blk.type === "string" ? blk.type : "text",
|
|
2383
|
+
content: blk.content,
|
|
2384
|
+
...blk.props ? { props: blk.props } : {},
|
|
2385
|
+
...blk.styles ? { styles: blk.styles } : {},
|
|
2386
|
+
...blk.behavior ? { behavior: blk.behavior } : {}
|
|
2387
|
+
};
|
|
2388
|
+
}) : [];
|
|
2389
|
+
const colProps = {
|
|
2390
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2391
|
+
...propsColumns[ci]?.props ?? contentColumns[ci]?.props ?? {}
|
|
2392
|
+
};
|
|
2393
|
+
return {
|
|
2394
|
+
id: `col_${ci + 1}`,
|
|
2395
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2396
|
+
props: cloneJson(colProps),
|
|
2397
|
+
blocks
|
|
2398
|
+
};
|
|
2399
|
+
});
|
|
2400
|
+
const rowProps = {
|
|
2401
|
+
bgColor: typeof p.bgColor === "string" ? p.bgColor : "",
|
|
2402
|
+
bgImage: typeof p.bgImage === "string" ? p.bgImage : "",
|
|
2403
|
+
bgRepeat: p.bgRepeat || "no-repeat",
|
|
2404
|
+
bgSize: p.bgSize || "cover",
|
|
2405
|
+
bgPosition: p.bgPosition || "center",
|
|
2406
|
+
bgGradient: p.bgGradient ?? null,
|
|
2407
|
+
padding: typeof p.padding === "number" ? { top: p.padding, right: p.padding, bottom: p.padding, left: p.padding } : normalizePadding(p.padding)
|
|
2408
|
+
};
|
|
2409
|
+
const ratios = Array.isArray(p.ratios) ? p.ratios : Array.isArray(c.ratios) ? c.ratios : void 0;
|
|
2410
|
+
return {
|
|
2411
|
+
id: typeof block.id === "string" ? block.id : uid(),
|
|
2412
|
+
type: "row",
|
|
2413
|
+
layout: {
|
|
2414
|
+
columns: cols,
|
|
2415
|
+
gap: typeof p.gap === "number" ? p.gap : typeof c.gap === "number" ? c.gap : 0,
|
|
2416
|
+
stackOnMobile: true,
|
|
2417
|
+
align: "center",
|
|
2418
|
+
...typeof p.preset === "string" && p.preset.trim() ? { preset: p.preset.trim() } : typeof c.preset === "string" && c.preset.trim() ? { preset: c.preset.trim() } : {},
|
|
2419
|
+
...ratios?.length ? { ratios: [...ratios] } : {}
|
|
2420
|
+
},
|
|
2421
|
+
props: rowProps,
|
|
2422
|
+
columns
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2425
|
+
function ensureBlockId(id, prefix) {
|
|
2426
|
+
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
2427
|
+
}
|
|
2428
|
+
function wrapRootContentBlock(block) {
|
|
2429
|
+
const row = makeLayoutRow(pickPresetByColCount(1));
|
|
2430
|
+
const internal = mapBlockToInternal(block, 0);
|
|
2431
|
+
if (internal) row.cells[0] = [internal];
|
|
2432
|
+
return row;
|
|
2433
|
+
}
|
|
2434
|
+
function rootBlockToEditorRow(block) {
|
|
2435
|
+
const t = block.type === "nestedRow" ? "layout" : block.type;
|
|
2436
|
+
if (t === "layout") {
|
|
2437
|
+
return rowToInternal(rootLayoutBlockToDocRow(block));
|
|
2438
|
+
}
|
|
2439
|
+
return wrapRootContentBlock(block);
|
|
2440
|
+
}
|
|
2441
|
+
function blocksToEditorRows(blocks) {
|
|
2442
|
+
return blocks.map(rootBlockToEditorRow);
|
|
2443
|
+
}
|
|
2444
|
+
function rowPropsToLegacyStyles(p) {
|
|
2445
|
+
return {
|
|
2446
|
+
backgroundColor: p.bgColor ?? "",
|
|
2447
|
+
backgroundImage: p.bgImage ?? "",
|
|
2448
|
+
backgroundRepeat: p.bgRepeat ?? "no-repeat",
|
|
2449
|
+
backgroundSize: p.bgSize ?? "cover",
|
|
2450
|
+
backgroundPosition: p.bgPosition ?? "center",
|
|
2451
|
+
backgroundGradient: p.bgGradient ?? null,
|
|
2452
|
+
padding: p.padding,
|
|
2453
|
+
borderRadius: p.borderRadius ?? 0,
|
|
2454
|
+
borderWidth: p.borderWidth ?? 0,
|
|
2455
|
+
borderColor: p.borderColor ?? "#e5e7eb",
|
|
2456
|
+
textAlign: p.textAlign ?? "left"
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
function columnPropsToLegacyStyles(cp) {
|
|
2460
|
+
return {
|
|
2461
|
+
padding: layoutColumnPaddingForDoc(cp.padding),
|
|
2462
|
+
backgroundColor: cp.bgColor ?? "",
|
|
2463
|
+
backgroundImage: cp.bgImage ?? "",
|
|
2464
|
+
backgroundRepeat: cp.bgRepeat ?? "no-repeat",
|
|
2465
|
+
backgroundSize: cp.bgSize ?? "cover",
|
|
2466
|
+
backgroundPosition: cp.bgPosition ?? "center",
|
|
2467
|
+
backgroundGradient: cp.bgGradient ?? null,
|
|
2468
|
+
borderRadius: layoutColumnBorderRadiusForDoc(cp.borderRadius)
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
function editorRowStudioSnapshot(r) {
|
|
2472
|
+
const p = editorRowToDocProps(r);
|
|
2473
|
+
const columns = getColumns(r).map((col) => ({
|
|
2474
|
+
props: cloneJson(col.props ?? DEFAULT_COLUMN_PROPS)
|
|
2475
|
+
}));
|
|
2476
|
+
return {
|
|
2477
|
+
id: r.id,
|
|
2478
|
+
type: "layout",
|
|
2479
|
+
preset: r.preset,
|
|
2480
|
+
cols: r.cols,
|
|
2481
|
+
ratios: Array.isArray(r.ratios) ? [...r.ratios] : [1],
|
|
2482
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2483
|
+
padding: p.padding,
|
|
2484
|
+
bgColor: p.bgColor ?? "",
|
|
2485
|
+
bgImage: p.bgImage ?? "",
|
|
2486
|
+
bgSize: p.bgSize ?? "cover",
|
|
2487
|
+
bgRepeat: p.bgRepeat ?? "no-repeat",
|
|
2488
|
+
bgPosition: p.bgPosition ?? "center",
|
|
2489
|
+
bgGradient: p.bgGradient ?? null,
|
|
2490
|
+
columns
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
function exportBlockForLegacyRow(b, depth = 0) {
|
|
2494
|
+
if (!b || typeof b !== "object") {
|
|
2495
|
+
return { id: uid(), type: "text", content: {}, props: {} };
|
|
2496
|
+
}
|
|
2497
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? cloneJson(b.props) : {};
|
|
2498
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2499
|
+
rawProps.cells = b.props.cells.map(
|
|
2500
|
+
(col) => Array.isArray(col) ? col.map((child) => exportBlockForLegacyRow(child, depth + 1)) : []
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
if (b.type === "html") {
|
|
2504
|
+
const body = typeof b.content === "string" ? b.content : typeof rawProps.content === "string" ? rawProps.content : "";
|
|
2505
|
+
const html = normalizeRichHtmlForStorage(body);
|
|
2506
|
+
rawProps.content = html;
|
|
2507
|
+
return {
|
|
2508
|
+
id: typeof b.id === "string" ? b.id : uid(),
|
|
2509
|
+
type: "html",
|
|
2510
|
+
content: { html },
|
|
2511
|
+
props: rawProps
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2515
|
+
const out = {
|
|
2516
|
+
id: doc.id,
|
|
2517
|
+
type: doc.type,
|
|
2518
|
+
content: doc.content ?? {},
|
|
2519
|
+
props: rawProps
|
|
2520
|
+
};
|
|
2521
|
+
if (doc.behavior) out.behavior = doc.behavior;
|
|
2522
|
+
return out;
|
|
2523
|
+
}
|
|
2524
|
+
function editorRowToEmailDocumentRow(r, rowIndex) {
|
|
2525
|
+
const rowProps = editorRowToDocProps(r);
|
|
2526
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
2527
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? Math.floor(r.cols) : 0;
|
|
2528
|
+
const colCount = Math.max(1, cellArrays.length, declaredCols);
|
|
2529
|
+
const rowColumns = getColumns(r);
|
|
2530
|
+
const columns = Array.from({ length: colCount }, (_, ci) => {
|
|
2531
|
+
const cp = {
|
|
2532
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2533
|
+
...rowColumns[ci]?.props ?? {}
|
|
2534
|
+
};
|
|
2535
|
+
const cellBlocks = cellArrays[ci];
|
|
2536
|
+
const blocks = Array.isArray(cellBlocks) ? cellBlocks.map((blk) => exportBlockForLegacyRow(blk)).filter((x) => x != null) : [];
|
|
2537
|
+
return {
|
|
2538
|
+
id: `col_${rowIndex + 1}_${ci + 1}`,
|
|
2539
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2540
|
+
styles: columnPropsToLegacyStyles(cp),
|
|
2541
|
+
props: cloneJson(cp),
|
|
2542
|
+
blocks
|
|
2543
|
+
};
|
|
2544
|
+
});
|
|
2545
|
+
const ratios = Array.isArray(r.ratios) ? [...r.ratios] : void 0;
|
|
2546
|
+
return {
|
|
2547
|
+
id: typeof r.id === "string" ? r.id : uid(),
|
|
2548
|
+
type: "row",
|
|
2549
|
+
_reactEmailStudio: { row: editorRowStudioSnapshot(r) },
|
|
2550
|
+
layout: {
|
|
2551
|
+
columns: colCount,
|
|
2552
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2553
|
+
stackOnMobile: true,
|
|
2554
|
+
align: "center",
|
|
2555
|
+
...typeof r.preset === "string" && r.preset.trim() ? { preset: r.preset.trim() } : {},
|
|
2556
|
+
...ratios?.length ? { ratios } : {}
|
|
2557
|
+
},
|
|
2558
|
+
props: rowProps,
|
|
2559
|
+
styles: rowPropsToLegacyStyles(rowProps),
|
|
2560
|
+
columns
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
function buildExportSettingsWithStudio(settings) {
|
|
2564
|
+
const base = buildExportSettings(settings) ?? {};
|
|
2565
|
+
const editorSettings = cloneJson(settings);
|
|
2566
|
+
return {
|
|
2567
|
+
...base,
|
|
2568
|
+
_reactEmailStudio: {
|
|
2569
|
+
contentPadding: base.contentPadding ?? 24,
|
|
2570
|
+
contentBorderRadius: base.contentBorderRadius ?? 8,
|
|
2571
|
+
editorSettings
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
function editorSettingStr(settings, key, fallback = "") {
|
|
2576
|
+
const v = settings[key];
|
|
2577
|
+
return typeof v === "string" ? v : fallback;
|
|
2578
|
+
}
|
|
2579
|
+
function buildExportSettings(settings) {
|
|
2580
|
+
const contentWidth = typeof settings.contentWidth === "number" ? settings.contentWidth : 600;
|
|
2581
|
+
const bgGradient = settings.bgGradient;
|
|
2582
|
+
const contentBgGradient = settings.contentBgGradient;
|
|
2583
|
+
return {
|
|
2584
|
+
width: contentWidth,
|
|
2585
|
+
backgroundColor: editorSettingStr(settings, "bgColor", "#f1f5f9"),
|
|
2586
|
+
backgroundImage: editorSettingStr(settings, "bgImage"),
|
|
2587
|
+
backgroundRepeat: editorSettingStr(settings, "bgRepeat", "no-repeat"),
|
|
2588
|
+
backgroundSize: editorSettingStr(settings, "bgSize", "cover"),
|
|
2589
|
+
backgroundPosition: editorSettingStr(settings, "bgPosition", "center"),
|
|
2590
|
+
backgroundGradient: bgGradient && typeof bgGradient === "object" && !Array.isArray(bgGradient) ? bgGradient : void 0,
|
|
2591
|
+
contentBackgroundColor: editorSettingStr(settings, "contentBg", "#ffffff"),
|
|
2592
|
+
contentBackgroundImage: editorSettingStr(settings, "contentBgImage"),
|
|
2593
|
+
contentBackgroundRepeat: editorSettingStr(settings, "contentBgRepeat", "no-repeat"),
|
|
2594
|
+
contentBackgroundSize: editorSettingStr(settings, "contentBgSize", "cover"),
|
|
2595
|
+
contentBackgroundPosition: editorSettingStr(settings, "contentBgPosition", "center"),
|
|
2596
|
+
contentBackgroundGradient: contentBgGradient && typeof contentBgGradient === "object" && !Array.isArray(contentBgGradient) ? contentBgGradient : void 0,
|
|
2597
|
+
fontFamily: editorSettingStr(settings, "fontFamily") || editorSettingStr(settings, "pageFontFamily", "Arial, Helvetica, sans-serif"),
|
|
2598
|
+
lineHeightBase: settings.pageLineHeight != null && settings.pageLineHeight !== "" ? Number(settings.pageLineHeight) : 1.6,
|
|
2599
|
+
color: editorSettingStr(settings, "pageTextColor", "#111827"),
|
|
2600
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
2601
|
+
rtl: !!settings.pageRtl,
|
|
2602
|
+
contentPadding: typeof settings.padding === "number" ? settings.padding : 24,
|
|
2603
|
+
contentBorderRadius: typeof settings.borderRadius === "number" ? settings.borderRadius : 8
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
function editorSettingsFromDoc(s, rawSettings) {
|
|
2607
|
+
const studioSettings = rawSettings?._reactEmailStudio;
|
|
1939
2608
|
const settings = {};
|
|
1940
2609
|
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1941
2610
|
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1942
2611
|
}
|
|
2612
|
+
const pageRtlFromEditor = settings.pageRtl;
|
|
2613
|
+
const contentPadding = typeof s.contentPadding === "number" ? s.contentPadding : typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : void 0;
|
|
2614
|
+
const contentBorderRadius = typeof s.contentBorderRadius === "number" ? s.contentBorderRadius : typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : void 0;
|
|
1943
2615
|
Object.assign(settings, {
|
|
1944
2616
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1945
2617
|
bgImage: s.backgroundImage || "",
|
|
@@ -1954,103 +2626,67 @@ function normalizeEmailDesignInput(input) {
|
|
|
1954
2626
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1955
2627
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1956
2628
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1957
|
-
padding:
|
|
1958
|
-
borderRadius:
|
|
2629
|
+
padding: contentPadding ?? (typeof settings.padding === "number" ? settings.padding : 24),
|
|
2630
|
+
borderRadius: contentBorderRadius ?? (typeof settings.borderRadius === "number" ? settings.borderRadius : 8),
|
|
1959
2631
|
pageFontFamily: s.fontFamily,
|
|
1960
2632
|
pageTextColor: s.color,
|
|
1961
2633
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1962
2634
|
pageResponsive: s.responsive,
|
|
1963
|
-
pageRtl: s.rtl,
|
|
2635
|
+
pageRtl: typeof s.rtl === "boolean" ? s.rtl : pageRtlFromEditor,
|
|
1964
2636
|
fontFamily: s.fontFamily
|
|
1965
2637
|
});
|
|
1966
|
-
|
|
2638
|
+
return settings;
|
|
2639
|
+
}
|
|
2640
|
+
function normalizeEmailDesignInput(input) {
|
|
2641
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2642
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2643
|
+
if (!doc) return null;
|
|
2644
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2645
|
+
const rawSettings = rawDoc?.settings && typeof rawDoc.settings === "object" && !Array.isArray(rawDoc.settings) ? rawDoc.settings : null;
|
|
2646
|
+
const settings = editorSettingsFromDoc(doc.settings || {}, rawSettings);
|
|
2647
|
+
let rows;
|
|
2648
|
+
if (doc.blocks?.length) {
|
|
2649
|
+
rows = blocksToEditorRows(doc.blocks);
|
|
2650
|
+
} else {
|
|
2651
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2652
|
+
rows = (doc.rows || []).map((r, i) => {
|
|
2653
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2654
|
+
if (legacy) {
|
|
2655
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2656
|
+
}
|
|
2657
|
+
return rowToInternal(r);
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
1967
2660
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1968
2661
|
}
|
|
2662
|
+
function emailDocumentToEditorRows(input) {
|
|
2663
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2664
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2665
|
+
if (!doc) return null;
|
|
2666
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2667
|
+
if (doc.blocks?.length) {
|
|
2668
|
+
return blocksToEditorRows(doc.blocks);
|
|
2669
|
+
}
|
|
2670
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2671
|
+
return (doc.rows || []).map((r, i) => {
|
|
2672
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2673
|
+
if (legacy) {
|
|
2674
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2675
|
+
}
|
|
2676
|
+
return rowToInternal(r);
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
1969
2679
|
function designToEmailDocument(rows, settings) {
|
|
1970
|
-
|
|
2680
|
+
return {
|
|
1971
2681
|
type: "email_document",
|
|
1972
|
-
settings:
|
|
1973
|
-
|
|
1974
|
-
backgroundColor: settings.bgColor || "#f1f5f9",
|
|
1975
|
-
backgroundImage: settings.bgImage || "",
|
|
1976
|
-
backgroundRepeat: settings.bgRepeat || "no-repeat",
|
|
1977
|
-
backgroundSize: settings.bgSize || "cover",
|
|
1978
|
-
backgroundPosition: settings.bgPosition || "center",
|
|
1979
|
-
backgroundGradient: settings.bgGradient || null,
|
|
1980
|
-
contentBackgroundColor: settings.contentBg || "#ffffff",
|
|
1981
|
-
contentBackgroundImage: settings.contentBgImage || "",
|
|
1982
|
-
contentBackgroundRepeat: settings.contentBgRepeat || "no-repeat",
|
|
1983
|
-
contentBackgroundSize: settings.contentBgSize || "cover",
|
|
1984
|
-
contentBackgroundPosition: settings.contentBgPosition || "center",
|
|
1985
|
-
contentBackgroundGradient: settings.contentBgGradient || null,
|
|
1986
|
-
fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
|
|
1987
|
-
lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
|
|
1988
|
-
color: settings.pageTextColor || "#111827",
|
|
1989
|
-
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
1990
|
-
rtl: !!settings.pageRtl,
|
|
1991
|
-
_reactEmailStudio: {
|
|
1992
|
-
contentPadding: settings.padding ?? 24,
|
|
1993
|
-
contentBorderRadius: settings.borderRadius ?? 8,
|
|
1994
|
-
editorSettings: cloneJson(settings)
|
|
1995
|
-
}
|
|
1996
|
-
},
|
|
1997
|
-
rows: rows.map((r, ri) => {
|
|
1998
|
-
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
1999
|
-
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
|
|
2000
|
-
const cols = Math.max(1, cellArrays.length, declaredCols);
|
|
2001
|
-
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
2002
|
-
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
2003
|
-
return { top: n, right: n, bottom: n, left: n };
|
|
2004
|
-
})();
|
|
2005
|
-
const { cells: _rowCells, ...rowEditorSnap } = r;
|
|
2006
|
-
return {
|
|
2007
|
-
id: r.id || `row_${ri + 1}`,
|
|
2008
|
-
type: "row",
|
|
2009
|
-
_reactEmailStudio: {
|
|
2010
|
-
row: cloneJson(rowEditorSnap)
|
|
2011
|
-
},
|
|
2012
|
-
layout: {
|
|
2013
|
-
columns: cols,
|
|
2014
|
-
gap: r.gap ?? 0,
|
|
2015
|
-
stackOnMobile: true,
|
|
2016
|
-
align: "center"
|
|
2017
|
-
},
|
|
2018
|
-
styles: {
|
|
2019
|
-
backgroundColor: r.bgColor || "",
|
|
2020
|
-
backgroundImage: r.bgImage || "",
|
|
2021
|
-
backgroundRepeat: r.bgRepeat || "no-repeat",
|
|
2022
|
-
backgroundSize: r.bgSize || "cover",
|
|
2023
|
-
backgroundPosition: r.bgPosition || "center",
|
|
2024
|
-
backgroundGradient: r.bgGradient || null,
|
|
2025
|
-
padding: rowPad,
|
|
2026
|
-
borderRadius: 0,
|
|
2027
|
-
borderWidth: 0,
|
|
2028
|
-
borderColor: "#e5e7eb",
|
|
2029
|
-
textAlign: "left"
|
|
2030
|
-
},
|
|
2031
|
-
columns: Array.from({ length: cols }, (_, ci) => {
|
|
2032
|
-
const cellBlocks = cellArrays[ci] ?? [];
|
|
2033
|
-
const cs = r.columnStyles && r.columnStyles[ci] || {};
|
|
2034
|
-
return {
|
|
2035
|
-
id: `col_${ri + 1}_${ci + 1}`,
|
|
2036
|
-
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2037
|
-
styles: {
|
|
2038
|
-
padding: layoutColumnPaddingForDoc(cs.padding),
|
|
2039
|
-
backgroundColor: cs.bgColor || "",
|
|
2040
|
-
backgroundImage: cs.bgImage || "",
|
|
2041
|
-
backgroundRepeat: cs.bgRepeat || "no-repeat",
|
|
2042
|
-
backgroundSize: cs.bgSize || "cover",
|
|
2043
|
-
backgroundPosition: cs.bgPosition || "center",
|
|
2044
|
-
backgroundGradient: cs.bgGradient || null,
|
|
2045
|
-
borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
|
|
2046
|
-
},
|
|
2047
|
-
blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
|
|
2048
|
-
};
|
|
2049
|
-
})
|
|
2050
|
-
};
|
|
2051
|
-
})
|
|
2682
|
+
settings: buildExportSettingsWithStudio(settings),
|
|
2683
|
+
rows: rows.map((r, i) => editorRowToEmailDocumentRow(r, i))
|
|
2052
2684
|
};
|
|
2053
|
-
|
|
2685
|
+
}
|
|
2686
|
+
function canonicalizeEmailDocument(input) {
|
|
2687
|
+
const loaded = normalizeEmailDesignInput(coerceEmailDocumentInput(input) ?? input);
|
|
2688
|
+
if (!loaded?.rows) return null;
|
|
2689
|
+
return designToEmailDocument(loaded.rows, loaded.settings);
|
|
2054
2690
|
}
|
|
2055
2691
|
function hydrateBlock(b, depth = 0) {
|
|
2056
2692
|
if (!b || typeof b !== "object") return null;
|
|
@@ -2064,13 +2700,13 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2064
2700
|
...b,
|
|
2065
2701
|
type: "layout",
|
|
2066
2702
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2067
|
-
props: {
|
|
2703
|
+
props: normalizeLayoutContainerProps({
|
|
2068
2704
|
...defaults2,
|
|
2069
2705
|
...b.props,
|
|
2070
2706
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2071
2707
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2072
2708
|
cells: ratios.map(() => [])
|
|
2073
|
-
}
|
|
2709
|
+
})
|
|
2074
2710
|
};
|
|
2075
2711
|
}
|
|
2076
2712
|
const cells = b.props.cells.map(
|
|
@@ -2080,24 +2716,33 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2080
2716
|
...b,
|
|
2081
2717
|
type: "layout",
|
|
2082
2718
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2083
|
-
props: {
|
|
2719
|
+
props: normalizeLayoutContainerProps({
|
|
2084
2720
|
...defaults2,
|
|
2085
2721
|
...b.props,
|
|
2086
2722
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2087
2723
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2088
2724
|
cells
|
|
2089
|
-
}
|
|
2725
|
+
})
|
|
2090
2726
|
};
|
|
2091
2727
|
}
|
|
2092
2728
|
const defaults = DEFAULT_BLOCK_PROPS[t];
|
|
2093
2729
|
if (typeof defaults !== "object" || defaults == null || Array.isArray(defaults)) return null;
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
)
|
|
2100
|
-
|
|
2730
|
+
if (t === "html") {
|
|
2731
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? b.props : {};
|
|
2732
|
+
const { content: legacyPropContent, ...restProps } = rawProps;
|
|
2733
|
+
const merged2 = { ...defaults, ...restProps };
|
|
2734
|
+
normalizeBoxStyles(merged2, t);
|
|
2735
|
+
const src = typeof b.content === "string" ? String(b.content) : typeof legacyPropContent === "string" ? legacyPropContent : "";
|
|
2736
|
+
const htmlNorm = normalizeRichHtmlForStorage(src);
|
|
2737
|
+
merged2.content = htmlNorm;
|
|
2738
|
+
return {
|
|
2739
|
+
...b,
|
|
2740
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2741
|
+
content: htmlNorm,
|
|
2742
|
+
props: merged2
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
const merged = { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
|
|
2101
2746
|
normalizeBoxStyles(merged, t);
|
|
2102
2747
|
return {
|
|
2103
2748
|
...b,
|
|
@@ -2113,70 +2758,50 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2113
2758
|
}
|
|
2114
2759
|
return mapBlockToInternal(r, layoutDepth);
|
|
2115
2760
|
}
|
|
2116
|
-
function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
|
|
2117
|
-
const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
2118
|
-
if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
|
|
2119
|
-
const a = isObj(fromContent) ? fromContent : {};
|
|
2120
|
-
const b = isObj(fromHydrated) ? fromHydrated : {};
|
|
2121
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
2122
|
-
const out = {};
|
|
2123
|
-
for (const k of keys) {
|
|
2124
|
-
const va = a[k];
|
|
2125
|
-
const vb = b[k];
|
|
2126
|
-
if (isObj(va) && isObj(vb)) {
|
|
2127
|
-
out[k] = { ...va, ...vb };
|
|
2128
|
-
} else if (vb !== void 0) {
|
|
2129
|
-
out[k] = vb;
|
|
2130
|
-
} else {
|
|
2131
|
-
out[k] = va;
|
|
2132
|
-
}
|
|
2133
|
-
}
|
|
2134
|
-
return Object.keys(out).length ? out : void 0;
|
|
2135
|
-
}
|
|
2136
2761
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2137
2762
|
const exp = b.props;
|
|
2138
|
-
if (!exp || typeof exp !== "object" || Array.isArray(exp))
|
|
2763
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) {
|
|
2764
|
+
if (t === "html" && typeof block.content === "string") {
|
|
2765
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2766
|
+
block.props.content = block.content;
|
|
2767
|
+
}
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2139
2770
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2140
|
-
const
|
|
2771
|
+
const columnsFromContent = block.props.columns ?? block.props.columnStyles;
|
|
2772
|
+
const expHasColumns = Array.isArray(exp.columns) || exp.columnStyles && typeof exp.columnStyles === "object" && !Array.isArray(exp.columnStyles);
|
|
2141
2773
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2142
2774
|
if (hb) {
|
|
2143
2775
|
block.props = hb.props;
|
|
2144
|
-
const merged =
|
|
2145
|
-
|
|
2146
|
-
|
|
2776
|
+
const merged = mergeLayoutColumns(
|
|
2777
|
+
columnsFromContent,
|
|
2778
|
+
expHasColumns ? block.props.columns : void 0
|
|
2779
|
+
);
|
|
2780
|
+
if (merged) block.props.columns = merged;
|
|
2781
|
+
else delete block.props.columns;
|
|
2782
|
+
delete block.props.columnStyles;
|
|
2147
2783
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2148
2784
|
}
|
|
2149
2785
|
return;
|
|
2150
2786
|
}
|
|
2787
|
+
if (t === "html") {
|
|
2788
|
+
const { content: expContent, ...expRest } = exp;
|
|
2789
|
+
Object.assign(block.props, expRest);
|
|
2790
|
+
normalizeBoxStyles(block.props, t);
|
|
2791
|
+
if (typeof expContent === "string") {
|
|
2792
|
+
block.content = normalizeRichHtmlForStorage(expContent);
|
|
2793
|
+
} else if (typeof block.content === "string") {
|
|
2794
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2795
|
+
}
|
|
2796
|
+
block.props.content = typeof block.content === "string" ? block.content : "";
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2151
2799
|
Object.assign(block.props, exp);
|
|
2152
2800
|
normalizeBoxStyles(block.props, t);
|
|
2153
|
-
if (t === "
|
|
2801
|
+
if (t === "text") {
|
|
2154
2802
|
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2155
2803
|
}
|
|
2156
2804
|
}
|
|
2157
|
-
function exportEmailDocBlock(b, depth = 0) {
|
|
2158
|
-
if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
|
|
2159
|
-
const doc = internalBlockToEmailDoc(b, depth);
|
|
2160
|
-
const out = { ...doc };
|
|
2161
|
-
if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
2162
|
-
out.props = cloneJson(b.props);
|
|
2163
|
-
}
|
|
2164
|
-
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2165
|
-
const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
|
|
2166
|
-
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2167
|
-
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
|
|
2168
|
-
out.content = { ...baseContent, cells: ratios.map(() => []) };
|
|
2169
|
-
} else {
|
|
2170
|
-
out.content = {
|
|
2171
|
-
...baseContent,
|
|
2172
|
-
cells: b.props.cells.map(
|
|
2173
|
-
(col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
|
|
2174
|
-
)
|
|
2175
|
-
};
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
return out;
|
|
2179
|
-
}
|
|
2180
2805
|
function hydrateLayoutRow(row) {
|
|
2181
2806
|
if (!row || typeof row !== "object") return row;
|
|
2182
2807
|
const cells = (row.cells || []).map(
|
|
@@ -2219,6 +2844,77 @@ function jsonToHtml(designInput, opts = {}) {
|
|
|
2219
2844
|
return designToHtml(d.rows, d.settings, opts);
|
|
2220
2845
|
}
|
|
2221
2846
|
|
|
2847
|
+
// src/lib/htmlToEmailDesign.ts
|
|
2848
|
+
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
2849
|
+
function extractHtmlForDesign(html) {
|
|
2850
|
+
const t = String(html ?? "").trim();
|
|
2851
|
+
if (!t) return "";
|
|
2852
|
+
const head = t.slice(0, 800).toLowerCase();
|
|
2853
|
+
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
2854
|
+
return normalizeRichHtmlForStorage(t);
|
|
2855
|
+
}
|
|
2856
|
+
try {
|
|
2857
|
+
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
2858
|
+
const body = doc.body;
|
|
2859
|
+
if (!body) return normalizeRichHtmlForStorage(t);
|
|
2860
|
+
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
2861
|
+
} catch {
|
|
2862
|
+
return normalizeRichHtmlForStorage(t);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
function htmlToEmailDesignTemplate(html) {
|
|
2866
|
+
const inner = extractHtmlForDesign(html);
|
|
2867
|
+
if (!inner) return null;
|
|
2868
|
+
const doc = {
|
|
2869
|
+
type: "email_document",
|
|
2870
|
+
settings: {
|
|
2871
|
+
width: 600,
|
|
2872
|
+
backgroundColor: "#f1f5f9",
|
|
2873
|
+
contentBackgroundColor: "#ffffff",
|
|
2874
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
2875
|
+
lineHeightBase: 1.6,
|
|
2876
|
+
color: "#111827",
|
|
2877
|
+
responsive: true,
|
|
2878
|
+
rtl: false
|
|
2879
|
+
},
|
|
2880
|
+
rows: [
|
|
2881
|
+
{
|
|
2882
|
+
id: "row_html_import",
|
|
2883
|
+
type: "row",
|
|
2884
|
+
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
2885
|
+
styles: {
|
|
2886
|
+
backgroundColor: "#ffffff",
|
|
2887
|
+
backgroundRepeat: "no-repeat",
|
|
2888
|
+
backgroundSize: "cover",
|
|
2889
|
+
padding: pad(24),
|
|
2890
|
+
textAlign: "left"
|
|
2891
|
+
},
|
|
2892
|
+
columns: [
|
|
2893
|
+
{
|
|
2894
|
+
id: "col_html_import",
|
|
2895
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2896
|
+
styles: { padding: pad(0), backgroundColor: "" },
|
|
2897
|
+
blocks: [
|
|
2898
|
+
{
|
|
2899
|
+
id: "block_html_import",
|
|
2900
|
+
type: "html",
|
|
2901
|
+
content: inner,
|
|
2902
|
+
props: { ...DEFAULT_BLOCK_PROPS.html }
|
|
2903
|
+
}
|
|
2904
|
+
]
|
|
2905
|
+
}
|
|
2906
|
+
]
|
|
2907
|
+
}
|
|
2908
|
+
]
|
|
2909
|
+
};
|
|
2910
|
+
return doc;
|
|
2911
|
+
}
|
|
2912
|
+
function htmlToJson(html, pretty = false) {
|
|
2913
|
+
const doc = htmlToEmailDesignTemplate(html);
|
|
2914
|
+
if (!doc) return "";
|
|
2915
|
+
return pretty ? JSON.stringify(doc, null, 2) : JSON.stringify(doc);
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2222
2918
|
// src/editor/canvas/DesignCanvas.tsx
|
|
2223
2919
|
var import_react = require("react");
|
|
2224
2920
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -2359,13 +3055,15 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2359
3055
|
)
|
|
2360
3056
|
);
|
|
2361
3057
|
if (type === "html") {
|
|
2362
|
-
const
|
|
3058
|
+
const richHtml = typeof block.content === "string" ? block.content : p.content;
|
|
3059
|
+
const emptyRich = isEffectivelyEmptyRichHtml(richHtml);
|
|
2363
3060
|
return wrap(
|
|
2364
3061
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", minHeight: emptyRich && !preview ? "2.75em" : void 0 }, children: [
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: RICH_HTML_CONTENT_CSS }),
|
|
2365
3063
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2366
3064
|
"div",
|
|
2367
3065
|
{
|
|
2368
|
-
className: "email-editor-rich-canvas",
|
|
3066
|
+
className: "email-editor-rich-canvas email-rich-html-content",
|
|
2369
3067
|
style: {
|
|
2370
3068
|
fontSize: p.fontSize,
|
|
2371
3069
|
color: p.color,
|
|
@@ -2376,7 +3074,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2376
3074
|
wordBreak: "break-word",
|
|
2377
3075
|
minHeight: emptyRich && !preview ? "2.75em" : void 0
|
|
2378
3076
|
},
|
|
2379
|
-
dangerouslySetInnerHTML: { __html:
|
|
3077
|
+
dangerouslySetInnerHTML: { __html: richHtml || "<p></p>" }
|
|
2380
3078
|
}
|
|
2381
3079
|
),
|
|
2382
3080
|
emptyRich && !preview ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2424,7 +3122,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2424
3122
|
}
|
|
2425
3123
|
}
|
|
2426
3124
|
);
|
|
2427
|
-
return wrap(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: p.align }, children: p
|
|
3125
|
+
return wrap(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: p.align }, children: imageLinkActive(p) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href: p.link, target: p.linkTarget || "_blank", rel: "noopener", children: img }) : img }));
|
|
2428
3126
|
}
|
|
2429
3127
|
if (type === "button") return wrap(
|
|
2430
3128
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: p.fullWidth ? "center" : p.align }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2597,7 +3295,7 @@ function NestedRowBlock({
|
|
|
2597
3295
|
);
|
|
2598
3296
|
},
|
|
2599
3297
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2600
|
-
const cS = p
|
|
3298
|
+
const cS = getColumnPropsAt(p, ici);
|
|
2601
3299
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2602
3300
|
"div",
|
|
2603
3301
|
{
|
|
@@ -2655,7 +3353,9 @@ function BlockItem({
|
|
|
2655
3353
|
onContextMenu,
|
|
2656
3354
|
preview,
|
|
2657
3355
|
C,
|
|
2658
|
-
Line
|
|
3356
|
+
Line,
|
|
3357
|
+
dragAsRow = false,
|
|
3358
|
+
beginRowDrag
|
|
2659
3359
|
}) {
|
|
2660
3360
|
const wrapperRef = (0, import_react.useRef)(null);
|
|
2661
3361
|
const bid = editorId ? `${editorId}-block-${cb.id}` : void 0;
|
|
@@ -2703,7 +3403,7 @@ function BlockItem({
|
|
|
2703
3403
|
{
|
|
2704
3404
|
"data-bidx": ci,
|
|
2705
3405
|
draggable: !preview,
|
|
2706
|
-
title: preview ? void 0 : "Click to select \xB7 drag to reorder or move",
|
|
3406
|
+
title: preview ? void 0 : dragAsRow ? "Click to select \xB7 drag to reorder section" : "Click to select \xB7 drag to reorder or move",
|
|
2707
3407
|
onPointerDown: (e) => {
|
|
2708
3408
|
if (preview || e.button !== 0) return;
|
|
2709
3409
|
selectThisBlock(e);
|
|
@@ -2711,6 +3411,10 @@ function BlockItem({
|
|
|
2711
3411
|
onDragStart: (e) => {
|
|
2712
3412
|
if (preview) return;
|
|
2713
3413
|
e.stopPropagation();
|
|
3414
|
+
if (dragAsRow && beginRowDrag) {
|
|
3415
|
+
beginRowDrag(e);
|
|
3416
|
+
return;
|
|
3417
|
+
}
|
|
2714
3418
|
e.dataTransfer.effectAllowed = "move";
|
|
2715
3419
|
e.dataTransfer.setData(
|
|
2716
3420
|
"application/json",
|
|
@@ -2782,7 +3486,9 @@ function Cell({
|
|
|
2782
3486
|
onDeleteContent,
|
|
2783
3487
|
onContextMenu,
|
|
2784
3488
|
preview,
|
|
2785
|
-
C
|
|
3489
|
+
C,
|
|
3490
|
+
dragAsRow = false,
|
|
3491
|
+
beginRowDrag
|
|
2786
3492
|
}) {
|
|
2787
3493
|
const [insertAt, setInsertAt] = (0, import_react.useState)(null);
|
|
2788
3494
|
const cellRef = (0, import_react.useRef)(null);
|
|
@@ -2797,6 +3503,14 @@ function Cell({
|
|
|
2797
3503
|
return els.length;
|
|
2798
3504
|
}, [blocks.length]);
|
|
2799
3505
|
const handleDragOver = (e) => {
|
|
3506
|
+
try {
|
|
3507
|
+
const raw = e.dataTransfer.getData("application/json");
|
|
3508
|
+
if (raw) {
|
|
3509
|
+
const data = JSON.parse(raw);
|
|
3510
|
+
if (data.moveRowId) return;
|
|
3511
|
+
}
|
|
3512
|
+
} catch {
|
|
3513
|
+
}
|
|
2800
3514
|
e.preventDefault();
|
|
2801
3515
|
e.stopPropagation();
|
|
2802
3516
|
setInsertAt(calcIdx(e.clientY));
|
|
@@ -2812,6 +3526,7 @@ function Cell({
|
|
|
2812
3526
|
setInsertAt(null);
|
|
2813
3527
|
try {
|
|
2814
3528
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
3529
|
+
if (data.moveRowId) return;
|
|
2815
3530
|
if (data.contentType) {
|
|
2816
3531
|
onDropContent(rowId, cellIdx, { kind: "new", contentType: data.contentType, insertAt: idx, nested: nestedPayload });
|
|
2817
3532
|
} else if (data.moveContent) {
|
|
@@ -2891,7 +3606,9 @@ function Cell({
|
|
|
2891
3606
|
onContextMenu,
|
|
2892
3607
|
preview,
|
|
2893
3608
|
C,
|
|
2894
|
-
Line
|
|
3609
|
+
Line,
|
|
3610
|
+
dragAsRow: dragAsRow && blocks.length === 1 && ci === 0,
|
|
3611
|
+
beginRowDrag
|
|
2895
3612
|
}
|
|
2896
3613
|
) }, cb.id))
|
|
2897
3614
|
]
|
|
@@ -2912,7 +3629,8 @@ function LayoutRow({
|
|
|
2912
3629
|
onLayoutContextMenu,
|
|
2913
3630
|
setSelectedRowId,
|
|
2914
3631
|
preview = false,
|
|
2915
|
-
|
|
3632
|
+
rootContentRow = false,
|
|
3633
|
+
beginRowDrag,
|
|
2916
3634
|
C
|
|
2917
3635
|
}) {
|
|
2918
3636
|
const rowStyle = {
|
|
@@ -2944,16 +3662,9 @@ function LayoutRow({
|
|
|
2944
3662
|
onContextMenu: preview || !onLayoutContextMenu ? void 0 : (e) => {
|
|
2945
3663
|
onLayoutContextMenu(e, row.id);
|
|
2946
3664
|
},
|
|
2947
|
-
|
|
2948
|
-
onDragStart: rowDrag?.onDragStart,
|
|
2949
|
-
onDragEnd: rowDrag?.onDragEnd,
|
|
2950
|
-
title: rowDrag?.draggable ? "Click row \xB7 drag to reorder" : void 0,
|
|
2951
|
-
style: {
|
|
2952
|
-
...rowStyle,
|
|
2953
|
-
...rowDrag?.draggable ? { cursor: "grab" } : {}
|
|
2954
|
-
},
|
|
3665
|
+
style: rowStyle,
|
|
2955
3666
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: row.gap }, children: row.cells.map((cellBlocks, ci) => {
|
|
2956
|
-
const cS = row
|
|
3667
|
+
const cS = getColumnPropsAt(row, ci);
|
|
2957
3668
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2958
3669
|
"div",
|
|
2959
3670
|
{
|
|
@@ -2989,7 +3700,9 @@ function LayoutRow({
|
|
|
2989
3700
|
onDeleteContent,
|
|
2990
3701
|
onContextMenu,
|
|
2991
3702
|
preview,
|
|
2992
|
-
C
|
|
3703
|
+
C,
|
|
3704
|
+
dragAsRow: rootContentRow && ci === 0,
|
|
3705
|
+
beginRowDrag
|
|
2993
3706
|
}
|
|
2994
3707
|
)
|
|
2995
3708
|
},
|
|
@@ -3006,10 +3719,11 @@ var CanvasRow = ({
|
|
|
3006
3719
|
selectedRowId,
|
|
3007
3720
|
selContentMeta: rowSelContentMeta,
|
|
3008
3721
|
dragOver,
|
|
3722
|
+
draggingRowId,
|
|
3009
3723
|
C,
|
|
3010
3724
|
setDragOver,
|
|
3011
3725
|
handleRowDrop,
|
|
3012
|
-
|
|
3726
|
+
setRowDrag,
|
|
3013
3727
|
setSelectedRowId,
|
|
3014
3728
|
setSelContentId,
|
|
3015
3729
|
setSelMeta,
|
|
@@ -3022,54 +3736,161 @@ var CanvasRow = ({
|
|
|
3022
3736
|
}) => {
|
|
3023
3737
|
const rowSelected = selectedRowId === row.id;
|
|
3024
3738
|
const rid = (s) => editorId ? `${editorId}-${s}` : void 0;
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3739
|
+
const dropH = dragOver === ri || dragOver === ri + 1 ? 28 : 10;
|
|
3740
|
+
const rowBodyRef = (0, import_react.useRef)(null);
|
|
3741
|
+
const rootContentRow = isRootContentRow(row);
|
|
3742
|
+
const beginRowDrag = (0, import_react.useCallback)(
|
|
3743
|
+
(e) => {
|
|
3744
|
+
e.stopPropagation();
|
|
3745
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3746
|
+
e.dataTransfer.setData(
|
|
3747
|
+
"application/json",
|
|
3748
|
+
JSON.stringify({ moveRowId: row.id })
|
|
3749
|
+
);
|
|
3750
|
+
setRowDrag(row.id);
|
|
3751
|
+
},
|
|
3752
|
+
[row.id, setRowDrag]
|
|
3753
|
+
);
|
|
3754
|
+
const rowDragOver = (0, import_react.useCallback)(
|
|
3755
|
+
(e) => {
|
|
3756
|
+
if (!draggingRowId || draggingRowId === row.id) return;
|
|
3757
|
+
e.preventDefault();
|
|
3758
|
+
e.stopPropagation();
|
|
3759
|
+
const el = rowBodyRef.current;
|
|
3760
|
+
if (!el) {
|
|
3761
|
+
setDragOver(ri);
|
|
3762
|
+
return;
|
|
3040
3763
|
}
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
onSelectContent: selectContent,
|
|
3057
|
-
onDropContent: dropContent,
|
|
3058
|
-
onDeleteContent: deleteContent,
|
|
3059
|
-
onContextMenu: handleContextMenu,
|
|
3060
|
-
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3061
|
-
rowDrag: {
|
|
3062
|
-
draggable: true,
|
|
3063
|
-
onDragStart: (e) => {
|
|
3064
|
-
e.stopPropagation();
|
|
3065
|
-
setDraggingRowId(row.id);
|
|
3066
|
-
},
|
|
3067
|
-
onDragEnd: () => setDraggingRowId(null)
|
|
3068
|
-
},
|
|
3069
|
-
C
|
|
3764
|
+
const rect = el.getBoundingClientRect();
|
|
3765
|
+
setDragOver(e.clientY < rect.top + rect.height / 2 ? ri : ri + 1);
|
|
3766
|
+
},
|
|
3767
|
+
[draggingRowId, row.id, ri, setDragOver]
|
|
3768
|
+
);
|
|
3769
|
+
const rowDrop = (0, import_react.useCallback)(
|
|
3770
|
+
(e) => {
|
|
3771
|
+
if (!draggingRowId) return;
|
|
3772
|
+
e.preventDefault();
|
|
3773
|
+
e.stopPropagation();
|
|
3774
|
+
const el = rowBodyRef.current;
|
|
3775
|
+
let targetIdx = ri;
|
|
3776
|
+
if (el) {
|
|
3777
|
+
const rect = el.getBoundingClientRect();
|
|
3778
|
+
targetIdx = e.clientY < rect.top + rect.height / 2 ? ri : ri + 1;
|
|
3070
3779
|
}
|
|
3071
|
-
|
|
3072
|
-
|
|
3780
|
+
handleRowDrop(targetIdx, e);
|
|
3781
|
+
},
|
|
3782
|
+
[draggingRowId, ri, handleRowDrop]
|
|
3783
|
+
);
|
|
3784
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3785
|
+
"div",
|
|
3786
|
+
{
|
|
3787
|
+
id: rid(`canvas-row-${row.id}`),
|
|
3788
|
+
style: { display: "flex", alignItems: "stretch", gap: 0, margin: "2px 0" },
|
|
3789
|
+
children: [
|
|
3790
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3791
|
+
"div",
|
|
3792
|
+
{
|
|
3793
|
+
draggable: true,
|
|
3794
|
+
title: "Drag to reorder section",
|
|
3795
|
+
onDragStart: beginRowDrag,
|
|
3796
|
+
onDragEnd: () => setRowDrag(null),
|
|
3797
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3798
|
+
onClick: (e) => {
|
|
3799
|
+
e.stopPropagation();
|
|
3800
|
+
setSelectedRowId(row.id);
|
|
3801
|
+
setSelContentId(null);
|
|
3802
|
+
setSelMeta(null);
|
|
3803
|
+
},
|
|
3804
|
+
onContextMenu: (e) => {
|
|
3805
|
+
e.preventDefault();
|
|
3806
|
+
e.stopPropagation();
|
|
3807
|
+
handleLayoutContextMenu(e, row.id);
|
|
3808
|
+
},
|
|
3809
|
+
style: {
|
|
3810
|
+
width: 20,
|
|
3811
|
+
flexShrink: 0,
|
|
3812
|
+
cursor: "grab",
|
|
3813
|
+
display: "flex",
|
|
3814
|
+
alignItems: "center",
|
|
3815
|
+
justifyContent: "center",
|
|
3816
|
+
color: rowSelected ? C.accent : C.muted,
|
|
3817
|
+
fontSize: 12,
|
|
3818
|
+
fontWeight: 700,
|
|
3819
|
+
letterSpacing: -3,
|
|
3820
|
+
userSelect: "none",
|
|
3821
|
+
opacity: rowSelected ? 1 : 0.5
|
|
3822
|
+
},
|
|
3823
|
+
children: "\u22EE\u22EE"
|
|
3824
|
+
}
|
|
3825
|
+
),
|
|
3826
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3827
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3828
|
+
"div",
|
|
3829
|
+
{
|
|
3830
|
+
id: rid(`row-drop-before-${ri}`),
|
|
3831
|
+
onDragOver: (e) => {
|
|
3832
|
+
e.preventDefault();
|
|
3833
|
+
e.stopPropagation();
|
|
3834
|
+
setDragOver(ri);
|
|
3835
|
+
},
|
|
3836
|
+
onDragLeave: () => setDragOver(null),
|
|
3837
|
+
onDrop: (e) => handleRowDrop(ri, e),
|
|
3838
|
+
style: {
|
|
3839
|
+
height: dropH,
|
|
3840
|
+
background: dragOver === ri ? `${C.accent}25` : "transparent",
|
|
3841
|
+
border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
3842
|
+
borderRadius: 4,
|
|
3843
|
+
transition: "all .12s",
|
|
3844
|
+
boxSizing: "border-box"
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
),
|
|
3848
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3849
|
+
"div",
|
|
3850
|
+
{
|
|
3851
|
+
ref: rowBodyRef,
|
|
3852
|
+
id: rid(`row-body-${row.id}`),
|
|
3853
|
+
onDragOver: rowDragOver,
|
|
3854
|
+
onDrop: rowDrop,
|
|
3855
|
+
style: {
|
|
3856
|
+
position: "relative",
|
|
3857
|
+
width: "100%",
|
|
3858
|
+
minWidth: 0,
|
|
3859
|
+
boxSizing: "border-box",
|
|
3860
|
+
outline: draggingRowId && draggingRowId !== row.id && (dragOver === ri || dragOver === ri + 1) ? `2px dashed ${C.accent}` : void 0,
|
|
3861
|
+
outlineOffset: 2,
|
|
3862
|
+
borderRadius: 6
|
|
3863
|
+
},
|
|
3864
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: rid(`row-layout-wrap-${row.id}`), style: { width: "100%", minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3865
|
+
LayoutRow,
|
|
3866
|
+
{
|
|
3867
|
+
editorId,
|
|
3868
|
+
row,
|
|
3869
|
+
selected: rowSelected,
|
|
3870
|
+
setSelectedRowId,
|
|
3871
|
+
onClick: () => {
|
|
3872
|
+
setSelectedRowId(row.id);
|
|
3873
|
+
setSelContentId(null);
|
|
3874
|
+
setSelMeta(null);
|
|
3875
|
+
},
|
|
3876
|
+
selectedContentKey: selContentId,
|
|
3877
|
+
selContentMeta: rowSelContentMeta,
|
|
3878
|
+
onSelectContent: selectContent,
|
|
3879
|
+
onDropContent: dropContent,
|
|
3880
|
+
onDeleteContent: deleteContent,
|
|
3881
|
+
onContextMenu: handleContextMenu,
|
|
3882
|
+
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3883
|
+
rootContentRow,
|
|
3884
|
+
beginRowDrag,
|
|
3885
|
+
C
|
|
3886
|
+
}
|
|
3887
|
+
) })
|
|
3888
|
+
}
|
|
3889
|
+
)
|
|
3890
|
+
] })
|
|
3891
|
+
]
|
|
3892
|
+
}
|
|
3893
|
+
);
|
|
3073
3894
|
};
|
|
3074
3895
|
|
|
3075
3896
|
// src/ReactEmailEditor.tsx
|
|
@@ -3150,6 +3971,10 @@ function paddingToCss(pad3, fallback) {
|
|
|
3150
3971
|
var FONT_SIZE_PX = [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28, 32, 36, 42, 48, 56, 64, 72];
|
|
3151
3972
|
var LINE_HEIGHT_OPTS = ["1", "1.15", "1.25", "1.5", "1.65", "1.75", "2", "2.5"];
|
|
3152
3973
|
var LETTER_SPACING_OPTS = ["-0.5px", "0px", "0.5px", "1px", "1.5px", "2px", "3px", "4px"];
|
|
3974
|
+
function rgbChannelToHex(n) {
|
|
3975
|
+
const v = Math.max(0, Math.min(255, Math.round(n)));
|
|
3976
|
+
return v.toString(16).padStart(2, "0");
|
|
3977
|
+
}
|
|
3153
3978
|
function hexForColorInput(color) {
|
|
3154
3979
|
if (!color || typeof color !== "string") return "#000000";
|
|
3155
3980
|
const c = color.trim();
|
|
@@ -3162,8 +3987,34 @@ function hexForColorInput(color) {
|
|
|
3162
3987
|
}
|
|
3163
3988
|
return c.slice(0, 7);
|
|
3164
3989
|
}
|
|
3990
|
+
const rgb = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
|
|
3991
|
+
if (rgb) {
|
|
3992
|
+
return `#${rgbChannelToHex(+rgb[1])}${rgbChannelToHex(+rgb[2])}${rgbChannelToHex(+rgb[3])}`;
|
|
3993
|
+
}
|
|
3165
3994
|
return "#000000";
|
|
3166
3995
|
}
|
|
3996
|
+
function textStyleColorFromEditor(editor) {
|
|
3997
|
+
const ts = editor.getAttributes("textStyle");
|
|
3998
|
+
if (ts.color && String(ts.color).trim()) return String(ts.color).trim();
|
|
3999
|
+
const stored = editor.state.storedMarks;
|
|
4000
|
+
if (stored) {
|
|
4001
|
+
for (const m of stored) {
|
|
4002
|
+
if (m.type.name === "textStyle" && m.attrs.color) {
|
|
4003
|
+
return String(m.attrs.color).trim();
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
const { from, to } = editor.state.selection;
|
|
4008
|
+
if (from === to) return null;
|
|
4009
|
+
const domAt = editor.view.domAtPos(from);
|
|
4010
|
+
let el = domAt.node.nodeType === Node.TEXT_NODE ? domAt.node.parentElement : domAt.node;
|
|
4011
|
+
while (el && el !== editor.view.dom) {
|
|
4012
|
+
const inline = el.style?.color?.trim();
|
|
4013
|
+
if (inline) return inline;
|
|
4014
|
+
el = el.parentElement;
|
|
4015
|
+
}
|
|
4016
|
+
return null;
|
|
4017
|
+
}
|
|
3167
4018
|
function parsePxSize(raw) {
|
|
3168
4019
|
if (!raw) return "";
|
|
3169
4020
|
const s = String(raw).trim();
|
|
@@ -3230,13 +4081,22 @@ function toolbarIconBtn(C, active) {
|
|
|
3230
4081
|
}
|
|
3231
4082
|
function ToolbarTypographyControls({ editor, C }) {
|
|
3232
4083
|
const sel = toolbarSelect(C);
|
|
3233
|
-
const ts =
|
|
4084
|
+
const { ts, align, textColor } = (0, import_react3.useEditorState)({
|
|
4085
|
+
editor,
|
|
4086
|
+
selector: ({ editor: ed }) => {
|
|
4087
|
+
const attrs = ed.getAttributes("textStyle");
|
|
4088
|
+
return {
|
|
4089
|
+
ts: attrs,
|
|
4090
|
+
align: currentBlockTextAlign(ed),
|
|
4091
|
+
textColor: textStyleColorFromEditor(ed)
|
|
4092
|
+
};
|
|
4093
|
+
}
|
|
4094
|
+
});
|
|
3234
4095
|
const fontFamily = ts.fontFamily || "";
|
|
3235
4096
|
const sizeVal = parsePxSize(ts.fontSize);
|
|
3236
4097
|
const lhVal = normalizeLineHeightAttr(ts.lineHeight);
|
|
3237
4098
|
const lsVal = normalizeLetterSpacingAttr(ts.letterSpacing);
|
|
3238
4099
|
const lsSelectValue = LETTER_SPACING_OPTS.includes(lsVal) ? lsVal : lsVal || "";
|
|
3239
|
-
const align = currentBlockTextAlign(editor);
|
|
3240
4100
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
3241
4101
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3242
4102
|
"select",
|
|
@@ -3319,9 +4179,10 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3319
4179
|
{
|
|
3320
4180
|
type: "color",
|
|
3321
4181
|
title: "Text color",
|
|
3322
|
-
value: hexForColorInput(ts.color),
|
|
4182
|
+
value: hexForColorInput(textColor ?? ts.color),
|
|
3323
4183
|
onChange: (e) => {
|
|
3324
|
-
|
|
4184
|
+
const hex = e.target.value;
|
|
4185
|
+
void editor.chain().focus().setColor(hex).run();
|
|
3325
4186
|
},
|
|
3326
4187
|
style: {
|
|
3327
4188
|
width: 28,
|
|
@@ -3694,7 +4555,7 @@ function TextRichEditor({
|
|
|
3694
4555
|
}
|
|
3695
4556
|
}),
|
|
3696
4557
|
import_extension_text_style.TextStyle,
|
|
3697
|
-
import_extension_text_style.Color,
|
|
4558
|
+
import_extension_text_style.Color.configure({ types: ["textStyle"] }),
|
|
3698
4559
|
import_extension_text_style.FontFamily,
|
|
3699
4560
|
import_extension_text_style.FontSize,
|
|
3700
4561
|
import_extension_text_style.LineHeight,
|
|
@@ -4871,11 +5732,141 @@ function BlockSurfaceBgInspector({
|
|
|
4871
5732
|
] }) : null
|
|
4872
5733
|
] });
|
|
4873
5734
|
}
|
|
5735
|
+
function HtmlBlockRichPanel({
|
|
5736
|
+
block,
|
|
5737
|
+
onChange,
|
|
5738
|
+
C
|
|
5739
|
+
}) {
|
|
5740
|
+
const p = block.props;
|
|
5741
|
+
const { IS } = useIS(C);
|
|
5742
|
+
const [mode, setMode] = (0, import_react4.useState)("visual");
|
|
5743
|
+
const [htmlDraft, setHtmlDraft] = (0, import_react4.useState)("");
|
|
5744
|
+
(0, import_react4.useEffect)(() => {
|
|
5745
|
+
setMode("visual");
|
|
5746
|
+
}, [block.id]);
|
|
5747
|
+
const body = normalizeRichHtmlForStorage(typeof block.content === "string" ? block.content : "");
|
|
5748
|
+
const applyHtml = (html) => {
|
|
5749
|
+
const norm = normalizeRichHtmlForStorage(html);
|
|
5750
|
+
onChange({ ...block, content: norm, props: { ...p, content: norm } });
|
|
5751
|
+
};
|
|
5752
|
+
const tabBtn = (active) => ({
|
|
5753
|
+
flex: 1,
|
|
5754
|
+
padding: "7px 10px",
|
|
5755
|
+
borderRadius: 6,
|
|
5756
|
+
border: `1px solid ${C.border}`,
|
|
5757
|
+
background: active ? C.accent : C.surface,
|
|
5758
|
+
color: active ? "#ffffff" : C.text,
|
|
5759
|
+
fontSize: 12,
|
|
5760
|
+
fontWeight: 700,
|
|
5761
|
+
cursor: "pointer"
|
|
5762
|
+
});
|
|
5763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5764
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
5765
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5766
|
+
"button",
|
|
5767
|
+
{
|
|
5768
|
+
type: "button",
|
|
5769
|
+
style: tabBtn(mode === "visual"),
|
|
5770
|
+
onClick: () => {
|
|
5771
|
+
if (mode === "html") applyHtml(htmlDraft);
|
|
5772
|
+
setMode("visual");
|
|
5773
|
+
},
|
|
5774
|
+
children: "Rich text"
|
|
5775
|
+
}
|
|
5776
|
+
),
|
|
5777
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5778
|
+
"button",
|
|
5779
|
+
{
|
|
5780
|
+
type: "button",
|
|
5781
|
+
style: tabBtn(mode === "html"),
|
|
5782
|
+
onClick: () => {
|
|
5783
|
+
setHtmlDraft(body);
|
|
5784
|
+
setMode("html");
|
|
5785
|
+
},
|
|
5786
|
+
children: "HTML"
|
|
5787
|
+
}
|
|
5788
|
+
)
|
|
5789
|
+
] }),
|
|
5790
|
+
mode === "visual" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5791
|
+
TextRichEditor,
|
|
5792
|
+
{
|
|
5793
|
+
value: body,
|
|
5794
|
+
onChange: applyHtml,
|
|
5795
|
+
typography: {
|
|
5796
|
+
fontSize: p.fontSize,
|
|
5797
|
+
color: p.color,
|
|
5798
|
+
align: p.align,
|
|
5799
|
+
fontFamily: p.fontFamily,
|
|
5800
|
+
lineHeight: p.lineHeight,
|
|
5801
|
+
letterSpacing: p.letterSpacing,
|
|
5802
|
+
padding: p.padding
|
|
5803
|
+
},
|
|
5804
|
+
placeholder: "Write your rich content\u2026",
|
|
5805
|
+
headerTitle: "Rich editor",
|
|
5806
|
+
C
|
|
5807
|
+
},
|
|
5808
|
+
block.id
|
|
5809
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5810
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5811
|
+
"textarea",
|
|
5812
|
+
{
|
|
5813
|
+
spellCheck: false,
|
|
5814
|
+
value: htmlDraft,
|
|
5815
|
+
onChange: (e) => setHtmlDraft(e.target.value),
|
|
5816
|
+
placeholder: "<p>Your HTML\u2026</p>",
|
|
5817
|
+
style: {
|
|
5818
|
+
...IS,
|
|
5819
|
+
minHeight: 220,
|
|
5820
|
+
resize: "vertical",
|
|
5821
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
5822
|
+
fontSize: 12,
|
|
5823
|
+
lineHeight: 1.45
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
),
|
|
5827
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 10, color: C.muted, lineHeight: 1.45 }, children: [
|
|
5828
|
+
"Raw HTML. Switch to ",
|
|
5829
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Rich text" }),
|
|
5830
|
+
" to apply and preview in the visual editor."
|
|
5831
|
+
] }),
|
|
5832
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5833
|
+
"button",
|
|
5834
|
+
{
|
|
5835
|
+
type: "button",
|
|
5836
|
+
onClick: () => applyHtml(htmlDraft),
|
|
5837
|
+
style: {
|
|
5838
|
+
alignSelf: "flex-start",
|
|
5839
|
+
background: C.surface,
|
|
5840
|
+
border: `1px solid ${C.border}`,
|
|
5841
|
+
color: C.accent,
|
|
5842
|
+
borderRadius: 5,
|
|
5843
|
+
cursor: "pointer",
|
|
5844
|
+
fontSize: 11,
|
|
5845
|
+
padding: "6px 12px",
|
|
5846
|
+
fontWeight: 600
|
|
5847
|
+
},
|
|
5848
|
+
children: "Apply (normalize)"
|
|
5849
|
+
}
|
|
5850
|
+
)
|
|
5851
|
+
] })
|
|
5852
|
+
] }) });
|
|
5853
|
+
}
|
|
4874
5854
|
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4875
5855
|
const { IS, CI } = useIS(C);
|
|
4876
5856
|
const imageFileRef = (0, import_react4.useRef)(null);
|
|
4877
5857
|
const p = block.props;
|
|
4878
|
-
const set = (k, v) =>
|
|
5858
|
+
const set = (k, v) => {
|
|
5859
|
+
if (block.type === "html" && k === "content") {
|
|
5860
|
+
onChange({ ...block, content: v, props: { ...p, content: v } });
|
|
5861
|
+
return;
|
|
5862
|
+
}
|
|
5863
|
+
if (block.type === "html") {
|
|
5864
|
+
const body = typeof block.content === "string" ? block.content : typeof p.content === "string" ? p.content : "";
|
|
5865
|
+
onChange({ ...block, props: { ...p, [k]: v, content: body } });
|
|
5866
|
+
return;
|
|
5867
|
+
}
|
|
5868
|
+
onChange({ ...block, props: { ...p, [k]: v } });
|
|
5869
|
+
};
|
|
4879
5870
|
const Num = (k, lbl, mn = 0, mx = 300) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: lbl, C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", style: IS, value: p[k] ?? 0, min: mn, max: mx, onChange: (e) => set(k, +e.target.value) }) });
|
|
4880
5871
|
const Txt = (k, lbl, multi = false) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: lbl, C, children: multi ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { style: { ...IS, minHeight: 68, resize: "vertical" }, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", style: IS, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) });
|
|
4881
5872
|
const Col = (k, lbl) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: lbl, C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
@@ -4969,25 +5960,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4969
5960
|
] });
|
|
4970
5961
|
case "html":
|
|
4971
5962
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
4972
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4973
|
-
TextRichEditor,
|
|
4974
|
-
{
|
|
4975
|
-
value: normalizeRichHtmlForStorage(p.content),
|
|
4976
|
-
onChange: (html) => set("content", html),
|
|
4977
|
-
typography: {
|
|
4978
|
-
fontSize: p.fontSize,
|
|
4979
|
-
color: p.color,
|
|
4980
|
-
align: p.align,
|
|
4981
|
-
fontFamily: p.fontFamily,
|
|
4982
|
-
lineHeight: p.lineHeight,
|
|
4983
|
-
letterSpacing: p.letterSpacing,
|
|
4984
|
-
padding: p.padding
|
|
4985
|
-
},
|
|
4986
|
-
placeholder: "Write your rich content\u2026",
|
|
4987
|
-
headerTitle: "Rich editor",
|
|
4988
|
-
C
|
|
4989
|
-
}
|
|
4990
|
-
) }, block.id),
|
|
5963
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HtmlBlockRichPanel, { block, onChange, C }),
|
|
4991
5964
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
4992
5965
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
|
|
4993
5966
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
|
|
@@ -5007,8 +5980,25 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5007
5980
|
"Object position"
|
|
5008
5981
|
] }), C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { style: IS, value: p.objectPosition || "center", onChange: (e) => set("objectPosition", e.target.value), children: ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"].map((v) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: v, children: v }, v)) }) }),
|
|
5009
5982
|
Txt("alt", "Alt Text"),
|
|
5010
|
-
|
|
5011
|
-
|
|
5983
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Link image", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: { display: "flex", alignItems: "center", gap: 7, cursor: "pointer" }, children: [
|
|
5984
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5985
|
+
"input",
|
|
5986
|
+
{
|
|
5987
|
+
type: "checkbox",
|
|
5988
|
+
checked: !!(p.linkEnabled ?? (p.link && String(p.link).trim())),
|
|
5989
|
+
onChange: (e) => {
|
|
5990
|
+
if (e.target.checked) set("linkEnabled", true);
|
|
5991
|
+
else onChange({ ...block, props: { ...p, linkEnabled: false, link: "" } });
|
|
5992
|
+
},
|
|
5993
|
+
style: { width: 15, height: 15, accentColor: C.accent }
|
|
5994
|
+
}
|
|
5995
|
+
),
|
|
5996
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p.linkEnabled ?? (p.link && String(p.link).trim()) ? "On" : "Off" })
|
|
5997
|
+
] }) }),
|
|
5998
|
+
p.linkEnabled ?? (p.link && String(p.link).trim()) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5999
|
+
Txt("link", "Link URL"),
|
|
6000
|
+
Sel("linkTarget", "Open In", ["_blank", "_self"])
|
|
6001
|
+
] }) : null,
|
|
5012
6002
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
|
|
5013
6003
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
|
|
5014
6004
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -5358,7 +6348,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5358
6348
|
bgPosition: p.bgPosition ?? "center",
|
|
5359
6349
|
bgGradient: p.bgGradient ?? null,
|
|
5360
6350
|
cells: p.cells || [],
|
|
5361
|
-
|
|
6351
|
+
columns: getColumns(p)
|
|
5362
6352
|
},
|
|
5363
6353
|
onChange: (row) => onChange({
|
|
5364
6354
|
...block,
|
|
@@ -5376,7 +6366,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5376
6366
|
bgGradient: row.bgGradient,
|
|
5377
6367
|
ratios: [...row.ratios || [1]],
|
|
5378
6368
|
cells: row.cells || [],
|
|
5379
|
-
|
|
6369
|
+
columns: row.columns ?? getColumns(row)
|
|
5380
6370
|
}
|
|
5381
6371
|
}),
|
|
5382
6372
|
onClose,
|
|
@@ -5395,8 +6385,8 @@ function inferLayoutRowBgStep(r) {
|
|
|
5395
6385
|
if (String(r.bgColor || "").trim()) return "solid";
|
|
5396
6386
|
return null;
|
|
5397
6387
|
}
|
|
5398
|
-
function inferColumnBgStep(
|
|
5399
|
-
const cs = (
|
|
6388
|
+
function inferColumnBgStep(container, selCol) {
|
|
6389
|
+
const cs = getColumnPropsAt(container, selCol);
|
|
5400
6390
|
if (cs.bgGradient) return "gradient";
|
|
5401
6391
|
if (String(cs.bgImage || "").trim()) return "image";
|
|
5402
6392
|
if (String(cs.bgColor || "").trim()) return "solid";
|
|
@@ -5408,6 +6398,10 @@ function LayoutRowEditor({
|
|
|
5408
6398
|
onClose,
|
|
5409
6399
|
onUpload,
|
|
5410
6400
|
initialCol = null,
|
|
6401
|
+
rowIndex = -1,
|
|
6402
|
+
rowCount = 1,
|
|
6403
|
+
onMoveUp,
|
|
6404
|
+
onMoveDown,
|
|
5411
6405
|
C,
|
|
5412
6406
|
/** Shown under the inspector header when editing a Layout block */
|
|
5413
6407
|
customizationHint
|
|
@@ -5420,8 +6414,8 @@ function LayoutRowEditor({
|
|
|
5420
6414
|
const [colBgPickerMode, setColBgPickerMode] = (0, import_react4.useState)(null);
|
|
5421
6415
|
const inferredRowBg = (0, import_react4.useMemo)(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5422
6416
|
const inferredColBg = (0, import_react4.useMemo)(
|
|
5423
|
-
() => inferColumnBgStep(row
|
|
5424
|
-
[row.columnStyles, selCol]
|
|
6417
|
+
() => inferColumnBgStep(row, selCol),
|
|
6418
|
+
[row.columns, row.columnStyles, selCol]
|
|
5425
6419
|
);
|
|
5426
6420
|
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5427
6421
|
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
@@ -5460,13 +6454,12 @@ function LayoutRowEditor({
|
|
|
5460
6454
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5461
6455
|
};
|
|
5462
6456
|
const updCol = (i, upd) => {
|
|
5463
|
-
const
|
|
5464
|
-
|
|
5465
|
-
set("columnStyles", styles);
|
|
6457
|
+
const columns = patchColumnPropsAt(row, i, upd);
|
|
6458
|
+
onChange(withColumnsOnly(row, columns));
|
|
5466
6459
|
};
|
|
5467
6460
|
const setColBgMode = (mode) => {
|
|
5468
6461
|
setColBgPickerMode(mode);
|
|
5469
|
-
const cs = (row
|
|
6462
|
+
const cs = getColumnPropsAt(row, selCol);
|
|
5470
6463
|
if (mode === "solid") {
|
|
5471
6464
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
5472
6465
|
return;
|
|
@@ -5498,7 +6491,51 @@ function LayoutRowEditor({
|
|
|
5498
6491
|
set("bgGradient", { angle: 135, colors: ["#0ea5e9", "#6366f1", "#ec4899"], stops: [0, 50, 100] });
|
|
5499
6492
|
}
|
|
5500
6493
|
};
|
|
6494
|
+
const canMoveUp = rowIndex > 0;
|
|
6495
|
+
const canMoveDown = rowIndex >= 0 && rowIndex < rowCount - 1;
|
|
5501
6496
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
6497
|
+
onMoveUp && onMoveDown ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [
|
|
6498
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
6499
|
+
"button",
|
|
6500
|
+
{
|
|
6501
|
+
type: "button",
|
|
6502
|
+
disabled: !canMoveUp,
|
|
6503
|
+
onClick: onMoveUp,
|
|
6504
|
+
style: {
|
|
6505
|
+
flex: 1,
|
|
6506
|
+
padding: "8px 10px",
|
|
6507
|
+
fontSize: 12,
|
|
6508
|
+
fontWeight: 600,
|
|
6509
|
+
borderRadius: 8,
|
|
6510
|
+
border: `1px solid ${C.border}`,
|
|
6511
|
+
background: canMoveUp ? C.surface : C.canvas,
|
|
6512
|
+
color: canMoveUp ? C.text : C.muted,
|
|
6513
|
+
cursor: canMoveUp ? "pointer" : "not-allowed"
|
|
6514
|
+
},
|
|
6515
|
+
children: "\u2191 Move section up"
|
|
6516
|
+
}
|
|
6517
|
+
),
|
|
6518
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
6519
|
+
"button",
|
|
6520
|
+
{
|
|
6521
|
+
type: "button",
|
|
6522
|
+
disabled: !canMoveDown,
|
|
6523
|
+
onClick: onMoveDown,
|
|
6524
|
+
style: {
|
|
6525
|
+
flex: 1,
|
|
6526
|
+
padding: "8px 10px",
|
|
6527
|
+
fontSize: 12,
|
|
6528
|
+
fontWeight: 600,
|
|
6529
|
+
borderRadius: 8,
|
|
6530
|
+
border: `1px solid ${C.border}`,
|
|
6531
|
+
background: canMoveDown ? C.surface : C.canvas,
|
|
6532
|
+
color: canMoveDown ? C.text : C.muted,
|
|
6533
|
+
cursor: canMoveDown ? "pointer" : "not-allowed"
|
|
6534
|
+
},
|
|
6535
|
+
children: "\u2193 Move section down"
|
|
6536
|
+
}
|
|
6537
|
+
)
|
|
6538
|
+
] }) : null,
|
|
5502
6539
|
customizationHint ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5503
6540
|
"div",
|
|
5504
6541
|
{
|
|
@@ -5648,15 +6685,15 @@ function LayoutRowEditor({
|
|
|
5648
6685
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5649
6686
|
ColorSwatchGrid,
|
|
5650
6687
|
{
|
|
5651
|
-
value: (row
|
|
6688
|
+
value: getColumnPropsAt(row, selCol)?.bgColor || "",
|
|
5652
6689
|
C,
|
|
5653
6690
|
compact: true,
|
|
5654
6691
|
onPick: (hex) => updCol(selCol, { bgColor: hex === "#ffffff" ? "" : hex })
|
|
5655
6692
|
}
|
|
5656
6693
|
),
|
|
5657
6694
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 5, alignItems: "center" }, children: [
|
|
5658
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "color", style: CI, value: (row
|
|
5659
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: (row
|
|
6695
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "color", style: CI, value: getColumnPropsAt(row, selCol)?.bgColor || "#ffffff", onChange: (e) => updCol(selCol, { bgColor: e.target.value === "#ffffff" ? "" : e.target.value }) }),
|
|
6696
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: getColumnPropsAt(row, selCol)?.bgColor || "", onChange: (e) => updCol(selCol, { bgColor: e.target.value }) })
|
|
5660
6697
|
] })
|
|
5661
6698
|
] }) }) : null,
|
|
5662
6699
|
colBgUiStep === "image" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
@@ -5664,7 +6701,7 @@ function LayoutRowEditor({
|
|
|
5664
6701
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Image, { size: 13, strokeWidth: 2.25, "aria-hidden": true }),
|
|
5665
6702
|
"bg image"
|
|
5666
6703
|
] }), C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
|
|
5667
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", style: IS, placeholder: "https://\u2026", value: (row
|
|
6704
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", style: IS, placeholder: "https://\u2026", value: getColumnPropsAt(row, selCol)?.bgImage || "", onChange: (e) => updCol(selCol, { bgImage: e.target.value }) }),
|
|
5668
6705
|
onUpload && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 0 }, children: [
|
|
5669
6706
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5670
6707
|
"input",
|
|
@@ -5702,18 +6739,18 @@ function LayoutRowEditor({
|
|
|
5702
6739
|
] })
|
|
5703
6740
|
] }) }),
|
|
5704
6741
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [
|
|
5705
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "fit", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { style: IS, value: (row
|
|
6742
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "fit", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgSize || "cover", onChange: (e) => updCol(selCol, { bgSize: e.target.value }), children: [
|
|
5706
6743
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "auto", children: "Auto" }),
|
|
5707
6744
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "cover", children: "Cover" }),
|
|
5708
6745
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "contain", children: "Contain" })
|
|
5709
6746
|
] }) }) }),
|
|
5710
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "repeat", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { style: IS, value: (row
|
|
6747
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "repeat", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgRepeat || "no-repeat", onChange: (e) => updCol(selCol, { bgRepeat: e.target.value }), children: [
|
|
5711
6748
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "no-repeat", children: "No repeat" }),
|
|
5712
6749
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat", children: "Repeat" }),
|
|
5713
6750
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat-x", children: "Repeat X" }),
|
|
5714
6751
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat-y", children: "Repeat Y" })
|
|
5715
6752
|
] }) }) }),
|
|
5716
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "align", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { style: IS, value: (row
|
|
6753
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "align", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgPosition || "center", onChange: (e) => updCol(selCol, { bgPosition: e.target.value }), children: POS_OPTS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p, children: p }, p)) }) }) })
|
|
5717
6754
|
] })
|
|
5718
6755
|
] }) : null,
|
|
5719
6756
|
colBgUiStep === "gradient" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("details", { open: true, style: { marginBottom: 12 }, children: [
|
|
@@ -5722,15 +6759,15 @@ function LayoutRowEditor({
|
|
|
5722
6759
|
DynamicGradientField,
|
|
5723
6760
|
{
|
|
5724
6761
|
label: "gradient",
|
|
5725
|
-
value: (row
|
|
6762
|
+
value: getColumnPropsAt(row, selCol)?.bgGradient,
|
|
5726
6763
|
onChange: (g) => updCol(selCol, { bgGradient: g }),
|
|
5727
6764
|
defaults: { colors: ["#0ea5e9", "#6366f1", "#ec4899"] },
|
|
5728
6765
|
C
|
|
5729
6766
|
}
|
|
5730
6767
|
) })
|
|
5731
6768
|
] }) : null,
|
|
5732
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: (row
|
|
5733
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: (row
|
|
6769
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: getColumnPropsAt(row, selCol).padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
|
|
6770
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: getColumnPropsAt(row, selCol).borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
|
|
5734
6771
|
] })
|
|
5735
6772
|
] })
|
|
5736
6773
|
] });
|
|
@@ -6521,6 +7558,11 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6521
7558
|
const [selContentMeta, setSelMeta] = (0, import_react8.useState)(null);
|
|
6522
7559
|
const [dragOver, setDragOver] = (0, import_react8.useState)(null);
|
|
6523
7560
|
const [draggingRowId, setDraggingRowId] = (0, import_react8.useState)(null);
|
|
7561
|
+
const draggingRowIdRef = (0, import_react8.useRef)(null);
|
|
7562
|
+
const setRowDrag = (0, import_react8.useCallback)((id) => {
|
|
7563
|
+
draggingRowIdRef.current = id;
|
|
7564
|
+
setDraggingRowId(id);
|
|
7565
|
+
}, []);
|
|
6524
7566
|
const [history, setHistory] = (0, import_react8.useState)([]);
|
|
6525
7567
|
const [future, setFuture] = (0, import_react8.useState)([]);
|
|
6526
7568
|
const [showPreviewModal, setShowPreview] = (0, import_react8.useState)(false);
|
|
@@ -6555,10 +7597,17 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6555
7597
|
});
|
|
6556
7598
|
const [pageBgUiStep, setPageBgUiStep] = (0, import_react8.useState)(null);
|
|
6557
7599
|
const [contentBgUiStep, setContentBgUiStep] = (0, import_react8.useState)(null);
|
|
7600
|
+
const pageBgLastSolidRef = (0, import_react8.useRef)("#f1f5f9");
|
|
7601
|
+
const pageBgIsOff = !String(settings.bgColor ?? "").trim() && !String(settings.bgImage ?? "").trim() && !settings.bgGradient;
|
|
6558
7602
|
const applyPageBgMode = (mode) => {
|
|
6559
7603
|
setPageBgUiStep(mode);
|
|
6560
7604
|
if (mode === "solid") {
|
|
6561
|
-
setSettings((s) =>
|
|
7605
|
+
setSettings((s) => {
|
|
7606
|
+
const next = { ...s, bgGradient: null, bgImage: "" };
|
|
7607
|
+
const raw = typeof next.bgColor === "string" ? next.bgColor.trim() : "";
|
|
7608
|
+
if (!raw) next.bgColor = pageBgLastSolidRef.current || "#f1f5f9";
|
|
7609
|
+
return next;
|
|
7610
|
+
});
|
|
6562
7611
|
return;
|
|
6563
7612
|
}
|
|
6564
7613
|
if (mode === "image") {
|
|
@@ -6668,7 +7717,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6668
7717
|
return () => window.removeEventListener("keydown", handler);
|
|
6669
7718
|
}, [rows, history, future, selContentMeta]);
|
|
6670
7719
|
const buildApi = (0, import_react8.useCallback)(() => ({
|
|
6671
|
-
loadJson(input) {
|
|
7720
|
+
loadJson(input, options2) {
|
|
6672
7721
|
setJsonLoading(true);
|
|
6673
7722
|
requestAnimationFrame(() => {
|
|
6674
7723
|
requestAnimationFrame(() => {
|
|
@@ -6681,7 +7730,14 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6681
7730
|
return;
|
|
6682
7731
|
}
|
|
6683
7732
|
}
|
|
6684
|
-
const
|
|
7733
|
+
const coerced = coerceEmailDocumentInput(parsed);
|
|
7734
|
+
if (options2?.mode === "append") {
|
|
7735
|
+
const extra = emailDocumentToEditorRows(coerced ?? parsed);
|
|
7736
|
+
if (!extra?.length) return;
|
|
7737
|
+
setRows((prev) => [...prev, ...extra]);
|
|
7738
|
+
return;
|
|
7739
|
+
}
|
|
7740
|
+
const norm = normalizeEmailDesignInput(coerced ?? parsed);
|
|
6685
7741
|
if (!norm) return;
|
|
6686
7742
|
setRows(norm.rows);
|
|
6687
7743
|
const ns = norm.settings;
|
|
@@ -6718,6 +7774,8 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6718
7774
|
}, []);
|
|
6719
7775
|
const handleRowDrop = (targetIdx, e) => {
|
|
6720
7776
|
if (e) {
|
|
7777
|
+
e.preventDefault();
|
|
7778
|
+
e.stopPropagation();
|
|
6721
7779
|
try {
|
|
6722
7780
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
6723
7781
|
if (data.layoutPresetKey) {
|
|
@@ -6736,18 +7794,28 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6736
7794
|
} catch {
|
|
6737
7795
|
}
|
|
6738
7796
|
}
|
|
6739
|
-
|
|
7797
|
+
const moveId = draggingRowIdRef.current ?? draggingRowId ?? (() => {
|
|
7798
|
+
if (!e) return null;
|
|
7799
|
+
try {
|
|
7800
|
+
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
7801
|
+
return typeof data.moveRowId === "string" ? data.moveRowId : null;
|
|
7802
|
+
} catch {
|
|
7803
|
+
return null;
|
|
7804
|
+
}
|
|
7805
|
+
})();
|
|
7806
|
+
if (moveId) {
|
|
6740
7807
|
mutate((prev) => {
|
|
6741
|
-
const fi = prev.findIndex((r) => r.id ===
|
|
7808
|
+
const fi = prev.findIndex((r) => r.id === moveId);
|
|
6742
7809
|
if (fi === -1) return prev;
|
|
6743
7810
|
const next = [...prev];
|
|
6744
7811
|
const [m] = next.splice(fi, 1);
|
|
6745
|
-
|
|
7812
|
+
const insertAt = targetIdx > fi ? targetIdx - 1 : targetIdx;
|
|
7813
|
+
next.splice(Math.max(0, Math.min(insertAt, next.length)), 0, m);
|
|
6746
7814
|
return next;
|
|
6747
7815
|
});
|
|
6748
7816
|
}
|
|
6749
7817
|
setDragOver(null);
|
|
6750
|
-
|
|
7818
|
+
setRowDrag(null);
|
|
6751
7819
|
};
|
|
6752
7820
|
const addRow = (preset) => mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
6753
7821
|
const deleteRow = (id) => {
|
|
@@ -6877,8 +7945,27 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6877
7945
|
}
|
|
6878
7946
|
}
|
|
6879
7947
|
};
|
|
7948
|
+
const rootContentPreset = LAYOUT_PRESETS.find((p) => p.cols === 1) ?? LAYOUT_PRESETS[0];
|
|
7949
|
+
const insertRootContentBlock = (contentType) => {
|
|
7950
|
+
const { row: newRow, block: nb } = makeRootContentRow(contentType, rootContentPreset);
|
|
7951
|
+
const anchorRowId = selContentMeta?.rowId ?? selectedRowId ?? null;
|
|
7952
|
+
mutate((prev) => {
|
|
7953
|
+
if (anchorRowId) {
|
|
7954
|
+
const i = prev.findIndex((r) => r.id === anchorRowId);
|
|
7955
|
+
if (i >= 0) {
|
|
7956
|
+
const next = [...prev];
|
|
7957
|
+
next.splice(i + 1, 0, newRow);
|
|
7958
|
+
return next;
|
|
7959
|
+
}
|
|
7960
|
+
}
|
|
7961
|
+
return [...prev, newRow];
|
|
7962
|
+
});
|
|
7963
|
+
setSelectedRowId(null);
|
|
7964
|
+
setSelContentId(nb.id);
|
|
7965
|
+
setSelMeta({ rowId: newRow.id, cellIdx: 0, contentIdx: 0 });
|
|
7966
|
+
};
|
|
6880
7967
|
const insertBlockFromLibrary = (contentType) => {
|
|
6881
|
-
|
|
7968
|
+
const isLayoutType = contentType === "layout" || contentType === "nestedRow";
|
|
6882
7969
|
if (selContentMeta?.inner) {
|
|
6883
7970
|
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
6884
7971
|
dropContent(rowId, cellIdx, {
|
|
@@ -6889,30 +7976,26 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6889
7976
|
});
|
|
6890
7977
|
return;
|
|
6891
7978
|
}
|
|
6892
|
-
if (
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
7979
|
+
if (isLayoutType) {
|
|
7980
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0 && typeof selContentMeta.contentIdx === "number" && selContentMeta.contentIdx >= 0) {
|
|
7981
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
7982
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
7983
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
7984
|
+
kind: "new",
|
|
7985
|
+
contentType,
|
|
7986
|
+
insertAt: null
|
|
7987
|
+
});
|
|
7988
|
+
return;
|
|
7989
|
+
}
|
|
6901
7990
|
}
|
|
7991
|
+
addRow(rootContentPreset);
|
|
7992
|
+
return;
|
|
6902
7993
|
}
|
|
6903
|
-
if (
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
dropContent(targetRow.id, 0, {
|
|
6907
|
-
kind: "new",
|
|
6908
|
-
contentType,
|
|
6909
|
-
insertAt: null
|
|
6910
|
-
});
|
|
6911
|
-
return;
|
|
6912
|
-
}
|
|
7994
|
+
if (!rows.length) {
|
|
7995
|
+
insertRootContentBlock(contentType);
|
|
7996
|
+
return;
|
|
6913
7997
|
}
|
|
6914
|
-
|
|
6915
|
-
dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
|
|
7998
|
+
insertRootContentBlock(contentType);
|
|
6916
7999
|
};
|
|
6917
8000
|
const deleteContent = (rowId, cellIdx, ci, inner = null) => {
|
|
6918
8001
|
if (!inner) {
|
|
@@ -7507,6 +8590,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7507
8590
|
e.preventDefault();
|
|
7508
8591
|
try {
|
|
7509
8592
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8593
|
+
if (data.moveRowId) return;
|
|
7510
8594
|
if (data.layoutPresetKey) {
|
|
7511
8595
|
const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
|
|
7512
8596
|
if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
@@ -7561,6 +8645,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7561
8645
|
e.stopPropagation();
|
|
7562
8646
|
try {
|
|
7563
8647
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8648
|
+
if (data.moveRowId) return;
|
|
7564
8649
|
if (data.contentType && typeof data.contentType === "string") {
|
|
7565
8650
|
insertBlockFromLibrary(data.contentType);
|
|
7566
8651
|
}
|
|
@@ -7582,10 +8667,11 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7582
8667
|
selectedRowId,
|
|
7583
8668
|
selContentMeta,
|
|
7584
8669
|
dragOver,
|
|
8670
|
+
draggingRowId,
|
|
7585
8671
|
C,
|
|
7586
8672
|
setDragOver,
|
|
7587
8673
|
handleRowDrop,
|
|
7588
|
-
|
|
8674
|
+
setRowDrag,
|
|
7589
8675
|
setSelectedRowId,
|
|
7590
8676
|
setSelContentId,
|
|
7591
8677
|
setSelMeta,
|
|
@@ -7603,14 +8689,20 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7603
8689
|
id: eid("canvas-row-drop-end"),
|
|
7604
8690
|
onDragOver: (e) => {
|
|
7605
8691
|
e.preventDefault();
|
|
8692
|
+
e.stopPropagation();
|
|
7606
8693
|
setDragOver(rows.length);
|
|
7607
8694
|
},
|
|
7608
8695
|
onDragLeave: () => setDragOver(null),
|
|
7609
|
-
onDrop: (e) =>
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
8696
|
+
onDrop: (e) => handleRowDrop(rows.length, e),
|
|
8697
|
+
style: {
|
|
8698
|
+
height: dragOver === rows.length ? 28 : 10,
|
|
8699
|
+
background: dragOver === rows.length ? `${C.accent}25` : "transparent",
|
|
8700
|
+
border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
8701
|
+
borderRadius: 4,
|
|
8702
|
+
transition: "all .12s",
|
|
8703
|
+
margin: "2px 0",
|
|
8704
|
+
boxSizing: "border-box"
|
|
8705
|
+
}
|
|
7614
8706
|
}
|
|
7615
8707
|
)
|
|
7616
8708
|
]
|
|
@@ -7722,6 +8814,10 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7722
8814
|
onClose: () => setSelectedRowId(null),
|
|
7723
8815
|
onUpload,
|
|
7724
8816
|
initialCol: selContentMeta?.rowId === selectedRowId ? selContentMeta.cellIdx : null,
|
|
8817
|
+
rowIndex: rows.findIndex((r) => r.id === selectedRowId),
|
|
8818
|
+
rowCount: rows.length,
|
|
8819
|
+
onMoveUp: () => selectedRow && moveRow(selectedRow.id, -1),
|
|
8820
|
+
onMoveDown: () => selectedRow && moveRow(selectedRow.id, 1),
|
|
7725
8821
|
C
|
|
7726
8822
|
}
|
|
7727
8823
|
) })
|
|
@@ -7898,7 +8994,40 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7898
8994
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("summary", { style: railSectionSummary, children: "Background" }),
|
|
7899
8995
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: railSectionBody, children: [
|
|
7900
8996
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 10, color: C.muted, fontWeight: 800, textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 }, children: "Page background" }),
|
|
7901
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
8997
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", userSelect: "none" }, children: [
|
|
8998
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
8999
|
+
"input",
|
|
9000
|
+
{
|
|
9001
|
+
type: "checkbox",
|
|
9002
|
+
checked: pageBgIsOff,
|
|
9003
|
+
onChange: (e) => {
|
|
9004
|
+
const off = e.target.checked;
|
|
9005
|
+
if (off) {
|
|
9006
|
+
const cur = String(settings.bgColor ?? "").trim();
|
|
9007
|
+
if (cur) pageBgLastSolidRef.current = cur;
|
|
9008
|
+
setPageBgUiStep(null);
|
|
9009
|
+
setSettings((s) => ({ ...s, bgColor: "", bgImage: "", bgGradient: null }));
|
|
9010
|
+
} else {
|
|
9011
|
+
setPageBgUiStep("solid");
|
|
9012
|
+
setSettings((s) => ({
|
|
9013
|
+
...s,
|
|
9014
|
+
bgColor: pageBgLastSolidRef.current || "#f1f5f9"
|
|
9015
|
+
}));
|
|
9016
|
+
}
|
|
9017
|
+
},
|
|
9018
|
+
style: { width: 15, height: 15, accentColor: C.accent, cursor: "pointer" }
|
|
9019
|
+
}
|
|
9020
|
+
),
|
|
9021
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: "No page background" })
|
|
9022
|
+
] }) }),
|
|
9023
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
9024
|
+
BgModeButtons,
|
|
9025
|
+
{
|
|
9026
|
+
value: pageBgIsOff ? null : pageBgUiStep,
|
|
9027
|
+
onChange: applyPageBgMode,
|
|
9028
|
+
C
|
|
9029
|
+
}
|
|
9030
|
+
),
|
|
7902
9031
|
pageBgUiStep === null ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 11, color: C.muted, marginBottom: 12, lineHeight: 1.45 }, children: "Choose Color, Gradient, or Image \u2014 then the matching settings appear below." }) : null,
|
|
7903
9032
|
pageBgUiStep === "solid" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
7904
9033
|
ColorField,
|
|
@@ -7908,7 +9037,10 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7908
9037
|
"bg color"
|
|
7909
9038
|
] }),
|
|
7910
9039
|
value: settings.bgColor,
|
|
7911
|
-
onChange: (v) =>
|
|
9040
|
+
onChange: (v) => {
|
|
9041
|
+
pageBgLastSolidRef.current = v;
|
|
9042
|
+
setSettings((s) => ({ ...s, bgColor: v }));
|
|
9043
|
+
},
|
|
7912
9044
|
placeholder: "#f1f5f9",
|
|
7913
9045
|
C
|
|
7914
9046
|
}
|
|
@@ -8149,81 +9281,18 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
8149
9281
|
);
|
|
8150
9282
|
}
|
|
8151
9283
|
);
|
|
8152
|
-
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml });
|
|
8153
|
-
|
|
8154
|
-
// src/lib/htmlToEmailDesign.ts
|
|
8155
|
-
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
8156
|
-
function extractHtmlForDesign(html) {
|
|
8157
|
-
const t = String(html ?? "").trim();
|
|
8158
|
-
if (!t) return "";
|
|
8159
|
-
const head = t.slice(0, 800).toLowerCase();
|
|
8160
|
-
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
8161
|
-
return normalizeRichHtmlForStorage(t);
|
|
8162
|
-
}
|
|
8163
|
-
try {
|
|
8164
|
-
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
8165
|
-
const body = doc.body;
|
|
8166
|
-
if (!body) return normalizeRichHtmlForStorage(t);
|
|
8167
|
-
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
8168
|
-
} catch {
|
|
8169
|
-
return normalizeRichHtmlForStorage(t);
|
|
8170
|
-
}
|
|
8171
|
-
}
|
|
8172
|
-
function htmlToEmailDesignTemplate(html) {
|
|
8173
|
-
const inner = extractHtmlForDesign(html);
|
|
8174
|
-
if (!inner) return null;
|
|
8175
|
-
const doc = {
|
|
8176
|
-
type: "email_document",
|
|
8177
|
-
settings: {
|
|
8178
|
-
width: 600,
|
|
8179
|
-
backgroundColor: "#f1f5f9",
|
|
8180
|
-
contentBackgroundColor: "#ffffff",
|
|
8181
|
-
fontFamily: "Arial, Helvetica, sans-serif",
|
|
8182
|
-
lineHeightBase: 1.6,
|
|
8183
|
-
color: "#111827",
|
|
8184
|
-
responsive: true,
|
|
8185
|
-
rtl: false
|
|
8186
|
-
},
|
|
8187
|
-
rows: [
|
|
8188
|
-
{
|
|
8189
|
-
id: "row_html_import",
|
|
8190
|
-
type: "row",
|
|
8191
|
-
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
8192
|
-
styles: {
|
|
8193
|
-
backgroundColor: "#ffffff",
|
|
8194
|
-
backgroundRepeat: "no-repeat",
|
|
8195
|
-
backgroundSize: "cover",
|
|
8196
|
-
padding: pad(24),
|
|
8197
|
-
textAlign: "left"
|
|
8198
|
-
},
|
|
8199
|
-
columns: [
|
|
8200
|
-
{
|
|
8201
|
-
id: "col_html_import",
|
|
8202
|
-
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
8203
|
-
styles: { padding: pad(0), backgroundColor: "" },
|
|
8204
|
-
blocks: [
|
|
8205
|
-
{
|
|
8206
|
-
id: "block_html_import",
|
|
8207
|
-
type: "html",
|
|
8208
|
-
content: { html: inner },
|
|
8209
|
-
styles: {}
|
|
8210
|
-
}
|
|
8211
|
-
]
|
|
8212
|
-
}
|
|
8213
|
-
]
|
|
8214
|
-
}
|
|
8215
|
-
]
|
|
8216
|
-
};
|
|
8217
|
-
return doc;
|
|
8218
|
-
}
|
|
9284
|
+
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml, htmlToJson });
|
|
8219
9285
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8220
9286
|
0 && (module.exports = {
|
|
8221
9287
|
EmailPreviewModal,
|
|
8222
9288
|
ReactEmailEditor,
|
|
8223
9289
|
base64ToUtf8,
|
|
9290
|
+
canonicalizeEmailDocument,
|
|
9291
|
+
coerceEmailDocumentInput,
|
|
8224
9292
|
emailPreviewDevices,
|
|
8225
9293
|
extractHtmlForDesign,
|
|
8226
9294
|
htmlToEmailDesignTemplate,
|
|
9295
|
+
htmlToJson,
|
|
8227
9296
|
jsonToHtml,
|
|
8228
9297
|
utf8ToBase64
|
|
8229
9298
|
});
|