react-email-studio 3.4.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -2
- package/README.md +12 -0
- package/TUTORIAL.md +56 -2
- package/USER_README.md +17 -0
- package/dist/index.cjs +1567 -496
- 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 +1573 -499
- 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, ">");
|
|
@@ -857,7 +906,7 @@ function videoUrlToEmbedSrc(url) {
|
|
|
857
906
|
if (vi) return `https://player.vimeo.com/video/${vi[1]}`;
|
|
858
907
|
return url;
|
|
859
908
|
}
|
|
860
|
-
var EMPTY_RICH_HTML = "
|
|
909
|
+
var EMPTY_RICH_HTML = "";
|
|
861
910
|
function unwrapAllDivElements(container) {
|
|
862
911
|
let el;
|
|
863
912
|
while (el = container.querySelector("div")) {
|
|
@@ -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) {
|
|
@@ -1008,19 +1212,23 @@ function blockToHtml(cb) {
|
|
|
1008
1212
|
case "text": {
|
|
1009
1213
|
const shell = emailSurfaceBgCss(p);
|
|
1010
1214
|
const inner = `font-size:${lenPx(p.fontSize)};color:${p.color};text-align:${p.align};font-weight:${p.fontWeight || 400};font-style:${p.italic ? "italic" : "normal"};text-decoration:${p.underline ? "underline" : "none"};line-height:${lh(p.lineHeight)};letter-spacing:${lenPx(p.letterSpacing)};font-family:${p.fontFamily || "Georgia,serif"}`;
|
|
1011
|
-
|
|
1215
|
+
const content = typeof p.content === "string" ? p.content : "";
|
|
1216
|
+
const body = /^\s*<[^>]+>/.test(content) ? content : escHtml(content).replace(/\r?\n/g, "<br/>");
|
|
1217
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div style="${inner}">${body}</div></div>`;
|
|
1012
1218
|
}
|
|
1013
1219
|
case "html": {
|
|
1014
1220
|
const shell = emailSurfaceBgCss(p);
|
|
1015
1221
|
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
|
-
|
|
1222
|
+
const bodyRaw = typeof cb.content === "string" ? cb.content : typeof p.content === "string" ? p.content : "";
|
|
1223
|
+
const body = enhanceRichHtmlForEmail(bodyRaw || "");
|
|
1224
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div class="email-rich-html-inner" style="${inner}">${body}</div></div>`;
|
|
1017
1225
|
}
|
|
1018
1226
|
case "image": {
|
|
1019
1227
|
const of = typeof p.objectFit === "string" && p.objectFit.trim() ? p.objectFit.trim() : "cover";
|
|
1020
1228
|
const op = typeof p.objectPosition === "string" && p.objectPosition.trim() ? p.objectPosition.trim() : "center";
|
|
1021
1229
|
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
1230
|
const shell = emailSurfaceBgCss(p);
|
|
1023
|
-
return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${p
|
|
1231
|
+
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
1232
|
}
|
|
1025
1233
|
case "button": {
|
|
1026
1234
|
const bd = emailBackdropBgCss(p);
|
|
@@ -1109,7 +1317,7 @@ function blockToHtml(cb) {
|
|
|
1109
1317
|
bgRepeat: p.bgRepeat || "no-repeat",
|
|
1110
1318
|
bgPosition: typeof p.bgPosition === "string" ? p.bgPosition : "center",
|
|
1111
1319
|
bgGradient: p.bgGradient ?? null,
|
|
1112
|
-
|
|
1320
|
+
columns: getColumns(p),
|
|
1113
1321
|
cells: p.cells || []
|
|
1114
1322
|
};
|
|
1115
1323
|
return rowToHtml(pseudo);
|
|
@@ -1123,7 +1331,7 @@ function rowToHtml(row) {
|
|
|
1123
1331
|
const shellBg = emailSurfaceBgCss(row);
|
|
1124
1332
|
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
1333
|
const r = row.ratios[i] ?? 1;
|
|
1126
|
-
const cs = row
|
|
1334
|
+
const cs = getColumnPropsAt(row, i);
|
|
1127
1335
|
const colShell = emailSurfaceBgCss(cs);
|
|
1128
1336
|
const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
|
|
1129
1337
|
const o = cs.padding;
|
|
@@ -1171,6 +1379,7 @@ table[role="presentation"] td{min-width:0!important;word-break:break-word;}
|
|
|
1171
1379
|
@media screen and (max-width:480px){
|
|
1172
1380
|
.email-content-td{padding-left:max(10px,env(safe-area-inset-left))!important;padding-right:max(10px,env(safe-area-inset-right))!important;}
|
|
1173
1381
|
}
|
|
1382
|
+
${RICH_HTML_CONTENT_CSS}
|
|
1174
1383
|
</style>`;
|
|
1175
1384
|
function previewEmailSrcDoc(html, viewportWidthPx) {
|
|
1176
1385
|
const viewportTag = `<meta name="viewport" content="width=${viewportWidthPx},initial-scale=1"/>`;
|
|
@@ -1222,6 +1431,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1222
1431
|
return `background-image:linear-gradient(${angle}deg, ${pairs.join(", ")});background-repeat:no-repeat;background-position:${pagePos};background-size:cover;`;
|
|
1223
1432
|
})();
|
|
1224
1433
|
const pageBgImgRaw = typeof settings.bgImage === "string" ? String(settings.bgImage).trim() : "";
|
|
1434
|
+
const pageBgColorRaw = typeof settings.bgColor === "string" ? String(settings.bgColor).trim() : "";
|
|
1435
|
+
const pageBgColor = pageBgColorRaw ? pageBgColorRaw : "transparent";
|
|
1225
1436
|
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
1437
|
const contentPos = typeof settings.contentBgPosition === "string" && settings.contentBgPosition.trim() ? settings.contentBgPosition.trim() : "center";
|
|
1227
1438
|
const contentGrad = settings.contentBgGradient && typeof settings.contentBgGradient === "object" ? settings.contentBgGradient : null;
|
|
@@ -1244,8 +1455,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1244
1455
|
<head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
|
1245
1456
|
<title>Email</title>${RESPONSIVE_EMAIL_CSS}${css}
|
|
1246
1457
|
</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:${
|
|
1458
|
+
<body style="margin:0;padding:0;background:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1459
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1249
1460
|
<tr><td align="center" style="padding:0;max-width:100%;">
|
|
1250
1461
|
<table class="email-shell-table" role="presentation" width="100%" cellpadding="0" cellspacing="0"
|
|
1251
1462
|
style="${contentShellStyle.join(";")}">
|
|
@@ -1279,14 +1490,49 @@ function paddingToUniform(p, fallback = 0) {
|
|
|
1279
1490
|
function ensureId(id, prefix) {
|
|
1280
1491
|
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
1281
1492
|
}
|
|
1493
|
+
function normalizeDocBlock(b, prefix) {
|
|
1494
|
+
const raw = b && typeof b === "object" && !Array.isArray(b) ? b : {};
|
|
1495
|
+
const base = {
|
|
1496
|
+
id: ensureId(raw.id, prefix),
|
|
1497
|
+
type: typeof raw.type === "string" ? raw.type : "text",
|
|
1498
|
+
content: typeof raw.content === "string" ? raw.content : raw.content && typeof raw.content === "object" && !Array.isArray(raw.content) ? raw.content : {},
|
|
1499
|
+
...raw.behavior && typeof raw.behavior === "object" && !Array.isArray(raw.behavior) ? { behavior: raw.behavior } : {},
|
|
1500
|
+
...raw.responsive && typeof raw.responsive === "object" && !Array.isArray(raw.responsive) ? { responsive: raw.responsive } : {}
|
|
1501
|
+
};
|
|
1502
|
+
if (raw.props && typeof raw.props === "object" && !Array.isArray(raw.props)) {
|
|
1503
|
+
base.props = { ...raw.props };
|
|
1504
|
+
} else if (raw.styles && typeof raw.styles === "object" && !Array.isArray(raw.styles)) {
|
|
1505
|
+
base.styles = { ...raw.styles };
|
|
1506
|
+
}
|
|
1507
|
+
return base;
|
|
1508
|
+
}
|
|
1509
|
+
function normalizeDocSettings(settings) {
|
|
1510
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return {};
|
|
1511
|
+
const s = { ...settings };
|
|
1512
|
+
delete s._reactEmailStudio;
|
|
1513
|
+
return s;
|
|
1514
|
+
}
|
|
1282
1515
|
function normalizeEmailDocument(input) {
|
|
1283
1516
|
if (input == null || typeof input !== "object" || Array.isArray(input)) return null;
|
|
1284
1517
|
const doc = input;
|
|
1285
1518
|
if (doc.type !== "email_document") return null;
|
|
1519
|
+
const rawBlocks = Array.isArray(doc.blocks) ? doc.blocks : [];
|
|
1520
|
+
const useBlocks = rawBlocks.length > 0;
|
|
1286
1521
|
const rows = Array.isArray(doc.rows) ? doc.rows : [];
|
|
1287
|
-
|
|
1522
|
+
const base = {
|
|
1288
1523
|
type: "email_document",
|
|
1289
|
-
settings:
|
|
1524
|
+
settings: normalizeDocSettings(doc.settings),
|
|
1525
|
+
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1526
|
+
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1527
|
+
};
|
|
1528
|
+
if (useBlocks) {
|
|
1529
|
+
return {
|
|
1530
|
+
...base,
|
|
1531
|
+
blocks: rawBlocks.map((b, bi) => normalizeDocBlock(b, `block${bi + 1}`))
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
return {
|
|
1535
|
+
...base,
|
|
1290
1536
|
rows: rows.map((r, ri) => {
|
|
1291
1537
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1292
1538
|
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
@@ -1308,28 +1554,76 @@ function normalizeEmailDocument(input) {
|
|
|
1308
1554
|
columns: colCount,
|
|
1309
1555
|
gap: typeof r.layout?.gap === "number" ? r.layout.gap : 0,
|
|
1310
1556
|
stackOnMobile: r.layout?.stackOnMobile !== false,
|
|
1311
|
-
align: r.layout?.align || "center"
|
|
1557
|
+
align: r.layout?.align || "center",
|
|
1558
|
+
...typeof r.layout?.preset === "string" && r.layout.preset.trim() ? { preset: r.layout.preset.trim() } : {},
|
|
1559
|
+
...Array.isArray(r.layout?.ratios) && r.layout.ratios.length ? {
|
|
1560
|
+
ratios: r.layout.ratios.filter(
|
|
1561
|
+
(x) => typeof x === "number" && Number.isFinite(x)
|
|
1562
|
+
)
|
|
1563
|
+
} : {}
|
|
1312
1564
|
},
|
|
1313
|
-
|
|
1314
|
-
|
|
1565
|
+
props: (() => {
|
|
1566
|
+
const p = r.props;
|
|
1567
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { ...p };
|
|
1568
|
+
const s = r.styles;
|
|
1569
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1570
|
+
return {
|
|
1571
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1572
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1573
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1574
|
+
bgSize: s.backgroundSize || "cover",
|
|
1575
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1576
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1577
|
+
padding: s.padding,
|
|
1578
|
+
borderRadius: s.borderRadius,
|
|
1579
|
+
borderWidth: s.borderWidth,
|
|
1580
|
+
borderColor: s.borderColor,
|
|
1581
|
+
textAlign: s.textAlign
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
return {};
|
|
1585
|
+
})(),
|
|
1315
1586
|
columns: colsNormalized.map((c, ci) => ({
|
|
1316
1587
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1317
1588
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
...
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1589
|
+
...(() => {
|
|
1590
|
+
const p = c.props;
|
|
1591
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { props: { ...p } };
|
|
1592
|
+
const s = c.styles;
|
|
1593
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1594
|
+
return {
|
|
1595
|
+
props: {
|
|
1596
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1597
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1598
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1599
|
+
bgSize: s.backgroundSize || "cover",
|
|
1600
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1601
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1602
|
+
padding: s.padding,
|
|
1603
|
+
borderRadius: s.borderRadius
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
return {};
|
|
1608
|
+
})(),
|
|
1609
|
+
blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => {
|
|
1610
|
+
const base2 = {
|
|
1611
|
+
id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
|
|
1612
|
+
type: typeof b?.type === "string" ? b.type : "text",
|
|
1613
|
+
content: typeof b?.content === "string" ? b.content : b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1614
|
+
...b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? { behavior: b.behavior } : {},
|
|
1615
|
+
...b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? { responsive: b.responsive } : {}
|
|
1616
|
+
};
|
|
1617
|
+
if (b?.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
1618
|
+
base2.props = { ...b.props };
|
|
1619
|
+
} else if (b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles)) {
|
|
1620
|
+
base2.styles = { ...b.styles };
|
|
1621
|
+
}
|
|
1622
|
+
return base2;
|
|
1623
|
+
}) : []
|
|
1328
1624
|
}))
|
|
1329
1625
|
};
|
|
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
|
|
1626
|
+
})
|
|
1333
1627
|
};
|
|
1334
1628
|
}
|
|
1335
1629
|
|
|
@@ -1342,6 +1636,60 @@ function cloneJson(v) {
|
|
|
1342
1636
|
return v;
|
|
1343
1637
|
}
|
|
1344
1638
|
}
|
|
1639
|
+
function legacyRowStylesToProps(s) {
|
|
1640
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return {};
|
|
1641
|
+
return {
|
|
1642
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1643
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1644
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1645
|
+
bgSize: s.backgroundSize || "cover",
|
|
1646
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1647
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1648
|
+
padding: s.padding,
|
|
1649
|
+
borderRadius: s.borderRadius,
|
|
1650
|
+
borderWidth: s.borderWidth,
|
|
1651
|
+
borderColor: s.borderColor,
|
|
1652
|
+
textAlign: s.textAlign
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
function rowPropsFromDocRow(r) {
|
|
1656
|
+
const p = r.props;
|
|
1657
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1658
|
+
return { ...p };
|
|
1659
|
+
}
|
|
1660
|
+
return legacyRowStylesToProps(r.styles);
|
|
1661
|
+
}
|
|
1662
|
+
function applyRowPropsToEditorRow(row, r) {
|
|
1663
|
+
const p = rowPropsFromDocRow(r);
|
|
1664
|
+
if (p.padding !== void 0 && p.padding !== null) {
|
|
1665
|
+
row.padding = paddingToUniform(p.padding, row.padding);
|
|
1666
|
+
}
|
|
1667
|
+
if (typeof p.bgColor === "string") row.bgColor = p.bgColor.trim();
|
|
1668
|
+
if (typeof p.bgImage === "string") row.bgImage = p.bgImage.trim();
|
|
1669
|
+
if (typeof p.bgRepeat === "string") row.bgRepeat = p.bgRepeat;
|
|
1670
|
+
if (typeof p.bgSize === "string") row.bgSize = p.bgSize;
|
|
1671
|
+
if (typeof p.bgPosition === "string") row.bgPosition = p.bgPosition;
|
|
1672
|
+
if (p.bgGradient !== void 0) row.bgGradient = p.bgGradient;
|
|
1673
|
+
}
|
|
1674
|
+
function editorRowToDocProps(r) {
|
|
1675
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1676
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1677
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
1678
|
+
})();
|
|
1679
|
+
return {
|
|
1680
|
+
bgColor: typeof r.bgColor === "string" ? r.bgColor : "",
|
|
1681
|
+
bgImage: typeof r.bgImage === "string" ? r.bgImage : "",
|
|
1682
|
+
bgRepeat: r.bgRepeat || "no-repeat",
|
|
1683
|
+
bgSize: r.bgSize || "cover",
|
|
1684
|
+
bgPosition: r.bgPosition || "center",
|
|
1685
|
+
bgGradient: r.bgGradient ?? null,
|
|
1686
|
+
padding: rowPad,
|
|
1687
|
+
borderRadius: 0,
|
|
1688
|
+
borderWidth: 0,
|
|
1689
|
+
borderColor: "#e5e7eb",
|
|
1690
|
+
textAlign: "left"
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1345
1693
|
function layoutColumnPaddingForDoc(p) {
|
|
1346
1694
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1347
1695
|
const n = Math.max(0, p);
|
|
@@ -1361,16 +1709,58 @@ function layoutColumnBorderRadiusForDoc(r) {
|
|
|
1361
1709
|
}
|
|
1362
1710
|
return 0;
|
|
1363
1711
|
}
|
|
1364
|
-
function
|
|
1365
|
-
|
|
1712
|
+
function normalizeLayoutContainerProps(props) {
|
|
1713
|
+
const columns = getColumns(props);
|
|
1714
|
+
const { columnStyles: _drop, ...rest } = props;
|
|
1715
|
+
return { ...rest, columns };
|
|
1716
|
+
}
|
|
1717
|
+
function mergeLayoutColumns(fromContent, fromHydrated) {
|
|
1718
|
+
const norm = (x) => {
|
|
1719
|
+
if (Array.isArray(x)) {
|
|
1720
|
+
return x.map((entry) => {
|
|
1721
|
+
const e = entry;
|
|
1722
|
+
const raw = e?.props && typeof e.props === "object" ? e.props : entry;
|
|
1723
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...raw } };
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
if (x && typeof x === "object" && !Array.isArray(x)) {
|
|
1727
|
+
return columnsFromLegacyMap(x, Object.keys(x).length);
|
|
1728
|
+
}
|
|
1729
|
+
return [];
|
|
1730
|
+
};
|
|
1731
|
+
const a = norm(fromContent);
|
|
1732
|
+
const b = norm(fromHydrated);
|
|
1733
|
+
const len = Math.max(a.length, b.length, 1);
|
|
1734
|
+
const out = [];
|
|
1735
|
+
for (let i = 0; i < len; i++) {
|
|
1736
|
+
out.push({
|
|
1737
|
+
props: { ...DEFAULT_COLUMN_PROPS, ...a[i]?.props ?? {}, ...b[i]?.props ?? {} }
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
return out.length ? out : void 0;
|
|
1366
1741
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
if (
|
|
1370
|
-
|
|
1371
|
-
return !!(o.tl || o.tr || o.br || o.bl);
|
|
1742
|
+
function columnPropsFromDocColumn(c) {
|
|
1743
|
+
const p = c?.props;
|
|
1744
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1745
|
+
return { ...DEFAULT_COLUMN_PROPS, ...p };
|
|
1372
1746
|
}
|
|
1373
|
-
|
|
1747
|
+
const s = c?.styles;
|
|
1748
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) {
|
|
1749
|
+
return { ...DEFAULT_COLUMN_PROPS };
|
|
1750
|
+
}
|
|
1751
|
+
const padding = layoutColumnPaddingForDoc(s.padding);
|
|
1752
|
+
const borderRadius = layoutColumnBorderRadiusForDoc(s.borderRadius);
|
|
1753
|
+
return {
|
|
1754
|
+
...DEFAULT_COLUMN_PROPS,
|
|
1755
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1756
|
+
bgImage: typeof s.backgroundImage === "string" ? String(s.backgroundImage).trim() : "",
|
|
1757
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1758
|
+
bgSize: s.backgroundSize || "cover",
|
|
1759
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1760
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1761
|
+
padding,
|
|
1762
|
+
borderRadius
|
|
1763
|
+
};
|
|
1374
1764
|
}
|
|
1375
1765
|
function asNum(v) {
|
|
1376
1766
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
@@ -1467,13 +1857,15 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1467
1857
|
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1468
1858
|
}
|
|
1469
1859
|
};
|
|
1470
|
-
case "html":
|
|
1860
|
+
case "html": {
|
|
1861
|
+
const body = typeof b.content === "string" ? b.content : typeof p.content === "string" ? p.content : "";
|
|
1471
1862
|
return {
|
|
1472
1863
|
id,
|
|
1473
1864
|
type,
|
|
1474
|
-
content: { html:
|
|
1865
|
+
content: { html: normalizeRichHtmlForStorage(body) },
|
|
1475
1866
|
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1476
1867
|
};
|
|
1868
|
+
}
|
|
1477
1869
|
case "image": {
|
|
1478
1870
|
const br = p.borderRadius;
|
|
1479
1871
|
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
@@ -1653,7 +2045,7 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1653
2045
|
bgRepeat: p.bgRepeat,
|
|
1654
2046
|
bgPosition: p.bgPosition,
|
|
1655
2047
|
bgGradient: p.bgGradient ?? null,
|
|
1656
|
-
|
|
2048
|
+
columns: getColumns(p).map((col) => ({ props: cloneJson(col.props) })),
|
|
1657
2049
|
cells: cellsOut
|
|
1658
2050
|
},
|
|
1659
2051
|
styles: {}
|
|
@@ -1693,17 +2085,26 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1693
2085
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1694
2086
|
}
|
|
1695
2087
|
break;
|
|
1696
|
-
case "html":
|
|
1697
|
-
|
|
1698
|
-
|
|
2088
|
+
case "html": {
|
|
2089
|
+
const br = b;
|
|
2090
|
+
const fromDoc = asStr(c.html);
|
|
2091
|
+
const fromRoot = typeof br.content === "string" ? String(br.content) : void 0;
|
|
2092
|
+
const fromLegacyProps = br.props && typeof br.props.content === "string" ? String(br.props.content) : void 0;
|
|
2093
|
+
const htmlNorm = normalizeRichHtmlForStorage(fromDoc ?? fromRoot ?? fromLegacyProps ?? "");
|
|
2094
|
+
block.content = htmlNorm;
|
|
2095
|
+
block.props.content = htmlNorm;
|
|
1699
2096
|
if (s.padding && typeof s.padding === "object") {
|
|
1700
2097
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1701
2098
|
}
|
|
1702
2099
|
break;
|
|
2100
|
+
}
|
|
1703
2101
|
case "image":
|
|
1704
2102
|
block.props.src = asStr(c.src) ?? block.props.src;
|
|
1705
2103
|
if (asStr(c.alt)) block.props.alt = c.alt;
|
|
1706
|
-
if (asStr(c.link))
|
|
2104
|
+
if (asStr(c.link)) {
|
|
2105
|
+
block.props.link = c.link;
|
|
2106
|
+
block.props.linkEnabled = true;
|
|
2107
|
+
}
|
|
1707
2108
|
if (beh.openInNewTab === true) block.props.linkTarget = "_blank";
|
|
1708
2109
|
if (asStr(s.width)) block.props.width = s.width;
|
|
1709
2110
|
if (asStr(s.align)) block.props.align = s.align;
|
|
@@ -1825,8 +2226,17 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1825
2226
|
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1826
2227
|
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1827
2228
|
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1828
|
-
if (c.
|
|
1829
|
-
|
|
2229
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
2230
|
+
if (Array.isArray(c.columns)) {
|
|
2231
|
+
block.props.columns = c.columns.map((col) => ({
|
|
2232
|
+
props: {
|
|
2233
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2234
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2235
|
+
}
|
|
2236
|
+
}));
|
|
2237
|
+
} else if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
2238
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios : [1];
|
|
2239
|
+
block.props.columns = columnsFromLegacyMap(c.columnStyles, ratios.length);
|
|
1830
2240
|
}
|
|
1831
2241
|
block.props.cells = c.cells.map(
|
|
1832
2242
|
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
@@ -1870,6 +2280,10 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1870
2280
|
}
|
|
1871
2281
|
}
|
|
1872
2282
|
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
2283
|
+
if (t === "image") {
|
|
2284
|
+
const link = asStr(block.props.link);
|
|
2285
|
+
if (link && block.props.linkEnabled == null) block.props.linkEnabled = true;
|
|
2286
|
+
}
|
|
1873
2287
|
return block;
|
|
1874
2288
|
}
|
|
1875
2289
|
function rowToInternal(r) {
|
|
@@ -1891,55 +2305,315 @@ function rowToInternal(r) {
|
|
|
1891
2305
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
1892
2306
|
row.cols = cols;
|
|
1893
2307
|
row.gap = typeof r.layout?.gap === "number" ? r.layout.gap : row.gap;
|
|
1894
|
-
row
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2308
|
+
applyRowPropsToEditorRow(row, r);
|
|
2309
|
+
const lay = r.layout;
|
|
2310
|
+
if (lay && typeof lay === "object" && !Array.isArray(lay)) {
|
|
2311
|
+
if (typeof lay.preset === "string" && lay.preset.trim()) row.preset = lay.preset.trim();
|
|
2312
|
+
if (Array.isArray(lay.ratios) && lay.ratios.length) {
|
|
2313
|
+
const rr = lay.ratios.filter((x) => typeof x === "number" && Number.isFinite(x));
|
|
2314
|
+
if (rr.length) row.ratios = rr;
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
1901
2317
|
row.cells = colList.map((c) => {
|
|
1902
2318
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1903
2319
|
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1904
2320
|
});
|
|
1905
2321
|
const studio = r._reactEmailStudio;
|
|
1906
2322
|
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
|
-
});
|
|
2323
|
+
const columnsFromDoc = colList.map((c) => ({
|
|
2324
|
+
props: columnPropsFromDocColumn(c)
|
|
2325
|
+
}));
|
|
1923
2326
|
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1924
|
-
const {
|
|
2327
|
+
const {
|
|
2328
|
+
cells: _omitCells,
|
|
2329
|
+
columnStyles: legacyCs,
|
|
2330
|
+
columns: snapCols,
|
|
2331
|
+
...rest
|
|
2332
|
+
} = snap;
|
|
1925
2333
|
Object.assign(row, rest);
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2334
|
+
const fromSnap = Array.isArray(snapCols) ? snapCols.map((col) => ({
|
|
2335
|
+
props: {
|
|
2336
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2337
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2338
|
+
}
|
|
2339
|
+
})) : columnsFromLegacyMap(legacyCs, cols);
|
|
2340
|
+
row.columns = mergeLayoutColumns(fromSnap, columnsFromDoc) ?? columnsFromDoc;
|
|
2341
|
+
delete row.columnStyles;
|
|
2342
|
+
} else {
|
|
2343
|
+
row.columns = columnsFromDoc;
|
|
1931
2344
|
}
|
|
1932
2345
|
return row;
|
|
1933
2346
|
}
|
|
1934
|
-
function
|
|
1935
|
-
|
|
1936
|
-
if (
|
|
1937
|
-
|
|
1938
|
-
|
|
2347
|
+
function coerceEmailDocumentInput(input) {
|
|
2348
|
+
if (input == null) return input;
|
|
2349
|
+
if (Array.isArray(input)) {
|
|
2350
|
+
if (input.length === 0) return input;
|
|
2351
|
+
return { type: "email_document", settings: {}, blocks: input };
|
|
2352
|
+
}
|
|
2353
|
+
if (typeof input !== "object") return input;
|
|
2354
|
+
const o = input;
|
|
2355
|
+
if (o.type === "email_document") return input;
|
|
2356
|
+
if (Array.isArray(o.blocks)) {
|
|
2357
|
+
return {
|
|
2358
|
+
type: "email_document",
|
|
2359
|
+
settings: o.settings && typeof o.settings === "object" ? o.settings : {},
|
|
2360
|
+
blocks: o.blocks
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
return input;
|
|
2364
|
+
}
|
|
2365
|
+
function rootLayoutBlockToDocRow(block) {
|
|
2366
|
+
const p = block.props && typeof block.props === "object" ? block.props : {};
|
|
2367
|
+
const c = block.content && typeof block.content === "object" && !Array.isArray(block.content) ? block.content : {};
|
|
2368
|
+
const cols = Math.max(
|
|
2369
|
+
1,
|
|
2370
|
+
typeof p.cols === "number" && p.cols > 0 ? Math.floor(p.cols) : 0,
|
|
2371
|
+
typeof c.cols === "number" && c.cols > 0 ? Math.floor(c.cols) : 0,
|
|
2372
|
+
Array.isArray(p.cells) ? p.cells.length : 0,
|
|
2373
|
+
Array.isArray(c.cells) ? c.cells.length : 0
|
|
2374
|
+
);
|
|
2375
|
+
const rawCells = Array.isArray(c.cells) ? c.cells : Array.isArray(p.cells) ? p.cells : [];
|
|
2376
|
+
const contentColumns = Array.isArray(c.columns) ? c.columns : [];
|
|
2377
|
+
const propsColumns = getColumns(p);
|
|
2378
|
+
const columns = Array.from({ length: cols }, (_, ci) => {
|
|
2379
|
+
const cellRaw = rawCells[ci];
|
|
2380
|
+
const blocks = Array.isArray(cellRaw) ? cellRaw.filter((x) => x && typeof x === "object").map((x, bi) => {
|
|
2381
|
+
const blk = x;
|
|
2382
|
+
return {
|
|
2383
|
+
id: ensureBlockId(blk.id, `b_${ci}_${bi}`),
|
|
2384
|
+
type: typeof blk.type === "string" ? blk.type : "text",
|
|
2385
|
+
content: blk.content,
|
|
2386
|
+
...blk.props ? { props: blk.props } : {},
|
|
2387
|
+
...blk.styles ? { styles: blk.styles } : {},
|
|
2388
|
+
...blk.behavior ? { behavior: blk.behavior } : {}
|
|
2389
|
+
};
|
|
2390
|
+
}) : [];
|
|
2391
|
+
const colProps = {
|
|
2392
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2393
|
+
...propsColumns[ci]?.props ?? contentColumns[ci]?.props ?? {}
|
|
2394
|
+
};
|
|
2395
|
+
return {
|
|
2396
|
+
id: `col_${ci + 1}`,
|
|
2397
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2398
|
+
props: cloneJson(colProps),
|
|
2399
|
+
blocks
|
|
2400
|
+
};
|
|
2401
|
+
});
|
|
2402
|
+
const rowProps = {
|
|
2403
|
+
bgColor: typeof p.bgColor === "string" ? p.bgColor : "",
|
|
2404
|
+
bgImage: typeof p.bgImage === "string" ? p.bgImage : "",
|
|
2405
|
+
bgRepeat: p.bgRepeat || "no-repeat",
|
|
2406
|
+
bgSize: p.bgSize || "cover",
|
|
2407
|
+
bgPosition: p.bgPosition || "center",
|
|
2408
|
+
bgGradient: p.bgGradient ?? null,
|
|
2409
|
+
padding: typeof p.padding === "number" ? { top: p.padding, right: p.padding, bottom: p.padding, left: p.padding } : normalizePadding(p.padding)
|
|
2410
|
+
};
|
|
2411
|
+
const ratios = Array.isArray(p.ratios) ? p.ratios : Array.isArray(c.ratios) ? c.ratios : void 0;
|
|
2412
|
+
return {
|
|
2413
|
+
id: typeof block.id === "string" ? block.id : uid(),
|
|
2414
|
+
type: "row",
|
|
2415
|
+
layout: {
|
|
2416
|
+
columns: cols,
|
|
2417
|
+
gap: typeof p.gap === "number" ? p.gap : typeof c.gap === "number" ? c.gap : 0,
|
|
2418
|
+
stackOnMobile: true,
|
|
2419
|
+
align: "center",
|
|
2420
|
+
...typeof p.preset === "string" && p.preset.trim() ? { preset: p.preset.trim() } : typeof c.preset === "string" && c.preset.trim() ? { preset: c.preset.trim() } : {},
|
|
2421
|
+
...ratios?.length ? { ratios: [...ratios] } : {}
|
|
2422
|
+
},
|
|
2423
|
+
props: rowProps,
|
|
2424
|
+
columns
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function ensureBlockId(id, prefix) {
|
|
2428
|
+
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
2429
|
+
}
|
|
2430
|
+
function wrapRootContentBlock(block) {
|
|
2431
|
+
const row = makeLayoutRow(pickPresetByColCount(1));
|
|
2432
|
+
const internal = mapBlockToInternal(block, 0);
|
|
2433
|
+
if (internal) row.cells[0] = [internal];
|
|
2434
|
+
return row;
|
|
2435
|
+
}
|
|
2436
|
+
function rootBlockToEditorRow(block) {
|
|
2437
|
+
const t = block.type === "nestedRow" ? "layout" : block.type;
|
|
2438
|
+
if (t === "layout") {
|
|
2439
|
+
return rowToInternal(rootLayoutBlockToDocRow(block));
|
|
2440
|
+
}
|
|
2441
|
+
return wrapRootContentBlock(block);
|
|
2442
|
+
}
|
|
2443
|
+
function blocksToEditorRows(blocks) {
|
|
2444
|
+
return blocks.map(rootBlockToEditorRow);
|
|
2445
|
+
}
|
|
2446
|
+
function rowPropsToLegacyStyles(p) {
|
|
2447
|
+
return {
|
|
2448
|
+
backgroundColor: p.bgColor ?? "",
|
|
2449
|
+
backgroundImage: p.bgImage ?? "",
|
|
2450
|
+
backgroundRepeat: p.bgRepeat ?? "no-repeat",
|
|
2451
|
+
backgroundSize: p.bgSize ?? "cover",
|
|
2452
|
+
backgroundPosition: p.bgPosition ?? "center",
|
|
2453
|
+
backgroundGradient: p.bgGradient ?? null,
|
|
2454
|
+
padding: p.padding,
|
|
2455
|
+
borderRadius: p.borderRadius ?? 0,
|
|
2456
|
+
borderWidth: p.borderWidth ?? 0,
|
|
2457
|
+
borderColor: p.borderColor ?? "#e5e7eb",
|
|
2458
|
+
textAlign: p.textAlign ?? "left"
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
function columnPropsToLegacyStyles(cp) {
|
|
2462
|
+
return {
|
|
2463
|
+
padding: layoutColumnPaddingForDoc(cp.padding),
|
|
2464
|
+
backgroundColor: cp.bgColor ?? "",
|
|
2465
|
+
backgroundImage: cp.bgImage ?? "",
|
|
2466
|
+
backgroundRepeat: cp.bgRepeat ?? "no-repeat",
|
|
2467
|
+
backgroundSize: cp.bgSize ?? "cover",
|
|
2468
|
+
backgroundPosition: cp.bgPosition ?? "center",
|
|
2469
|
+
backgroundGradient: cp.bgGradient ?? null,
|
|
2470
|
+
borderRadius: layoutColumnBorderRadiusForDoc(cp.borderRadius)
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
function editorRowStudioSnapshot(r) {
|
|
2474
|
+
const p = editorRowToDocProps(r);
|
|
2475
|
+
const columns = getColumns(r).map((col) => ({
|
|
2476
|
+
props: cloneJson(col.props ?? DEFAULT_COLUMN_PROPS)
|
|
2477
|
+
}));
|
|
2478
|
+
return {
|
|
2479
|
+
id: r.id,
|
|
2480
|
+
type: "layout",
|
|
2481
|
+
preset: r.preset,
|
|
2482
|
+
cols: r.cols,
|
|
2483
|
+
ratios: Array.isArray(r.ratios) ? [...r.ratios] : [1],
|
|
2484
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2485
|
+
padding: p.padding,
|
|
2486
|
+
bgColor: p.bgColor ?? "",
|
|
2487
|
+
bgImage: p.bgImage ?? "",
|
|
2488
|
+
bgSize: p.bgSize ?? "cover",
|
|
2489
|
+
bgRepeat: p.bgRepeat ?? "no-repeat",
|
|
2490
|
+
bgPosition: p.bgPosition ?? "center",
|
|
2491
|
+
bgGradient: p.bgGradient ?? null,
|
|
2492
|
+
columns
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
function exportBlockForLegacyRow(b, depth = 0) {
|
|
2496
|
+
if (!b || typeof b !== "object") {
|
|
2497
|
+
return { id: uid(), type: "text", content: {}, props: {} };
|
|
2498
|
+
}
|
|
2499
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? cloneJson(b.props) : {};
|
|
2500
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2501
|
+
rawProps.cells = b.props.cells.map(
|
|
2502
|
+
(col) => Array.isArray(col) ? col.map((child) => exportBlockForLegacyRow(child, depth + 1)) : []
|
|
2503
|
+
);
|
|
2504
|
+
}
|
|
2505
|
+
if (b.type === "html") {
|
|
2506
|
+
const body = typeof b.content === "string" ? b.content : typeof rawProps.content === "string" ? rawProps.content : "";
|
|
2507
|
+
const html = normalizeRichHtmlForStorage(body);
|
|
2508
|
+
rawProps.content = html;
|
|
2509
|
+
return {
|
|
2510
|
+
id: typeof b.id === "string" ? b.id : uid(),
|
|
2511
|
+
type: "html",
|
|
2512
|
+
content: { html },
|
|
2513
|
+
props: rawProps
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2517
|
+
const out = {
|
|
2518
|
+
id: doc.id,
|
|
2519
|
+
type: doc.type,
|
|
2520
|
+
content: doc.content ?? {},
|
|
2521
|
+
props: rawProps
|
|
2522
|
+
};
|
|
2523
|
+
if (doc.behavior) out.behavior = doc.behavior;
|
|
2524
|
+
return out;
|
|
2525
|
+
}
|
|
2526
|
+
function editorRowToEmailDocumentRow(r, rowIndex) {
|
|
2527
|
+
const rowProps = editorRowToDocProps(r);
|
|
2528
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
2529
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? Math.floor(r.cols) : 0;
|
|
2530
|
+
const colCount = Math.max(1, cellArrays.length, declaredCols);
|
|
2531
|
+
const rowColumns = getColumns(r);
|
|
2532
|
+
const columns = Array.from({ length: colCount }, (_, ci) => {
|
|
2533
|
+
const cp = {
|
|
2534
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2535
|
+
...rowColumns[ci]?.props ?? {}
|
|
2536
|
+
};
|
|
2537
|
+
const cellBlocks = cellArrays[ci];
|
|
2538
|
+
const blocks = Array.isArray(cellBlocks) ? cellBlocks.map((blk) => exportBlockForLegacyRow(blk)).filter((x) => x != null) : [];
|
|
2539
|
+
return {
|
|
2540
|
+
id: `col_${rowIndex + 1}_${ci + 1}`,
|
|
2541
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2542
|
+
styles: columnPropsToLegacyStyles(cp),
|
|
2543
|
+
props: cloneJson(cp),
|
|
2544
|
+
blocks
|
|
2545
|
+
};
|
|
2546
|
+
});
|
|
2547
|
+
const ratios = Array.isArray(r.ratios) ? [...r.ratios] : void 0;
|
|
2548
|
+
return {
|
|
2549
|
+
id: typeof r.id === "string" ? r.id : uid(),
|
|
2550
|
+
type: "row",
|
|
2551
|
+
_reactEmailStudio: { row: editorRowStudioSnapshot(r) },
|
|
2552
|
+
layout: {
|
|
2553
|
+
columns: colCount,
|
|
2554
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2555
|
+
stackOnMobile: true,
|
|
2556
|
+
align: "center",
|
|
2557
|
+
...typeof r.preset === "string" && r.preset.trim() ? { preset: r.preset.trim() } : {},
|
|
2558
|
+
...ratios?.length ? { ratios } : {}
|
|
2559
|
+
},
|
|
2560
|
+
props: rowProps,
|
|
2561
|
+
styles: rowPropsToLegacyStyles(rowProps),
|
|
2562
|
+
columns
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
function buildExportSettingsWithStudio(settings) {
|
|
2566
|
+
const base = buildExportSettings(settings) ?? {};
|
|
2567
|
+
const editorSettings = cloneJson(settings);
|
|
2568
|
+
return {
|
|
2569
|
+
...base,
|
|
2570
|
+
_reactEmailStudio: {
|
|
2571
|
+
contentPadding: base.contentPadding ?? 24,
|
|
2572
|
+
contentBorderRadius: base.contentBorderRadius ?? 8,
|
|
2573
|
+
editorSettings
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
function editorSettingStr(settings, key, fallback = "") {
|
|
2578
|
+
const v = settings[key];
|
|
2579
|
+
return typeof v === "string" ? v : fallback;
|
|
2580
|
+
}
|
|
2581
|
+
function buildExportSettings(settings) {
|
|
2582
|
+
const contentWidth = typeof settings.contentWidth === "number" ? settings.contentWidth : 600;
|
|
2583
|
+
const bgGradient = settings.bgGradient;
|
|
2584
|
+
const contentBgGradient = settings.contentBgGradient;
|
|
2585
|
+
return {
|
|
2586
|
+
width: contentWidth,
|
|
2587
|
+
backgroundColor: editorSettingStr(settings, "bgColor", "#f1f5f9"),
|
|
2588
|
+
backgroundImage: editorSettingStr(settings, "bgImage"),
|
|
2589
|
+
backgroundRepeat: editorSettingStr(settings, "bgRepeat", "no-repeat"),
|
|
2590
|
+
backgroundSize: editorSettingStr(settings, "bgSize", "cover"),
|
|
2591
|
+
backgroundPosition: editorSettingStr(settings, "bgPosition", "center"),
|
|
2592
|
+
backgroundGradient: bgGradient && typeof bgGradient === "object" && !Array.isArray(bgGradient) ? bgGradient : void 0,
|
|
2593
|
+
contentBackgroundColor: editorSettingStr(settings, "contentBg", "#ffffff"),
|
|
2594
|
+
contentBackgroundImage: editorSettingStr(settings, "contentBgImage"),
|
|
2595
|
+
contentBackgroundRepeat: editorSettingStr(settings, "contentBgRepeat", "no-repeat"),
|
|
2596
|
+
contentBackgroundSize: editorSettingStr(settings, "contentBgSize", "cover"),
|
|
2597
|
+
contentBackgroundPosition: editorSettingStr(settings, "contentBgPosition", "center"),
|
|
2598
|
+
contentBackgroundGradient: contentBgGradient && typeof contentBgGradient === "object" && !Array.isArray(contentBgGradient) ? contentBgGradient : void 0,
|
|
2599
|
+
fontFamily: editorSettingStr(settings, "fontFamily") || editorSettingStr(settings, "pageFontFamily", "Arial, Helvetica, sans-serif"),
|
|
2600
|
+
lineHeightBase: settings.pageLineHeight != null && settings.pageLineHeight !== "" ? Number(settings.pageLineHeight) : 1.6,
|
|
2601
|
+
color: editorSettingStr(settings, "pageTextColor", "#111827"),
|
|
2602
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
2603
|
+
rtl: !!settings.pageRtl,
|
|
2604
|
+
contentPadding: typeof settings.padding === "number" ? settings.padding : 24,
|
|
2605
|
+
contentBorderRadius: typeof settings.borderRadius === "number" ? settings.borderRadius : 8
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
function editorSettingsFromDoc(s, rawSettings) {
|
|
2609
|
+
const studioSettings = rawSettings?._reactEmailStudio;
|
|
1939
2610
|
const settings = {};
|
|
1940
2611
|
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1941
2612
|
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1942
2613
|
}
|
|
2614
|
+
const pageRtlFromEditor = settings.pageRtl;
|
|
2615
|
+
const contentPadding = typeof s.contentPadding === "number" ? s.contentPadding : typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : void 0;
|
|
2616
|
+
const contentBorderRadius = typeof s.contentBorderRadius === "number" ? s.contentBorderRadius : typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : void 0;
|
|
1943
2617
|
Object.assign(settings, {
|
|
1944
2618
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1945
2619
|
bgImage: s.backgroundImage || "",
|
|
@@ -1954,103 +2628,67 @@ function normalizeEmailDesignInput(input) {
|
|
|
1954
2628
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1955
2629
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1956
2630
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1957
|
-
padding:
|
|
1958
|
-
borderRadius:
|
|
2631
|
+
padding: contentPadding ?? (typeof settings.padding === "number" ? settings.padding : 24),
|
|
2632
|
+
borderRadius: contentBorderRadius ?? (typeof settings.borderRadius === "number" ? settings.borderRadius : 8),
|
|
1959
2633
|
pageFontFamily: s.fontFamily,
|
|
1960
2634
|
pageTextColor: s.color,
|
|
1961
2635
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1962
2636
|
pageResponsive: s.responsive,
|
|
1963
|
-
pageRtl: s.rtl,
|
|
2637
|
+
pageRtl: typeof s.rtl === "boolean" ? s.rtl : pageRtlFromEditor,
|
|
1964
2638
|
fontFamily: s.fontFamily
|
|
1965
2639
|
});
|
|
1966
|
-
|
|
2640
|
+
return settings;
|
|
2641
|
+
}
|
|
2642
|
+
function normalizeEmailDesignInput(input) {
|
|
2643
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2644
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2645
|
+
if (!doc) return null;
|
|
2646
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2647
|
+
const rawSettings = rawDoc?.settings && typeof rawDoc.settings === "object" && !Array.isArray(rawDoc.settings) ? rawDoc.settings : null;
|
|
2648
|
+
const settings = editorSettingsFromDoc(doc.settings || {}, rawSettings);
|
|
2649
|
+
let rows;
|
|
2650
|
+
if (doc.blocks?.length) {
|
|
2651
|
+
rows = blocksToEditorRows(doc.blocks);
|
|
2652
|
+
} else {
|
|
2653
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2654
|
+
rows = (doc.rows || []).map((r, i) => {
|
|
2655
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2656
|
+
if (legacy) {
|
|
2657
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2658
|
+
}
|
|
2659
|
+
return rowToInternal(r);
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
1967
2662
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1968
2663
|
}
|
|
2664
|
+
function emailDocumentToEditorRows(input) {
|
|
2665
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2666
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2667
|
+
if (!doc) return null;
|
|
2668
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2669
|
+
if (doc.blocks?.length) {
|
|
2670
|
+
return blocksToEditorRows(doc.blocks);
|
|
2671
|
+
}
|
|
2672
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2673
|
+
return (doc.rows || []).map((r, i) => {
|
|
2674
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2675
|
+
if (legacy) {
|
|
2676
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2677
|
+
}
|
|
2678
|
+
return rowToInternal(r);
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
1969
2681
|
function designToEmailDocument(rows, settings) {
|
|
1970
|
-
|
|
2682
|
+
return {
|
|
1971
2683
|
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
|
-
})
|
|
2684
|
+
settings: buildExportSettingsWithStudio(settings),
|
|
2685
|
+
rows: rows.map((r, i) => editorRowToEmailDocumentRow(r, i))
|
|
2052
2686
|
};
|
|
2053
|
-
|
|
2687
|
+
}
|
|
2688
|
+
function canonicalizeEmailDocument(input) {
|
|
2689
|
+
const loaded = normalizeEmailDesignInput(coerceEmailDocumentInput(input) ?? input);
|
|
2690
|
+
if (!loaded?.rows) return null;
|
|
2691
|
+
return designToEmailDocument(loaded.rows, loaded.settings);
|
|
2054
2692
|
}
|
|
2055
2693
|
function hydrateBlock(b, depth = 0) {
|
|
2056
2694
|
if (!b || typeof b !== "object") return null;
|
|
@@ -2064,13 +2702,13 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2064
2702
|
...b,
|
|
2065
2703
|
type: "layout",
|
|
2066
2704
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2067
|
-
props: {
|
|
2705
|
+
props: normalizeLayoutContainerProps({
|
|
2068
2706
|
...defaults2,
|
|
2069
2707
|
...b.props,
|
|
2070
2708
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2071
2709
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2072
2710
|
cells: ratios.map(() => [])
|
|
2073
|
-
}
|
|
2711
|
+
})
|
|
2074
2712
|
};
|
|
2075
2713
|
}
|
|
2076
2714
|
const cells = b.props.cells.map(
|
|
@@ -2080,24 +2718,33 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2080
2718
|
...b,
|
|
2081
2719
|
type: "layout",
|
|
2082
2720
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2083
|
-
props: {
|
|
2721
|
+
props: normalizeLayoutContainerProps({
|
|
2084
2722
|
...defaults2,
|
|
2085
2723
|
...b.props,
|
|
2086
2724
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2087
2725
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2088
2726
|
cells
|
|
2089
|
-
}
|
|
2727
|
+
})
|
|
2090
2728
|
};
|
|
2091
2729
|
}
|
|
2092
2730
|
const defaults = DEFAULT_BLOCK_PROPS[t];
|
|
2093
2731
|
if (typeof defaults !== "object" || defaults == null || Array.isArray(defaults)) return null;
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
)
|
|
2100
|
-
|
|
2732
|
+
if (t === "html") {
|
|
2733
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? b.props : {};
|
|
2734
|
+
const { content: legacyPropContent, ...restProps } = rawProps;
|
|
2735
|
+
const merged2 = { ...defaults, ...restProps };
|
|
2736
|
+
normalizeBoxStyles(merged2, t);
|
|
2737
|
+
const src = typeof b.content === "string" ? String(b.content) : typeof legacyPropContent === "string" ? legacyPropContent : "";
|
|
2738
|
+
const htmlNorm = normalizeRichHtmlForStorage(src);
|
|
2739
|
+
merged2.content = htmlNorm;
|
|
2740
|
+
return {
|
|
2741
|
+
...b,
|
|
2742
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2743
|
+
content: htmlNorm,
|
|
2744
|
+
props: merged2
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
const merged = { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
|
|
2101
2748
|
normalizeBoxStyles(merged, t);
|
|
2102
2749
|
return {
|
|
2103
2750
|
...b,
|
|
@@ -2113,70 +2760,50 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2113
2760
|
}
|
|
2114
2761
|
return mapBlockToInternal(r, layoutDepth);
|
|
2115
2762
|
}
|
|
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
2763
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2137
2764
|
const exp = b.props;
|
|
2138
|
-
if (!exp || typeof exp !== "object" || Array.isArray(exp))
|
|
2765
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) {
|
|
2766
|
+
if (t === "html" && typeof block.content === "string") {
|
|
2767
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2768
|
+
block.props.content = block.content;
|
|
2769
|
+
}
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2139
2772
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2140
|
-
const
|
|
2773
|
+
const columnsFromContent = block.props.columns ?? block.props.columnStyles;
|
|
2774
|
+
const expHasColumns = Array.isArray(exp.columns) || exp.columnStyles && typeof exp.columnStyles === "object" && !Array.isArray(exp.columnStyles);
|
|
2141
2775
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2142
2776
|
if (hb) {
|
|
2143
2777
|
block.props = hb.props;
|
|
2144
|
-
const merged =
|
|
2145
|
-
|
|
2146
|
-
|
|
2778
|
+
const merged = mergeLayoutColumns(
|
|
2779
|
+
columnsFromContent,
|
|
2780
|
+
expHasColumns ? block.props.columns : void 0
|
|
2781
|
+
);
|
|
2782
|
+
if (merged) block.props.columns = merged;
|
|
2783
|
+
else delete block.props.columns;
|
|
2784
|
+
delete block.props.columnStyles;
|
|
2147
2785
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2148
2786
|
}
|
|
2149
2787
|
return;
|
|
2150
2788
|
}
|
|
2789
|
+
if (t === "html") {
|
|
2790
|
+
const { content: expContent, ...expRest } = exp;
|
|
2791
|
+
Object.assign(block.props, expRest);
|
|
2792
|
+
normalizeBoxStyles(block.props, t);
|
|
2793
|
+
if (typeof expContent === "string") {
|
|
2794
|
+
block.content = normalizeRichHtmlForStorage(expContent);
|
|
2795
|
+
} else if (typeof block.content === "string") {
|
|
2796
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2797
|
+
}
|
|
2798
|
+
block.props.content = typeof block.content === "string" ? block.content : "";
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2151
2801
|
Object.assign(block.props, exp);
|
|
2152
2802
|
normalizeBoxStyles(block.props, t);
|
|
2153
|
-
if (t === "
|
|
2803
|
+
if (t === "text") {
|
|
2154
2804
|
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2155
2805
|
}
|
|
2156
2806
|
}
|
|
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
2807
|
function hydrateLayoutRow(row) {
|
|
2181
2808
|
if (!row || typeof row !== "object") return row;
|
|
2182
2809
|
const cells = (row.cells || []).map(
|
|
@@ -2219,6 +2846,77 @@ function jsonToHtml(designInput, opts = {}) {
|
|
|
2219
2846
|
return designToHtml(d.rows, d.settings, opts);
|
|
2220
2847
|
}
|
|
2221
2848
|
|
|
2849
|
+
// src/lib/htmlToEmailDesign.ts
|
|
2850
|
+
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
2851
|
+
function extractHtmlForDesign(html) {
|
|
2852
|
+
const t = String(html ?? "").trim();
|
|
2853
|
+
if (!t) return "";
|
|
2854
|
+
const head = t.slice(0, 800).toLowerCase();
|
|
2855
|
+
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
2856
|
+
return normalizeRichHtmlForStorage(t);
|
|
2857
|
+
}
|
|
2858
|
+
try {
|
|
2859
|
+
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
2860
|
+
const body = doc.body;
|
|
2861
|
+
if (!body) return normalizeRichHtmlForStorage(t);
|
|
2862
|
+
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
2863
|
+
} catch {
|
|
2864
|
+
return normalizeRichHtmlForStorage(t);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
function htmlToEmailDesignTemplate(html) {
|
|
2868
|
+
const inner = extractHtmlForDesign(html);
|
|
2869
|
+
if (!inner) return null;
|
|
2870
|
+
const doc = {
|
|
2871
|
+
type: "email_document",
|
|
2872
|
+
settings: {
|
|
2873
|
+
width: 600,
|
|
2874
|
+
backgroundColor: "#f1f5f9",
|
|
2875
|
+
contentBackgroundColor: "#ffffff",
|
|
2876
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
2877
|
+
lineHeightBase: 1.6,
|
|
2878
|
+
color: "#111827",
|
|
2879
|
+
responsive: true,
|
|
2880
|
+
rtl: false
|
|
2881
|
+
},
|
|
2882
|
+
rows: [
|
|
2883
|
+
{
|
|
2884
|
+
id: "row_html_import",
|
|
2885
|
+
type: "row",
|
|
2886
|
+
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
2887
|
+
styles: {
|
|
2888
|
+
backgroundColor: "#ffffff",
|
|
2889
|
+
backgroundRepeat: "no-repeat",
|
|
2890
|
+
backgroundSize: "cover",
|
|
2891
|
+
padding: pad(24),
|
|
2892
|
+
textAlign: "left"
|
|
2893
|
+
},
|
|
2894
|
+
columns: [
|
|
2895
|
+
{
|
|
2896
|
+
id: "col_html_import",
|
|
2897
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2898
|
+
styles: { padding: pad(0), backgroundColor: "" },
|
|
2899
|
+
blocks: [
|
|
2900
|
+
{
|
|
2901
|
+
id: "block_html_import",
|
|
2902
|
+
type: "html",
|
|
2903
|
+
content: inner,
|
|
2904
|
+
props: { ...DEFAULT_BLOCK_PROPS.html }
|
|
2905
|
+
}
|
|
2906
|
+
]
|
|
2907
|
+
}
|
|
2908
|
+
]
|
|
2909
|
+
}
|
|
2910
|
+
]
|
|
2911
|
+
};
|
|
2912
|
+
return doc;
|
|
2913
|
+
}
|
|
2914
|
+
function htmlToJson(html, pretty = false) {
|
|
2915
|
+
const doc = htmlToEmailDesignTemplate(html);
|
|
2916
|
+
if (!doc) return "";
|
|
2917
|
+
return pretty ? JSON.stringify(doc, null, 2) : JSON.stringify(doc);
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2222
2920
|
// src/editor/canvas/DesignCanvas.tsx
|
|
2223
2921
|
var import_react = require("react");
|
|
2224
2922
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -2359,13 +3057,15 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2359
3057
|
)
|
|
2360
3058
|
);
|
|
2361
3059
|
if (type === "html") {
|
|
2362
|
-
const
|
|
3060
|
+
const richHtml = typeof block.content === "string" ? block.content : p.content;
|
|
3061
|
+
const emptyRich = isEffectivelyEmptyRichHtml(richHtml);
|
|
2363
3062
|
return wrap(
|
|
2364
3063
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", minHeight: emptyRich && !preview ? "2.75em" : void 0 }, children: [
|
|
3064
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: RICH_HTML_CONTENT_CSS }),
|
|
2365
3065
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2366
3066
|
"div",
|
|
2367
3067
|
{
|
|
2368
|
-
className: "email-editor-rich-canvas",
|
|
3068
|
+
className: "email-editor-rich-canvas email-rich-html-content",
|
|
2369
3069
|
style: {
|
|
2370
3070
|
fontSize: p.fontSize,
|
|
2371
3071
|
color: p.color,
|
|
@@ -2376,7 +3076,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2376
3076
|
wordBreak: "break-word",
|
|
2377
3077
|
minHeight: emptyRich && !preview ? "2.75em" : void 0
|
|
2378
3078
|
},
|
|
2379
|
-
dangerouslySetInnerHTML: { __html:
|
|
3079
|
+
dangerouslySetInnerHTML: { __html: richHtml || "" }
|
|
2380
3080
|
}
|
|
2381
3081
|
),
|
|
2382
3082
|
emptyRich && !preview ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2424,7 +3124,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2424
3124
|
}
|
|
2425
3125
|
}
|
|
2426
3126
|
);
|
|
2427
|
-
return wrap(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: p.align }, children: p
|
|
3127
|
+
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
3128
|
}
|
|
2429
3129
|
if (type === "button") return wrap(
|
|
2430
3130
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: p.fullWidth ? "center" : p.align }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2597,7 +3297,7 @@ function NestedRowBlock({
|
|
|
2597
3297
|
);
|
|
2598
3298
|
},
|
|
2599
3299
|
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
|
|
3300
|
+
const cS = getColumnPropsAt(p, ici);
|
|
2601
3301
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2602
3302
|
"div",
|
|
2603
3303
|
{
|
|
@@ -2655,7 +3355,9 @@ function BlockItem({
|
|
|
2655
3355
|
onContextMenu,
|
|
2656
3356
|
preview,
|
|
2657
3357
|
C,
|
|
2658
|
-
Line
|
|
3358
|
+
Line,
|
|
3359
|
+
dragAsRow = false,
|
|
3360
|
+
beginRowDrag
|
|
2659
3361
|
}) {
|
|
2660
3362
|
const wrapperRef = (0, import_react.useRef)(null);
|
|
2661
3363
|
const bid = editorId ? `${editorId}-block-${cb.id}` : void 0;
|
|
@@ -2703,7 +3405,7 @@ function BlockItem({
|
|
|
2703
3405
|
{
|
|
2704
3406
|
"data-bidx": ci,
|
|
2705
3407
|
draggable: !preview,
|
|
2706
|
-
title: preview ? void 0 : "Click to select \xB7 drag to reorder or move",
|
|
3408
|
+
title: preview ? void 0 : dragAsRow ? "Click to select \xB7 drag to reorder section" : "Click to select \xB7 drag to reorder or move",
|
|
2707
3409
|
onPointerDown: (e) => {
|
|
2708
3410
|
if (preview || e.button !== 0) return;
|
|
2709
3411
|
selectThisBlock(e);
|
|
@@ -2711,6 +3413,10 @@ function BlockItem({
|
|
|
2711
3413
|
onDragStart: (e) => {
|
|
2712
3414
|
if (preview) return;
|
|
2713
3415
|
e.stopPropagation();
|
|
3416
|
+
if (dragAsRow && beginRowDrag) {
|
|
3417
|
+
beginRowDrag(e);
|
|
3418
|
+
return;
|
|
3419
|
+
}
|
|
2714
3420
|
e.dataTransfer.effectAllowed = "move";
|
|
2715
3421
|
e.dataTransfer.setData(
|
|
2716
3422
|
"application/json",
|
|
@@ -2782,7 +3488,9 @@ function Cell({
|
|
|
2782
3488
|
onDeleteContent,
|
|
2783
3489
|
onContextMenu,
|
|
2784
3490
|
preview,
|
|
2785
|
-
C
|
|
3491
|
+
C,
|
|
3492
|
+
dragAsRow = false,
|
|
3493
|
+
beginRowDrag
|
|
2786
3494
|
}) {
|
|
2787
3495
|
const [insertAt, setInsertAt] = (0, import_react.useState)(null);
|
|
2788
3496
|
const cellRef = (0, import_react.useRef)(null);
|
|
@@ -2797,6 +3505,14 @@ function Cell({
|
|
|
2797
3505
|
return els.length;
|
|
2798
3506
|
}, [blocks.length]);
|
|
2799
3507
|
const handleDragOver = (e) => {
|
|
3508
|
+
try {
|
|
3509
|
+
const raw = e.dataTransfer.getData("application/json");
|
|
3510
|
+
if (raw) {
|
|
3511
|
+
const data = JSON.parse(raw);
|
|
3512
|
+
if (data.moveRowId) return;
|
|
3513
|
+
}
|
|
3514
|
+
} catch {
|
|
3515
|
+
}
|
|
2800
3516
|
e.preventDefault();
|
|
2801
3517
|
e.stopPropagation();
|
|
2802
3518
|
setInsertAt(calcIdx(e.clientY));
|
|
@@ -2812,6 +3528,7 @@ function Cell({
|
|
|
2812
3528
|
setInsertAt(null);
|
|
2813
3529
|
try {
|
|
2814
3530
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
3531
|
+
if (data.moveRowId) return;
|
|
2815
3532
|
if (data.contentType) {
|
|
2816
3533
|
onDropContent(rowId, cellIdx, { kind: "new", contentType: data.contentType, insertAt: idx, nested: nestedPayload });
|
|
2817
3534
|
} else if (data.moveContent) {
|
|
@@ -2891,7 +3608,9 @@ function Cell({
|
|
|
2891
3608
|
onContextMenu,
|
|
2892
3609
|
preview,
|
|
2893
3610
|
C,
|
|
2894
|
-
Line
|
|
3611
|
+
Line,
|
|
3612
|
+
dragAsRow: dragAsRow && blocks.length === 1 && ci === 0,
|
|
3613
|
+
beginRowDrag
|
|
2895
3614
|
}
|
|
2896
3615
|
) }, cb.id))
|
|
2897
3616
|
]
|
|
@@ -2912,7 +3631,8 @@ function LayoutRow({
|
|
|
2912
3631
|
onLayoutContextMenu,
|
|
2913
3632
|
setSelectedRowId,
|
|
2914
3633
|
preview = false,
|
|
2915
|
-
|
|
3634
|
+
rootContentRow = false,
|
|
3635
|
+
beginRowDrag,
|
|
2916
3636
|
C
|
|
2917
3637
|
}) {
|
|
2918
3638
|
const rowStyle = {
|
|
@@ -2944,16 +3664,9 @@ function LayoutRow({
|
|
|
2944
3664
|
onContextMenu: preview || !onLayoutContextMenu ? void 0 : (e) => {
|
|
2945
3665
|
onLayoutContextMenu(e, row.id);
|
|
2946
3666
|
},
|
|
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
|
-
},
|
|
3667
|
+
style: rowStyle,
|
|
2955
3668
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: row.gap }, children: row.cells.map((cellBlocks, ci) => {
|
|
2956
|
-
const cS = row
|
|
3669
|
+
const cS = getColumnPropsAt(row, ci);
|
|
2957
3670
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2958
3671
|
"div",
|
|
2959
3672
|
{
|
|
@@ -2989,7 +3702,9 @@ function LayoutRow({
|
|
|
2989
3702
|
onDeleteContent,
|
|
2990
3703
|
onContextMenu,
|
|
2991
3704
|
preview,
|
|
2992
|
-
C
|
|
3705
|
+
C,
|
|
3706
|
+
dragAsRow: rootContentRow && ci === 0,
|
|
3707
|
+
beginRowDrag
|
|
2993
3708
|
}
|
|
2994
3709
|
)
|
|
2995
3710
|
},
|
|
@@ -3006,10 +3721,11 @@ var CanvasRow = ({
|
|
|
3006
3721
|
selectedRowId,
|
|
3007
3722
|
selContentMeta: rowSelContentMeta,
|
|
3008
3723
|
dragOver,
|
|
3724
|
+
draggingRowId,
|
|
3009
3725
|
C,
|
|
3010
3726
|
setDragOver,
|
|
3011
3727
|
handleRowDrop,
|
|
3012
|
-
|
|
3728
|
+
setRowDrag,
|
|
3013
3729
|
setSelectedRowId,
|
|
3014
3730
|
setSelContentId,
|
|
3015
3731
|
setSelMeta,
|
|
@@ -3022,54 +3738,161 @@ var CanvasRow = ({
|
|
|
3022
3738
|
}) => {
|
|
3023
3739
|
const rowSelected = selectedRowId === row.id;
|
|
3024
3740
|
const rid = (s) => editorId ? `${editorId}-${s}` : void 0;
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3741
|
+
const dropH = dragOver === ri || dragOver === ri + 1 ? 28 : 10;
|
|
3742
|
+
const rowBodyRef = (0, import_react.useRef)(null);
|
|
3743
|
+
const rootContentRow = isRootContentRow(row);
|
|
3744
|
+
const beginRowDrag = (0, import_react.useCallback)(
|
|
3745
|
+
(e) => {
|
|
3746
|
+
e.stopPropagation();
|
|
3747
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3748
|
+
e.dataTransfer.setData(
|
|
3749
|
+
"application/json",
|
|
3750
|
+
JSON.stringify({ moveRowId: row.id })
|
|
3751
|
+
);
|
|
3752
|
+
setRowDrag(row.id);
|
|
3753
|
+
},
|
|
3754
|
+
[row.id, setRowDrag]
|
|
3755
|
+
);
|
|
3756
|
+
const rowDragOver = (0, import_react.useCallback)(
|
|
3757
|
+
(e) => {
|
|
3758
|
+
if (!draggingRowId || draggingRowId === row.id) return;
|
|
3759
|
+
e.preventDefault();
|
|
3760
|
+
e.stopPropagation();
|
|
3761
|
+
const el = rowBodyRef.current;
|
|
3762
|
+
if (!el) {
|
|
3763
|
+
setDragOver(ri);
|
|
3764
|
+
return;
|
|
3040
3765
|
}
|
|
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
|
|
3766
|
+
const rect = el.getBoundingClientRect();
|
|
3767
|
+
setDragOver(e.clientY < rect.top + rect.height / 2 ? ri : ri + 1);
|
|
3768
|
+
},
|
|
3769
|
+
[draggingRowId, row.id, ri, setDragOver]
|
|
3770
|
+
);
|
|
3771
|
+
const rowDrop = (0, import_react.useCallback)(
|
|
3772
|
+
(e) => {
|
|
3773
|
+
if (!draggingRowId) return;
|
|
3774
|
+
e.preventDefault();
|
|
3775
|
+
e.stopPropagation();
|
|
3776
|
+
const el = rowBodyRef.current;
|
|
3777
|
+
let targetIdx = ri;
|
|
3778
|
+
if (el) {
|
|
3779
|
+
const rect = el.getBoundingClientRect();
|
|
3780
|
+
targetIdx = e.clientY < rect.top + rect.height / 2 ? ri : ri + 1;
|
|
3070
3781
|
}
|
|
3071
|
-
|
|
3072
|
-
|
|
3782
|
+
handleRowDrop(targetIdx, e);
|
|
3783
|
+
},
|
|
3784
|
+
[draggingRowId, ri, handleRowDrop]
|
|
3785
|
+
);
|
|
3786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3787
|
+
"div",
|
|
3788
|
+
{
|
|
3789
|
+
id: rid(`canvas-row-${row.id}`),
|
|
3790
|
+
style: { display: "flex", alignItems: "stretch", gap: 0, margin: "2px 0" },
|
|
3791
|
+
children: [
|
|
3792
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3793
|
+
"div",
|
|
3794
|
+
{
|
|
3795
|
+
draggable: true,
|
|
3796
|
+
title: "Drag to reorder section",
|
|
3797
|
+
onDragStart: beginRowDrag,
|
|
3798
|
+
onDragEnd: () => setRowDrag(null),
|
|
3799
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3800
|
+
onClick: (e) => {
|
|
3801
|
+
e.stopPropagation();
|
|
3802
|
+
setSelectedRowId(row.id);
|
|
3803
|
+
setSelContentId(null);
|
|
3804
|
+
setSelMeta(null);
|
|
3805
|
+
},
|
|
3806
|
+
onContextMenu: (e) => {
|
|
3807
|
+
e.preventDefault();
|
|
3808
|
+
e.stopPropagation();
|
|
3809
|
+
handleLayoutContextMenu(e, row.id);
|
|
3810
|
+
},
|
|
3811
|
+
style: {
|
|
3812
|
+
width: 20,
|
|
3813
|
+
flexShrink: 0,
|
|
3814
|
+
cursor: "grab",
|
|
3815
|
+
display: "flex",
|
|
3816
|
+
alignItems: "center",
|
|
3817
|
+
justifyContent: "center",
|
|
3818
|
+
color: rowSelected ? C.accent : C.muted,
|
|
3819
|
+
fontSize: 12,
|
|
3820
|
+
fontWeight: 700,
|
|
3821
|
+
letterSpacing: -3,
|
|
3822
|
+
userSelect: "none",
|
|
3823
|
+
opacity: rowSelected ? 1 : 0.5
|
|
3824
|
+
},
|
|
3825
|
+
children: "\u22EE\u22EE"
|
|
3826
|
+
}
|
|
3827
|
+
),
|
|
3828
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3829
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3830
|
+
"div",
|
|
3831
|
+
{
|
|
3832
|
+
id: rid(`row-drop-before-${ri}`),
|
|
3833
|
+
onDragOver: (e) => {
|
|
3834
|
+
e.preventDefault();
|
|
3835
|
+
e.stopPropagation();
|
|
3836
|
+
setDragOver(ri);
|
|
3837
|
+
},
|
|
3838
|
+
onDragLeave: () => setDragOver(null),
|
|
3839
|
+
onDrop: (e) => handleRowDrop(ri, e),
|
|
3840
|
+
style: {
|
|
3841
|
+
height: dropH,
|
|
3842
|
+
background: dragOver === ri ? `${C.accent}25` : "transparent",
|
|
3843
|
+
border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
3844
|
+
borderRadius: 4,
|
|
3845
|
+
transition: "all .12s",
|
|
3846
|
+
boxSizing: "border-box"
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
),
|
|
3850
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3851
|
+
"div",
|
|
3852
|
+
{
|
|
3853
|
+
ref: rowBodyRef,
|
|
3854
|
+
id: rid(`row-body-${row.id}`),
|
|
3855
|
+
onDragOver: rowDragOver,
|
|
3856
|
+
onDrop: rowDrop,
|
|
3857
|
+
style: {
|
|
3858
|
+
position: "relative",
|
|
3859
|
+
width: "100%",
|
|
3860
|
+
minWidth: 0,
|
|
3861
|
+
boxSizing: "border-box",
|
|
3862
|
+
outline: draggingRowId && draggingRowId !== row.id && (dragOver === ri || dragOver === ri + 1) ? `2px dashed ${C.accent}` : void 0,
|
|
3863
|
+
outlineOffset: 2,
|
|
3864
|
+
borderRadius: 6
|
|
3865
|
+
},
|
|
3866
|
+
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)(
|
|
3867
|
+
LayoutRow,
|
|
3868
|
+
{
|
|
3869
|
+
editorId,
|
|
3870
|
+
row,
|
|
3871
|
+
selected: rowSelected,
|
|
3872
|
+
setSelectedRowId,
|
|
3873
|
+
onClick: () => {
|
|
3874
|
+
setSelectedRowId(row.id);
|
|
3875
|
+
setSelContentId(null);
|
|
3876
|
+
setSelMeta(null);
|
|
3877
|
+
},
|
|
3878
|
+
selectedContentKey: selContentId,
|
|
3879
|
+
selContentMeta: rowSelContentMeta,
|
|
3880
|
+
onSelectContent: selectContent,
|
|
3881
|
+
onDropContent: dropContent,
|
|
3882
|
+
onDeleteContent: deleteContent,
|
|
3883
|
+
onContextMenu: handleContextMenu,
|
|
3884
|
+
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3885
|
+
rootContentRow,
|
|
3886
|
+
beginRowDrag,
|
|
3887
|
+
C
|
|
3888
|
+
}
|
|
3889
|
+
) })
|
|
3890
|
+
}
|
|
3891
|
+
)
|
|
3892
|
+
] })
|
|
3893
|
+
]
|
|
3894
|
+
}
|
|
3895
|
+
);
|
|
3073
3896
|
};
|
|
3074
3897
|
|
|
3075
3898
|
// src/ReactEmailEditor.tsx
|
|
@@ -3150,6 +3973,10 @@ function paddingToCss(pad3, fallback) {
|
|
|
3150
3973
|
var FONT_SIZE_PX = [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28, 32, 36, 42, 48, 56, 64, 72];
|
|
3151
3974
|
var LINE_HEIGHT_OPTS = ["1", "1.15", "1.25", "1.5", "1.65", "1.75", "2", "2.5"];
|
|
3152
3975
|
var LETTER_SPACING_OPTS = ["-0.5px", "0px", "0.5px", "1px", "1.5px", "2px", "3px", "4px"];
|
|
3976
|
+
function rgbChannelToHex(n) {
|
|
3977
|
+
const v = Math.max(0, Math.min(255, Math.round(n)));
|
|
3978
|
+
return v.toString(16).padStart(2, "0");
|
|
3979
|
+
}
|
|
3153
3980
|
function hexForColorInput(color) {
|
|
3154
3981
|
if (!color || typeof color !== "string") return "#000000";
|
|
3155
3982
|
const c = color.trim();
|
|
@@ -3162,8 +3989,34 @@ function hexForColorInput(color) {
|
|
|
3162
3989
|
}
|
|
3163
3990
|
return c.slice(0, 7);
|
|
3164
3991
|
}
|
|
3992
|
+
const rgb = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
|
|
3993
|
+
if (rgb) {
|
|
3994
|
+
return `#${rgbChannelToHex(+rgb[1])}${rgbChannelToHex(+rgb[2])}${rgbChannelToHex(+rgb[3])}`;
|
|
3995
|
+
}
|
|
3165
3996
|
return "#000000";
|
|
3166
3997
|
}
|
|
3998
|
+
function textStyleColorFromEditor(editor) {
|
|
3999
|
+
const ts = editor.getAttributes("textStyle");
|
|
4000
|
+
if (ts.color && String(ts.color).trim()) return String(ts.color).trim();
|
|
4001
|
+
const stored = editor.state.storedMarks;
|
|
4002
|
+
if (stored) {
|
|
4003
|
+
for (const m of stored) {
|
|
4004
|
+
if (m.type.name === "textStyle" && m.attrs.color) {
|
|
4005
|
+
return String(m.attrs.color).trim();
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
const { from, to } = editor.state.selection;
|
|
4010
|
+
if (from === to) return null;
|
|
4011
|
+
const domAt = editor.view.domAtPos(from);
|
|
4012
|
+
let el = domAt.node.nodeType === Node.TEXT_NODE ? domAt.node.parentElement : domAt.node;
|
|
4013
|
+
while (el && el !== editor.view.dom) {
|
|
4014
|
+
const inline = el.style?.color?.trim();
|
|
4015
|
+
if (inline) return inline;
|
|
4016
|
+
el = el.parentElement;
|
|
4017
|
+
}
|
|
4018
|
+
return null;
|
|
4019
|
+
}
|
|
3167
4020
|
function parsePxSize(raw) {
|
|
3168
4021
|
if (!raw) return "";
|
|
3169
4022
|
const s = String(raw).trim();
|
|
@@ -3230,13 +4083,22 @@ function toolbarIconBtn(C, active) {
|
|
|
3230
4083
|
}
|
|
3231
4084
|
function ToolbarTypographyControls({ editor, C }) {
|
|
3232
4085
|
const sel = toolbarSelect(C);
|
|
3233
|
-
const ts =
|
|
4086
|
+
const { ts, align, textColor } = (0, import_react3.useEditorState)({
|
|
4087
|
+
editor,
|
|
4088
|
+
selector: ({ editor: ed }) => {
|
|
4089
|
+
const attrs = ed.getAttributes("textStyle");
|
|
4090
|
+
return {
|
|
4091
|
+
ts: attrs,
|
|
4092
|
+
align: currentBlockTextAlign(ed),
|
|
4093
|
+
textColor: textStyleColorFromEditor(ed)
|
|
4094
|
+
};
|
|
4095
|
+
}
|
|
4096
|
+
});
|
|
3234
4097
|
const fontFamily = ts.fontFamily || "";
|
|
3235
4098
|
const sizeVal = parsePxSize(ts.fontSize);
|
|
3236
4099
|
const lhVal = normalizeLineHeightAttr(ts.lineHeight);
|
|
3237
4100
|
const lsVal = normalizeLetterSpacingAttr(ts.letterSpacing);
|
|
3238
4101
|
const lsSelectValue = LETTER_SPACING_OPTS.includes(lsVal) ? lsVal : lsVal || "";
|
|
3239
|
-
const align = currentBlockTextAlign(editor);
|
|
3240
4102
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
3241
4103
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3242
4104
|
"select",
|
|
@@ -3319,9 +4181,10 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3319
4181
|
{
|
|
3320
4182
|
type: "color",
|
|
3321
4183
|
title: "Text color",
|
|
3322
|
-
value: hexForColorInput(ts.color),
|
|
4184
|
+
value: hexForColorInput(textColor ?? ts.color),
|
|
3323
4185
|
onChange: (e) => {
|
|
3324
|
-
|
|
4186
|
+
const hex = e.target.value;
|
|
4187
|
+
void editor.chain().focus().setColor(hex).run();
|
|
3325
4188
|
},
|
|
3326
4189
|
style: {
|
|
3327
4190
|
width: 28,
|
|
@@ -3694,7 +4557,7 @@ function TextRichEditor({
|
|
|
3694
4557
|
}
|
|
3695
4558
|
}),
|
|
3696
4559
|
import_extension_text_style.TextStyle,
|
|
3697
|
-
import_extension_text_style.Color,
|
|
4560
|
+
import_extension_text_style.Color.configure({ types: ["textStyle"] }),
|
|
3698
4561
|
import_extension_text_style.FontFamily,
|
|
3699
4562
|
import_extension_text_style.FontSize,
|
|
3700
4563
|
import_extension_text_style.LineHeight,
|
|
@@ -4871,11 +5734,141 @@ function BlockSurfaceBgInspector({
|
|
|
4871
5734
|
] }) : null
|
|
4872
5735
|
] });
|
|
4873
5736
|
}
|
|
5737
|
+
function HtmlBlockRichPanel({
|
|
5738
|
+
block,
|
|
5739
|
+
onChange,
|
|
5740
|
+
C
|
|
5741
|
+
}) {
|
|
5742
|
+
const p = block.props;
|
|
5743
|
+
const { IS } = useIS(C);
|
|
5744
|
+
const [mode, setMode] = (0, import_react4.useState)("visual");
|
|
5745
|
+
const [htmlDraft, setHtmlDraft] = (0, import_react4.useState)("");
|
|
5746
|
+
(0, import_react4.useEffect)(() => {
|
|
5747
|
+
setMode("visual");
|
|
5748
|
+
}, [block.id]);
|
|
5749
|
+
const body = normalizeRichHtmlForStorage(typeof block.content === "string" ? block.content : "");
|
|
5750
|
+
const applyHtml = (html) => {
|
|
5751
|
+
const norm = normalizeRichHtmlForStorage(html);
|
|
5752
|
+
onChange({ ...block, content: norm, props: { ...p, content: norm } });
|
|
5753
|
+
};
|
|
5754
|
+
const tabBtn = (active) => ({
|
|
5755
|
+
flex: 1,
|
|
5756
|
+
padding: "7px 10px",
|
|
5757
|
+
borderRadius: 6,
|
|
5758
|
+
border: `1px solid ${C.border}`,
|
|
5759
|
+
background: active ? C.accent : C.surface,
|
|
5760
|
+
color: active ? "#ffffff" : C.text,
|
|
5761
|
+
fontSize: 12,
|
|
5762
|
+
fontWeight: 700,
|
|
5763
|
+
cursor: "pointer"
|
|
5764
|
+
});
|
|
5765
|
+
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: [
|
|
5766
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
5767
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5768
|
+
"button",
|
|
5769
|
+
{
|
|
5770
|
+
type: "button",
|
|
5771
|
+
style: tabBtn(mode === "visual"),
|
|
5772
|
+
onClick: () => {
|
|
5773
|
+
if (mode === "html") applyHtml(htmlDraft);
|
|
5774
|
+
setMode("visual");
|
|
5775
|
+
},
|
|
5776
|
+
children: "Rich text"
|
|
5777
|
+
}
|
|
5778
|
+
),
|
|
5779
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5780
|
+
"button",
|
|
5781
|
+
{
|
|
5782
|
+
type: "button",
|
|
5783
|
+
style: tabBtn(mode === "html"),
|
|
5784
|
+
onClick: () => {
|
|
5785
|
+
setHtmlDraft(body);
|
|
5786
|
+
setMode("html");
|
|
5787
|
+
},
|
|
5788
|
+
children: "HTML"
|
|
5789
|
+
}
|
|
5790
|
+
)
|
|
5791
|
+
] }),
|
|
5792
|
+
mode === "visual" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5793
|
+
TextRichEditor,
|
|
5794
|
+
{
|
|
5795
|
+
value: body,
|
|
5796
|
+
onChange: applyHtml,
|
|
5797
|
+
typography: {
|
|
5798
|
+
fontSize: p.fontSize,
|
|
5799
|
+
color: p.color,
|
|
5800
|
+
align: p.align,
|
|
5801
|
+
fontFamily: p.fontFamily,
|
|
5802
|
+
lineHeight: p.lineHeight,
|
|
5803
|
+
letterSpacing: p.letterSpacing,
|
|
5804
|
+
padding: p.padding
|
|
5805
|
+
},
|
|
5806
|
+
placeholder: "Write your rich content\u2026",
|
|
5807
|
+
headerTitle: "Rich editor",
|
|
5808
|
+
C
|
|
5809
|
+
},
|
|
5810
|
+
block.id
|
|
5811
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5812
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5813
|
+
"textarea",
|
|
5814
|
+
{
|
|
5815
|
+
spellCheck: false,
|
|
5816
|
+
value: htmlDraft,
|
|
5817
|
+
onChange: (e) => setHtmlDraft(e.target.value),
|
|
5818
|
+
placeholder: "<p>Your HTML\u2026</p>",
|
|
5819
|
+
style: {
|
|
5820
|
+
...IS,
|
|
5821
|
+
minHeight: 220,
|
|
5822
|
+
resize: "vertical",
|
|
5823
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
5824
|
+
fontSize: 12,
|
|
5825
|
+
lineHeight: 1.45
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
),
|
|
5829
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 10, color: C.muted, lineHeight: 1.45 }, children: [
|
|
5830
|
+
"Raw HTML. Switch to ",
|
|
5831
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Rich text" }),
|
|
5832
|
+
" to apply and preview in the visual editor."
|
|
5833
|
+
] }),
|
|
5834
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5835
|
+
"button",
|
|
5836
|
+
{
|
|
5837
|
+
type: "button",
|
|
5838
|
+
onClick: () => applyHtml(htmlDraft),
|
|
5839
|
+
style: {
|
|
5840
|
+
alignSelf: "flex-start",
|
|
5841
|
+
background: C.surface,
|
|
5842
|
+
border: `1px solid ${C.border}`,
|
|
5843
|
+
color: C.accent,
|
|
5844
|
+
borderRadius: 5,
|
|
5845
|
+
cursor: "pointer",
|
|
5846
|
+
fontSize: 11,
|
|
5847
|
+
padding: "6px 12px",
|
|
5848
|
+
fontWeight: 600
|
|
5849
|
+
},
|
|
5850
|
+
children: "Apply (normalize)"
|
|
5851
|
+
}
|
|
5852
|
+
)
|
|
5853
|
+
] })
|
|
5854
|
+
] }) });
|
|
5855
|
+
}
|
|
4874
5856
|
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4875
5857
|
const { IS, CI } = useIS(C);
|
|
4876
5858
|
const imageFileRef = (0, import_react4.useRef)(null);
|
|
4877
5859
|
const p = block.props;
|
|
4878
|
-
const set = (k, v) =>
|
|
5860
|
+
const set = (k, v) => {
|
|
5861
|
+
if (block.type === "html" && k === "content") {
|
|
5862
|
+
onChange({ ...block, content: v, props: { ...p, content: v } });
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
if (block.type === "html") {
|
|
5866
|
+
const body = typeof block.content === "string" ? block.content : typeof p.content === "string" ? p.content : "";
|
|
5867
|
+
onChange({ ...block, props: { ...p, [k]: v, content: body } });
|
|
5868
|
+
return;
|
|
5869
|
+
}
|
|
5870
|
+
onChange({ ...block, props: { ...p, [k]: v } });
|
|
5871
|
+
};
|
|
4879
5872
|
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
5873
|
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
5874
|
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: [
|
|
@@ -4947,10 +5940,10 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4947
5940
|
"textarea",
|
|
4948
5941
|
{
|
|
4949
5942
|
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
4950
|
-
value:
|
|
4951
|
-
onChange: (e) => set("content",
|
|
5943
|
+
value: String(p.content ?? ""),
|
|
5944
|
+
onChange: (e) => set("content", e.target.value),
|
|
4952
5945
|
spellCheck: false,
|
|
4953
|
-
placeholder: "
|
|
5946
|
+
placeholder: "Write your text\u2026"
|
|
4954
5947
|
},
|
|
4955
5948
|
block.id
|
|
4956
5949
|
) }),
|
|
@@ -4969,25 +5962,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4969
5962
|
] });
|
|
4970
5963
|
case "html":
|
|
4971
5964
|
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),
|
|
5965
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HtmlBlockRichPanel, { block, onChange, C }),
|
|
4991
5966
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
4992
5967
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
|
|
4993
5968
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
|
|
@@ -5007,8 +5982,25 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5007
5982
|
"Object position"
|
|
5008
5983
|
] }), 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
5984
|
Txt("alt", "Alt Text"),
|
|
5010
|
-
|
|
5011
|
-
|
|
5985
|
+
/* @__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: [
|
|
5986
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5987
|
+
"input",
|
|
5988
|
+
{
|
|
5989
|
+
type: "checkbox",
|
|
5990
|
+
checked: !!(p.linkEnabled ?? (p.link && String(p.link).trim())),
|
|
5991
|
+
onChange: (e) => {
|
|
5992
|
+
if (e.target.checked) set("linkEnabled", true);
|
|
5993
|
+
else onChange({ ...block, props: { ...p, linkEnabled: false, link: "" } });
|
|
5994
|
+
},
|
|
5995
|
+
style: { width: 15, height: 15, accentColor: C.accent }
|
|
5996
|
+
}
|
|
5997
|
+
),
|
|
5998
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p.linkEnabled ?? (p.link && String(p.link).trim()) ? "On" : "Off" })
|
|
5999
|
+
] }) }),
|
|
6000
|
+
p.linkEnabled ?? (p.link && String(p.link).trim()) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
6001
|
+
Txt("link", "Link URL"),
|
|
6002
|
+
Sel("linkTarget", "Open In", ["_blank", "_self"])
|
|
6003
|
+
] }) : null,
|
|
5012
6004
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
|
|
5013
6005
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
|
|
5014
6006
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -5358,7 +6350,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5358
6350
|
bgPosition: p.bgPosition ?? "center",
|
|
5359
6351
|
bgGradient: p.bgGradient ?? null,
|
|
5360
6352
|
cells: p.cells || [],
|
|
5361
|
-
|
|
6353
|
+
columns: getColumns(p)
|
|
5362
6354
|
},
|
|
5363
6355
|
onChange: (row) => onChange({
|
|
5364
6356
|
...block,
|
|
@@ -5376,7 +6368,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5376
6368
|
bgGradient: row.bgGradient,
|
|
5377
6369
|
ratios: [...row.ratios || [1]],
|
|
5378
6370
|
cells: row.cells || [],
|
|
5379
|
-
|
|
6371
|
+
columns: row.columns ?? getColumns(row)
|
|
5380
6372
|
}
|
|
5381
6373
|
}),
|
|
5382
6374
|
onClose,
|
|
@@ -5395,8 +6387,8 @@ function inferLayoutRowBgStep(r) {
|
|
|
5395
6387
|
if (String(r.bgColor || "").trim()) return "solid";
|
|
5396
6388
|
return null;
|
|
5397
6389
|
}
|
|
5398
|
-
function inferColumnBgStep(
|
|
5399
|
-
const cs = (
|
|
6390
|
+
function inferColumnBgStep(container, selCol) {
|
|
6391
|
+
const cs = getColumnPropsAt(container, selCol);
|
|
5400
6392
|
if (cs.bgGradient) return "gradient";
|
|
5401
6393
|
if (String(cs.bgImage || "").trim()) return "image";
|
|
5402
6394
|
if (String(cs.bgColor || "").trim()) return "solid";
|
|
@@ -5408,6 +6400,10 @@ function LayoutRowEditor({
|
|
|
5408
6400
|
onClose,
|
|
5409
6401
|
onUpload,
|
|
5410
6402
|
initialCol = null,
|
|
6403
|
+
rowIndex = -1,
|
|
6404
|
+
rowCount = 1,
|
|
6405
|
+
onMoveUp,
|
|
6406
|
+
onMoveDown,
|
|
5411
6407
|
C,
|
|
5412
6408
|
/** Shown under the inspector header when editing a Layout block */
|
|
5413
6409
|
customizationHint
|
|
@@ -5420,8 +6416,8 @@ function LayoutRowEditor({
|
|
|
5420
6416
|
const [colBgPickerMode, setColBgPickerMode] = (0, import_react4.useState)(null);
|
|
5421
6417
|
const inferredRowBg = (0, import_react4.useMemo)(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5422
6418
|
const inferredColBg = (0, import_react4.useMemo)(
|
|
5423
|
-
() => inferColumnBgStep(row
|
|
5424
|
-
[row.columnStyles, selCol]
|
|
6419
|
+
() => inferColumnBgStep(row, selCol),
|
|
6420
|
+
[row.columns, row.columnStyles, selCol]
|
|
5425
6421
|
);
|
|
5426
6422
|
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5427
6423
|
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
@@ -5460,13 +6456,12 @@ function LayoutRowEditor({
|
|
|
5460
6456
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5461
6457
|
};
|
|
5462
6458
|
const updCol = (i, upd) => {
|
|
5463
|
-
const
|
|
5464
|
-
|
|
5465
|
-
set("columnStyles", styles);
|
|
6459
|
+
const columns = patchColumnPropsAt(row, i, upd);
|
|
6460
|
+
onChange(withColumnsOnly(row, columns));
|
|
5466
6461
|
};
|
|
5467
6462
|
const setColBgMode = (mode) => {
|
|
5468
6463
|
setColBgPickerMode(mode);
|
|
5469
|
-
const cs = (row
|
|
6464
|
+
const cs = getColumnPropsAt(row, selCol);
|
|
5470
6465
|
if (mode === "solid") {
|
|
5471
6466
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
5472
6467
|
return;
|
|
@@ -5498,7 +6493,51 @@ function LayoutRowEditor({
|
|
|
5498
6493
|
set("bgGradient", { angle: 135, colors: ["#0ea5e9", "#6366f1", "#ec4899"], stops: [0, 50, 100] });
|
|
5499
6494
|
}
|
|
5500
6495
|
};
|
|
6496
|
+
const canMoveUp = rowIndex > 0;
|
|
6497
|
+
const canMoveDown = rowIndex >= 0 && rowIndex < rowCount - 1;
|
|
5501
6498
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
6499
|
+
onMoveUp && onMoveDown ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [
|
|
6500
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
6501
|
+
"button",
|
|
6502
|
+
{
|
|
6503
|
+
type: "button",
|
|
6504
|
+
disabled: !canMoveUp,
|
|
6505
|
+
onClick: onMoveUp,
|
|
6506
|
+
style: {
|
|
6507
|
+
flex: 1,
|
|
6508
|
+
padding: "8px 10px",
|
|
6509
|
+
fontSize: 12,
|
|
6510
|
+
fontWeight: 600,
|
|
6511
|
+
borderRadius: 8,
|
|
6512
|
+
border: `1px solid ${C.border}`,
|
|
6513
|
+
background: canMoveUp ? C.surface : C.canvas,
|
|
6514
|
+
color: canMoveUp ? C.text : C.muted,
|
|
6515
|
+
cursor: canMoveUp ? "pointer" : "not-allowed"
|
|
6516
|
+
},
|
|
6517
|
+
children: "\u2191 Move section up"
|
|
6518
|
+
}
|
|
6519
|
+
),
|
|
6520
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
6521
|
+
"button",
|
|
6522
|
+
{
|
|
6523
|
+
type: "button",
|
|
6524
|
+
disabled: !canMoveDown,
|
|
6525
|
+
onClick: onMoveDown,
|
|
6526
|
+
style: {
|
|
6527
|
+
flex: 1,
|
|
6528
|
+
padding: "8px 10px",
|
|
6529
|
+
fontSize: 12,
|
|
6530
|
+
fontWeight: 600,
|
|
6531
|
+
borderRadius: 8,
|
|
6532
|
+
border: `1px solid ${C.border}`,
|
|
6533
|
+
background: canMoveDown ? C.surface : C.canvas,
|
|
6534
|
+
color: canMoveDown ? C.text : C.muted,
|
|
6535
|
+
cursor: canMoveDown ? "pointer" : "not-allowed"
|
|
6536
|
+
},
|
|
6537
|
+
children: "\u2193 Move section down"
|
|
6538
|
+
}
|
|
6539
|
+
)
|
|
6540
|
+
] }) : null,
|
|
5502
6541
|
customizationHint ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5503
6542
|
"div",
|
|
5504
6543
|
{
|
|
@@ -5648,15 +6687,15 @@ function LayoutRowEditor({
|
|
|
5648
6687
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5649
6688
|
ColorSwatchGrid,
|
|
5650
6689
|
{
|
|
5651
|
-
value: (row
|
|
6690
|
+
value: getColumnPropsAt(row, selCol)?.bgColor || "",
|
|
5652
6691
|
C,
|
|
5653
6692
|
compact: true,
|
|
5654
6693
|
onPick: (hex) => updCol(selCol, { bgColor: hex === "#ffffff" ? "" : hex })
|
|
5655
6694
|
}
|
|
5656
6695
|
),
|
|
5657
6696
|
/* @__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
|
|
6697
|
+
/* @__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 }) }),
|
|
6698
|
+
/* @__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
6699
|
] })
|
|
5661
6700
|
] }) }) : null,
|
|
5662
6701
|
colBgUiStep === "image" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
@@ -5664,7 +6703,7 @@ function LayoutRowEditor({
|
|
|
5664
6703
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Image, { size: 13, strokeWidth: 2.25, "aria-hidden": true }),
|
|
5665
6704
|
"bg image"
|
|
5666
6705
|
] }), 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
|
|
6706
|
+
/* @__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
6707
|
onUpload && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 0 }, children: [
|
|
5669
6708
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5670
6709
|
"input",
|
|
@@ -5702,18 +6741,18 @@ function LayoutRowEditor({
|
|
|
5702
6741
|
] })
|
|
5703
6742
|
] }) }),
|
|
5704
6743
|
/* @__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
|
|
6744
|
+
/* @__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
6745
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "auto", children: "Auto" }),
|
|
5707
6746
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "cover", children: "Cover" }),
|
|
5708
6747
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "contain", children: "Contain" })
|
|
5709
6748
|
] }) }) }),
|
|
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
|
|
6749
|
+
/* @__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
6750
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "no-repeat", children: "No repeat" }),
|
|
5712
6751
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat", children: "Repeat" }),
|
|
5713
6752
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat-x", children: "Repeat X" }),
|
|
5714
6753
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "repeat-y", children: "Repeat Y" })
|
|
5715
6754
|
] }) }) }),
|
|
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
|
|
6755
|
+
/* @__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
6756
|
] })
|
|
5718
6757
|
] }) : null,
|
|
5719
6758
|
colBgUiStep === "gradient" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("details", { open: true, style: { marginBottom: 12 }, children: [
|
|
@@ -5722,15 +6761,15 @@ function LayoutRowEditor({
|
|
|
5722
6761
|
DynamicGradientField,
|
|
5723
6762
|
{
|
|
5724
6763
|
label: "gradient",
|
|
5725
|
-
value: (row
|
|
6764
|
+
value: getColumnPropsAt(row, selCol)?.bgGradient,
|
|
5726
6765
|
onChange: (g) => updCol(selCol, { bgGradient: g }),
|
|
5727
6766
|
defaults: { colors: ["#0ea5e9", "#6366f1", "#ec4899"] },
|
|
5728
6767
|
C
|
|
5729
6768
|
}
|
|
5730
6769
|
) })
|
|
5731
6770
|
] }) : 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
|
|
6771
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: getColumnPropsAt(row, selCol).padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
|
|
6772
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: getColumnPropsAt(row, selCol).borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
|
|
5734
6773
|
] })
|
|
5735
6774
|
] })
|
|
5736
6775
|
] });
|
|
@@ -6521,6 +7560,11 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6521
7560
|
const [selContentMeta, setSelMeta] = (0, import_react8.useState)(null);
|
|
6522
7561
|
const [dragOver, setDragOver] = (0, import_react8.useState)(null);
|
|
6523
7562
|
const [draggingRowId, setDraggingRowId] = (0, import_react8.useState)(null);
|
|
7563
|
+
const draggingRowIdRef = (0, import_react8.useRef)(null);
|
|
7564
|
+
const setRowDrag = (0, import_react8.useCallback)((id) => {
|
|
7565
|
+
draggingRowIdRef.current = id;
|
|
7566
|
+
setDraggingRowId(id);
|
|
7567
|
+
}, []);
|
|
6524
7568
|
const [history, setHistory] = (0, import_react8.useState)([]);
|
|
6525
7569
|
const [future, setFuture] = (0, import_react8.useState)([]);
|
|
6526
7570
|
const [showPreviewModal, setShowPreview] = (0, import_react8.useState)(false);
|
|
@@ -6555,10 +7599,17 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6555
7599
|
});
|
|
6556
7600
|
const [pageBgUiStep, setPageBgUiStep] = (0, import_react8.useState)(null);
|
|
6557
7601
|
const [contentBgUiStep, setContentBgUiStep] = (0, import_react8.useState)(null);
|
|
7602
|
+
const pageBgLastSolidRef = (0, import_react8.useRef)("#f1f5f9");
|
|
7603
|
+
const pageBgIsOff = !String(settings.bgColor ?? "").trim() && !String(settings.bgImage ?? "").trim() && !settings.bgGradient;
|
|
6558
7604
|
const applyPageBgMode = (mode) => {
|
|
6559
7605
|
setPageBgUiStep(mode);
|
|
6560
7606
|
if (mode === "solid") {
|
|
6561
|
-
setSettings((s) =>
|
|
7607
|
+
setSettings((s) => {
|
|
7608
|
+
const next = { ...s, bgGradient: null, bgImage: "" };
|
|
7609
|
+
const raw = typeof next.bgColor === "string" ? next.bgColor.trim() : "";
|
|
7610
|
+
if (!raw) next.bgColor = pageBgLastSolidRef.current || "#f1f5f9";
|
|
7611
|
+
return next;
|
|
7612
|
+
});
|
|
6562
7613
|
return;
|
|
6563
7614
|
}
|
|
6564
7615
|
if (mode === "image") {
|
|
@@ -6668,7 +7719,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6668
7719
|
return () => window.removeEventListener("keydown", handler);
|
|
6669
7720
|
}, [rows, history, future, selContentMeta]);
|
|
6670
7721
|
const buildApi = (0, import_react8.useCallback)(() => ({
|
|
6671
|
-
loadJson(input) {
|
|
7722
|
+
loadJson(input, options2) {
|
|
6672
7723
|
setJsonLoading(true);
|
|
6673
7724
|
requestAnimationFrame(() => {
|
|
6674
7725
|
requestAnimationFrame(() => {
|
|
@@ -6681,7 +7732,14 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6681
7732
|
return;
|
|
6682
7733
|
}
|
|
6683
7734
|
}
|
|
6684
|
-
const
|
|
7735
|
+
const coerced = coerceEmailDocumentInput(parsed);
|
|
7736
|
+
if (options2?.mode === "append") {
|
|
7737
|
+
const extra = emailDocumentToEditorRows(coerced ?? parsed);
|
|
7738
|
+
if (!extra?.length) return;
|
|
7739
|
+
setRows((prev) => [...prev, ...extra]);
|
|
7740
|
+
return;
|
|
7741
|
+
}
|
|
7742
|
+
const norm = normalizeEmailDesignInput(coerced ?? parsed);
|
|
6685
7743
|
if (!norm) return;
|
|
6686
7744
|
setRows(norm.rows);
|
|
6687
7745
|
const ns = norm.settings;
|
|
@@ -6718,6 +7776,8 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6718
7776
|
}, []);
|
|
6719
7777
|
const handleRowDrop = (targetIdx, e) => {
|
|
6720
7778
|
if (e) {
|
|
7779
|
+
e.preventDefault();
|
|
7780
|
+
e.stopPropagation();
|
|
6721
7781
|
try {
|
|
6722
7782
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
6723
7783
|
if (data.layoutPresetKey) {
|
|
@@ -6736,18 +7796,28 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6736
7796
|
} catch {
|
|
6737
7797
|
}
|
|
6738
7798
|
}
|
|
6739
|
-
|
|
7799
|
+
const moveId = draggingRowIdRef.current ?? draggingRowId ?? (() => {
|
|
7800
|
+
if (!e) return null;
|
|
7801
|
+
try {
|
|
7802
|
+
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
7803
|
+
return typeof data.moveRowId === "string" ? data.moveRowId : null;
|
|
7804
|
+
} catch {
|
|
7805
|
+
return null;
|
|
7806
|
+
}
|
|
7807
|
+
})();
|
|
7808
|
+
if (moveId) {
|
|
6740
7809
|
mutate((prev) => {
|
|
6741
|
-
const fi = prev.findIndex((r) => r.id ===
|
|
7810
|
+
const fi = prev.findIndex((r) => r.id === moveId);
|
|
6742
7811
|
if (fi === -1) return prev;
|
|
6743
7812
|
const next = [...prev];
|
|
6744
7813
|
const [m] = next.splice(fi, 1);
|
|
6745
|
-
|
|
7814
|
+
const insertAt = targetIdx > fi ? targetIdx - 1 : targetIdx;
|
|
7815
|
+
next.splice(Math.max(0, Math.min(insertAt, next.length)), 0, m);
|
|
6746
7816
|
return next;
|
|
6747
7817
|
});
|
|
6748
7818
|
}
|
|
6749
7819
|
setDragOver(null);
|
|
6750
|
-
|
|
7820
|
+
setRowDrag(null);
|
|
6751
7821
|
};
|
|
6752
7822
|
const addRow = (preset) => mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
6753
7823
|
const deleteRow = (id) => {
|
|
@@ -6877,8 +7947,27 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6877
7947
|
}
|
|
6878
7948
|
}
|
|
6879
7949
|
};
|
|
7950
|
+
const rootContentPreset = LAYOUT_PRESETS.find((p) => p.cols === 1) ?? LAYOUT_PRESETS[0];
|
|
7951
|
+
const insertRootContentBlock = (contentType) => {
|
|
7952
|
+
const { row: newRow, block: nb } = makeRootContentRow(contentType, rootContentPreset);
|
|
7953
|
+
const anchorRowId = selContentMeta?.rowId ?? selectedRowId ?? null;
|
|
7954
|
+
mutate((prev) => {
|
|
7955
|
+
if (anchorRowId) {
|
|
7956
|
+
const i = prev.findIndex((r) => r.id === anchorRowId);
|
|
7957
|
+
if (i >= 0) {
|
|
7958
|
+
const next = [...prev];
|
|
7959
|
+
next.splice(i + 1, 0, newRow);
|
|
7960
|
+
return next;
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
return [...prev, newRow];
|
|
7964
|
+
});
|
|
7965
|
+
setSelectedRowId(null);
|
|
7966
|
+
setSelContentId(nb.id);
|
|
7967
|
+
setSelMeta({ rowId: newRow.id, cellIdx: 0, contentIdx: 0 });
|
|
7968
|
+
};
|
|
6880
7969
|
const insertBlockFromLibrary = (contentType) => {
|
|
6881
|
-
|
|
7970
|
+
const isLayoutType = contentType === "layout" || contentType === "nestedRow";
|
|
6882
7971
|
if (selContentMeta?.inner) {
|
|
6883
7972
|
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
6884
7973
|
dropContent(rowId, cellIdx, {
|
|
@@ -6889,30 +7978,26 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
6889
7978
|
});
|
|
6890
7979
|
return;
|
|
6891
7980
|
}
|
|
6892
|
-
if (
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
7981
|
+
if (isLayoutType) {
|
|
7982
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0 && typeof selContentMeta.contentIdx === "number" && selContentMeta.contentIdx >= 0) {
|
|
7983
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
7984
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
7985
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
7986
|
+
kind: "new",
|
|
7987
|
+
contentType,
|
|
7988
|
+
insertAt: null
|
|
7989
|
+
});
|
|
7990
|
+
return;
|
|
7991
|
+
}
|
|
6901
7992
|
}
|
|
7993
|
+
addRow(rootContentPreset);
|
|
7994
|
+
return;
|
|
6902
7995
|
}
|
|
6903
|
-
if (
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
dropContent(targetRow.id, 0, {
|
|
6907
|
-
kind: "new",
|
|
6908
|
-
contentType,
|
|
6909
|
-
insertAt: null
|
|
6910
|
-
});
|
|
6911
|
-
return;
|
|
6912
|
-
}
|
|
7996
|
+
if (!rows.length) {
|
|
7997
|
+
insertRootContentBlock(contentType);
|
|
7998
|
+
return;
|
|
6913
7999
|
}
|
|
6914
|
-
|
|
6915
|
-
dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
|
|
8000
|
+
insertRootContentBlock(contentType);
|
|
6916
8001
|
};
|
|
6917
8002
|
const deleteContent = (rowId, cellIdx, ci, inner = null) => {
|
|
6918
8003
|
if (!inner) {
|
|
@@ -7507,6 +8592,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7507
8592
|
e.preventDefault();
|
|
7508
8593
|
try {
|
|
7509
8594
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8595
|
+
if (data.moveRowId) return;
|
|
7510
8596
|
if (data.layoutPresetKey) {
|
|
7511
8597
|
const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
|
|
7512
8598
|
if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
@@ -7561,6 +8647,7 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7561
8647
|
e.stopPropagation();
|
|
7562
8648
|
try {
|
|
7563
8649
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8650
|
+
if (data.moveRowId) return;
|
|
7564
8651
|
if (data.contentType && typeof data.contentType === "string") {
|
|
7565
8652
|
insertBlockFromLibrary(data.contentType);
|
|
7566
8653
|
}
|
|
@@ -7582,10 +8669,11 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7582
8669
|
selectedRowId,
|
|
7583
8670
|
selContentMeta,
|
|
7584
8671
|
dragOver,
|
|
8672
|
+
draggingRowId,
|
|
7585
8673
|
C,
|
|
7586
8674
|
setDragOver,
|
|
7587
8675
|
handleRowDrop,
|
|
7588
|
-
|
|
8676
|
+
setRowDrag,
|
|
7589
8677
|
setSelectedRowId,
|
|
7590
8678
|
setSelContentId,
|
|
7591
8679
|
setSelMeta,
|
|
@@ -7603,14 +8691,20 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7603
8691
|
id: eid("canvas-row-drop-end"),
|
|
7604
8692
|
onDragOver: (e) => {
|
|
7605
8693
|
e.preventDefault();
|
|
8694
|
+
e.stopPropagation();
|
|
7606
8695
|
setDragOver(rows.length);
|
|
7607
8696
|
},
|
|
7608
8697
|
onDragLeave: () => setDragOver(null),
|
|
7609
|
-
onDrop: (e) =>
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
8698
|
+
onDrop: (e) => handleRowDrop(rows.length, e),
|
|
8699
|
+
style: {
|
|
8700
|
+
height: dragOver === rows.length ? 28 : 10,
|
|
8701
|
+
background: dragOver === rows.length ? `${C.accent}25` : "transparent",
|
|
8702
|
+
border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
8703
|
+
borderRadius: 4,
|
|
8704
|
+
transition: "all .12s",
|
|
8705
|
+
margin: "2px 0",
|
|
8706
|
+
boxSizing: "border-box"
|
|
8707
|
+
}
|
|
7614
8708
|
}
|
|
7615
8709
|
)
|
|
7616
8710
|
]
|
|
@@ -7722,6 +8816,10 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7722
8816
|
onClose: () => setSelectedRowId(null),
|
|
7723
8817
|
onUpload,
|
|
7724
8818
|
initialCol: selContentMeta?.rowId === selectedRowId ? selContentMeta.cellIdx : null,
|
|
8819
|
+
rowIndex: rows.findIndex((r) => r.id === selectedRowId),
|
|
8820
|
+
rowCount: rows.length,
|
|
8821
|
+
onMoveUp: () => selectedRow && moveRow(selectedRow.id, -1),
|
|
8822
|
+
onMoveDown: () => selectedRow && moveRow(selectedRow.id, 1),
|
|
7725
8823
|
C
|
|
7726
8824
|
}
|
|
7727
8825
|
) })
|
|
@@ -7898,7 +8996,40 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7898
8996
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("summary", { style: railSectionSummary, children: "Background" }),
|
|
7899
8997
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: railSectionBody, children: [
|
|
7900
8998
|
/* @__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)(
|
|
8999
|
+
/* @__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: [
|
|
9000
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
9001
|
+
"input",
|
|
9002
|
+
{
|
|
9003
|
+
type: "checkbox",
|
|
9004
|
+
checked: pageBgIsOff,
|
|
9005
|
+
onChange: (e) => {
|
|
9006
|
+
const off = e.target.checked;
|
|
9007
|
+
if (off) {
|
|
9008
|
+
const cur = String(settings.bgColor ?? "").trim();
|
|
9009
|
+
if (cur) pageBgLastSolidRef.current = cur;
|
|
9010
|
+
setPageBgUiStep(null);
|
|
9011
|
+
setSettings((s) => ({ ...s, bgColor: "", bgImage: "", bgGradient: null }));
|
|
9012
|
+
} else {
|
|
9013
|
+
setPageBgUiStep("solid");
|
|
9014
|
+
setSettings((s) => ({
|
|
9015
|
+
...s,
|
|
9016
|
+
bgColor: pageBgLastSolidRef.current || "#f1f5f9"
|
|
9017
|
+
}));
|
|
9018
|
+
}
|
|
9019
|
+
},
|
|
9020
|
+
style: { width: 15, height: 15, accentColor: C.accent, cursor: "pointer" }
|
|
9021
|
+
}
|
|
9022
|
+
),
|
|
9023
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: "No page background" })
|
|
9024
|
+
] }) }),
|
|
9025
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
9026
|
+
BgModeButtons,
|
|
9027
|
+
{
|
|
9028
|
+
value: pageBgIsOff ? null : pageBgUiStep,
|
|
9029
|
+
onChange: applyPageBgMode,
|
|
9030
|
+
C
|
|
9031
|
+
}
|
|
9032
|
+
),
|
|
7902
9033
|
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
9034
|
pageBgUiStep === "solid" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
7904
9035
|
ColorField,
|
|
@@ -7908,7 +9039,10 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
7908
9039
|
"bg color"
|
|
7909
9040
|
] }),
|
|
7910
9041
|
value: settings.bgColor,
|
|
7911
|
-
onChange: (v) =>
|
|
9042
|
+
onChange: (v) => {
|
|
9043
|
+
pageBgLastSolidRef.current = v;
|
|
9044
|
+
setSettings((s) => ({ ...s, bgColor: v }));
|
|
9045
|
+
},
|
|
7912
9046
|
placeholder: "#f1f5f9",
|
|
7913
9047
|
C
|
|
7914
9048
|
}
|
|
@@ -8149,81 +9283,18 @@ var ReactEmailEditorComponent = (0, import_react8.forwardRef)(
|
|
|
8149
9283
|
);
|
|
8150
9284
|
}
|
|
8151
9285
|
);
|
|
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
|
-
}
|
|
9286
|
+
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml, htmlToJson });
|
|
8219
9287
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8220
9288
|
0 && (module.exports = {
|
|
8221
9289
|
EmailPreviewModal,
|
|
8222
9290
|
ReactEmailEditor,
|
|
8223
9291
|
base64ToUtf8,
|
|
9292
|
+
canonicalizeEmailDocument,
|
|
9293
|
+
coerceEmailDocumentInput,
|
|
8224
9294
|
emailPreviewDevices,
|
|
8225
9295
|
extractHtmlForDesign,
|
|
8226
9296
|
htmlToEmailDesignTemplate,
|
|
9297
|
+
htmlToJson,
|
|
8227
9298
|
jsonToHtml,
|
|
8228
9299
|
utf8ToBase64
|
|
8229
9300
|
});
|