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.js
CHANGED
|
@@ -464,9 +464,9 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
464
464
|
fontFamily: "Georgia,serif",
|
|
465
465
|
margin: { ...BOX0 }
|
|
466
466
|
},
|
|
467
|
+
/** Rich HTML body lives on the block root as `content` (string), not in `props`. */
|
|
467
468
|
html: {
|
|
468
469
|
...BLOCK_BG,
|
|
469
|
-
content: "<p>Rich HTML content. Edit with the rich editor.</p>",
|
|
470
470
|
fontSize: 15,
|
|
471
471
|
color: "#1e293b",
|
|
472
472
|
align: "left",
|
|
@@ -486,6 +486,7 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
486
486
|
align: "center",
|
|
487
487
|
padding: boxAll(8),
|
|
488
488
|
link: "",
|
|
489
|
+
linkEnabled: false,
|
|
489
490
|
linkTarget: "_blank",
|
|
490
491
|
borderRadius: { tl: 0, tr: 0, br: 0, bl: 0 },
|
|
491
492
|
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
|
@@ -607,10 +608,82 @@ var DEFAULT_BLOCK_PROPS = {
|
|
|
607
608
|
margin: { ...BOX0 }
|
|
608
609
|
}
|
|
609
610
|
};
|
|
611
|
+
var DEFAULT_HTML_BLOCK_MARKUP = "<p>Rich HTML content. Edit with the rich editor.</p>";
|
|
612
|
+
function imageLinkActive(p) {
|
|
613
|
+
const url = typeof p.link === "string" ? p.link.trim() : "";
|
|
614
|
+
const enabled = p.linkEnabled ?? !!url;
|
|
615
|
+
return enabled && !!url;
|
|
616
|
+
}
|
|
610
617
|
function isKnownBlockType(type) {
|
|
611
618
|
return typeof type === "string" && type in DEFAULT_BLOCK_PROPS;
|
|
612
619
|
}
|
|
613
620
|
|
|
621
|
+
// src/lib/columnProps.ts
|
|
622
|
+
var BOX02 = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
623
|
+
var DEFAULT_COLUMN_PROPS = {
|
|
624
|
+
bgColor: "",
|
|
625
|
+
bgImage: "",
|
|
626
|
+
bgGradient: null,
|
|
627
|
+
bgSize: "cover",
|
|
628
|
+
bgRepeat: "no-repeat",
|
|
629
|
+
bgPosition: "center",
|
|
630
|
+
padding: { ...BOX02 },
|
|
631
|
+
borderRadius: 0
|
|
632
|
+
};
|
|
633
|
+
function isObj(x) {
|
|
634
|
+
return x !== null && typeof x === "object" && !Array.isArray(x);
|
|
635
|
+
}
|
|
636
|
+
function colCountFromContainer(c) {
|
|
637
|
+
const cells = Array.isArray(c.cells) ? c.cells.length : 0;
|
|
638
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios.length : 0;
|
|
639
|
+
const cols = typeof c.cols === "number" && c.cols > 0 ? c.cols : 0;
|
|
640
|
+
return Math.max(1, cells, ratios, cols);
|
|
641
|
+
}
|
|
642
|
+
function columnsFromLegacyMap(map, count) {
|
|
643
|
+
const n = Math.max(1, count);
|
|
644
|
+
const src = isObj(map) ? map : {};
|
|
645
|
+
return Array.from({ length: n }, (_, i) => {
|
|
646
|
+
const raw = src[i] ?? src[String(i)];
|
|
647
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...isObj(raw) ? raw : {} } };
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
function getColumns(container) {
|
|
651
|
+
const count = colCountFromContainer(container);
|
|
652
|
+
if (Array.isArray(container.columns)) {
|
|
653
|
+
const cols = container.columns;
|
|
654
|
+
return Array.from({ length: count }, (_, i) => {
|
|
655
|
+
const entry = cols[i];
|
|
656
|
+
if (isObj(entry) && isObj(entry.props)) {
|
|
657
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...entry.props } };
|
|
658
|
+
}
|
|
659
|
+
if (isObj(entry)) {
|
|
660
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...entry } };
|
|
661
|
+
}
|
|
662
|
+
return { props: { ...DEFAULT_COLUMN_PROPS } };
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (container.columnStyles) {
|
|
666
|
+
return columnsFromLegacyMap(container.columnStyles, count);
|
|
667
|
+
}
|
|
668
|
+
return columnsFromLegacyMap({}, count);
|
|
669
|
+
}
|
|
670
|
+
function getColumnPropsAt(container, index) {
|
|
671
|
+
const cols = getColumns(container);
|
|
672
|
+
const i = Math.max(0, Math.min(index, cols.length - 1));
|
|
673
|
+
return cols[i]?.props ?? { ...DEFAULT_COLUMN_PROPS };
|
|
674
|
+
}
|
|
675
|
+
function patchColumnPropsAt(container, index, patch) {
|
|
676
|
+
const cols = getColumns(container);
|
|
677
|
+
const i = Math.max(0, index);
|
|
678
|
+
while (cols.length <= i) cols.push({ props: { ...DEFAULT_COLUMN_PROPS } });
|
|
679
|
+
cols[i] = { props: { ...cols[i].props, ...patch } };
|
|
680
|
+
return cols;
|
|
681
|
+
}
|
|
682
|
+
function withColumnsOnly(row, columns) {
|
|
683
|
+
const { columnStyles: _drop, ...rest } = row;
|
|
684
|
+
return { ...rest, columns };
|
|
685
|
+
}
|
|
686
|
+
|
|
614
687
|
// src/lib/factories.ts
|
|
615
688
|
function uid() {
|
|
616
689
|
return Math.random().toString(36).slice(2, 10);
|
|
@@ -650,14 +723,30 @@ function makeNestedRowBlock(preset) {
|
|
|
650
723
|
bgRepeat: row.bgRepeat ?? "no-repeat",
|
|
651
724
|
bgPosition: row.bgPosition ?? "center",
|
|
652
725
|
bgGradient: row.bgGradient ?? null,
|
|
653
|
-
|
|
726
|
+
columns: row.columns ?? row.cells?.map(() => ({ props: { ...DEFAULT_COLUMN_PROPS } })),
|
|
654
727
|
cells: row.cells
|
|
655
728
|
}
|
|
656
729
|
};
|
|
657
730
|
}
|
|
658
731
|
function makeContentBlock(type) {
|
|
732
|
+
if (type === "html") {
|
|
733
|
+
return {
|
|
734
|
+
id: uid(),
|
|
735
|
+
type,
|
|
736
|
+
content: DEFAULT_HTML_BLOCK_MARKUP,
|
|
737
|
+
props: { ...DEFAULT_BLOCK_PROPS.html, content: DEFAULT_HTML_BLOCK_MARKUP }
|
|
738
|
+
};
|
|
739
|
+
}
|
|
659
740
|
return { id: uid(), type, props: { ...DEFAULT_BLOCK_PROPS[type] } };
|
|
660
741
|
}
|
|
742
|
+
function makeRootContentRow(contentType, preset) {
|
|
743
|
+
const row = makeLayoutRow(preset);
|
|
744
|
+
row.gap = 0;
|
|
745
|
+
row.padding = 0;
|
|
746
|
+
const block = makeContentBlock(contentType);
|
|
747
|
+
row.cells[0] = [block];
|
|
748
|
+
return { row, block };
|
|
749
|
+
}
|
|
661
750
|
|
|
662
751
|
// src/lib/columnPath.ts
|
|
663
752
|
var MAX_NESTED_LAYOUT_DEPTH = 1;
|
|
@@ -667,6 +756,13 @@ function nestedLayoutDepth(nested) {
|
|
|
667
756
|
function isSplitLayoutBlock(b) {
|
|
668
757
|
return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
|
|
669
758
|
}
|
|
759
|
+
function isRootContentRow(row) {
|
|
760
|
+
if (!row?.cells || row.cells.length !== 1) return false;
|
|
761
|
+
const col = row.cells[0];
|
|
762
|
+
if (!Array.isArray(col) || col.length !== 1) return false;
|
|
763
|
+
const b = col[0];
|
|
764
|
+
return !!b && typeof b.type === "string" && b.type !== "layout" && b.type !== "nestedRow";
|
|
765
|
+
}
|
|
670
766
|
function getColumnBlocks(rows, loc) {
|
|
671
767
|
const r = rows.find((x) => x.id === loc.rowId);
|
|
672
768
|
if (!r) return [];
|
|
@@ -732,56 +828,6 @@ function countBlocksInDesign(rows) {
|
|
|
732
828
|
return rows.reduce((sum, r) => sum + r.cells.reduce((s, c) => s + colBlocks(c), 0), 0);
|
|
733
829
|
}
|
|
734
830
|
|
|
735
|
-
// src/socialIcons.tsx
|
|
736
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
737
|
-
var GLYPHS = {
|
|
738
|
-
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",
|
|
739
|
-
/** X (formerly Twitter) — Simple Icons “X” */
|
|
740
|
-
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",
|
|
741
|
-
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",
|
|
742
|
-
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",
|
|
743
|
-
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",
|
|
744
|
-
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",
|
|
745
|
-
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",
|
|
746
|
-
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",
|
|
747
|
-
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",
|
|
748
|
-
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"
|
|
749
|
-
};
|
|
750
|
-
function iconFillForNetwork(network) {
|
|
751
|
-
return network === "snapchat" ? "#000000" : "#ffffff";
|
|
752
|
-
}
|
|
753
|
-
function glyphPath(network) {
|
|
754
|
-
return GLYPHS[network] || null;
|
|
755
|
-
}
|
|
756
|
-
function socialIconSvgString(network, pixelSize) {
|
|
757
|
-
const d = glyphPath(network);
|
|
758
|
-
const fill = iconFillForNetwork(network);
|
|
759
|
-
const s = Math.max(8, Math.round(pixelSize));
|
|
760
|
-
if (!d) {
|
|
761
|
-
const letter = (network && network[0] ? network[0] : "?").toUpperCase();
|
|
762
|
-
return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
|
|
763
|
-
}
|
|
764
|
-
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>`;
|
|
765
|
-
}
|
|
766
|
-
function SocialGlyph({ network, size }) {
|
|
767
|
-
const d = glyphPath(network);
|
|
768
|
-
const s = Math.max(8, Math.round(size));
|
|
769
|
-
if (!d) {
|
|
770
|
-
return /* @__PURE__ */ jsx2("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
|
|
771
|
-
}
|
|
772
|
-
return /* @__PURE__ */ jsx2(
|
|
773
|
-
"svg",
|
|
774
|
-
{
|
|
775
|
-
width: s,
|
|
776
|
-
height: s,
|
|
777
|
-
viewBox: "0 0 24 24",
|
|
778
|
-
style: { display: "block", flexShrink: 0 },
|
|
779
|
-
"aria-hidden": true,
|
|
780
|
-
children: /* @__PURE__ */ jsx2("path", { fill: "currentColor", d })
|
|
781
|
-
}
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
831
|
// src/lib/htmlUtils.ts
|
|
786
832
|
function escHtmlAttr(s) {
|
|
787
833
|
return String(s ?? "").replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -823,7 +869,7 @@ function videoUrlToEmbedSrc(url) {
|
|
|
823
869
|
if (vi) return `https://player.vimeo.com/video/${vi[1]}`;
|
|
824
870
|
return url;
|
|
825
871
|
}
|
|
826
|
-
var EMPTY_RICH_HTML = "
|
|
872
|
+
var EMPTY_RICH_HTML = "";
|
|
827
873
|
function unwrapAllDivElements(container) {
|
|
828
874
|
let el;
|
|
829
875
|
while (el = container.querySelector("div")) {
|
|
@@ -850,6 +896,161 @@ function normalizeRichHtmlForStorage(html) {
|
|
|
850
896
|
function isEffectivelyEmptyRichHtml(html) {
|
|
851
897
|
return normalizeRichHtmlForStorage(html) === EMPTY_RICH_HTML;
|
|
852
898
|
}
|
|
899
|
+
var RICH_HTML_CONTENT_CSS = `
|
|
900
|
+
.email-rich-html-content ul,
|
|
901
|
+
.email-rich-html-content ol {
|
|
902
|
+
margin: 0.35em 0;
|
|
903
|
+
padding-left: 1.35rem;
|
|
904
|
+
list-style-position: outside;
|
|
905
|
+
}
|
|
906
|
+
.email-rich-html-content ul { list-style-type: disc; }
|
|
907
|
+
.email-rich-html-content ol { list-style-type: decimal; }
|
|
908
|
+
.email-rich-html-content li { margin: 0.2em 0; }
|
|
909
|
+
.email-rich-html-content li > p { margin: 0; }
|
|
910
|
+
.email-rich-html-content p { margin: 0.35em 0; }
|
|
911
|
+
.email-rich-html-content h2,
|
|
912
|
+
.email-rich-html-content h3,
|
|
913
|
+
.email-rich-html-content h4 {
|
|
914
|
+
margin: 0.5em 0 0.25em;
|
|
915
|
+
line-height: 1.25;
|
|
916
|
+
font-weight: 700;
|
|
917
|
+
}
|
|
918
|
+
.email-rich-html-content blockquote {
|
|
919
|
+
margin: 0.35em 0;
|
|
920
|
+
padding-left: 0.75rem;
|
|
921
|
+
border-left: 3px solid #cbd5e1;
|
|
922
|
+
}
|
|
923
|
+
.email-rich-html-content hr {
|
|
924
|
+
border: none;
|
|
925
|
+
border-top: 1px solid #e2e8f0;
|
|
926
|
+
margin: 0.75em 0;
|
|
927
|
+
}
|
|
928
|
+
.email-rich-html-content code {
|
|
929
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
930
|
+
font-size: 0.92em;
|
|
931
|
+
background: rgba(0, 0, 0, 0.06);
|
|
932
|
+
padding: 0.1em 0.35em;
|
|
933
|
+
border-radius: 3px;
|
|
934
|
+
}
|
|
935
|
+
.email-rich-html-inner ul,
|
|
936
|
+
.email-rich-html-inner ol {
|
|
937
|
+
margin: 0.35em 0;
|
|
938
|
+
padding-left: 1.35rem;
|
|
939
|
+
list-style-position: outside;
|
|
940
|
+
}
|
|
941
|
+
.email-rich-html-inner ul { list-style-type: disc; }
|
|
942
|
+
.email-rich-html-inner ol { list-style-type: decimal; }
|
|
943
|
+
.email-rich-html-inner li { margin: 0.2em 0; }
|
|
944
|
+
.email-rich-html-inner li > p { margin: 0; }
|
|
945
|
+
.email-rich-html-inner p { margin: 0.35em 0; }
|
|
946
|
+
.email-rich-html-inner h2,
|
|
947
|
+
.email-rich-html-inner h3,
|
|
948
|
+
.email-rich-html-inner h4 {
|
|
949
|
+
margin: 0.5em 0 0.25em;
|
|
950
|
+
line-height: 1.25;
|
|
951
|
+
font-weight: 700;
|
|
952
|
+
}
|
|
953
|
+
.email-rich-html-inner blockquote {
|
|
954
|
+
margin: 0.35em 0;
|
|
955
|
+
padding-left: 0.75rem;
|
|
956
|
+
border-left: 3px solid #cbd5e1;
|
|
957
|
+
}
|
|
958
|
+
.email-rich-html-inner hr {
|
|
959
|
+
border: none;
|
|
960
|
+
border-top: 1px solid #e2e8f0;
|
|
961
|
+
margin: 0.75em 0;
|
|
962
|
+
}
|
|
963
|
+
`.trim();
|
|
964
|
+
function applyInlineListStyles(root) {
|
|
965
|
+
root.querySelectorAll("ul").forEach((ul) => {
|
|
966
|
+
const el = ul;
|
|
967
|
+
el.style.margin = el.style.margin || "0.35em 0";
|
|
968
|
+
el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
|
|
969
|
+
el.style.listStyleType = el.style.listStyleType || "disc";
|
|
970
|
+
el.style.listStylePosition = el.style.listStylePosition || "outside";
|
|
971
|
+
});
|
|
972
|
+
root.querySelectorAll("ol").forEach((ol) => {
|
|
973
|
+
const el = ol;
|
|
974
|
+
el.style.margin = el.style.margin || "0.35em 0";
|
|
975
|
+
el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
|
|
976
|
+
el.style.listStyleType = el.style.listStyleType || "decimal";
|
|
977
|
+
el.style.listStylePosition = el.style.listStylePosition || "outside";
|
|
978
|
+
});
|
|
979
|
+
root.querySelectorAll("li").forEach((li) => {
|
|
980
|
+
const el = li;
|
|
981
|
+
if (!el.style.margin) el.style.margin = "0.2em 0";
|
|
982
|
+
});
|
|
983
|
+
root.querySelectorAll("li > p").forEach((p) => {
|
|
984
|
+
const el = p;
|
|
985
|
+
if (!el.style.margin) el.style.margin = "0";
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
function enhanceRichHtmlForEmail(html) {
|
|
989
|
+
const raw = String(html ?? "").trim();
|
|
990
|
+
if (!raw || raw === EMPTY_RICH_HTML) return raw || EMPTY_RICH_HTML;
|
|
991
|
+
if (typeof document === "undefined") return raw;
|
|
992
|
+
const tmp = document.createElement("div");
|
|
993
|
+
tmp.innerHTML = raw;
|
|
994
|
+
if (!tmp.querySelector("ul,ol,blockquote,h2,h3,h4,hr")) return raw;
|
|
995
|
+
applyInlineListStyles(tmp);
|
|
996
|
+
tmp.querySelectorAll("blockquote").forEach((bq) => {
|
|
997
|
+
const el = bq;
|
|
998
|
+
if (!el.style.margin) el.style.margin = "0.35em 0";
|
|
999
|
+
if (!el.style.paddingLeft) el.style.paddingLeft = "0.75rem";
|
|
1000
|
+
if (!el.style.borderLeft) el.style.borderLeft = "3px solid #cbd5e1";
|
|
1001
|
+
});
|
|
1002
|
+
return tmp.innerHTML.trim();
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/socialIcons.tsx
|
|
1006
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1007
|
+
var GLYPHS = {
|
|
1008
|
+
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",
|
|
1009
|
+
/** X (formerly Twitter) — Simple Icons “X” */
|
|
1010
|
+
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",
|
|
1011
|
+
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",
|
|
1012
|
+
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",
|
|
1013
|
+
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",
|
|
1014
|
+
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",
|
|
1015
|
+
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",
|
|
1016
|
+
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",
|
|
1017
|
+
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",
|
|
1018
|
+
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"
|
|
1019
|
+
};
|
|
1020
|
+
function iconFillForNetwork(network) {
|
|
1021
|
+
return network === "snapchat" ? "#000000" : "#ffffff";
|
|
1022
|
+
}
|
|
1023
|
+
function glyphPath(network) {
|
|
1024
|
+
return GLYPHS[network] || null;
|
|
1025
|
+
}
|
|
1026
|
+
function socialIconSvgString(network, pixelSize) {
|
|
1027
|
+
const d = glyphPath(network);
|
|
1028
|
+
const fill = iconFillForNetwork(network);
|
|
1029
|
+
const s = Math.max(8, Math.round(pixelSize));
|
|
1030
|
+
if (!d) {
|
|
1031
|
+
const letter = (network && network[0] ? network[0] : "?").toUpperCase();
|
|
1032
|
+
return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
|
|
1033
|
+
}
|
|
1034
|
+
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>`;
|
|
1035
|
+
}
|
|
1036
|
+
function SocialGlyph({ network, size }) {
|
|
1037
|
+
const d = glyphPath(network);
|
|
1038
|
+
const s = Math.max(8, Math.round(size));
|
|
1039
|
+
if (!d) {
|
|
1040
|
+
return /* @__PURE__ */ jsx2("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
|
|
1041
|
+
}
|
|
1042
|
+
return /* @__PURE__ */ jsx2(
|
|
1043
|
+
"svg",
|
|
1044
|
+
{
|
|
1045
|
+
width: s,
|
|
1046
|
+
height: s,
|
|
1047
|
+
viewBox: "0 0 24 24",
|
|
1048
|
+
style: { display: "block", flexShrink: 0 },
|
|
1049
|
+
"aria-hidden": true,
|
|
1050
|
+
children: /* @__PURE__ */ jsx2("path", { fill: "currentColor", d })
|
|
1051
|
+
}
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
853
1054
|
|
|
854
1055
|
// src/lib/blockBackground.ts
|
|
855
1056
|
function linearGradientCssInner(g) {
|
|
@@ -974,19 +1175,23 @@ function blockToHtml(cb) {
|
|
|
974
1175
|
case "text": {
|
|
975
1176
|
const shell = emailSurfaceBgCss(p);
|
|
976
1177
|
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"}`;
|
|
977
|
-
|
|
1178
|
+
const content = typeof p.content === "string" ? p.content : "";
|
|
1179
|
+
const body = /^\s*<[^>]+>/.test(content) ? content : escHtml(content).replace(/\r?\n/g, "<br/>");
|
|
1180
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div style="${inner}">${body}</div></div>`;
|
|
978
1181
|
}
|
|
979
1182
|
case "html": {
|
|
980
1183
|
const shell = emailSurfaceBgCss(p);
|
|
981
1184
|
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"}`;
|
|
982
|
-
|
|
1185
|
+
const bodyRaw = typeof cb.content === "string" ? cb.content : typeof p.content === "string" ? p.content : "";
|
|
1186
|
+
const body = enhanceRichHtmlForEmail(bodyRaw || "");
|
|
1187
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div class="email-rich-html-inner" style="${inner}">${body}</div></div>`;
|
|
983
1188
|
}
|
|
984
1189
|
case "image": {
|
|
985
1190
|
const of = typeof p.objectFit === "string" && p.objectFit.trim() ? p.objectFit.trim() : "cover";
|
|
986
1191
|
const op = typeof p.objectPosition === "string" && p.objectPosition.trim() ? p.objectPosition.trim() : "center";
|
|
987
1192
|
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};"/>`;
|
|
988
1193
|
const shell = emailSurfaceBgCss(p);
|
|
989
|
-
return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${p
|
|
1194
|
+
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>`;
|
|
990
1195
|
}
|
|
991
1196
|
case "button": {
|
|
992
1197
|
const bd = emailBackdropBgCss(p);
|
|
@@ -1075,7 +1280,7 @@ function blockToHtml(cb) {
|
|
|
1075
1280
|
bgRepeat: p.bgRepeat || "no-repeat",
|
|
1076
1281
|
bgPosition: typeof p.bgPosition === "string" ? p.bgPosition : "center",
|
|
1077
1282
|
bgGradient: p.bgGradient ?? null,
|
|
1078
|
-
|
|
1283
|
+
columns: getColumns(p),
|
|
1079
1284
|
cells: p.cells || []
|
|
1080
1285
|
};
|
|
1081
1286
|
return rowToHtml(pseudo);
|
|
@@ -1089,7 +1294,7 @@ function rowToHtml(row) {
|
|
|
1089
1294
|
const shellBg = emailSurfaceBgCss(row);
|
|
1090
1295
|
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) => {
|
|
1091
1296
|
const r = row.ratios[i] ?? 1;
|
|
1092
|
-
const cs = row
|
|
1297
|
+
const cs = getColumnPropsAt(row, i);
|
|
1093
1298
|
const colShell = emailSurfaceBgCss(cs);
|
|
1094
1299
|
const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
|
|
1095
1300
|
const o = cs.padding;
|
|
@@ -1137,6 +1342,7 @@ table[role="presentation"] td{min-width:0!important;word-break:break-word;}
|
|
|
1137
1342
|
@media screen and (max-width:480px){
|
|
1138
1343
|
.email-content-td{padding-left:max(10px,env(safe-area-inset-left))!important;padding-right:max(10px,env(safe-area-inset-right))!important;}
|
|
1139
1344
|
}
|
|
1345
|
+
${RICH_HTML_CONTENT_CSS}
|
|
1140
1346
|
</style>`;
|
|
1141
1347
|
function previewEmailSrcDoc(html, viewportWidthPx) {
|
|
1142
1348
|
const viewportTag = `<meta name="viewport" content="width=${viewportWidthPx},initial-scale=1"/>`;
|
|
@@ -1188,6 +1394,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1188
1394
|
return `background-image:linear-gradient(${angle}deg, ${pairs.join(", ")});background-repeat:no-repeat;background-position:${pagePos};background-size:cover;`;
|
|
1189
1395
|
})();
|
|
1190
1396
|
const pageBgImgRaw = typeof settings.bgImage === "string" ? String(settings.bgImage).trim() : "";
|
|
1397
|
+
const pageBgColorRaw = typeof settings.bgColor === "string" ? String(settings.bgColor).trim() : "";
|
|
1398
|
+
const pageBgColor = pageBgColorRaw ? pageBgColorRaw : "transparent";
|
|
1191
1399
|
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};` : "";
|
|
1192
1400
|
const contentPos = typeof settings.contentBgPosition === "string" && settings.contentBgPosition.trim() ? settings.contentBgPosition.trim() : "center";
|
|
1193
1401
|
const contentGrad = settings.contentBgGradient && typeof settings.contentBgGradient === "object" ? settings.contentBgGradient : null;
|
|
@@ -1210,8 +1418,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1210
1418
|
<head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
|
1211
1419
|
<title>Email</title>${RESPONSIVE_EMAIL_CSS}${css}
|
|
1212
1420
|
</head>
|
|
1213
|
-
<body style="margin:0;padding:0;background:${
|
|
1214
|
-
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${
|
|
1421
|
+
<body style="margin:0;padding:0;background:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1422
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1215
1423
|
<tr><td align="center" style="padding:0;max-width:100%;">
|
|
1216
1424
|
<table class="email-shell-table" role="presentation" width="100%" cellpadding="0" cellspacing="0"
|
|
1217
1425
|
style="${contentShellStyle.join(";")}">
|
|
@@ -1245,14 +1453,49 @@ function paddingToUniform(p, fallback = 0) {
|
|
|
1245
1453
|
function ensureId(id, prefix) {
|
|
1246
1454
|
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
1247
1455
|
}
|
|
1456
|
+
function normalizeDocBlock(b, prefix) {
|
|
1457
|
+
const raw = b && typeof b === "object" && !Array.isArray(b) ? b : {};
|
|
1458
|
+
const base = {
|
|
1459
|
+
id: ensureId(raw.id, prefix),
|
|
1460
|
+
type: typeof raw.type === "string" ? raw.type : "text",
|
|
1461
|
+
content: typeof raw.content === "string" ? raw.content : raw.content && typeof raw.content === "object" && !Array.isArray(raw.content) ? raw.content : {},
|
|
1462
|
+
...raw.behavior && typeof raw.behavior === "object" && !Array.isArray(raw.behavior) ? { behavior: raw.behavior } : {},
|
|
1463
|
+
...raw.responsive && typeof raw.responsive === "object" && !Array.isArray(raw.responsive) ? { responsive: raw.responsive } : {}
|
|
1464
|
+
};
|
|
1465
|
+
if (raw.props && typeof raw.props === "object" && !Array.isArray(raw.props)) {
|
|
1466
|
+
base.props = { ...raw.props };
|
|
1467
|
+
} else if (raw.styles && typeof raw.styles === "object" && !Array.isArray(raw.styles)) {
|
|
1468
|
+
base.styles = { ...raw.styles };
|
|
1469
|
+
}
|
|
1470
|
+
return base;
|
|
1471
|
+
}
|
|
1472
|
+
function normalizeDocSettings(settings) {
|
|
1473
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return {};
|
|
1474
|
+
const s = { ...settings };
|
|
1475
|
+
delete s._reactEmailStudio;
|
|
1476
|
+
return s;
|
|
1477
|
+
}
|
|
1248
1478
|
function normalizeEmailDocument(input) {
|
|
1249
1479
|
if (input == null || typeof input !== "object" || Array.isArray(input)) return null;
|
|
1250
1480
|
const doc = input;
|
|
1251
1481
|
if (doc.type !== "email_document") return null;
|
|
1482
|
+
const rawBlocks = Array.isArray(doc.blocks) ? doc.blocks : [];
|
|
1483
|
+
const useBlocks = rawBlocks.length > 0;
|
|
1252
1484
|
const rows = Array.isArray(doc.rows) ? doc.rows : [];
|
|
1253
|
-
|
|
1485
|
+
const base = {
|
|
1254
1486
|
type: "email_document",
|
|
1255
|
-
settings:
|
|
1487
|
+
settings: normalizeDocSettings(doc.settings),
|
|
1488
|
+
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1489
|
+
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1490
|
+
};
|
|
1491
|
+
if (useBlocks) {
|
|
1492
|
+
return {
|
|
1493
|
+
...base,
|
|
1494
|
+
blocks: rawBlocks.map((b, bi) => normalizeDocBlock(b, `block${bi + 1}`))
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
return {
|
|
1498
|
+
...base,
|
|
1256
1499
|
rows: rows.map((r, ri) => {
|
|
1257
1500
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1258
1501
|
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
@@ -1274,28 +1517,76 @@ function normalizeEmailDocument(input) {
|
|
|
1274
1517
|
columns: colCount,
|
|
1275
1518
|
gap: typeof r.layout?.gap === "number" ? r.layout.gap : 0,
|
|
1276
1519
|
stackOnMobile: r.layout?.stackOnMobile !== false,
|
|
1277
|
-
align: r.layout?.align || "center"
|
|
1520
|
+
align: r.layout?.align || "center",
|
|
1521
|
+
...typeof r.layout?.preset === "string" && r.layout.preset.trim() ? { preset: r.layout.preset.trim() } : {},
|
|
1522
|
+
...Array.isArray(r.layout?.ratios) && r.layout.ratios.length ? {
|
|
1523
|
+
ratios: r.layout.ratios.filter(
|
|
1524
|
+
(x) => typeof x === "number" && Number.isFinite(x)
|
|
1525
|
+
)
|
|
1526
|
+
} : {}
|
|
1278
1527
|
},
|
|
1279
|
-
|
|
1280
|
-
|
|
1528
|
+
props: (() => {
|
|
1529
|
+
const p = r.props;
|
|
1530
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { ...p };
|
|
1531
|
+
const s = r.styles;
|
|
1532
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1533
|
+
return {
|
|
1534
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1535
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1536
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1537
|
+
bgSize: s.backgroundSize || "cover",
|
|
1538
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1539
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1540
|
+
padding: s.padding,
|
|
1541
|
+
borderRadius: s.borderRadius,
|
|
1542
|
+
borderWidth: s.borderWidth,
|
|
1543
|
+
borderColor: s.borderColor,
|
|
1544
|
+
textAlign: s.textAlign
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
return {};
|
|
1548
|
+
})(),
|
|
1281
1549
|
columns: colsNormalized.map((c, ci) => ({
|
|
1282
1550
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1283
1551
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
...
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1552
|
+
...(() => {
|
|
1553
|
+
const p = c.props;
|
|
1554
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { props: { ...p } };
|
|
1555
|
+
const s = c.styles;
|
|
1556
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1557
|
+
return {
|
|
1558
|
+
props: {
|
|
1559
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1560
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1561
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1562
|
+
bgSize: s.backgroundSize || "cover",
|
|
1563
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1564
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1565
|
+
padding: s.padding,
|
|
1566
|
+
borderRadius: s.borderRadius
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
return {};
|
|
1571
|
+
})(),
|
|
1572
|
+
blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => {
|
|
1573
|
+
const base2 = {
|
|
1574
|
+
id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
|
|
1575
|
+
type: typeof b?.type === "string" ? b.type : "text",
|
|
1576
|
+
content: typeof b?.content === "string" ? b.content : b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1577
|
+
...b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? { behavior: b.behavior } : {},
|
|
1578
|
+
...b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? { responsive: b.responsive } : {}
|
|
1579
|
+
};
|
|
1580
|
+
if (b?.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
1581
|
+
base2.props = { ...b.props };
|
|
1582
|
+
} else if (b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles)) {
|
|
1583
|
+
base2.styles = { ...b.styles };
|
|
1584
|
+
}
|
|
1585
|
+
return base2;
|
|
1586
|
+
}) : []
|
|
1294
1587
|
}))
|
|
1295
1588
|
};
|
|
1296
|
-
})
|
|
1297
|
-
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1298
|
-
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1589
|
+
})
|
|
1299
1590
|
};
|
|
1300
1591
|
}
|
|
1301
1592
|
|
|
@@ -1308,6 +1599,60 @@ function cloneJson(v) {
|
|
|
1308
1599
|
return v;
|
|
1309
1600
|
}
|
|
1310
1601
|
}
|
|
1602
|
+
function legacyRowStylesToProps(s) {
|
|
1603
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return {};
|
|
1604
|
+
return {
|
|
1605
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1606
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1607
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1608
|
+
bgSize: s.backgroundSize || "cover",
|
|
1609
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1610
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1611
|
+
padding: s.padding,
|
|
1612
|
+
borderRadius: s.borderRadius,
|
|
1613
|
+
borderWidth: s.borderWidth,
|
|
1614
|
+
borderColor: s.borderColor,
|
|
1615
|
+
textAlign: s.textAlign
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
function rowPropsFromDocRow(r) {
|
|
1619
|
+
const p = r.props;
|
|
1620
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1621
|
+
return { ...p };
|
|
1622
|
+
}
|
|
1623
|
+
return legacyRowStylesToProps(r.styles);
|
|
1624
|
+
}
|
|
1625
|
+
function applyRowPropsToEditorRow(row, r) {
|
|
1626
|
+
const p = rowPropsFromDocRow(r);
|
|
1627
|
+
if (p.padding !== void 0 && p.padding !== null) {
|
|
1628
|
+
row.padding = paddingToUniform(p.padding, row.padding);
|
|
1629
|
+
}
|
|
1630
|
+
if (typeof p.bgColor === "string") row.bgColor = p.bgColor.trim();
|
|
1631
|
+
if (typeof p.bgImage === "string") row.bgImage = p.bgImage.trim();
|
|
1632
|
+
if (typeof p.bgRepeat === "string") row.bgRepeat = p.bgRepeat;
|
|
1633
|
+
if (typeof p.bgSize === "string") row.bgSize = p.bgSize;
|
|
1634
|
+
if (typeof p.bgPosition === "string") row.bgPosition = p.bgPosition;
|
|
1635
|
+
if (p.bgGradient !== void 0) row.bgGradient = p.bgGradient;
|
|
1636
|
+
}
|
|
1637
|
+
function editorRowToDocProps(r) {
|
|
1638
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1639
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1640
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
1641
|
+
})();
|
|
1642
|
+
return {
|
|
1643
|
+
bgColor: typeof r.bgColor === "string" ? r.bgColor : "",
|
|
1644
|
+
bgImage: typeof r.bgImage === "string" ? r.bgImage : "",
|
|
1645
|
+
bgRepeat: r.bgRepeat || "no-repeat",
|
|
1646
|
+
bgSize: r.bgSize || "cover",
|
|
1647
|
+
bgPosition: r.bgPosition || "center",
|
|
1648
|
+
bgGradient: r.bgGradient ?? null,
|
|
1649
|
+
padding: rowPad,
|
|
1650
|
+
borderRadius: 0,
|
|
1651
|
+
borderWidth: 0,
|
|
1652
|
+
borderColor: "#e5e7eb",
|
|
1653
|
+
textAlign: "left"
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1311
1656
|
function layoutColumnPaddingForDoc(p) {
|
|
1312
1657
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1313
1658
|
const n = Math.max(0, p);
|
|
@@ -1327,16 +1672,58 @@ function layoutColumnBorderRadiusForDoc(r) {
|
|
|
1327
1672
|
}
|
|
1328
1673
|
return 0;
|
|
1329
1674
|
}
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1675
|
+
function normalizeLayoutContainerProps(props) {
|
|
1676
|
+
const columns = getColumns(props);
|
|
1677
|
+
const { columnStyles: _drop, ...rest } = props;
|
|
1678
|
+
return { ...rest, columns };
|
|
1679
|
+
}
|
|
1680
|
+
function mergeLayoutColumns(fromContent, fromHydrated) {
|
|
1681
|
+
const norm = (x) => {
|
|
1682
|
+
if (Array.isArray(x)) {
|
|
1683
|
+
return x.map((entry) => {
|
|
1684
|
+
const e = entry;
|
|
1685
|
+
const raw = e?.props && typeof e.props === "object" ? e.props : entry;
|
|
1686
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...raw } };
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
if (x && typeof x === "object" && !Array.isArray(x)) {
|
|
1690
|
+
return columnsFromLegacyMap(x, Object.keys(x).length);
|
|
1691
|
+
}
|
|
1692
|
+
return [];
|
|
1693
|
+
};
|
|
1694
|
+
const a = norm(fromContent);
|
|
1695
|
+
const b = norm(fromHydrated);
|
|
1696
|
+
const len = Math.max(a.length, b.length, 1);
|
|
1697
|
+
const out = [];
|
|
1698
|
+
for (let i = 0; i < len; i++) {
|
|
1699
|
+
out.push({
|
|
1700
|
+
props: { ...DEFAULT_COLUMN_PROPS, ...a[i]?.props ?? {}, ...b[i]?.props ?? {} }
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
return out.length ? out : void 0;
|
|
1332
1704
|
}
|
|
1333
|
-
function
|
|
1334
|
-
|
|
1335
|
-
if (
|
|
1336
|
-
|
|
1337
|
-
return !!(o.tl || o.tr || o.br || o.bl);
|
|
1705
|
+
function columnPropsFromDocColumn(c) {
|
|
1706
|
+
const p = c?.props;
|
|
1707
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1708
|
+
return { ...DEFAULT_COLUMN_PROPS, ...p };
|
|
1338
1709
|
}
|
|
1339
|
-
|
|
1710
|
+
const s = c?.styles;
|
|
1711
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) {
|
|
1712
|
+
return { ...DEFAULT_COLUMN_PROPS };
|
|
1713
|
+
}
|
|
1714
|
+
const padding = layoutColumnPaddingForDoc(s.padding);
|
|
1715
|
+
const borderRadius = layoutColumnBorderRadiusForDoc(s.borderRadius);
|
|
1716
|
+
return {
|
|
1717
|
+
...DEFAULT_COLUMN_PROPS,
|
|
1718
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1719
|
+
bgImage: typeof s.backgroundImage === "string" ? String(s.backgroundImage).trim() : "",
|
|
1720
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1721
|
+
bgSize: s.backgroundSize || "cover",
|
|
1722
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1723
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1724
|
+
padding,
|
|
1725
|
+
borderRadius
|
|
1726
|
+
};
|
|
1340
1727
|
}
|
|
1341
1728
|
function asNum(v) {
|
|
1342
1729
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
@@ -1433,13 +1820,15 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1433
1820
|
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1434
1821
|
}
|
|
1435
1822
|
};
|
|
1436
|
-
case "html":
|
|
1823
|
+
case "html": {
|
|
1824
|
+
const body = typeof b.content === "string" ? b.content : typeof p.content === "string" ? p.content : "";
|
|
1437
1825
|
return {
|
|
1438
1826
|
id,
|
|
1439
1827
|
type,
|
|
1440
|
-
content: { html:
|
|
1828
|
+
content: { html: normalizeRichHtmlForStorage(body) },
|
|
1441
1829
|
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1442
1830
|
};
|
|
1831
|
+
}
|
|
1443
1832
|
case "image": {
|
|
1444
1833
|
const br = p.borderRadius;
|
|
1445
1834
|
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
@@ -1619,7 +2008,7 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1619
2008
|
bgRepeat: p.bgRepeat,
|
|
1620
2009
|
bgPosition: p.bgPosition,
|
|
1621
2010
|
bgGradient: p.bgGradient ?? null,
|
|
1622
|
-
|
|
2011
|
+
columns: getColumns(p).map((col) => ({ props: cloneJson(col.props) })),
|
|
1623
2012
|
cells: cellsOut
|
|
1624
2013
|
},
|
|
1625
2014
|
styles: {}
|
|
@@ -1659,17 +2048,26 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1659
2048
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1660
2049
|
}
|
|
1661
2050
|
break;
|
|
1662
|
-
case "html":
|
|
1663
|
-
|
|
1664
|
-
|
|
2051
|
+
case "html": {
|
|
2052
|
+
const br = b;
|
|
2053
|
+
const fromDoc = asStr(c.html);
|
|
2054
|
+
const fromRoot = typeof br.content === "string" ? String(br.content) : void 0;
|
|
2055
|
+
const fromLegacyProps = br.props && typeof br.props.content === "string" ? String(br.props.content) : void 0;
|
|
2056
|
+
const htmlNorm = normalizeRichHtmlForStorage(fromDoc ?? fromRoot ?? fromLegacyProps ?? "");
|
|
2057
|
+
block.content = htmlNorm;
|
|
2058
|
+
block.props.content = htmlNorm;
|
|
1665
2059
|
if (s.padding && typeof s.padding === "object") {
|
|
1666
2060
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1667
2061
|
}
|
|
1668
2062
|
break;
|
|
2063
|
+
}
|
|
1669
2064
|
case "image":
|
|
1670
2065
|
block.props.src = asStr(c.src) ?? block.props.src;
|
|
1671
2066
|
if (asStr(c.alt)) block.props.alt = c.alt;
|
|
1672
|
-
if (asStr(c.link))
|
|
2067
|
+
if (asStr(c.link)) {
|
|
2068
|
+
block.props.link = c.link;
|
|
2069
|
+
block.props.linkEnabled = true;
|
|
2070
|
+
}
|
|
1673
2071
|
if (beh.openInNewTab === true) block.props.linkTarget = "_blank";
|
|
1674
2072
|
if (asStr(s.width)) block.props.width = s.width;
|
|
1675
2073
|
if (asStr(s.align)) block.props.align = s.align;
|
|
@@ -1791,8 +2189,17 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1791
2189
|
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1792
2190
|
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1793
2191
|
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1794
|
-
if (c.
|
|
1795
|
-
|
|
2192
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
2193
|
+
if (Array.isArray(c.columns)) {
|
|
2194
|
+
block.props.columns = c.columns.map((col) => ({
|
|
2195
|
+
props: {
|
|
2196
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2197
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2198
|
+
}
|
|
2199
|
+
}));
|
|
2200
|
+
} else if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
2201
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios : [1];
|
|
2202
|
+
block.props.columns = columnsFromLegacyMap(c.columnStyles, ratios.length);
|
|
1796
2203
|
}
|
|
1797
2204
|
block.props.cells = c.cells.map(
|
|
1798
2205
|
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
@@ -1836,6 +2243,10 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1836
2243
|
}
|
|
1837
2244
|
}
|
|
1838
2245
|
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
2246
|
+
if (t === "image") {
|
|
2247
|
+
const link = asStr(block.props.link);
|
|
2248
|
+
if (link && block.props.linkEnabled == null) block.props.linkEnabled = true;
|
|
2249
|
+
}
|
|
1839
2250
|
return block;
|
|
1840
2251
|
}
|
|
1841
2252
|
function rowToInternal(r) {
|
|
@@ -1857,55 +2268,315 @@ function rowToInternal(r) {
|
|
|
1857
2268
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
1858
2269
|
row.cols = cols;
|
|
1859
2270
|
row.gap = typeof r.layout?.gap === "number" ? r.layout.gap : row.gap;
|
|
1860
|
-
row
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2271
|
+
applyRowPropsToEditorRow(row, r);
|
|
2272
|
+
const lay = r.layout;
|
|
2273
|
+
if (lay && typeof lay === "object" && !Array.isArray(lay)) {
|
|
2274
|
+
if (typeof lay.preset === "string" && lay.preset.trim()) row.preset = lay.preset.trim();
|
|
2275
|
+
if (Array.isArray(lay.ratios) && lay.ratios.length) {
|
|
2276
|
+
const rr = lay.ratios.filter((x) => typeof x === "number" && Number.isFinite(x));
|
|
2277
|
+
if (rr.length) row.ratios = rr;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
1867
2280
|
row.cells = colList.map((c) => {
|
|
1868
2281
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1869
2282
|
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1870
2283
|
});
|
|
1871
2284
|
const studio = r._reactEmailStudio;
|
|
1872
2285
|
const snap = studio?.row;
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
const padding = layoutColumnPaddingForDoc(c.styles?.padding);
|
|
1877
|
-
const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
|
|
1878
|
-
const bgImage = typeof c.styles?.backgroundImage === "string" ? String(c.styles.backgroundImage).trim() : "";
|
|
1879
|
-
const bgRepeat = typeof c.styles?.backgroundRepeat === "string" ? c.styles.backgroundRepeat : void 0;
|
|
1880
|
-
const bgSize = typeof c.styles?.backgroundSize === "string" ? c.styles.backgroundSize : void 0;
|
|
1881
|
-
const bgPosition = typeof c.styles?.backgroundPosition === "string" ? c.styles.backgroundPosition : void 0;
|
|
1882
|
-
const bgGradient = c.styles?.backgroundGradient || null;
|
|
1883
|
-
const hasPad = columnPaddingNonZero(padding);
|
|
1884
|
-
const hasBr = columnRadiusNonZero(borderRadius);
|
|
1885
|
-
if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
|
|
1886
|
-
columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
|
|
1887
|
-
}
|
|
1888
|
-
});
|
|
2286
|
+
const columnsFromDoc = colList.map((c) => ({
|
|
2287
|
+
props: columnPropsFromDocColumn(c)
|
|
2288
|
+
}));
|
|
1889
2289
|
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1890
|
-
const {
|
|
2290
|
+
const {
|
|
2291
|
+
cells: _omitCells,
|
|
2292
|
+
columnStyles: legacyCs,
|
|
2293
|
+
columns: snapCols,
|
|
2294
|
+
...rest
|
|
2295
|
+
} = snap;
|
|
1891
2296
|
Object.assign(row, rest);
|
|
1892
|
-
const
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2297
|
+
const fromSnap = Array.isArray(snapCols) ? snapCols.map((col) => ({
|
|
2298
|
+
props: {
|
|
2299
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2300
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2301
|
+
}
|
|
2302
|
+
})) : columnsFromLegacyMap(legacyCs, cols);
|
|
2303
|
+
row.columns = mergeLayoutColumns(fromSnap, columnsFromDoc) ?? columnsFromDoc;
|
|
2304
|
+
delete row.columnStyles;
|
|
2305
|
+
} else {
|
|
2306
|
+
row.columns = columnsFromDoc;
|
|
1897
2307
|
}
|
|
1898
2308
|
return row;
|
|
1899
2309
|
}
|
|
1900
|
-
function
|
|
1901
|
-
|
|
1902
|
-
if (
|
|
1903
|
-
|
|
1904
|
-
|
|
2310
|
+
function coerceEmailDocumentInput(input) {
|
|
2311
|
+
if (input == null) return input;
|
|
2312
|
+
if (Array.isArray(input)) {
|
|
2313
|
+
if (input.length === 0) return input;
|
|
2314
|
+
return { type: "email_document", settings: {}, blocks: input };
|
|
2315
|
+
}
|
|
2316
|
+
if (typeof input !== "object") return input;
|
|
2317
|
+
const o = input;
|
|
2318
|
+
if (o.type === "email_document") return input;
|
|
2319
|
+
if (Array.isArray(o.blocks)) {
|
|
2320
|
+
return {
|
|
2321
|
+
type: "email_document",
|
|
2322
|
+
settings: o.settings && typeof o.settings === "object" ? o.settings : {},
|
|
2323
|
+
blocks: o.blocks
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
return input;
|
|
2327
|
+
}
|
|
2328
|
+
function rootLayoutBlockToDocRow(block) {
|
|
2329
|
+
const p = block.props && typeof block.props === "object" ? block.props : {};
|
|
2330
|
+
const c = block.content && typeof block.content === "object" && !Array.isArray(block.content) ? block.content : {};
|
|
2331
|
+
const cols = Math.max(
|
|
2332
|
+
1,
|
|
2333
|
+
typeof p.cols === "number" && p.cols > 0 ? Math.floor(p.cols) : 0,
|
|
2334
|
+
typeof c.cols === "number" && c.cols > 0 ? Math.floor(c.cols) : 0,
|
|
2335
|
+
Array.isArray(p.cells) ? p.cells.length : 0,
|
|
2336
|
+
Array.isArray(c.cells) ? c.cells.length : 0
|
|
2337
|
+
);
|
|
2338
|
+
const rawCells = Array.isArray(c.cells) ? c.cells : Array.isArray(p.cells) ? p.cells : [];
|
|
2339
|
+
const contentColumns = Array.isArray(c.columns) ? c.columns : [];
|
|
2340
|
+
const propsColumns = getColumns(p);
|
|
2341
|
+
const columns = Array.from({ length: cols }, (_, ci) => {
|
|
2342
|
+
const cellRaw = rawCells[ci];
|
|
2343
|
+
const blocks = Array.isArray(cellRaw) ? cellRaw.filter((x) => x && typeof x === "object").map((x, bi) => {
|
|
2344
|
+
const blk = x;
|
|
2345
|
+
return {
|
|
2346
|
+
id: ensureBlockId(blk.id, `b_${ci}_${bi}`),
|
|
2347
|
+
type: typeof blk.type === "string" ? blk.type : "text",
|
|
2348
|
+
content: blk.content,
|
|
2349
|
+
...blk.props ? { props: blk.props } : {},
|
|
2350
|
+
...blk.styles ? { styles: blk.styles } : {},
|
|
2351
|
+
...blk.behavior ? { behavior: blk.behavior } : {}
|
|
2352
|
+
};
|
|
2353
|
+
}) : [];
|
|
2354
|
+
const colProps = {
|
|
2355
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2356
|
+
...propsColumns[ci]?.props ?? contentColumns[ci]?.props ?? {}
|
|
2357
|
+
};
|
|
2358
|
+
return {
|
|
2359
|
+
id: `col_${ci + 1}`,
|
|
2360
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2361
|
+
props: cloneJson(colProps),
|
|
2362
|
+
blocks
|
|
2363
|
+
};
|
|
2364
|
+
});
|
|
2365
|
+
const rowProps = {
|
|
2366
|
+
bgColor: typeof p.bgColor === "string" ? p.bgColor : "",
|
|
2367
|
+
bgImage: typeof p.bgImage === "string" ? p.bgImage : "",
|
|
2368
|
+
bgRepeat: p.bgRepeat || "no-repeat",
|
|
2369
|
+
bgSize: p.bgSize || "cover",
|
|
2370
|
+
bgPosition: p.bgPosition || "center",
|
|
2371
|
+
bgGradient: p.bgGradient ?? null,
|
|
2372
|
+
padding: typeof p.padding === "number" ? { top: p.padding, right: p.padding, bottom: p.padding, left: p.padding } : normalizePadding(p.padding)
|
|
2373
|
+
};
|
|
2374
|
+
const ratios = Array.isArray(p.ratios) ? p.ratios : Array.isArray(c.ratios) ? c.ratios : void 0;
|
|
2375
|
+
return {
|
|
2376
|
+
id: typeof block.id === "string" ? block.id : uid(),
|
|
2377
|
+
type: "row",
|
|
2378
|
+
layout: {
|
|
2379
|
+
columns: cols,
|
|
2380
|
+
gap: typeof p.gap === "number" ? p.gap : typeof c.gap === "number" ? c.gap : 0,
|
|
2381
|
+
stackOnMobile: true,
|
|
2382
|
+
align: "center",
|
|
2383
|
+
...typeof p.preset === "string" && p.preset.trim() ? { preset: p.preset.trim() } : typeof c.preset === "string" && c.preset.trim() ? { preset: c.preset.trim() } : {},
|
|
2384
|
+
...ratios?.length ? { ratios: [...ratios] } : {}
|
|
2385
|
+
},
|
|
2386
|
+
props: rowProps,
|
|
2387
|
+
columns
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
function ensureBlockId(id, prefix) {
|
|
2391
|
+
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
2392
|
+
}
|
|
2393
|
+
function wrapRootContentBlock(block) {
|
|
2394
|
+
const row = makeLayoutRow(pickPresetByColCount(1));
|
|
2395
|
+
const internal = mapBlockToInternal(block, 0);
|
|
2396
|
+
if (internal) row.cells[0] = [internal];
|
|
2397
|
+
return row;
|
|
2398
|
+
}
|
|
2399
|
+
function rootBlockToEditorRow(block) {
|
|
2400
|
+
const t = block.type === "nestedRow" ? "layout" : block.type;
|
|
2401
|
+
if (t === "layout") {
|
|
2402
|
+
return rowToInternal(rootLayoutBlockToDocRow(block));
|
|
2403
|
+
}
|
|
2404
|
+
return wrapRootContentBlock(block);
|
|
2405
|
+
}
|
|
2406
|
+
function blocksToEditorRows(blocks) {
|
|
2407
|
+
return blocks.map(rootBlockToEditorRow);
|
|
2408
|
+
}
|
|
2409
|
+
function rowPropsToLegacyStyles(p) {
|
|
2410
|
+
return {
|
|
2411
|
+
backgroundColor: p.bgColor ?? "",
|
|
2412
|
+
backgroundImage: p.bgImage ?? "",
|
|
2413
|
+
backgroundRepeat: p.bgRepeat ?? "no-repeat",
|
|
2414
|
+
backgroundSize: p.bgSize ?? "cover",
|
|
2415
|
+
backgroundPosition: p.bgPosition ?? "center",
|
|
2416
|
+
backgroundGradient: p.bgGradient ?? null,
|
|
2417
|
+
padding: p.padding,
|
|
2418
|
+
borderRadius: p.borderRadius ?? 0,
|
|
2419
|
+
borderWidth: p.borderWidth ?? 0,
|
|
2420
|
+
borderColor: p.borderColor ?? "#e5e7eb",
|
|
2421
|
+
textAlign: p.textAlign ?? "left"
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
function columnPropsToLegacyStyles(cp) {
|
|
2425
|
+
return {
|
|
2426
|
+
padding: layoutColumnPaddingForDoc(cp.padding),
|
|
2427
|
+
backgroundColor: cp.bgColor ?? "",
|
|
2428
|
+
backgroundImage: cp.bgImage ?? "",
|
|
2429
|
+
backgroundRepeat: cp.bgRepeat ?? "no-repeat",
|
|
2430
|
+
backgroundSize: cp.bgSize ?? "cover",
|
|
2431
|
+
backgroundPosition: cp.bgPosition ?? "center",
|
|
2432
|
+
backgroundGradient: cp.bgGradient ?? null,
|
|
2433
|
+
borderRadius: layoutColumnBorderRadiusForDoc(cp.borderRadius)
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
function editorRowStudioSnapshot(r) {
|
|
2437
|
+
const p = editorRowToDocProps(r);
|
|
2438
|
+
const columns = getColumns(r).map((col) => ({
|
|
2439
|
+
props: cloneJson(col.props ?? DEFAULT_COLUMN_PROPS)
|
|
2440
|
+
}));
|
|
2441
|
+
return {
|
|
2442
|
+
id: r.id,
|
|
2443
|
+
type: "layout",
|
|
2444
|
+
preset: r.preset,
|
|
2445
|
+
cols: r.cols,
|
|
2446
|
+
ratios: Array.isArray(r.ratios) ? [...r.ratios] : [1],
|
|
2447
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2448
|
+
padding: p.padding,
|
|
2449
|
+
bgColor: p.bgColor ?? "",
|
|
2450
|
+
bgImage: p.bgImage ?? "",
|
|
2451
|
+
bgSize: p.bgSize ?? "cover",
|
|
2452
|
+
bgRepeat: p.bgRepeat ?? "no-repeat",
|
|
2453
|
+
bgPosition: p.bgPosition ?? "center",
|
|
2454
|
+
bgGradient: p.bgGradient ?? null,
|
|
2455
|
+
columns
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
function exportBlockForLegacyRow(b, depth = 0) {
|
|
2459
|
+
if (!b || typeof b !== "object") {
|
|
2460
|
+
return { id: uid(), type: "text", content: {}, props: {} };
|
|
2461
|
+
}
|
|
2462
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? cloneJson(b.props) : {};
|
|
2463
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2464
|
+
rawProps.cells = b.props.cells.map(
|
|
2465
|
+
(col) => Array.isArray(col) ? col.map((child) => exportBlockForLegacyRow(child, depth + 1)) : []
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
if (b.type === "html") {
|
|
2469
|
+
const body = typeof b.content === "string" ? b.content : typeof rawProps.content === "string" ? rawProps.content : "";
|
|
2470
|
+
const html = normalizeRichHtmlForStorage(body);
|
|
2471
|
+
rawProps.content = html;
|
|
2472
|
+
return {
|
|
2473
|
+
id: typeof b.id === "string" ? b.id : uid(),
|
|
2474
|
+
type: "html",
|
|
2475
|
+
content: { html },
|
|
2476
|
+
props: rawProps
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2480
|
+
const out = {
|
|
2481
|
+
id: doc.id,
|
|
2482
|
+
type: doc.type,
|
|
2483
|
+
content: doc.content ?? {},
|
|
2484
|
+
props: rawProps
|
|
2485
|
+
};
|
|
2486
|
+
if (doc.behavior) out.behavior = doc.behavior;
|
|
2487
|
+
return out;
|
|
2488
|
+
}
|
|
2489
|
+
function editorRowToEmailDocumentRow(r, rowIndex) {
|
|
2490
|
+
const rowProps = editorRowToDocProps(r);
|
|
2491
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
2492
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? Math.floor(r.cols) : 0;
|
|
2493
|
+
const colCount = Math.max(1, cellArrays.length, declaredCols);
|
|
2494
|
+
const rowColumns = getColumns(r);
|
|
2495
|
+
const columns = Array.from({ length: colCount }, (_, ci) => {
|
|
2496
|
+
const cp = {
|
|
2497
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2498
|
+
...rowColumns[ci]?.props ?? {}
|
|
2499
|
+
};
|
|
2500
|
+
const cellBlocks = cellArrays[ci];
|
|
2501
|
+
const blocks = Array.isArray(cellBlocks) ? cellBlocks.map((blk) => exportBlockForLegacyRow(blk)).filter((x) => x != null) : [];
|
|
2502
|
+
return {
|
|
2503
|
+
id: `col_${rowIndex + 1}_${ci + 1}`,
|
|
2504
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2505
|
+
styles: columnPropsToLegacyStyles(cp),
|
|
2506
|
+
props: cloneJson(cp),
|
|
2507
|
+
blocks
|
|
2508
|
+
};
|
|
2509
|
+
});
|
|
2510
|
+
const ratios = Array.isArray(r.ratios) ? [...r.ratios] : void 0;
|
|
2511
|
+
return {
|
|
2512
|
+
id: typeof r.id === "string" ? r.id : uid(),
|
|
2513
|
+
type: "row",
|
|
2514
|
+
_reactEmailStudio: { row: editorRowStudioSnapshot(r) },
|
|
2515
|
+
layout: {
|
|
2516
|
+
columns: colCount,
|
|
2517
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2518
|
+
stackOnMobile: true,
|
|
2519
|
+
align: "center",
|
|
2520
|
+
...typeof r.preset === "string" && r.preset.trim() ? { preset: r.preset.trim() } : {},
|
|
2521
|
+
...ratios?.length ? { ratios } : {}
|
|
2522
|
+
},
|
|
2523
|
+
props: rowProps,
|
|
2524
|
+
styles: rowPropsToLegacyStyles(rowProps),
|
|
2525
|
+
columns
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
function buildExportSettingsWithStudio(settings) {
|
|
2529
|
+
const base = buildExportSettings(settings) ?? {};
|
|
2530
|
+
const editorSettings = cloneJson(settings);
|
|
2531
|
+
return {
|
|
2532
|
+
...base,
|
|
2533
|
+
_reactEmailStudio: {
|
|
2534
|
+
contentPadding: base.contentPadding ?? 24,
|
|
2535
|
+
contentBorderRadius: base.contentBorderRadius ?? 8,
|
|
2536
|
+
editorSettings
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
function editorSettingStr(settings, key, fallback = "") {
|
|
2541
|
+
const v = settings[key];
|
|
2542
|
+
return typeof v === "string" ? v : fallback;
|
|
2543
|
+
}
|
|
2544
|
+
function buildExportSettings(settings) {
|
|
2545
|
+
const contentWidth = typeof settings.contentWidth === "number" ? settings.contentWidth : 600;
|
|
2546
|
+
const bgGradient = settings.bgGradient;
|
|
2547
|
+
const contentBgGradient = settings.contentBgGradient;
|
|
2548
|
+
return {
|
|
2549
|
+
width: contentWidth,
|
|
2550
|
+
backgroundColor: editorSettingStr(settings, "bgColor", "#f1f5f9"),
|
|
2551
|
+
backgroundImage: editorSettingStr(settings, "bgImage"),
|
|
2552
|
+
backgroundRepeat: editorSettingStr(settings, "bgRepeat", "no-repeat"),
|
|
2553
|
+
backgroundSize: editorSettingStr(settings, "bgSize", "cover"),
|
|
2554
|
+
backgroundPosition: editorSettingStr(settings, "bgPosition", "center"),
|
|
2555
|
+
backgroundGradient: bgGradient && typeof bgGradient === "object" && !Array.isArray(bgGradient) ? bgGradient : void 0,
|
|
2556
|
+
contentBackgroundColor: editorSettingStr(settings, "contentBg", "#ffffff"),
|
|
2557
|
+
contentBackgroundImage: editorSettingStr(settings, "contentBgImage"),
|
|
2558
|
+
contentBackgroundRepeat: editorSettingStr(settings, "contentBgRepeat", "no-repeat"),
|
|
2559
|
+
contentBackgroundSize: editorSettingStr(settings, "contentBgSize", "cover"),
|
|
2560
|
+
contentBackgroundPosition: editorSettingStr(settings, "contentBgPosition", "center"),
|
|
2561
|
+
contentBackgroundGradient: contentBgGradient && typeof contentBgGradient === "object" && !Array.isArray(contentBgGradient) ? contentBgGradient : void 0,
|
|
2562
|
+
fontFamily: editorSettingStr(settings, "fontFamily") || editorSettingStr(settings, "pageFontFamily", "Arial, Helvetica, sans-serif"),
|
|
2563
|
+
lineHeightBase: settings.pageLineHeight != null && settings.pageLineHeight !== "" ? Number(settings.pageLineHeight) : 1.6,
|
|
2564
|
+
color: editorSettingStr(settings, "pageTextColor", "#111827"),
|
|
2565
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
2566
|
+
rtl: !!settings.pageRtl,
|
|
2567
|
+
contentPadding: typeof settings.padding === "number" ? settings.padding : 24,
|
|
2568
|
+
contentBorderRadius: typeof settings.borderRadius === "number" ? settings.borderRadius : 8
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
function editorSettingsFromDoc(s, rawSettings) {
|
|
2572
|
+
const studioSettings = rawSettings?._reactEmailStudio;
|
|
1905
2573
|
const settings = {};
|
|
1906
2574
|
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1907
2575
|
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1908
2576
|
}
|
|
2577
|
+
const pageRtlFromEditor = settings.pageRtl;
|
|
2578
|
+
const contentPadding = typeof s.contentPadding === "number" ? s.contentPadding : typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : void 0;
|
|
2579
|
+
const contentBorderRadius = typeof s.contentBorderRadius === "number" ? s.contentBorderRadius : typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : void 0;
|
|
1909
2580
|
Object.assign(settings, {
|
|
1910
2581
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1911
2582
|
bgImage: s.backgroundImage || "",
|
|
@@ -1920,103 +2591,67 @@ function normalizeEmailDesignInput(input) {
|
|
|
1920
2591
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1921
2592
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1922
2593
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1923
|
-
padding:
|
|
1924
|
-
borderRadius:
|
|
2594
|
+
padding: contentPadding ?? (typeof settings.padding === "number" ? settings.padding : 24),
|
|
2595
|
+
borderRadius: contentBorderRadius ?? (typeof settings.borderRadius === "number" ? settings.borderRadius : 8),
|
|
1925
2596
|
pageFontFamily: s.fontFamily,
|
|
1926
2597
|
pageTextColor: s.color,
|
|
1927
2598
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1928
2599
|
pageResponsive: s.responsive,
|
|
1929
|
-
pageRtl: s.rtl,
|
|
2600
|
+
pageRtl: typeof s.rtl === "boolean" ? s.rtl : pageRtlFromEditor,
|
|
1930
2601
|
fontFamily: s.fontFamily
|
|
1931
2602
|
});
|
|
1932
|
-
|
|
2603
|
+
return settings;
|
|
2604
|
+
}
|
|
2605
|
+
function normalizeEmailDesignInput(input) {
|
|
2606
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2607
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2608
|
+
if (!doc) return null;
|
|
2609
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2610
|
+
const rawSettings = rawDoc?.settings && typeof rawDoc.settings === "object" && !Array.isArray(rawDoc.settings) ? rawDoc.settings : null;
|
|
2611
|
+
const settings = editorSettingsFromDoc(doc.settings || {}, rawSettings);
|
|
2612
|
+
let rows;
|
|
2613
|
+
if (doc.blocks?.length) {
|
|
2614
|
+
rows = blocksToEditorRows(doc.blocks);
|
|
2615
|
+
} else {
|
|
2616
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2617
|
+
rows = (doc.rows || []).map((r, i) => {
|
|
2618
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2619
|
+
if (legacy) {
|
|
2620
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2621
|
+
}
|
|
2622
|
+
return rowToInternal(r);
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
1933
2625
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1934
2626
|
}
|
|
2627
|
+
function emailDocumentToEditorRows(input) {
|
|
2628
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2629
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2630
|
+
if (!doc) return null;
|
|
2631
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2632
|
+
if (doc.blocks?.length) {
|
|
2633
|
+
return blocksToEditorRows(doc.blocks);
|
|
2634
|
+
}
|
|
2635
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2636
|
+
return (doc.rows || []).map((r, i) => {
|
|
2637
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2638
|
+
if (legacy) {
|
|
2639
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2640
|
+
}
|
|
2641
|
+
return rowToInternal(r);
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
1935
2644
|
function designToEmailDocument(rows, settings) {
|
|
1936
|
-
|
|
2645
|
+
return {
|
|
1937
2646
|
type: "email_document",
|
|
1938
|
-
settings:
|
|
1939
|
-
|
|
1940
|
-
backgroundColor: settings.bgColor || "#f1f5f9",
|
|
1941
|
-
backgroundImage: settings.bgImage || "",
|
|
1942
|
-
backgroundRepeat: settings.bgRepeat || "no-repeat",
|
|
1943
|
-
backgroundSize: settings.bgSize || "cover",
|
|
1944
|
-
backgroundPosition: settings.bgPosition || "center",
|
|
1945
|
-
backgroundGradient: settings.bgGradient || null,
|
|
1946
|
-
contentBackgroundColor: settings.contentBg || "#ffffff",
|
|
1947
|
-
contentBackgroundImage: settings.contentBgImage || "",
|
|
1948
|
-
contentBackgroundRepeat: settings.contentBgRepeat || "no-repeat",
|
|
1949
|
-
contentBackgroundSize: settings.contentBgSize || "cover",
|
|
1950
|
-
contentBackgroundPosition: settings.contentBgPosition || "center",
|
|
1951
|
-
contentBackgroundGradient: settings.contentBgGradient || null,
|
|
1952
|
-
fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
|
|
1953
|
-
lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
|
|
1954
|
-
color: settings.pageTextColor || "#111827",
|
|
1955
|
-
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
1956
|
-
rtl: !!settings.pageRtl,
|
|
1957
|
-
_reactEmailStudio: {
|
|
1958
|
-
contentPadding: settings.padding ?? 24,
|
|
1959
|
-
contentBorderRadius: settings.borderRadius ?? 8,
|
|
1960
|
-
editorSettings: cloneJson(settings)
|
|
1961
|
-
}
|
|
1962
|
-
},
|
|
1963
|
-
rows: rows.map((r, ri) => {
|
|
1964
|
-
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
1965
|
-
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
|
|
1966
|
-
const cols = Math.max(1, cellArrays.length, declaredCols);
|
|
1967
|
-
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1968
|
-
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1969
|
-
return { top: n, right: n, bottom: n, left: n };
|
|
1970
|
-
})();
|
|
1971
|
-
const { cells: _rowCells, ...rowEditorSnap } = r;
|
|
1972
|
-
return {
|
|
1973
|
-
id: r.id || `row_${ri + 1}`,
|
|
1974
|
-
type: "row",
|
|
1975
|
-
_reactEmailStudio: {
|
|
1976
|
-
row: cloneJson(rowEditorSnap)
|
|
1977
|
-
},
|
|
1978
|
-
layout: {
|
|
1979
|
-
columns: cols,
|
|
1980
|
-
gap: r.gap ?? 0,
|
|
1981
|
-
stackOnMobile: true,
|
|
1982
|
-
align: "center"
|
|
1983
|
-
},
|
|
1984
|
-
styles: {
|
|
1985
|
-
backgroundColor: r.bgColor || "",
|
|
1986
|
-
backgroundImage: r.bgImage || "",
|
|
1987
|
-
backgroundRepeat: r.bgRepeat || "no-repeat",
|
|
1988
|
-
backgroundSize: r.bgSize || "cover",
|
|
1989
|
-
backgroundPosition: r.bgPosition || "center",
|
|
1990
|
-
backgroundGradient: r.bgGradient || null,
|
|
1991
|
-
padding: rowPad,
|
|
1992
|
-
borderRadius: 0,
|
|
1993
|
-
borderWidth: 0,
|
|
1994
|
-
borderColor: "#e5e7eb",
|
|
1995
|
-
textAlign: "left"
|
|
1996
|
-
},
|
|
1997
|
-
columns: Array.from({ length: cols }, (_, ci) => {
|
|
1998
|
-
const cellBlocks = cellArrays[ci] ?? [];
|
|
1999
|
-
const cs = r.columnStyles && r.columnStyles[ci] || {};
|
|
2000
|
-
return {
|
|
2001
|
-
id: `col_${ri + 1}_${ci + 1}`,
|
|
2002
|
-
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2003
|
-
styles: {
|
|
2004
|
-
padding: layoutColumnPaddingForDoc(cs.padding),
|
|
2005
|
-
backgroundColor: cs.bgColor || "",
|
|
2006
|
-
backgroundImage: cs.bgImage || "",
|
|
2007
|
-
backgroundRepeat: cs.bgRepeat || "no-repeat",
|
|
2008
|
-
backgroundSize: cs.bgSize || "cover",
|
|
2009
|
-
backgroundPosition: cs.bgPosition || "center",
|
|
2010
|
-
backgroundGradient: cs.bgGradient || null,
|
|
2011
|
-
borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
|
|
2012
|
-
},
|
|
2013
|
-
blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
|
|
2014
|
-
};
|
|
2015
|
-
})
|
|
2016
|
-
};
|
|
2017
|
-
})
|
|
2647
|
+
settings: buildExportSettingsWithStudio(settings),
|
|
2648
|
+
rows: rows.map((r, i) => editorRowToEmailDocumentRow(r, i))
|
|
2018
2649
|
};
|
|
2019
|
-
|
|
2650
|
+
}
|
|
2651
|
+
function canonicalizeEmailDocument(input) {
|
|
2652
|
+
const loaded = normalizeEmailDesignInput(coerceEmailDocumentInput(input) ?? input);
|
|
2653
|
+
if (!loaded?.rows) return null;
|
|
2654
|
+
return designToEmailDocument(loaded.rows, loaded.settings);
|
|
2020
2655
|
}
|
|
2021
2656
|
function hydrateBlock(b, depth = 0) {
|
|
2022
2657
|
if (!b || typeof b !== "object") return null;
|
|
@@ -2030,13 +2665,13 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2030
2665
|
...b,
|
|
2031
2666
|
type: "layout",
|
|
2032
2667
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2033
|
-
props: {
|
|
2668
|
+
props: normalizeLayoutContainerProps({
|
|
2034
2669
|
...defaults2,
|
|
2035
2670
|
...b.props,
|
|
2036
2671
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2037
2672
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2038
2673
|
cells: ratios.map(() => [])
|
|
2039
|
-
}
|
|
2674
|
+
})
|
|
2040
2675
|
};
|
|
2041
2676
|
}
|
|
2042
2677
|
const cells = b.props.cells.map(
|
|
@@ -2046,24 +2681,33 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2046
2681
|
...b,
|
|
2047
2682
|
type: "layout",
|
|
2048
2683
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2049
|
-
props: {
|
|
2684
|
+
props: normalizeLayoutContainerProps({
|
|
2050
2685
|
...defaults2,
|
|
2051
2686
|
...b.props,
|
|
2052
2687
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2053
2688
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2054
2689
|
cells
|
|
2055
|
-
}
|
|
2690
|
+
})
|
|
2056
2691
|
};
|
|
2057
2692
|
}
|
|
2058
2693
|
const defaults = DEFAULT_BLOCK_PROPS[t];
|
|
2059
2694
|
if (typeof defaults !== "object" || defaults == null || Array.isArray(defaults)) return null;
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
)
|
|
2066
|
-
|
|
2695
|
+
if (t === "html") {
|
|
2696
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? b.props : {};
|
|
2697
|
+
const { content: legacyPropContent, ...restProps } = rawProps;
|
|
2698
|
+
const merged2 = { ...defaults, ...restProps };
|
|
2699
|
+
normalizeBoxStyles(merged2, t);
|
|
2700
|
+
const src = typeof b.content === "string" ? String(b.content) : typeof legacyPropContent === "string" ? legacyPropContent : "";
|
|
2701
|
+
const htmlNorm = normalizeRichHtmlForStorage(src);
|
|
2702
|
+
merged2.content = htmlNorm;
|
|
2703
|
+
return {
|
|
2704
|
+
...b,
|
|
2705
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2706
|
+
content: htmlNorm,
|
|
2707
|
+
props: merged2
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
const merged = { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
|
|
2067
2711
|
normalizeBoxStyles(merged, t);
|
|
2068
2712
|
return {
|
|
2069
2713
|
...b,
|
|
@@ -2079,70 +2723,50 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2079
2723
|
}
|
|
2080
2724
|
return mapBlockToInternal(r, layoutDepth);
|
|
2081
2725
|
}
|
|
2082
|
-
function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
|
|
2083
|
-
const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
2084
|
-
if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
|
|
2085
|
-
const a = isObj(fromContent) ? fromContent : {};
|
|
2086
|
-
const b = isObj(fromHydrated) ? fromHydrated : {};
|
|
2087
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
2088
|
-
const out = {};
|
|
2089
|
-
for (const k of keys) {
|
|
2090
|
-
const va = a[k];
|
|
2091
|
-
const vb = b[k];
|
|
2092
|
-
if (isObj(va) && isObj(vb)) {
|
|
2093
|
-
out[k] = { ...va, ...vb };
|
|
2094
|
-
} else if (vb !== void 0) {
|
|
2095
|
-
out[k] = vb;
|
|
2096
|
-
} else {
|
|
2097
|
-
out[k] = va;
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
return Object.keys(out).length ? out : void 0;
|
|
2101
|
-
}
|
|
2102
2726
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2103
2727
|
const exp = b.props;
|
|
2104
|
-
if (!exp || typeof exp !== "object" || Array.isArray(exp))
|
|
2728
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) {
|
|
2729
|
+
if (t === "html" && typeof block.content === "string") {
|
|
2730
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2731
|
+
block.props.content = block.content;
|
|
2732
|
+
}
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2105
2735
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2106
|
-
const
|
|
2736
|
+
const columnsFromContent = block.props.columns ?? block.props.columnStyles;
|
|
2737
|
+
const expHasColumns = Array.isArray(exp.columns) || exp.columnStyles && typeof exp.columnStyles === "object" && !Array.isArray(exp.columnStyles);
|
|
2107
2738
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2108
2739
|
if (hb) {
|
|
2109
2740
|
block.props = hb.props;
|
|
2110
|
-
const merged =
|
|
2111
|
-
|
|
2112
|
-
|
|
2741
|
+
const merged = mergeLayoutColumns(
|
|
2742
|
+
columnsFromContent,
|
|
2743
|
+
expHasColumns ? block.props.columns : void 0
|
|
2744
|
+
);
|
|
2745
|
+
if (merged) block.props.columns = merged;
|
|
2746
|
+
else delete block.props.columns;
|
|
2747
|
+
delete block.props.columnStyles;
|
|
2113
2748
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2114
2749
|
}
|
|
2115
2750
|
return;
|
|
2116
2751
|
}
|
|
2752
|
+
if (t === "html") {
|
|
2753
|
+
const { content: expContent, ...expRest } = exp;
|
|
2754
|
+
Object.assign(block.props, expRest);
|
|
2755
|
+
normalizeBoxStyles(block.props, t);
|
|
2756
|
+
if (typeof expContent === "string") {
|
|
2757
|
+
block.content = normalizeRichHtmlForStorage(expContent);
|
|
2758
|
+
} else if (typeof block.content === "string") {
|
|
2759
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2760
|
+
}
|
|
2761
|
+
block.props.content = typeof block.content === "string" ? block.content : "";
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2117
2764
|
Object.assign(block.props, exp);
|
|
2118
2765
|
normalizeBoxStyles(block.props, t);
|
|
2119
|
-
if (t === "
|
|
2766
|
+
if (t === "text") {
|
|
2120
2767
|
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2121
2768
|
}
|
|
2122
2769
|
}
|
|
2123
|
-
function exportEmailDocBlock(b, depth = 0) {
|
|
2124
|
-
if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
|
|
2125
|
-
const doc = internalBlockToEmailDoc(b, depth);
|
|
2126
|
-
const out = { ...doc };
|
|
2127
|
-
if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
2128
|
-
out.props = cloneJson(b.props);
|
|
2129
|
-
}
|
|
2130
|
-
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2131
|
-
const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
|
|
2132
|
-
if (depth >= MAX_LAYOUT_TREE_DEPTH) {
|
|
2133
|
-
const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
|
|
2134
|
-
out.content = { ...baseContent, cells: ratios.map(() => []) };
|
|
2135
|
-
} else {
|
|
2136
|
-
out.content = {
|
|
2137
|
-
...baseContent,
|
|
2138
|
-
cells: b.props.cells.map(
|
|
2139
|
-
(col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
|
|
2140
|
-
)
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
return out;
|
|
2145
|
-
}
|
|
2146
2770
|
function hydrateLayoutRow(row) {
|
|
2147
2771
|
if (!row || typeof row !== "object") return row;
|
|
2148
2772
|
const cells = (row.cells || []).map(
|
|
@@ -2185,8 +2809,85 @@ function jsonToHtml(designInput, opts = {}) {
|
|
|
2185
2809
|
return designToHtml(d.rows, d.settings, opts);
|
|
2186
2810
|
}
|
|
2187
2811
|
|
|
2812
|
+
// src/lib/htmlToEmailDesign.ts
|
|
2813
|
+
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
2814
|
+
function extractHtmlForDesign(html) {
|
|
2815
|
+
const t = String(html ?? "").trim();
|
|
2816
|
+
if (!t) return "";
|
|
2817
|
+
const head = t.slice(0, 800).toLowerCase();
|
|
2818
|
+
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
2819
|
+
return normalizeRichHtmlForStorage(t);
|
|
2820
|
+
}
|
|
2821
|
+
try {
|
|
2822
|
+
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
2823
|
+
const body = doc.body;
|
|
2824
|
+
if (!body) return normalizeRichHtmlForStorage(t);
|
|
2825
|
+
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
2826
|
+
} catch {
|
|
2827
|
+
return normalizeRichHtmlForStorage(t);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
function htmlToEmailDesignTemplate(html) {
|
|
2831
|
+
const inner = extractHtmlForDesign(html);
|
|
2832
|
+
if (!inner) return null;
|
|
2833
|
+
const doc = {
|
|
2834
|
+
type: "email_document",
|
|
2835
|
+
settings: {
|
|
2836
|
+
width: 600,
|
|
2837
|
+
backgroundColor: "#f1f5f9",
|
|
2838
|
+
contentBackgroundColor: "#ffffff",
|
|
2839
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
2840
|
+
lineHeightBase: 1.6,
|
|
2841
|
+
color: "#111827",
|
|
2842
|
+
responsive: true,
|
|
2843
|
+
rtl: false
|
|
2844
|
+
},
|
|
2845
|
+
rows: [
|
|
2846
|
+
{
|
|
2847
|
+
id: "row_html_import",
|
|
2848
|
+
type: "row",
|
|
2849
|
+
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
2850
|
+
styles: {
|
|
2851
|
+
backgroundColor: "#ffffff",
|
|
2852
|
+
backgroundRepeat: "no-repeat",
|
|
2853
|
+
backgroundSize: "cover",
|
|
2854
|
+
padding: pad(24),
|
|
2855
|
+
textAlign: "left"
|
|
2856
|
+
},
|
|
2857
|
+
columns: [
|
|
2858
|
+
{
|
|
2859
|
+
id: "col_html_import",
|
|
2860
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2861
|
+
styles: { padding: pad(0), backgroundColor: "" },
|
|
2862
|
+
blocks: [
|
|
2863
|
+
{
|
|
2864
|
+
id: "block_html_import",
|
|
2865
|
+
type: "html",
|
|
2866
|
+
content: inner,
|
|
2867
|
+
props: { ...DEFAULT_BLOCK_PROPS.html }
|
|
2868
|
+
}
|
|
2869
|
+
]
|
|
2870
|
+
}
|
|
2871
|
+
]
|
|
2872
|
+
}
|
|
2873
|
+
]
|
|
2874
|
+
};
|
|
2875
|
+
return doc;
|
|
2876
|
+
}
|
|
2877
|
+
function htmlToJson(html, pretty = false) {
|
|
2878
|
+
const doc = htmlToEmailDesignTemplate(html);
|
|
2879
|
+
if (!doc) return "";
|
|
2880
|
+
return pretty ? JSON.stringify(doc, null, 2) : JSON.stringify(doc);
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2188
2883
|
// src/editor/canvas/DesignCanvas.tsx
|
|
2189
|
-
import {
|
|
2884
|
+
import {
|
|
2885
|
+
useState,
|
|
2886
|
+
useRef,
|
|
2887
|
+
useCallback,
|
|
2888
|
+
useEffect,
|
|
2889
|
+
Fragment
|
|
2890
|
+
} from "react";
|
|
2190
2891
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2191
2892
|
function useLiveCountdown(endDate) {
|
|
2192
2893
|
const [diff, setDiff] = useState(0);
|
|
@@ -2325,13 +3026,15 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2325
3026
|
)
|
|
2326
3027
|
);
|
|
2327
3028
|
if (type === "html") {
|
|
2328
|
-
const
|
|
3029
|
+
const richHtml = typeof block.content === "string" ? block.content : p.content;
|
|
3030
|
+
const emptyRich = isEffectivelyEmptyRichHtml(richHtml);
|
|
2329
3031
|
return wrap(
|
|
2330
3032
|
/* @__PURE__ */ jsxs2("div", { style: { position: "relative", minHeight: emptyRich && !preview ? "2.75em" : void 0 }, children: [
|
|
3033
|
+
/* @__PURE__ */ jsx3("style", { children: RICH_HTML_CONTENT_CSS }),
|
|
2331
3034
|
/* @__PURE__ */ jsx3(
|
|
2332
3035
|
"div",
|
|
2333
3036
|
{
|
|
2334
|
-
className: "email-editor-rich-canvas",
|
|
3037
|
+
className: "email-editor-rich-canvas email-rich-html-content",
|
|
2335
3038
|
style: {
|
|
2336
3039
|
fontSize: p.fontSize,
|
|
2337
3040
|
color: p.color,
|
|
@@ -2342,7 +3045,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2342
3045
|
wordBreak: "break-word",
|
|
2343
3046
|
minHeight: emptyRich && !preview ? "2.75em" : void 0
|
|
2344
3047
|
},
|
|
2345
|
-
dangerouslySetInnerHTML: { __html:
|
|
3048
|
+
dangerouslySetInnerHTML: { __html: richHtml || "" }
|
|
2346
3049
|
}
|
|
2347
3050
|
),
|
|
2348
3051
|
emptyRich && !preview ? /* @__PURE__ */ jsx3(
|
|
@@ -2390,7 +3093,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2390
3093
|
}
|
|
2391
3094
|
}
|
|
2392
3095
|
);
|
|
2393
|
-
return wrap(/* @__PURE__ */ jsx3("div", { style: { textAlign: p.align }, children: p
|
|
3096
|
+
return wrap(/* @__PURE__ */ jsx3("div", { style: { textAlign: p.align }, children: imageLinkActive(p) ? /* @__PURE__ */ jsx3("a", { href: p.link, target: p.linkTarget || "_blank", rel: "noopener", children: img }) : img }));
|
|
2394
3097
|
}
|
|
2395
3098
|
if (type === "button") return wrap(
|
|
2396
3099
|
/* @__PURE__ */ jsx3("div", { style: { textAlign: p.fullWidth ? "center" : p.align }, children: /* @__PURE__ */ jsx3(
|
|
@@ -2563,7 +3266,7 @@ function NestedRowBlock({
|
|
|
2563
3266
|
);
|
|
2564
3267
|
},
|
|
2565
3268
|
children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2566
|
-
const cS = p
|
|
3269
|
+
const cS = getColumnPropsAt(p, ici);
|
|
2567
3270
|
return /* @__PURE__ */ jsx3(
|
|
2568
3271
|
"div",
|
|
2569
3272
|
{
|
|
@@ -2621,7 +3324,9 @@ function BlockItem({
|
|
|
2621
3324
|
onContextMenu,
|
|
2622
3325
|
preview,
|
|
2623
3326
|
C,
|
|
2624
|
-
Line
|
|
3327
|
+
Line,
|
|
3328
|
+
dragAsRow = false,
|
|
3329
|
+
beginRowDrag
|
|
2625
3330
|
}) {
|
|
2626
3331
|
const wrapperRef = useRef(null);
|
|
2627
3332
|
const bid = editorId ? `${editorId}-block-${cb.id}` : void 0;
|
|
@@ -2669,7 +3374,7 @@ function BlockItem({
|
|
|
2669
3374
|
{
|
|
2670
3375
|
"data-bidx": ci,
|
|
2671
3376
|
draggable: !preview,
|
|
2672
|
-
title: preview ? void 0 : "Click to select \xB7 drag to reorder or move",
|
|
3377
|
+
title: preview ? void 0 : dragAsRow ? "Click to select \xB7 drag to reorder section" : "Click to select \xB7 drag to reorder or move",
|
|
2673
3378
|
onPointerDown: (e) => {
|
|
2674
3379
|
if (preview || e.button !== 0) return;
|
|
2675
3380
|
selectThisBlock(e);
|
|
@@ -2677,6 +3382,10 @@ function BlockItem({
|
|
|
2677
3382
|
onDragStart: (e) => {
|
|
2678
3383
|
if (preview) return;
|
|
2679
3384
|
e.stopPropagation();
|
|
3385
|
+
if (dragAsRow && beginRowDrag) {
|
|
3386
|
+
beginRowDrag(e);
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
2680
3389
|
e.dataTransfer.effectAllowed = "move";
|
|
2681
3390
|
e.dataTransfer.setData(
|
|
2682
3391
|
"application/json",
|
|
@@ -2748,7 +3457,9 @@ function Cell({
|
|
|
2748
3457
|
onDeleteContent,
|
|
2749
3458
|
onContextMenu,
|
|
2750
3459
|
preview,
|
|
2751
|
-
C
|
|
3460
|
+
C,
|
|
3461
|
+
dragAsRow = false,
|
|
3462
|
+
beginRowDrag
|
|
2752
3463
|
}) {
|
|
2753
3464
|
const [insertAt, setInsertAt] = useState(null);
|
|
2754
3465
|
const cellRef = useRef(null);
|
|
@@ -2763,6 +3474,14 @@ function Cell({
|
|
|
2763
3474
|
return els.length;
|
|
2764
3475
|
}, [blocks.length]);
|
|
2765
3476
|
const handleDragOver = (e) => {
|
|
3477
|
+
try {
|
|
3478
|
+
const raw = e.dataTransfer.getData("application/json");
|
|
3479
|
+
if (raw) {
|
|
3480
|
+
const data = JSON.parse(raw);
|
|
3481
|
+
if (data.moveRowId) return;
|
|
3482
|
+
}
|
|
3483
|
+
} catch {
|
|
3484
|
+
}
|
|
2766
3485
|
e.preventDefault();
|
|
2767
3486
|
e.stopPropagation();
|
|
2768
3487
|
setInsertAt(calcIdx(e.clientY));
|
|
@@ -2778,6 +3497,7 @@ function Cell({
|
|
|
2778
3497
|
setInsertAt(null);
|
|
2779
3498
|
try {
|
|
2780
3499
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
3500
|
+
if (data.moveRowId) return;
|
|
2781
3501
|
if (data.contentType) {
|
|
2782
3502
|
onDropContent(rowId, cellIdx, { kind: "new", contentType: data.contentType, insertAt: idx, nested: nestedPayload });
|
|
2783
3503
|
} else if (data.moveContent) {
|
|
@@ -2857,7 +3577,9 @@ function Cell({
|
|
|
2857
3577
|
onContextMenu,
|
|
2858
3578
|
preview,
|
|
2859
3579
|
C,
|
|
2860
|
-
Line
|
|
3580
|
+
Line,
|
|
3581
|
+
dragAsRow: dragAsRow && blocks.length === 1 && ci === 0,
|
|
3582
|
+
beginRowDrag
|
|
2861
3583
|
}
|
|
2862
3584
|
) }, cb.id))
|
|
2863
3585
|
]
|
|
@@ -2878,7 +3600,8 @@ function LayoutRow({
|
|
|
2878
3600
|
onLayoutContextMenu,
|
|
2879
3601
|
setSelectedRowId,
|
|
2880
3602
|
preview = false,
|
|
2881
|
-
|
|
3603
|
+
rootContentRow = false,
|
|
3604
|
+
beginRowDrag,
|
|
2882
3605
|
C
|
|
2883
3606
|
}) {
|
|
2884
3607
|
const rowStyle = {
|
|
@@ -2910,16 +3633,9 @@ function LayoutRow({
|
|
|
2910
3633
|
onContextMenu: preview || !onLayoutContextMenu ? void 0 : (e) => {
|
|
2911
3634
|
onLayoutContextMenu(e, row.id);
|
|
2912
3635
|
},
|
|
2913
|
-
|
|
2914
|
-
onDragStart: rowDrag?.onDragStart,
|
|
2915
|
-
onDragEnd: rowDrag?.onDragEnd,
|
|
2916
|
-
title: rowDrag?.draggable ? "Click row \xB7 drag to reorder" : void 0,
|
|
2917
|
-
style: {
|
|
2918
|
-
...rowStyle,
|
|
2919
|
-
...rowDrag?.draggable ? { cursor: "grab" } : {}
|
|
2920
|
-
},
|
|
3636
|
+
style: rowStyle,
|
|
2921
3637
|
children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: row.gap }, children: row.cells.map((cellBlocks, ci) => {
|
|
2922
|
-
const cS = row
|
|
3638
|
+
const cS = getColumnPropsAt(row, ci);
|
|
2923
3639
|
return /* @__PURE__ */ jsx3(
|
|
2924
3640
|
"div",
|
|
2925
3641
|
{
|
|
@@ -2955,7 +3671,9 @@ function LayoutRow({
|
|
|
2955
3671
|
onDeleteContent,
|
|
2956
3672
|
onContextMenu,
|
|
2957
3673
|
preview,
|
|
2958
|
-
C
|
|
3674
|
+
C,
|
|
3675
|
+
dragAsRow: rootContentRow && ci === 0,
|
|
3676
|
+
beginRowDrag
|
|
2959
3677
|
}
|
|
2960
3678
|
)
|
|
2961
3679
|
},
|
|
@@ -2972,10 +3690,11 @@ var CanvasRow = ({
|
|
|
2972
3690
|
selectedRowId,
|
|
2973
3691
|
selContentMeta: rowSelContentMeta,
|
|
2974
3692
|
dragOver,
|
|
3693
|
+
draggingRowId,
|
|
2975
3694
|
C,
|
|
2976
3695
|
setDragOver,
|
|
2977
3696
|
handleRowDrop,
|
|
2978
|
-
|
|
3697
|
+
setRowDrag,
|
|
2979
3698
|
setSelectedRowId,
|
|
2980
3699
|
setSelContentId,
|
|
2981
3700
|
setSelMeta,
|
|
@@ -2988,61 +3707,168 @@ var CanvasRow = ({
|
|
|
2988
3707
|
}) => {
|
|
2989
3708
|
const rowSelected = selectedRowId === row.id;
|
|
2990
3709
|
const rid = (s) => editorId ? `${editorId}-${s}` : void 0;
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3710
|
+
const dropH = dragOver === ri || dragOver === ri + 1 ? 28 : 10;
|
|
3711
|
+
const rowBodyRef = useRef(null);
|
|
3712
|
+
const rootContentRow = isRootContentRow(row);
|
|
3713
|
+
const beginRowDrag = useCallback(
|
|
3714
|
+
(e) => {
|
|
3715
|
+
e.stopPropagation();
|
|
3716
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3717
|
+
e.dataTransfer.setData(
|
|
3718
|
+
"application/json",
|
|
3719
|
+
JSON.stringify({ moveRowId: row.id })
|
|
3720
|
+
);
|
|
3721
|
+
setRowDrag(row.id);
|
|
3722
|
+
},
|
|
3723
|
+
[row.id, setRowDrag]
|
|
3724
|
+
);
|
|
3725
|
+
const rowDragOver = useCallback(
|
|
3726
|
+
(e) => {
|
|
3727
|
+
if (!draggingRowId || draggingRowId === row.id) return;
|
|
3728
|
+
e.preventDefault();
|
|
3729
|
+
e.stopPropagation();
|
|
3730
|
+
const el = rowBodyRef.current;
|
|
3731
|
+
if (!el) {
|
|
3732
|
+
setDragOver(ri);
|
|
3733
|
+
return;
|
|
3006
3734
|
}
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
onSelectContent: selectContent,
|
|
3023
|
-
onDropContent: dropContent,
|
|
3024
|
-
onDeleteContent: deleteContent,
|
|
3025
|
-
onContextMenu: handleContextMenu,
|
|
3026
|
-
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3027
|
-
rowDrag: {
|
|
3028
|
-
draggable: true,
|
|
3029
|
-
onDragStart: (e) => {
|
|
3030
|
-
e.stopPropagation();
|
|
3031
|
-
setDraggingRowId(row.id);
|
|
3032
|
-
},
|
|
3033
|
-
onDragEnd: () => setDraggingRowId(null)
|
|
3034
|
-
},
|
|
3035
|
-
C
|
|
3735
|
+
const rect = el.getBoundingClientRect();
|
|
3736
|
+
setDragOver(e.clientY < rect.top + rect.height / 2 ? ri : ri + 1);
|
|
3737
|
+
},
|
|
3738
|
+
[draggingRowId, row.id, ri, setDragOver]
|
|
3739
|
+
);
|
|
3740
|
+
const rowDrop = useCallback(
|
|
3741
|
+
(e) => {
|
|
3742
|
+
if (!draggingRowId) return;
|
|
3743
|
+
e.preventDefault();
|
|
3744
|
+
e.stopPropagation();
|
|
3745
|
+
const el = rowBodyRef.current;
|
|
3746
|
+
let targetIdx = ri;
|
|
3747
|
+
if (el) {
|
|
3748
|
+
const rect = el.getBoundingClientRect();
|
|
3749
|
+
targetIdx = e.clientY < rect.top + rect.height / 2 ? ri : ri + 1;
|
|
3036
3750
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3751
|
+
handleRowDrop(targetIdx, e);
|
|
3752
|
+
},
|
|
3753
|
+
[draggingRowId, ri, handleRowDrop]
|
|
3754
|
+
);
|
|
3755
|
+
return /* @__PURE__ */ jsxs2(
|
|
3756
|
+
"div",
|
|
3757
|
+
{
|
|
3758
|
+
id: rid(`canvas-row-${row.id}`),
|
|
3759
|
+
style: { display: "flex", alignItems: "stretch", gap: 0, margin: "2px 0" },
|
|
3760
|
+
children: [
|
|
3761
|
+
/* @__PURE__ */ jsx3(
|
|
3762
|
+
"div",
|
|
3763
|
+
{
|
|
3764
|
+
draggable: true,
|
|
3765
|
+
title: "Drag to reorder section",
|
|
3766
|
+
onDragStart: beginRowDrag,
|
|
3767
|
+
onDragEnd: () => setRowDrag(null),
|
|
3768
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3769
|
+
onClick: (e) => {
|
|
3770
|
+
e.stopPropagation();
|
|
3771
|
+
setSelectedRowId(row.id);
|
|
3772
|
+
setSelContentId(null);
|
|
3773
|
+
setSelMeta(null);
|
|
3774
|
+
},
|
|
3775
|
+
onContextMenu: (e) => {
|
|
3776
|
+
e.preventDefault();
|
|
3777
|
+
e.stopPropagation();
|
|
3778
|
+
handleLayoutContextMenu(e, row.id);
|
|
3779
|
+
},
|
|
3780
|
+
style: {
|
|
3781
|
+
width: 20,
|
|
3782
|
+
flexShrink: 0,
|
|
3783
|
+
cursor: "grab",
|
|
3784
|
+
display: "flex",
|
|
3785
|
+
alignItems: "center",
|
|
3786
|
+
justifyContent: "center",
|
|
3787
|
+
color: rowSelected ? C.accent : C.muted,
|
|
3788
|
+
fontSize: 12,
|
|
3789
|
+
fontWeight: 700,
|
|
3790
|
+
letterSpacing: -3,
|
|
3791
|
+
userSelect: "none",
|
|
3792
|
+
opacity: rowSelected ? 1 : 0.5
|
|
3793
|
+
},
|
|
3794
|
+
children: "\u22EE\u22EE"
|
|
3795
|
+
}
|
|
3796
|
+
),
|
|
3797
|
+
/* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3798
|
+
/* @__PURE__ */ jsx3(
|
|
3799
|
+
"div",
|
|
3800
|
+
{
|
|
3801
|
+
id: rid(`row-drop-before-${ri}`),
|
|
3802
|
+
onDragOver: (e) => {
|
|
3803
|
+
e.preventDefault();
|
|
3804
|
+
e.stopPropagation();
|
|
3805
|
+
setDragOver(ri);
|
|
3806
|
+
},
|
|
3807
|
+
onDragLeave: () => setDragOver(null),
|
|
3808
|
+
onDrop: (e) => handleRowDrop(ri, e),
|
|
3809
|
+
style: {
|
|
3810
|
+
height: dropH,
|
|
3811
|
+
background: dragOver === ri ? `${C.accent}25` : "transparent",
|
|
3812
|
+
border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
3813
|
+
borderRadius: 4,
|
|
3814
|
+
transition: "all .12s",
|
|
3815
|
+
boxSizing: "border-box"
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
),
|
|
3819
|
+
/* @__PURE__ */ jsx3(
|
|
3820
|
+
"div",
|
|
3821
|
+
{
|
|
3822
|
+
ref: rowBodyRef,
|
|
3823
|
+
id: rid(`row-body-${row.id}`),
|
|
3824
|
+
onDragOver: rowDragOver,
|
|
3825
|
+
onDrop: rowDrop,
|
|
3826
|
+
style: {
|
|
3827
|
+
position: "relative",
|
|
3828
|
+
width: "100%",
|
|
3829
|
+
minWidth: 0,
|
|
3830
|
+
boxSizing: "border-box",
|
|
3831
|
+
outline: draggingRowId && draggingRowId !== row.id && (dragOver === ri || dragOver === ri + 1) ? `2px dashed ${C.accent}` : void 0,
|
|
3832
|
+
outlineOffset: 2,
|
|
3833
|
+
borderRadius: 6
|
|
3834
|
+
},
|
|
3835
|
+
children: /* @__PURE__ */ jsx3("div", { id: rid(`row-layout-wrap-${row.id}`), style: { width: "100%", minWidth: 0 }, children: /* @__PURE__ */ jsx3(
|
|
3836
|
+
LayoutRow,
|
|
3837
|
+
{
|
|
3838
|
+
editorId,
|
|
3839
|
+
row,
|
|
3840
|
+
selected: rowSelected,
|
|
3841
|
+
setSelectedRowId,
|
|
3842
|
+
onClick: () => {
|
|
3843
|
+
setSelectedRowId(row.id);
|
|
3844
|
+
setSelContentId(null);
|
|
3845
|
+
setSelMeta(null);
|
|
3846
|
+
},
|
|
3847
|
+
selectedContentKey: selContentId,
|
|
3848
|
+
selContentMeta: rowSelContentMeta,
|
|
3849
|
+
onSelectContent: selectContent,
|
|
3850
|
+
onDropContent: dropContent,
|
|
3851
|
+
onDeleteContent: deleteContent,
|
|
3852
|
+
onContextMenu: handleContextMenu,
|
|
3853
|
+
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3854
|
+
rootContentRow,
|
|
3855
|
+
beginRowDrag,
|
|
3856
|
+
C
|
|
3857
|
+
}
|
|
3858
|
+
) })
|
|
3859
|
+
}
|
|
3860
|
+
)
|
|
3861
|
+
] })
|
|
3862
|
+
]
|
|
3863
|
+
}
|
|
3864
|
+
);
|
|
3039
3865
|
};
|
|
3040
3866
|
|
|
3041
3867
|
// src/ReactEmailEditor.tsx
|
|
3042
3868
|
import { Image as ImageIcon2, Repeat as RepeatIcon, Scaling as Scaling2, Crosshair as Crosshair2, Blend as GradientIcon2, Droplets as ColorIcon } from "lucide-react";
|
|
3043
3869
|
|
|
3044
3870
|
// src/editor/properties/PropertyEditors.tsx
|
|
3045
|
-
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo
|
|
3871
|
+
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo } from "react";
|
|
3046
3872
|
import {
|
|
3047
3873
|
AlignCenter as AlignCenter2,
|
|
3048
3874
|
AlignJustify as AlignJustify2,
|
|
@@ -3059,7 +3885,7 @@ import {
|
|
|
3059
3885
|
// src/editor/properties/TextRichEditor.tsx
|
|
3060
3886
|
import { useEffect as useEffect2, useId, useState as useState2 } from "react";
|
|
3061
3887
|
import { createPortal } from "react-dom";
|
|
3062
|
-
import { useEditor, EditorContent } from "@tiptap/react";
|
|
3888
|
+
import { useEditor, EditorContent, useEditorState } from "@tiptap/react";
|
|
3063
3889
|
import StarterKit from "@tiptap/starter-kit";
|
|
3064
3890
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
3065
3891
|
import TextAlign from "@tiptap/extension-text-align";
|
|
@@ -3157,6 +3983,10 @@ function paddingToCss(pad3, fallback) {
|
|
|
3157
3983
|
var FONT_SIZE_PX = [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28, 32, 36, 42, 48, 56, 64, 72];
|
|
3158
3984
|
var LINE_HEIGHT_OPTS = ["1", "1.15", "1.25", "1.5", "1.65", "1.75", "2", "2.5"];
|
|
3159
3985
|
var LETTER_SPACING_OPTS = ["-0.5px", "0px", "0.5px", "1px", "1.5px", "2px", "3px", "4px"];
|
|
3986
|
+
function rgbChannelToHex(n) {
|
|
3987
|
+
const v = Math.max(0, Math.min(255, Math.round(n)));
|
|
3988
|
+
return v.toString(16).padStart(2, "0");
|
|
3989
|
+
}
|
|
3160
3990
|
function hexForColorInput(color) {
|
|
3161
3991
|
if (!color || typeof color !== "string") return "#000000";
|
|
3162
3992
|
const c = color.trim();
|
|
@@ -3169,8 +3999,34 @@ function hexForColorInput(color) {
|
|
|
3169
3999
|
}
|
|
3170
4000
|
return c.slice(0, 7);
|
|
3171
4001
|
}
|
|
4002
|
+
const rgb = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
|
|
4003
|
+
if (rgb) {
|
|
4004
|
+
return `#${rgbChannelToHex(+rgb[1])}${rgbChannelToHex(+rgb[2])}${rgbChannelToHex(+rgb[3])}`;
|
|
4005
|
+
}
|
|
3172
4006
|
return "#000000";
|
|
3173
4007
|
}
|
|
4008
|
+
function textStyleColorFromEditor(editor) {
|
|
4009
|
+
const ts = editor.getAttributes("textStyle");
|
|
4010
|
+
if (ts.color && String(ts.color).trim()) return String(ts.color).trim();
|
|
4011
|
+
const stored = editor.state.storedMarks;
|
|
4012
|
+
if (stored) {
|
|
4013
|
+
for (const m of stored) {
|
|
4014
|
+
if (m.type.name === "textStyle" && m.attrs.color) {
|
|
4015
|
+
return String(m.attrs.color).trim();
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
const { from, to } = editor.state.selection;
|
|
4020
|
+
if (from === to) return null;
|
|
4021
|
+
const domAt = editor.view.domAtPos(from);
|
|
4022
|
+
let el = domAt.node.nodeType === Node.TEXT_NODE ? domAt.node.parentElement : domAt.node;
|
|
4023
|
+
while (el && el !== editor.view.dom) {
|
|
4024
|
+
const inline = el.style?.color?.trim();
|
|
4025
|
+
if (inline) return inline;
|
|
4026
|
+
el = el.parentElement;
|
|
4027
|
+
}
|
|
4028
|
+
return null;
|
|
4029
|
+
}
|
|
3174
4030
|
function parsePxSize(raw) {
|
|
3175
4031
|
if (!raw) return "";
|
|
3176
4032
|
const s = String(raw).trim();
|
|
@@ -3237,13 +4093,22 @@ function toolbarIconBtn(C, active) {
|
|
|
3237
4093
|
}
|
|
3238
4094
|
function ToolbarTypographyControls({ editor, C }) {
|
|
3239
4095
|
const sel = toolbarSelect(C);
|
|
3240
|
-
const ts =
|
|
4096
|
+
const { ts, align, textColor } = useEditorState({
|
|
4097
|
+
editor,
|
|
4098
|
+
selector: ({ editor: ed }) => {
|
|
4099
|
+
const attrs = ed.getAttributes("textStyle");
|
|
4100
|
+
return {
|
|
4101
|
+
ts: attrs,
|
|
4102
|
+
align: currentBlockTextAlign(ed),
|
|
4103
|
+
textColor: textStyleColorFromEditor(ed)
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
});
|
|
3241
4107
|
const fontFamily = ts.fontFamily || "";
|
|
3242
4108
|
const sizeVal = parsePxSize(ts.fontSize);
|
|
3243
4109
|
const lhVal = normalizeLineHeightAttr(ts.lineHeight);
|
|
3244
4110
|
const lsVal = normalizeLetterSpacingAttr(ts.letterSpacing);
|
|
3245
4111
|
const lsSelectValue = LETTER_SPACING_OPTS.includes(lsVal) ? lsVal : lsVal || "";
|
|
3246
|
-
const align = currentBlockTextAlign(editor);
|
|
3247
4112
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3248
4113
|
/* @__PURE__ */ jsxs3(
|
|
3249
4114
|
"select",
|
|
@@ -3326,9 +4191,10 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3326
4191
|
{
|
|
3327
4192
|
type: "color",
|
|
3328
4193
|
title: "Text color",
|
|
3329
|
-
value: hexForColorInput(ts.color),
|
|
4194
|
+
value: hexForColorInput(textColor ?? ts.color),
|
|
3330
4195
|
onChange: (e) => {
|
|
3331
|
-
|
|
4196
|
+
const hex = e.target.value;
|
|
4197
|
+
void editor.chain().focus().setColor(hex).run();
|
|
3332
4198
|
},
|
|
3333
4199
|
style: {
|
|
3334
4200
|
width: 28,
|
|
@@ -3701,7 +4567,7 @@ function TextRichEditor({
|
|
|
3701
4567
|
}
|
|
3702
4568
|
}),
|
|
3703
4569
|
TextStyle,
|
|
3704
|
-
Color,
|
|
4570
|
+
Color.configure({ types: ["textStyle"] }),
|
|
3705
4571
|
FontFamily,
|
|
3706
4572
|
FontSize,
|
|
3707
4573
|
LineHeight,
|
|
@@ -4878,11 +5744,141 @@ function BlockSurfaceBgInspector({
|
|
|
4878
5744
|
] }) : null
|
|
4879
5745
|
] });
|
|
4880
5746
|
}
|
|
5747
|
+
function HtmlBlockRichPanel({
|
|
5748
|
+
block,
|
|
5749
|
+
onChange,
|
|
5750
|
+
C
|
|
5751
|
+
}) {
|
|
5752
|
+
const p = block.props;
|
|
5753
|
+
const { IS } = useIS(C);
|
|
5754
|
+
const [mode, setMode] = useState3("visual");
|
|
5755
|
+
const [htmlDraft, setHtmlDraft] = useState3("");
|
|
5756
|
+
useEffect3(() => {
|
|
5757
|
+
setMode("visual");
|
|
5758
|
+
}, [block.id]);
|
|
5759
|
+
const body = normalizeRichHtmlForStorage(typeof block.content === "string" ? block.content : "");
|
|
5760
|
+
const applyHtml = (html) => {
|
|
5761
|
+
const norm = normalizeRichHtmlForStorage(html);
|
|
5762
|
+
onChange({ ...block, content: norm, props: { ...p, content: norm } });
|
|
5763
|
+
};
|
|
5764
|
+
const tabBtn = (active) => ({
|
|
5765
|
+
flex: 1,
|
|
5766
|
+
padding: "7px 10px",
|
|
5767
|
+
borderRadius: 6,
|
|
5768
|
+
border: `1px solid ${C.border}`,
|
|
5769
|
+
background: active ? C.accent : C.surface,
|
|
5770
|
+
color: active ? "#ffffff" : C.text,
|
|
5771
|
+
fontSize: 12,
|
|
5772
|
+
fontWeight: 700,
|
|
5773
|
+
cursor: "pointer"
|
|
5774
|
+
});
|
|
5775
|
+
return /* @__PURE__ */ jsx6(PR, { label: "Content", C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5776
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8 }, children: [
|
|
5777
|
+
/* @__PURE__ */ jsx6(
|
|
5778
|
+
"button",
|
|
5779
|
+
{
|
|
5780
|
+
type: "button",
|
|
5781
|
+
style: tabBtn(mode === "visual"),
|
|
5782
|
+
onClick: () => {
|
|
5783
|
+
if (mode === "html") applyHtml(htmlDraft);
|
|
5784
|
+
setMode("visual");
|
|
5785
|
+
},
|
|
5786
|
+
children: "Rich text"
|
|
5787
|
+
}
|
|
5788
|
+
),
|
|
5789
|
+
/* @__PURE__ */ jsx6(
|
|
5790
|
+
"button",
|
|
5791
|
+
{
|
|
5792
|
+
type: "button",
|
|
5793
|
+
style: tabBtn(mode === "html"),
|
|
5794
|
+
onClick: () => {
|
|
5795
|
+
setHtmlDraft(body);
|
|
5796
|
+
setMode("html");
|
|
5797
|
+
},
|
|
5798
|
+
children: "HTML"
|
|
5799
|
+
}
|
|
5800
|
+
)
|
|
5801
|
+
] }),
|
|
5802
|
+
mode === "visual" ? /* @__PURE__ */ jsx6(
|
|
5803
|
+
TextRichEditor,
|
|
5804
|
+
{
|
|
5805
|
+
value: body,
|
|
5806
|
+
onChange: applyHtml,
|
|
5807
|
+
typography: {
|
|
5808
|
+
fontSize: p.fontSize,
|
|
5809
|
+
color: p.color,
|
|
5810
|
+
align: p.align,
|
|
5811
|
+
fontFamily: p.fontFamily,
|
|
5812
|
+
lineHeight: p.lineHeight,
|
|
5813
|
+
letterSpacing: p.letterSpacing,
|
|
5814
|
+
padding: p.padding
|
|
5815
|
+
},
|
|
5816
|
+
placeholder: "Write your rich content\u2026",
|
|
5817
|
+
headerTitle: "Rich editor",
|
|
5818
|
+
C
|
|
5819
|
+
},
|
|
5820
|
+
block.id
|
|
5821
|
+
) : /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5822
|
+
/* @__PURE__ */ jsx6(
|
|
5823
|
+
"textarea",
|
|
5824
|
+
{
|
|
5825
|
+
spellCheck: false,
|
|
5826
|
+
value: htmlDraft,
|
|
5827
|
+
onChange: (e) => setHtmlDraft(e.target.value),
|
|
5828
|
+
placeholder: "<p>Your HTML\u2026</p>",
|
|
5829
|
+
style: {
|
|
5830
|
+
...IS,
|
|
5831
|
+
minHeight: 220,
|
|
5832
|
+
resize: "vertical",
|
|
5833
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
5834
|
+
fontSize: 12,
|
|
5835
|
+
lineHeight: 1.45
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
),
|
|
5839
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontSize: 10, color: C.muted, lineHeight: 1.45 }, children: [
|
|
5840
|
+
"Raw HTML. Switch to ",
|
|
5841
|
+
/* @__PURE__ */ jsx6("strong", { children: "Rich text" }),
|
|
5842
|
+
" to apply and preview in the visual editor."
|
|
5843
|
+
] }),
|
|
5844
|
+
/* @__PURE__ */ jsx6(
|
|
5845
|
+
"button",
|
|
5846
|
+
{
|
|
5847
|
+
type: "button",
|
|
5848
|
+
onClick: () => applyHtml(htmlDraft),
|
|
5849
|
+
style: {
|
|
5850
|
+
alignSelf: "flex-start",
|
|
5851
|
+
background: C.surface,
|
|
5852
|
+
border: `1px solid ${C.border}`,
|
|
5853
|
+
color: C.accent,
|
|
5854
|
+
borderRadius: 5,
|
|
5855
|
+
cursor: "pointer",
|
|
5856
|
+
fontSize: 11,
|
|
5857
|
+
padding: "6px 12px",
|
|
5858
|
+
fontWeight: 600
|
|
5859
|
+
},
|
|
5860
|
+
children: "Apply (normalize)"
|
|
5861
|
+
}
|
|
5862
|
+
)
|
|
5863
|
+
] })
|
|
5864
|
+
] }) });
|
|
5865
|
+
}
|
|
4881
5866
|
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4882
5867
|
const { IS, CI } = useIS(C);
|
|
4883
5868
|
const imageFileRef = useRef2(null);
|
|
4884
5869
|
const p = block.props;
|
|
4885
|
-
const set = (k, v) =>
|
|
5870
|
+
const set = (k, v) => {
|
|
5871
|
+
if (block.type === "html" && k === "content") {
|
|
5872
|
+
onChange({ ...block, content: v, props: { ...p, content: v } });
|
|
5873
|
+
return;
|
|
5874
|
+
}
|
|
5875
|
+
if (block.type === "html") {
|
|
5876
|
+
const body = typeof block.content === "string" ? block.content : typeof p.content === "string" ? p.content : "";
|
|
5877
|
+
onChange({ ...block, props: { ...p, [k]: v, content: body } });
|
|
5878
|
+
return;
|
|
5879
|
+
}
|
|
5880
|
+
onChange({ ...block, props: { ...p, [k]: v } });
|
|
5881
|
+
};
|
|
4886
5882
|
const Num = (k, lbl, mn = 0, mx = 300) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: /* @__PURE__ */ jsx6("input", { type: "number", style: IS, value: p[k] ?? 0, min: mn, max: mx, onChange: (e) => set(k, +e.target.value) }) });
|
|
4887
5883
|
const Txt = (k, lbl, multi = false) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: multi ? /* @__PURE__ */ jsx6("textarea", { style: { ...IS, minHeight: 68, resize: "vertical" }, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) : /* @__PURE__ */ jsx6("input", { type: "text", style: IS, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) });
|
|
4888
5884
|
const Col = (k, lbl) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
@@ -4954,10 +5950,10 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4954
5950
|
"textarea",
|
|
4955
5951
|
{
|
|
4956
5952
|
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
4957
|
-
value:
|
|
4958
|
-
onChange: (e) => set("content",
|
|
5953
|
+
value: String(p.content ?? ""),
|
|
5954
|
+
onChange: (e) => set("content", e.target.value),
|
|
4959
5955
|
spellCheck: false,
|
|
4960
|
-
placeholder: "
|
|
5956
|
+
placeholder: "Write your text\u2026"
|
|
4961
5957
|
},
|
|
4962
5958
|
block.id
|
|
4963
5959
|
) }),
|
|
@@ -4976,25 +5972,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4976
5972
|
] });
|
|
4977
5973
|
case "html":
|
|
4978
5974
|
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
4979
|
-
/* @__PURE__ */ jsx6(
|
|
4980
|
-
TextRichEditor,
|
|
4981
|
-
{
|
|
4982
|
-
value: normalizeRichHtmlForStorage(p.content),
|
|
4983
|
-
onChange: (html) => set("content", html),
|
|
4984
|
-
typography: {
|
|
4985
|
-
fontSize: p.fontSize,
|
|
4986
|
-
color: p.color,
|
|
4987
|
-
align: p.align,
|
|
4988
|
-
fontFamily: p.fontFamily,
|
|
4989
|
-
lineHeight: p.lineHeight,
|
|
4990
|
-
letterSpacing: p.letterSpacing,
|
|
4991
|
-
padding: p.padding
|
|
4992
|
-
},
|
|
4993
|
-
placeholder: "Write your rich content\u2026",
|
|
4994
|
-
headerTitle: "Rich editor",
|
|
4995
|
-
C
|
|
4996
|
-
}
|
|
4997
|
-
) }, block.id),
|
|
5975
|
+
/* @__PURE__ */ jsx6(HtmlBlockRichPanel, { block, onChange, C }),
|
|
4998
5976
|
/* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
4999
5977
|
/* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
|
|
5000
5978
|
/* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
|
|
@@ -5014,8 +5992,25 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5014
5992
|
"Object position"
|
|
5015
5993
|
] }), C, children: /* @__PURE__ */ jsx6("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__ */ jsx6("option", { value: v, children: v }, v)) }) }),
|
|
5016
5994
|
Txt("alt", "Alt Text"),
|
|
5017
|
-
|
|
5018
|
-
|
|
5995
|
+
/* @__PURE__ */ jsx6(PR, { label: "Link image", C, children: /* @__PURE__ */ jsxs4("label", { style: { display: "flex", alignItems: "center", gap: 7, cursor: "pointer" }, children: [
|
|
5996
|
+
/* @__PURE__ */ jsx6(
|
|
5997
|
+
"input",
|
|
5998
|
+
{
|
|
5999
|
+
type: "checkbox",
|
|
6000
|
+
checked: !!(p.linkEnabled ?? (p.link && String(p.link).trim())),
|
|
6001
|
+
onChange: (e) => {
|
|
6002
|
+
if (e.target.checked) set("linkEnabled", true);
|
|
6003
|
+
else onChange({ ...block, props: { ...p, linkEnabled: false, link: "" } });
|
|
6004
|
+
},
|
|
6005
|
+
style: { width: 15, height: 15, accentColor: C.accent }
|
|
6006
|
+
}
|
|
6007
|
+
),
|
|
6008
|
+
/* @__PURE__ */ jsx6("span", { style: { color: C.muted, fontSize: 12 }, children: p.linkEnabled ?? (p.link && String(p.link).trim()) ? "On" : "Off" })
|
|
6009
|
+
] }) }),
|
|
6010
|
+
p.linkEnabled ?? (p.link && String(p.link).trim()) ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
6011
|
+
Txt("link", "Link URL"),
|
|
6012
|
+
Sel("linkTarget", "Open In", ["_blank", "_self"])
|
|
6013
|
+
] }) : null,
|
|
5019
6014
|
/* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
|
|
5020
6015
|
/* @__PURE__ */ jsx6(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
|
|
5021
6016
|
/* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -5365,7 +6360,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5365
6360
|
bgPosition: p.bgPosition ?? "center",
|
|
5366
6361
|
bgGradient: p.bgGradient ?? null,
|
|
5367
6362
|
cells: p.cells || [],
|
|
5368
|
-
|
|
6363
|
+
columns: getColumns(p)
|
|
5369
6364
|
},
|
|
5370
6365
|
onChange: (row) => onChange({
|
|
5371
6366
|
...block,
|
|
@@ -5383,7 +6378,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5383
6378
|
bgGradient: row.bgGradient,
|
|
5384
6379
|
ratios: [...row.ratios || [1]],
|
|
5385
6380
|
cells: row.cells || [],
|
|
5386
|
-
|
|
6381
|
+
columns: row.columns ?? getColumns(row)
|
|
5387
6382
|
}
|
|
5388
6383
|
}),
|
|
5389
6384
|
onClose,
|
|
@@ -5402,8 +6397,8 @@ function inferLayoutRowBgStep(r) {
|
|
|
5402
6397
|
if (String(r.bgColor || "").trim()) return "solid";
|
|
5403
6398
|
return null;
|
|
5404
6399
|
}
|
|
5405
|
-
function inferColumnBgStep(
|
|
5406
|
-
const cs = (
|
|
6400
|
+
function inferColumnBgStep(container, selCol) {
|
|
6401
|
+
const cs = getColumnPropsAt(container, selCol);
|
|
5407
6402
|
if (cs.bgGradient) return "gradient";
|
|
5408
6403
|
if (String(cs.bgImage || "").trim()) return "image";
|
|
5409
6404
|
if (String(cs.bgColor || "").trim()) return "solid";
|
|
@@ -5415,6 +6410,10 @@ function LayoutRowEditor({
|
|
|
5415
6410
|
onClose,
|
|
5416
6411
|
onUpload,
|
|
5417
6412
|
initialCol = null,
|
|
6413
|
+
rowIndex = -1,
|
|
6414
|
+
rowCount = 1,
|
|
6415
|
+
onMoveUp,
|
|
6416
|
+
onMoveDown,
|
|
5418
6417
|
C,
|
|
5419
6418
|
/** Shown under the inspector header when editing a Layout block */
|
|
5420
6419
|
customizationHint
|
|
@@ -5427,8 +6426,8 @@ function LayoutRowEditor({
|
|
|
5427
6426
|
const [colBgPickerMode, setColBgPickerMode] = useState3(null);
|
|
5428
6427
|
const inferredRowBg = useMemo(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5429
6428
|
const inferredColBg = useMemo(
|
|
5430
|
-
() => inferColumnBgStep(row
|
|
5431
|
-
[row.columnStyles, selCol]
|
|
6429
|
+
() => inferColumnBgStep(row, selCol),
|
|
6430
|
+
[row.columns, row.columnStyles, selCol]
|
|
5432
6431
|
);
|
|
5433
6432
|
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5434
6433
|
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
@@ -5467,13 +6466,12 @@ function LayoutRowEditor({
|
|
|
5467
6466
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5468
6467
|
};
|
|
5469
6468
|
const updCol = (i, upd) => {
|
|
5470
|
-
const
|
|
5471
|
-
|
|
5472
|
-
set("columnStyles", styles);
|
|
6469
|
+
const columns = patchColumnPropsAt(row, i, upd);
|
|
6470
|
+
onChange(withColumnsOnly(row, columns));
|
|
5473
6471
|
};
|
|
5474
6472
|
const setColBgMode = (mode) => {
|
|
5475
6473
|
setColBgPickerMode(mode);
|
|
5476
|
-
const cs = (row
|
|
6474
|
+
const cs = getColumnPropsAt(row, selCol);
|
|
5477
6475
|
if (mode === "solid") {
|
|
5478
6476
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
5479
6477
|
return;
|
|
@@ -5505,7 +6503,51 @@ function LayoutRowEditor({
|
|
|
5505
6503
|
set("bgGradient", { angle: 135, colors: ["#0ea5e9", "#6366f1", "#ec4899"], stops: [0, 50, 100] });
|
|
5506
6504
|
}
|
|
5507
6505
|
};
|
|
6506
|
+
const canMoveUp = rowIndex > 0;
|
|
6507
|
+
const canMoveDown = rowIndex >= 0 && rowIndex < rowCount - 1;
|
|
5508
6508
|
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
6509
|
+
onMoveUp && onMoveDown ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [
|
|
6510
|
+
/* @__PURE__ */ jsx6(
|
|
6511
|
+
"button",
|
|
6512
|
+
{
|
|
6513
|
+
type: "button",
|
|
6514
|
+
disabled: !canMoveUp,
|
|
6515
|
+
onClick: onMoveUp,
|
|
6516
|
+
style: {
|
|
6517
|
+
flex: 1,
|
|
6518
|
+
padding: "8px 10px",
|
|
6519
|
+
fontSize: 12,
|
|
6520
|
+
fontWeight: 600,
|
|
6521
|
+
borderRadius: 8,
|
|
6522
|
+
border: `1px solid ${C.border}`,
|
|
6523
|
+
background: canMoveUp ? C.surface : C.canvas,
|
|
6524
|
+
color: canMoveUp ? C.text : C.muted,
|
|
6525
|
+
cursor: canMoveUp ? "pointer" : "not-allowed"
|
|
6526
|
+
},
|
|
6527
|
+
children: "\u2191 Move section up"
|
|
6528
|
+
}
|
|
6529
|
+
),
|
|
6530
|
+
/* @__PURE__ */ jsx6(
|
|
6531
|
+
"button",
|
|
6532
|
+
{
|
|
6533
|
+
type: "button",
|
|
6534
|
+
disabled: !canMoveDown,
|
|
6535
|
+
onClick: onMoveDown,
|
|
6536
|
+
style: {
|
|
6537
|
+
flex: 1,
|
|
6538
|
+
padding: "8px 10px",
|
|
6539
|
+
fontSize: 12,
|
|
6540
|
+
fontWeight: 600,
|
|
6541
|
+
borderRadius: 8,
|
|
6542
|
+
border: `1px solid ${C.border}`,
|
|
6543
|
+
background: canMoveDown ? C.surface : C.canvas,
|
|
6544
|
+
color: canMoveDown ? C.text : C.muted,
|
|
6545
|
+
cursor: canMoveDown ? "pointer" : "not-allowed"
|
|
6546
|
+
},
|
|
6547
|
+
children: "\u2193 Move section down"
|
|
6548
|
+
}
|
|
6549
|
+
)
|
|
6550
|
+
] }) : null,
|
|
5509
6551
|
customizationHint ? /* @__PURE__ */ jsx6(
|
|
5510
6552
|
"div",
|
|
5511
6553
|
{
|
|
@@ -5655,15 +6697,15 @@ function LayoutRowEditor({
|
|
|
5655
6697
|
/* @__PURE__ */ jsx6(
|
|
5656
6698
|
ColorSwatchGrid,
|
|
5657
6699
|
{
|
|
5658
|
-
value: (row
|
|
6700
|
+
value: getColumnPropsAt(row, selCol)?.bgColor || "",
|
|
5659
6701
|
C,
|
|
5660
6702
|
compact: true,
|
|
5661
6703
|
onPick: (hex) => updCol(selCol, { bgColor: hex === "#ffffff" ? "" : hex })
|
|
5662
6704
|
}
|
|
5663
6705
|
),
|
|
5664
6706
|
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 5, alignItems: "center" }, children: [
|
|
5665
|
-
/* @__PURE__ */ jsx6("input", { type: "color", style: CI, value: (row
|
|
5666
|
-
/* @__PURE__ */ jsx6("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: (row
|
|
6707
|
+
/* @__PURE__ */ jsx6("input", { type: "color", style: CI, value: getColumnPropsAt(row, selCol)?.bgColor || "#ffffff", onChange: (e) => updCol(selCol, { bgColor: e.target.value === "#ffffff" ? "" : e.target.value }) }),
|
|
6708
|
+
/* @__PURE__ */ jsx6("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: getColumnPropsAt(row, selCol)?.bgColor || "", onChange: (e) => updCol(selCol, { bgColor: e.target.value }) })
|
|
5667
6709
|
] })
|
|
5668
6710
|
] }) }) : null,
|
|
5669
6711
|
colBgUiStep === "image" ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
@@ -5671,7 +6713,7 @@ function LayoutRowEditor({
|
|
|
5671
6713
|
/* @__PURE__ */ jsx6(ImageIcon, { size: 13, strokeWidth: 2.25, "aria-hidden": true }),
|
|
5672
6714
|
"bg image"
|
|
5673
6715
|
] }), C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
|
|
5674
|
-
/* @__PURE__ */ jsx6("input", { type: "text", style: IS, placeholder: "https://\u2026", value: (row
|
|
6716
|
+
/* @__PURE__ */ jsx6("input", { type: "text", style: IS, placeholder: "https://\u2026", value: getColumnPropsAt(row, selCol)?.bgImage || "", onChange: (e) => updCol(selCol, { bgImage: e.target.value }) }),
|
|
5675
6717
|
onUpload && /* @__PURE__ */ jsxs4("div", { style: { marginTop: 0 }, children: [
|
|
5676
6718
|
/* @__PURE__ */ jsx6(
|
|
5677
6719
|
"input",
|
|
@@ -5709,18 +6751,18 @@ function LayoutRowEditor({
|
|
|
5709
6751
|
] })
|
|
5710
6752
|
] }) }),
|
|
5711
6753
|
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [
|
|
5712
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "fit", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: (row
|
|
6754
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "fit", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgSize || "cover", onChange: (e) => updCol(selCol, { bgSize: e.target.value }), children: [
|
|
5713
6755
|
/* @__PURE__ */ jsx6("option", { value: "auto", children: "Auto" }),
|
|
5714
6756
|
/* @__PURE__ */ jsx6("option", { value: "cover", children: "Cover" }),
|
|
5715
6757
|
/* @__PURE__ */ jsx6("option", { value: "contain", children: "Contain" })
|
|
5716
6758
|
] }) }) }),
|
|
5717
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "repeat", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: (row
|
|
6759
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "repeat", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgRepeat || "no-repeat", onChange: (e) => updCol(selCol, { bgRepeat: e.target.value }), children: [
|
|
5718
6760
|
/* @__PURE__ */ jsx6("option", { value: "no-repeat", children: "No repeat" }),
|
|
5719
6761
|
/* @__PURE__ */ jsx6("option", { value: "repeat", children: "Repeat" }),
|
|
5720
6762
|
/* @__PURE__ */ jsx6("option", { value: "repeat-x", children: "Repeat X" }),
|
|
5721
6763
|
/* @__PURE__ */ jsx6("option", { value: "repeat-y", children: "Repeat Y" })
|
|
5722
6764
|
] }) }) }),
|
|
5723
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "align", C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: (row
|
|
6765
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "align", C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgPosition || "center", onChange: (e) => updCol(selCol, { bgPosition: e.target.value }), children: POS_OPTS.map((p) => /* @__PURE__ */ jsx6("option", { value: p, children: p }, p)) }) }) })
|
|
5724
6766
|
] })
|
|
5725
6767
|
] }) : null,
|
|
5726
6768
|
colBgUiStep === "gradient" ? /* @__PURE__ */ jsxs4("details", { open: true, style: { marginBottom: 12 }, children: [
|
|
@@ -5729,15 +6771,15 @@ function LayoutRowEditor({
|
|
|
5729
6771
|
DynamicGradientField,
|
|
5730
6772
|
{
|
|
5731
6773
|
label: "gradient",
|
|
5732
|
-
value: (row
|
|
6774
|
+
value: getColumnPropsAt(row, selCol)?.bgGradient,
|
|
5733
6775
|
onChange: (g) => updCol(selCol, { bgGradient: g }),
|
|
5734
6776
|
defaults: { colors: ["#0ea5e9", "#6366f1", "#ec4899"] },
|
|
5735
6777
|
C
|
|
5736
6778
|
}
|
|
5737
6779
|
) })
|
|
5738
6780
|
] }) : null,
|
|
5739
|
-
/* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: (row
|
|
5740
|
-
/* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: (row
|
|
6781
|
+
/* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: getColumnPropsAt(row, selCol).padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
|
|
6782
|
+
/* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: getColumnPropsAt(row, selCol).borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
|
|
5741
6783
|
] })
|
|
5742
6784
|
] })
|
|
5743
6785
|
] });
|
|
@@ -6534,6 +7576,11 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6534
7576
|
const [selContentMeta, setSelMeta] = useState6(null);
|
|
6535
7577
|
const [dragOver, setDragOver] = useState6(null);
|
|
6536
7578
|
const [draggingRowId, setDraggingRowId] = useState6(null);
|
|
7579
|
+
const draggingRowIdRef = useRef6(null);
|
|
7580
|
+
const setRowDrag = useCallback2((id) => {
|
|
7581
|
+
draggingRowIdRef.current = id;
|
|
7582
|
+
setDraggingRowId(id);
|
|
7583
|
+
}, []);
|
|
6537
7584
|
const [history, setHistory] = useState6([]);
|
|
6538
7585
|
const [future, setFuture] = useState6([]);
|
|
6539
7586
|
const [showPreviewModal, setShowPreview] = useState6(false);
|
|
@@ -6568,10 +7615,17 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6568
7615
|
});
|
|
6569
7616
|
const [pageBgUiStep, setPageBgUiStep] = useState6(null);
|
|
6570
7617
|
const [contentBgUiStep, setContentBgUiStep] = useState6(null);
|
|
7618
|
+
const pageBgLastSolidRef = useRef6("#f1f5f9");
|
|
7619
|
+
const pageBgIsOff = !String(settings.bgColor ?? "").trim() && !String(settings.bgImage ?? "").trim() && !settings.bgGradient;
|
|
6571
7620
|
const applyPageBgMode = (mode) => {
|
|
6572
7621
|
setPageBgUiStep(mode);
|
|
6573
7622
|
if (mode === "solid") {
|
|
6574
|
-
setSettings((s) =>
|
|
7623
|
+
setSettings((s) => {
|
|
7624
|
+
const next = { ...s, bgGradient: null, bgImage: "" };
|
|
7625
|
+
const raw = typeof next.bgColor === "string" ? next.bgColor.trim() : "";
|
|
7626
|
+
if (!raw) next.bgColor = pageBgLastSolidRef.current || "#f1f5f9";
|
|
7627
|
+
return next;
|
|
7628
|
+
});
|
|
6575
7629
|
return;
|
|
6576
7630
|
}
|
|
6577
7631
|
if (mode === "image") {
|
|
@@ -6681,7 +7735,7 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6681
7735
|
return () => window.removeEventListener("keydown", handler);
|
|
6682
7736
|
}, [rows, history, future, selContentMeta]);
|
|
6683
7737
|
const buildApi = useCallback2(() => ({
|
|
6684
|
-
loadJson(input) {
|
|
7738
|
+
loadJson(input, options2) {
|
|
6685
7739
|
setJsonLoading(true);
|
|
6686
7740
|
requestAnimationFrame(() => {
|
|
6687
7741
|
requestAnimationFrame(() => {
|
|
@@ -6694,7 +7748,14 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6694
7748
|
return;
|
|
6695
7749
|
}
|
|
6696
7750
|
}
|
|
6697
|
-
const
|
|
7751
|
+
const coerced = coerceEmailDocumentInput(parsed);
|
|
7752
|
+
if (options2?.mode === "append") {
|
|
7753
|
+
const extra = emailDocumentToEditorRows(coerced ?? parsed);
|
|
7754
|
+
if (!extra?.length) return;
|
|
7755
|
+
setRows((prev) => [...prev, ...extra]);
|
|
7756
|
+
return;
|
|
7757
|
+
}
|
|
7758
|
+
const norm = normalizeEmailDesignInput(coerced ?? parsed);
|
|
6698
7759
|
if (!norm) return;
|
|
6699
7760
|
setRows(norm.rows);
|
|
6700
7761
|
const ns = norm.settings;
|
|
@@ -6731,6 +7792,8 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6731
7792
|
}, []);
|
|
6732
7793
|
const handleRowDrop = (targetIdx, e) => {
|
|
6733
7794
|
if (e) {
|
|
7795
|
+
e.preventDefault();
|
|
7796
|
+
e.stopPropagation();
|
|
6734
7797
|
try {
|
|
6735
7798
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
6736
7799
|
if (data.layoutPresetKey) {
|
|
@@ -6749,18 +7812,28 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6749
7812
|
} catch {
|
|
6750
7813
|
}
|
|
6751
7814
|
}
|
|
6752
|
-
|
|
7815
|
+
const moveId = draggingRowIdRef.current ?? draggingRowId ?? (() => {
|
|
7816
|
+
if (!e) return null;
|
|
7817
|
+
try {
|
|
7818
|
+
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
7819
|
+
return typeof data.moveRowId === "string" ? data.moveRowId : null;
|
|
7820
|
+
} catch {
|
|
7821
|
+
return null;
|
|
7822
|
+
}
|
|
7823
|
+
})();
|
|
7824
|
+
if (moveId) {
|
|
6753
7825
|
mutate((prev) => {
|
|
6754
|
-
const fi = prev.findIndex((r) => r.id ===
|
|
7826
|
+
const fi = prev.findIndex((r) => r.id === moveId);
|
|
6755
7827
|
if (fi === -1) return prev;
|
|
6756
7828
|
const next = [...prev];
|
|
6757
7829
|
const [m] = next.splice(fi, 1);
|
|
6758
|
-
|
|
7830
|
+
const insertAt = targetIdx > fi ? targetIdx - 1 : targetIdx;
|
|
7831
|
+
next.splice(Math.max(0, Math.min(insertAt, next.length)), 0, m);
|
|
6759
7832
|
return next;
|
|
6760
7833
|
});
|
|
6761
7834
|
}
|
|
6762
7835
|
setDragOver(null);
|
|
6763
|
-
|
|
7836
|
+
setRowDrag(null);
|
|
6764
7837
|
};
|
|
6765
7838
|
const addRow = (preset) => mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
6766
7839
|
const deleteRow = (id) => {
|
|
@@ -6890,8 +7963,27 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6890
7963
|
}
|
|
6891
7964
|
}
|
|
6892
7965
|
};
|
|
7966
|
+
const rootContentPreset = LAYOUT_PRESETS.find((p) => p.cols === 1) ?? LAYOUT_PRESETS[0];
|
|
7967
|
+
const insertRootContentBlock = (contentType) => {
|
|
7968
|
+
const { row: newRow, block: nb } = makeRootContentRow(contentType, rootContentPreset);
|
|
7969
|
+
const anchorRowId = selContentMeta?.rowId ?? selectedRowId ?? null;
|
|
7970
|
+
mutate((prev) => {
|
|
7971
|
+
if (anchorRowId) {
|
|
7972
|
+
const i = prev.findIndex((r) => r.id === anchorRowId);
|
|
7973
|
+
if (i >= 0) {
|
|
7974
|
+
const next = [...prev];
|
|
7975
|
+
next.splice(i + 1, 0, newRow);
|
|
7976
|
+
return next;
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
return [...prev, newRow];
|
|
7980
|
+
});
|
|
7981
|
+
setSelectedRowId(null);
|
|
7982
|
+
setSelContentId(nb.id);
|
|
7983
|
+
setSelMeta({ rowId: newRow.id, cellIdx: 0, contentIdx: 0 });
|
|
7984
|
+
};
|
|
6893
7985
|
const insertBlockFromLibrary = (contentType) => {
|
|
6894
|
-
|
|
7986
|
+
const isLayoutType = contentType === "layout" || contentType === "nestedRow";
|
|
6895
7987
|
if (selContentMeta?.inner) {
|
|
6896
7988
|
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
6897
7989
|
dropContent(rowId, cellIdx, {
|
|
@@ -6902,30 +7994,26 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
6902
7994
|
});
|
|
6903
7995
|
return;
|
|
6904
7996
|
}
|
|
6905
|
-
if (
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
7997
|
+
if (isLayoutType) {
|
|
7998
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0 && typeof selContentMeta.contentIdx === "number" && selContentMeta.contentIdx >= 0) {
|
|
7999
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
8000
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
8001
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
8002
|
+
kind: "new",
|
|
8003
|
+
contentType,
|
|
8004
|
+
insertAt: null
|
|
8005
|
+
});
|
|
8006
|
+
return;
|
|
8007
|
+
}
|
|
6914
8008
|
}
|
|
8009
|
+
addRow(rootContentPreset);
|
|
8010
|
+
return;
|
|
6915
8011
|
}
|
|
6916
|
-
if (
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
dropContent(targetRow.id, 0, {
|
|
6920
|
-
kind: "new",
|
|
6921
|
-
contentType,
|
|
6922
|
-
insertAt: null
|
|
6923
|
-
});
|
|
6924
|
-
return;
|
|
6925
|
-
}
|
|
8012
|
+
if (!rows.length) {
|
|
8013
|
+
insertRootContentBlock(contentType);
|
|
8014
|
+
return;
|
|
6926
8015
|
}
|
|
6927
|
-
|
|
6928
|
-
dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
|
|
8016
|
+
insertRootContentBlock(contentType);
|
|
6929
8017
|
};
|
|
6930
8018
|
const deleteContent = (rowId, cellIdx, ci, inner = null) => {
|
|
6931
8019
|
if (!inner) {
|
|
@@ -7520,6 +8608,7 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7520
8608
|
e.preventDefault();
|
|
7521
8609
|
try {
|
|
7522
8610
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8611
|
+
if (data.moveRowId) return;
|
|
7523
8612
|
if (data.layoutPresetKey) {
|
|
7524
8613
|
const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
|
|
7525
8614
|
if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
@@ -7574,6 +8663,7 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7574
8663
|
e.stopPropagation();
|
|
7575
8664
|
try {
|
|
7576
8665
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8666
|
+
if (data.moveRowId) return;
|
|
7577
8667
|
if (data.contentType && typeof data.contentType === "string") {
|
|
7578
8668
|
insertBlockFromLibrary(data.contentType);
|
|
7579
8669
|
}
|
|
@@ -7595,10 +8685,11 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7595
8685
|
selectedRowId,
|
|
7596
8686
|
selContentMeta,
|
|
7597
8687
|
dragOver,
|
|
8688
|
+
draggingRowId,
|
|
7598
8689
|
C,
|
|
7599
8690
|
setDragOver,
|
|
7600
8691
|
handleRowDrop,
|
|
7601
|
-
|
|
8692
|
+
setRowDrag,
|
|
7602
8693
|
setSelectedRowId,
|
|
7603
8694
|
setSelContentId,
|
|
7604
8695
|
setSelMeta,
|
|
@@ -7616,14 +8707,20 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7616
8707
|
id: eid("canvas-row-drop-end"),
|
|
7617
8708
|
onDragOver: (e) => {
|
|
7618
8709
|
e.preventDefault();
|
|
8710
|
+
e.stopPropagation();
|
|
7619
8711
|
setDragOver(rows.length);
|
|
7620
8712
|
},
|
|
7621
8713
|
onDragLeave: () => setDragOver(null),
|
|
7622
|
-
onDrop: (e) =>
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
8714
|
+
onDrop: (e) => handleRowDrop(rows.length, e),
|
|
8715
|
+
style: {
|
|
8716
|
+
height: dragOver === rows.length ? 28 : 10,
|
|
8717
|
+
background: dragOver === rows.length ? `${C.accent}25` : "transparent",
|
|
8718
|
+
border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
8719
|
+
borderRadius: 4,
|
|
8720
|
+
transition: "all .12s",
|
|
8721
|
+
margin: "2px 0",
|
|
8722
|
+
boxSizing: "border-box"
|
|
8723
|
+
}
|
|
7627
8724
|
}
|
|
7628
8725
|
)
|
|
7629
8726
|
]
|
|
@@ -7735,6 +8832,10 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7735
8832
|
onClose: () => setSelectedRowId(null),
|
|
7736
8833
|
onUpload,
|
|
7737
8834
|
initialCol: selContentMeta?.rowId === selectedRowId ? selContentMeta.cellIdx : null,
|
|
8835
|
+
rowIndex: rows.findIndex((r) => r.id === selectedRowId),
|
|
8836
|
+
rowCount: rows.length,
|
|
8837
|
+
onMoveUp: () => selectedRow && moveRow(selectedRow.id, -1),
|
|
8838
|
+
onMoveDown: () => selectedRow && moveRow(selectedRow.id, 1),
|
|
7738
8839
|
C
|
|
7739
8840
|
}
|
|
7740
8841
|
) })
|
|
@@ -7911,7 +9012,40 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7911
9012
|
/* @__PURE__ */ jsx11("summary", { style: railSectionSummary, children: "Background" }),
|
|
7912
9013
|
/* @__PURE__ */ jsxs9("div", { style: railSectionBody, children: [
|
|
7913
9014
|
/* @__PURE__ */ jsx11("div", { style: { fontSize: 10, color: C.muted, fontWeight: 800, textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 }, children: "Page background" }),
|
|
7914
|
-
/* @__PURE__ */ jsx11(
|
|
9015
|
+
/* @__PURE__ */ jsx11("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }, children: /* @__PURE__ */ jsxs9("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", userSelect: "none" }, children: [
|
|
9016
|
+
/* @__PURE__ */ jsx11(
|
|
9017
|
+
"input",
|
|
9018
|
+
{
|
|
9019
|
+
type: "checkbox",
|
|
9020
|
+
checked: pageBgIsOff,
|
|
9021
|
+
onChange: (e) => {
|
|
9022
|
+
const off = e.target.checked;
|
|
9023
|
+
if (off) {
|
|
9024
|
+
const cur = String(settings.bgColor ?? "").trim();
|
|
9025
|
+
if (cur) pageBgLastSolidRef.current = cur;
|
|
9026
|
+
setPageBgUiStep(null);
|
|
9027
|
+
setSettings((s) => ({ ...s, bgColor: "", bgImage: "", bgGradient: null }));
|
|
9028
|
+
} else {
|
|
9029
|
+
setPageBgUiStep("solid");
|
|
9030
|
+
setSettings((s) => ({
|
|
9031
|
+
...s,
|
|
9032
|
+
bgColor: pageBgLastSolidRef.current || "#f1f5f9"
|
|
9033
|
+
}));
|
|
9034
|
+
}
|
|
9035
|
+
},
|
|
9036
|
+
style: { width: 15, height: 15, accentColor: C.accent, cursor: "pointer" }
|
|
9037
|
+
}
|
|
9038
|
+
),
|
|
9039
|
+
/* @__PURE__ */ jsx11("span", { style: { color: C.muted, fontSize: 12 }, children: "No page background" })
|
|
9040
|
+
] }) }),
|
|
9041
|
+
/* @__PURE__ */ jsx11(
|
|
9042
|
+
BgModeButtons,
|
|
9043
|
+
{
|
|
9044
|
+
value: pageBgIsOff ? null : pageBgUiStep,
|
|
9045
|
+
onChange: applyPageBgMode,
|
|
9046
|
+
C
|
|
9047
|
+
}
|
|
9048
|
+
),
|
|
7915
9049
|
pageBgUiStep === null ? /* @__PURE__ */ jsx11("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,
|
|
7916
9050
|
pageBgUiStep === "solid" ? /* @__PURE__ */ jsx11(
|
|
7917
9051
|
ColorField,
|
|
@@ -7921,7 +9055,10 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
7921
9055
|
"bg color"
|
|
7922
9056
|
] }),
|
|
7923
9057
|
value: settings.bgColor,
|
|
7924
|
-
onChange: (v) =>
|
|
9058
|
+
onChange: (v) => {
|
|
9059
|
+
pageBgLastSolidRef.current = v;
|
|
9060
|
+
setSettings((s) => ({ ...s, bgColor: v }));
|
|
9061
|
+
},
|
|
7925
9062
|
placeholder: "#f1f5f9",
|
|
7926
9063
|
C
|
|
7927
9064
|
}
|
|
@@ -8162,80 +9299,17 @@ var ReactEmailEditorComponent = forwardRef(
|
|
|
8162
9299
|
);
|
|
8163
9300
|
}
|
|
8164
9301
|
);
|
|
8165
|
-
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml });
|
|
8166
|
-
|
|
8167
|
-
// src/lib/htmlToEmailDesign.ts
|
|
8168
|
-
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
8169
|
-
function extractHtmlForDesign(html) {
|
|
8170
|
-
const t = String(html ?? "").trim();
|
|
8171
|
-
if (!t) return "";
|
|
8172
|
-
const head = t.slice(0, 800).toLowerCase();
|
|
8173
|
-
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
8174
|
-
return normalizeRichHtmlForStorage(t);
|
|
8175
|
-
}
|
|
8176
|
-
try {
|
|
8177
|
-
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
8178
|
-
const body = doc.body;
|
|
8179
|
-
if (!body) return normalizeRichHtmlForStorage(t);
|
|
8180
|
-
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
8181
|
-
} catch {
|
|
8182
|
-
return normalizeRichHtmlForStorage(t);
|
|
8183
|
-
}
|
|
8184
|
-
}
|
|
8185
|
-
function htmlToEmailDesignTemplate(html) {
|
|
8186
|
-
const inner = extractHtmlForDesign(html);
|
|
8187
|
-
if (!inner) return null;
|
|
8188
|
-
const doc = {
|
|
8189
|
-
type: "email_document",
|
|
8190
|
-
settings: {
|
|
8191
|
-
width: 600,
|
|
8192
|
-
backgroundColor: "#f1f5f9",
|
|
8193
|
-
contentBackgroundColor: "#ffffff",
|
|
8194
|
-
fontFamily: "Arial, Helvetica, sans-serif",
|
|
8195
|
-
lineHeightBase: 1.6,
|
|
8196
|
-
color: "#111827",
|
|
8197
|
-
responsive: true,
|
|
8198
|
-
rtl: false
|
|
8199
|
-
},
|
|
8200
|
-
rows: [
|
|
8201
|
-
{
|
|
8202
|
-
id: "row_html_import",
|
|
8203
|
-
type: "row",
|
|
8204
|
-
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
8205
|
-
styles: {
|
|
8206
|
-
backgroundColor: "#ffffff",
|
|
8207
|
-
backgroundRepeat: "no-repeat",
|
|
8208
|
-
backgroundSize: "cover",
|
|
8209
|
-
padding: pad(24),
|
|
8210
|
-
textAlign: "left"
|
|
8211
|
-
},
|
|
8212
|
-
columns: [
|
|
8213
|
-
{
|
|
8214
|
-
id: "col_html_import",
|
|
8215
|
-
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
8216
|
-
styles: { padding: pad(0), backgroundColor: "" },
|
|
8217
|
-
blocks: [
|
|
8218
|
-
{
|
|
8219
|
-
id: "block_html_import",
|
|
8220
|
-
type: "html",
|
|
8221
|
-
content: { html: inner },
|
|
8222
|
-
styles: {}
|
|
8223
|
-
}
|
|
8224
|
-
]
|
|
8225
|
-
}
|
|
8226
|
-
]
|
|
8227
|
-
}
|
|
8228
|
-
]
|
|
8229
|
-
};
|
|
8230
|
-
return doc;
|
|
8231
|
-
}
|
|
9302
|
+
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml, htmlToJson });
|
|
8232
9303
|
export {
|
|
8233
9304
|
PreviewModal as EmailPreviewModal,
|
|
8234
9305
|
ReactEmailEditor2 as ReactEmailEditor,
|
|
8235
9306
|
base64ToUtf8,
|
|
9307
|
+
canonicalizeEmailDocument,
|
|
9308
|
+
coerceEmailDocumentInput,
|
|
8236
9309
|
DEVICES as emailPreviewDevices,
|
|
8237
9310
|
extractHtmlForDesign,
|
|
8238
9311
|
htmlToEmailDesignTemplate,
|
|
9312
|
+
htmlToJson,
|
|
8239
9313
|
jsonToHtml,
|
|
8240
9314
|
utf8ToBase64
|
|
8241
9315
|
};
|