react-email-studio 3.3.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -2
- package/README.md +12 -0
- package/TUTORIAL.md +56 -2
- package/USER_README.md +17 -0
- package/dist/index.cjs +1563 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -73
- package/dist/index.d.ts +126 -73
- package/dist/index.js +1569 -496
- 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, ">");
|
|
@@ -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) {
|
|
@@ -979,14 +1180,16 @@ function blockToHtml(cb) {
|
|
|
979
1180
|
case "html": {
|
|
980
1181
|
const shell = emailSurfaceBgCss(p);
|
|
981
1182
|
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
|
-
|
|
1183
|
+
const bodyRaw = typeof cb.content === "string" ? cb.content : typeof p.content === "string" ? p.content : "";
|
|
1184
|
+
const body = enhanceRichHtmlForEmail(bodyRaw || "<p></p>");
|
|
1185
|
+
return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div class="email-rich-html-inner" style="${inner}">${body}</div></div>`;
|
|
983
1186
|
}
|
|
984
1187
|
case "image": {
|
|
985
1188
|
const of = typeof p.objectFit === "string" && p.objectFit.trim() ? p.objectFit.trim() : "cover";
|
|
986
1189
|
const op = typeof p.objectPosition === "string" && p.objectPosition.trim() ? p.objectPosition.trim() : "center";
|
|
987
1190
|
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
1191
|
const shell = emailSurfaceBgCss(p);
|
|
989
|
-
return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${p
|
|
1192
|
+
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
1193
|
}
|
|
991
1194
|
case "button": {
|
|
992
1195
|
const bd = emailBackdropBgCss(p);
|
|
@@ -1075,7 +1278,7 @@ function blockToHtml(cb) {
|
|
|
1075
1278
|
bgRepeat: p.bgRepeat || "no-repeat",
|
|
1076
1279
|
bgPosition: typeof p.bgPosition === "string" ? p.bgPosition : "center",
|
|
1077
1280
|
bgGradient: p.bgGradient ?? null,
|
|
1078
|
-
|
|
1281
|
+
columns: getColumns(p),
|
|
1079
1282
|
cells: p.cells || []
|
|
1080
1283
|
};
|
|
1081
1284
|
return rowToHtml(pseudo);
|
|
@@ -1089,7 +1292,7 @@ function rowToHtml(row) {
|
|
|
1089
1292
|
const shellBg = emailSurfaceBgCss(row);
|
|
1090
1293
|
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
1294
|
const r = row.ratios[i] ?? 1;
|
|
1092
|
-
const cs = row
|
|
1295
|
+
const cs = getColumnPropsAt(row, i);
|
|
1093
1296
|
const colShell = emailSurfaceBgCss(cs);
|
|
1094
1297
|
const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
|
|
1095
1298
|
const o = cs.padding;
|
|
@@ -1137,6 +1340,7 @@ table[role="presentation"] td{min-width:0!important;word-break:break-word;}
|
|
|
1137
1340
|
@media screen and (max-width:480px){
|
|
1138
1341
|
.email-content-td{padding-left:max(10px,env(safe-area-inset-left))!important;padding-right:max(10px,env(safe-area-inset-right))!important;}
|
|
1139
1342
|
}
|
|
1343
|
+
${RICH_HTML_CONTENT_CSS}
|
|
1140
1344
|
</style>`;
|
|
1141
1345
|
function previewEmailSrcDoc(html, viewportWidthPx) {
|
|
1142
1346
|
const viewportTag = `<meta name="viewport" content="width=${viewportWidthPx},initial-scale=1"/>`;
|
|
@@ -1188,6 +1392,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1188
1392
|
return `background-image:linear-gradient(${angle}deg, ${pairs.join(", ")});background-repeat:no-repeat;background-position:${pagePos};background-size:cover;`;
|
|
1189
1393
|
})();
|
|
1190
1394
|
const pageBgImgRaw = typeof settings.bgImage === "string" ? String(settings.bgImage).trim() : "";
|
|
1395
|
+
const pageBgColorRaw = typeof settings.bgColor === "string" ? String(settings.bgColor).trim() : "";
|
|
1396
|
+
const pageBgColor = pageBgColorRaw ? pageBgColorRaw : "transparent";
|
|
1191
1397
|
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
1398
|
const contentPos = typeof settings.contentBgPosition === "string" && settings.contentBgPosition.trim() ? settings.contentBgPosition.trim() : "center";
|
|
1193
1399
|
const contentGrad = settings.contentBgGradient && typeof settings.contentBgGradient === "object" ? settings.contentBgGradient : null;
|
|
@@ -1210,8 +1416,8 @@ function designToHtml(rows, settings, opts = {}) {
|
|
|
1210
1416
|
<head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
|
1211
1417
|
<title>Email</title>${RESPONSIVE_EMAIL_CSS}${css}
|
|
1212
1418
|
</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:${
|
|
1419
|
+
<body style="margin:0;padding:0;background:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1420
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${pageBgColor};${pageGradCss || pageBgImgCss}">
|
|
1215
1421
|
<tr><td align="center" style="padding:0;max-width:100%;">
|
|
1216
1422
|
<table class="email-shell-table" role="presentation" width="100%" cellpadding="0" cellspacing="0"
|
|
1217
1423
|
style="${contentShellStyle.join(";")}">
|
|
@@ -1245,14 +1451,49 @@ function paddingToUniform(p, fallback = 0) {
|
|
|
1245
1451
|
function ensureId(id, prefix) {
|
|
1246
1452
|
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
1247
1453
|
}
|
|
1454
|
+
function normalizeDocBlock(b, prefix) {
|
|
1455
|
+
const raw = b && typeof b === "object" && !Array.isArray(b) ? b : {};
|
|
1456
|
+
const base = {
|
|
1457
|
+
id: ensureId(raw.id, prefix),
|
|
1458
|
+
type: typeof raw.type === "string" ? raw.type : "text",
|
|
1459
|
+
content: typeof raw.content === "string" ? raw.content : raw.content && typeof raw.content === "object" && !Array.isArray(raw.content) ? raw.content : {},
|
|
1460
|
+
...raw.behavior && typeof raw.behavior === "object" && !Array.isArray(raw.behavior) ? { behavior: raw.behavior } : {},
|
|
1461
|
+
...raw.responsive && typeof raw.responsive === "object" && !Array.isArray(raw.responsive) ? { responsive: raw.responsive } : {}
|
|
1462
|
+
};
|
|
1463
|
+
if (raw.props && typeof raw.props === "object" && !Array.isArray(raw.props)) {
|
|
1464
|
+
base.props = { ...raw.props };
|
|
1465
|
+
} else if (raw.styles && typeof raw.styles === "object" && !Array.isArray(raw.styles)) {
|
|
1466
|
+
base.styles = { ...raw.styles };
|
|
1467
|
+
}
|
|
1468
|
+
return base;
|
|
1469
|
+
}
|
|
1470
|
+
function normalizeDocSettings(settings) {
|
|
1471
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return {};
|
|
1472
|
+
const s = { ...settings };
|
|
1473
|
+
delete s._reactEmailStudio;
|
|
1474
|
+
return s;
|
|
1475
|
+
}
|
|
1248
1476
|
function normalizeEmailDocument(input) {
|
|
1249
1477
|
if (input == null || typeof input !== "object" || Array.isArray(input)) return null;
|
|
1250
1478
|
const doc = input;
|
|
1251
1479
|
if (doc.type !== "email_document") return null;
|
|
1480
|
+
const rawBlocks = Array.isArray(doc.blocks) ? doc.blocks : [];
|
|
1481
|
+
const useBlocks = rawBlocks.length > 0;
|
|
1252
1482
|
const rows = Array.isArray(doc.rows) ? doc.rows : [];
|
|
1253
|
-
|
|
1483
|
+
const base = {
|
|
1254
1484
|
type: "email_document",
|
|
1255
|
-
settings:
|
|
1485
|
+
settings: normalizeDocSettings(doc.settings),
|
|
1486
|
+
globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
|
|
1487
|
+
responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
|
|
1488
|
+
};
|
|
1489
|
+
if (useBlocks) {
|
|
1490
|
+
return {
|
|
1491
|
+
...base,
|
|
1492
|
+
blocks: rawBlocks.map((b, bi) => normalizeDocBlock(b, `block${bi + 1}`))
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
...base,
|
|
1256
1497
|
rows: rows.map((r, ri) => {
|
|
1257
1498
|
const columns = Array.isArray(r.columns) ? r.columns : [];
|
|
1258
1499
|
const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
|
|
@@ -1274,28 +1515,76 @@ function normalizeEmailDocument(input) {
|
|
|
1274
1515
|
columns: colCount,
|
|
1275
1516
|
gap: typeof r.layout?.gap === "number" ? r.layout.gap : 0,
|
|
1276
1517
|
stackOnMobile: r.layout?.stackOnMobile !== false,
|
|
1277
|
-
align: r.layout?.align || "center"
|
|
1518
|
+
align: r.layout?.align || "center",
|
|
1519
|
+
...typeof r.layout?.preset === "string" && r.layout.preset.trim() ? { preset: r.layout.preset.trim() } : {},
|
|
1520
|
+
...Array.isArray(r.layout?.ratios) && r.layout.ratios.length ? {
|
|
1521
|
+
ratios: r.layout.ratios.filter(
|
|
1522
|
+
(x) => typeof x === "number" && Number.isFinite(x)
|
|
1523
|
+
)
|
|
1524
|
+
} : {}
|
|
1278
1525
|
},
|
|
1279
|
-
|
|
1280
|
-
|
|
1526
|
+
props: (() => {
|
|
1527
|
+
const p = r.props;
|
|
1528
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { ...p };
|
|
1529
|
+
const s = r.styles;
|
|
1530
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1531
|
+
return {
|
|
1532
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1533
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1534
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1535
|
+
bgSize: s.backgroundSize || "cover",
|
|
1536
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1537
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1538
|
+
padding: s.padding,
|
|
1539
|
+
borderRadius: s.borderRadius,
|
|
1540
|
+
borderWidth: s.borderWidth,
|
|
1541
|
+
borderColor: s.borderColor,
|
|
1542
|
+
textAlign: s.textAlign
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
return {};
|
|
1546
|
+
})(),
|
|
1281
1547
|
columns: colsNormalized.map((c, ci) => ({
|
|
1282
1548
|
id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
|
|
1283
1549
|
layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
...
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1550
|
+
...(() => {
|
|
1551
|
+
const p = c.props;
|
|
1552
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return { props: { ...p } };
|
|
1553
|
+
const s = c.styles;
|
|
1554
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
1555
|
+
return {
|
|
1556
|
+
props: {
|
|
1557
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1558
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1559
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1560
|
+
bgSize: s.backgroundSize || "cover",
|
|
1561
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1562
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1563
|
+
padding: s.padding,
|
|
1564
|
+
borderRadius: s.borderRadius
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
return {};
|
|
1569
|
+
})(),
|
|
1570
|
+
blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => {
|
|
1571
|
+
const base2 = {
|
|
1572
|
+
id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
|
|
1573
|
+
type: typeof b?.type === "string" ? b.type : "text",
|
|
1574
|
+
content: typeof b?.content === "string" ? b.content : b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
|
|
1575
|
+
...b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? { behavior: b.behavior } : {},
|
|
1576
|
+
...b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? { responsive: b.responsive } : {}
|
|
1577
|
+
};
|
|
1578
|
+
if (b?.props && typeof b.props === "object" && !Array.isArray(b.props)) {
|
|
1579
|
+
base2.props = { ...b.props };
|
|
1580
|
+
} else if (b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles)) {
|
|
1581
|
+
base2.styles = { ...b.styles };
|
|
1582
|
+
}
|
|
1583
|
+
return base2;
|
|
1584
|
+
}) : []
|
|
1294
1585
|
}))
|
|
1295
1586
|
};
|
|
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
|
|
1587
|
+
})
|
|
1299
1588
|
};
|
|
1300
1589
|
}
|
|
1301
1590
|
|
|
@@ -1308,6 +1597,60 @@ function cloneJson(v) {
|
|
|
1308
1597
|
return v;
|
|
1309
1598
|
}
|
|
1310
1599
|
}
|
|
1600
|
+
function legacyRowStylesToProps(s) {
|
|
1601
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return {};
|
|
1602
|
+
return {
|
|
1603
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1604
|
+
bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
|
|
1605
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1606
|
+
bgSize: s.backgroundSize || "cover",
|
|
1607
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1608
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1609
|
+
padding: s.padding,
|
|
1610
|
+
borderRadius: s.borderRadius,
|
|
1611
|
+
borderWidth: s.borderWidth,
|
|
1612
|
+
borderColor: s.borderColor,
|
|
1613
|
+
textAlign: s.textAlign
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
function rowPropsFromDocRow(r) {
|
|
1617
|
+
const p = r.props;
|
|
1618
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1619
|
+
return { ...p };
|
|
1620
|
+
}
|
|
1621
|
+
return legacyRowStylesToProps(r.styles);
|
|
1622
|
+
}
|
|
1623
|
+
function applyRowPropsToEditorRow(row, r) {
|
|
1624
|
+
const p = rowPropsFromDocRow(r);
|
|
1625
|
+
if (p.padding !== void 0 && p.padding !== null) {
|
|
1626
|
+
row.padding = paddingToUniform(p.padding, row.padding);
|
|
1627
|
+
}
|
|
1628
|
+
if (typeof p.bgColor === "string") row.bgColor = p.bgColor.trim();
|
|
1629
|
+
if (typeof p.bgImage === "string") row.bgImage = p.bgImage.trim();
|
|
1630
|
+
if (typeof p.bgRepeat === "string") row.bgRepeat = p.bgRepeat;
|
|
1631
|
+
if (typeof p.bgSize === "string") row.bgSize = p.bgSize;
|
|
1632
|
+
if (typeof p.bgPosition === "string") row.bgPosition = p.bgPosition;
|
|
1633
|
+
if (p.bgGradient !== void 0) row.bgGradient = p.bgGradient;
|
|
1634
|
+
}
|
|
1635
|
+
function editorRowToDocProps(r) {
|
|
1636
|
+
const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
|
|
1637
|
+
const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
|
|
1638
|
+
return { top: n, right: n, bottom: n, left: n };
|
|
1639
|
+
})();
|
|
1640
|
+
return {
|
|
1641
|
+
bgColor: typeof r.bgColor === "string" ? r.bgColor : "",
|
|
1642
|
+
bgImage: typeof r.bgImage === "string" ? r.bgImage : "",
|
|
1643
|
+
bgRepeat: r.bgRepeat || "no-repeat",
|
|
1644
|
+
bgSize: r.bgSize || "cover",
|
|
1645
|
+
bgPosition: r.bgPosition || "center",
|
|
1646
|
+
bgGradient: r.bgGradient ?? null,
|
|
1647
|
+
padding: rowPad,
|
|
1648
|
+
borderRadius: 0,
|
|
1649
|
+
borderWidth: 0,
|
|
1650
|
+
borderColor: "#e5e7eb",
|
|
1651
|
+
textAlign: "left"
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1311
1654
|
function layoutColumnPaddingForDoc(p) {
|
|
1312
1655
|
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1313
1656
|
const n = Math.max(0, p);
|
|
@@ -1327,16 +1670,58 @@ function layoutColumnBorderRadiusForDoc(r) {
|
|
|
1327
1670
|
}
|
|
1328
1671
|
return 0;
|
|
1329
1672
|
}
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1673
|
+
function normalizeLayoutContainerProps(props) {
|
|
1674
|
+
const columns = getColumns(props);
|
|
1675
|
+
const { columnStyles: _drop, ...rest } = props;
|
|
1676
|
+
return { ...rest, columns };
|
|
1677
|
+
}
|
|
1678
|
+
function mergeLayoutColumns(fromContent, fromHydrated) {
|
|
1679
|
+
const norm = (x) => {
|
|
1680
|
+
if (Array.isArray(x)) {
|
|
1681
|
+
return x.map((entry) => {
|
|
1682
|
+
const e = entry;
|
|
1683
|
+
const raw = e?.props && typeof e.props === "object" ? e.props : entry;
|
|
1684
|
+
return { props: { ...DEFAULT_COLUMN_PROPS, ...raw } };
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
if (x && typeof x === "object" && !Array.isArray(x)) {
|
|
1688
|
+
return columnsFromLegacyMap(x, Object.keys(x).length);
|
|
1689
|
+
}
|
|
1690
|
+
return [];
|
|
1691
|
+
};
|
|
1692
|
+
const a = norm(fromContent);
|
|
1693
|
+
const b = norm(fromHydrated);
|
|
1694
|
+
const len = Math.max(a.length, b.length, 1);
|
|
1695
|
+
const out = [];
|
|
1696
|
+
for (let i = 0; i < len; i++) {
|
|
1697
|
+
out.push({
|
|
1698
|
+
props: { ...DEFAULT_COLUMN_PROPS, ...a[i]?.props ?? {}, ...b[i]?.props ?? {} }
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
return out.length ? out : void 0;
|
|
1332
1702
|
}
|
|
1333
|
-
function
|
|
1334
|
-
|
|
1335
|
-
if (
|
|
1336
|
-
|
|
1337
|
-
return !!(o.tl || o.tr || o.br || o.bl);
|
|
1703
|
+
function columnPropsFromDocColumn(c) {
|
|
1704
|
+
const p = c?.props;
|
|
1705
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1706
|
+
return { ...DEFAULT_COLUMN_PROPS, ...p };
|
|
1338
1707
|
}
|
|
1339
|
-
|
|
1708
|
+
const s = c?.styles;
|
|
1709
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) {
|
|
1710
|
+
return { ...DEFAULT_COLUMN_PROPS };
|
|
1711
|
+
}
|
|
1712
|
+
const padding = layoutColumnPaddingForDoc(s.padding);
|
|
1713
|
+
const borderRadius = layoutColumnBorderRadiusForDoc(s.borderRadius);
|
|
1714
|
+
return {
|
|
1715
|
+
...DEFAULT_COLUMN_PROPS,
|
|
1716
|
+
bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
|
|
1717
|
+
bgImage: typeof s.backgroundImage === "string" ? String(s.backgroundImage).trim() : "",
|
|
1718
|
+
bgRepeat: s.backgroundRepeat || "no-repeat",
|
|
1719
|
+
bgSize: s.backgroundSize || "cover",
|
|
1720
|
+
bgPosition: s.backgroundPosition || "center",
|
|
1721
|
+
bgGradient: s.backgroundGradient ?? null,
|
|
1722
|
+
padding,
|
|
1723
|
+
borderRadius
|
|
1724
|
+
};
|
|
1340
1725
|
}
|
|
1341
1726
|
function asNum(v) {
|
|
1342
1727
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
@@ -1433,13 +1818,15 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1433
1818
|
...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1434
1819
|
}
|
|
1435
1820
|
};
|
|
1436
|
-
case "html":
|
|
1821
|
+
case "html": {
|
|
1822
|
+
const body = typeof b.content === "string" ? b.content : typeof p.content === "string" ? p.content : "";
|
|
1437
1823
|
return {
|
|
1438
1824
|
id,
|
|
1439
1825
|
type,
|
|
1440
|
-
content: { html:
|
|
1826
|
+
content: { html: normalizeRichHtmlForStorage(body) },
|
|
1441
1827
|
styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
|
|
1442
1828
|
};
|
|
1829
|
+
}
|
|
1443
1830
|
case "image": {
|
|
1444
1831
|
const br = p.borderRadius;
|
|
1445
1832
|
const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
|
|
@@ -1619,7 +2006,7 @@ function internalBlockToEmailDoc(b, depth = 0) {
|
|
|
1619
2006
|
bgRepeat: p.bgRepeat,
|
|
1620
2007
|
bgPosition: p.bgPosition,
|
|
1621
2008
|
bgGradient: p.bgGradient ?? null,
|
|
1622
|
-
|
|
2009
|
+
columns: getColumns(p).map((col) => ({ props: cloneJson(col.props) })),
|
|
1623
2010
|
cells: cellsOut
|
|
1624
2011
|
},
|
|
1625
2012
|
styles: {}
|
|
@@ -1659,17 +2046,26 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1659
2046
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1660
2047
|
}
|
|
1661
2048
|
break;
|
|
1662
|
-
case "html":
|
|
1663
|
-
|
|
1664
|
-
|
|
2049
|
+
case "html": {
|
|
2050
|
+
const br = b;
|
|
2051
|
+
const fromDoc = asStr(c.html);
|
|
2052
|
+
const fromRoot = typeof br.content === "string" ? String(br.content) : void 0;
|
|
2053
|
+
const fromLegacyProps = br.props && typeof br.props.content === "string" ? String(br.props.content) : void 0;
|
|
2054
|
+
const htmlNorm = normalizeRichHtmlForStorage(fromDoc ?? fromRoot ?? fromLegacyProps ?? "");
|
|
2055
|
+
block.content = htmlNorm;
|
|
2056
|
+
block.props.content = htmlNorm;
|
|
1665
2057
|
if (s.padding && typeof s.padding === "object") {
|
|
1666
2058
|
block.props.padding = paddingToUniform(s.padding, block.props.padding);
|
|
1667
2059
|
}
|
|
1668
2060
|
break;
|
|
2061
|
+
}
|
|
1669
2062
|
case "image":
|
|
1670
2063
|
block.props.src = asStr(c.src) ?? block.props.src;
|
|
1671
2064
|
if (asStr(c.alt)) block.props.alt = c.alt;
|
|
1672
|
-
if (asStr(c.link))
|
|
2065
|
+
if (asStr(c.link)) {
|
|
2066
|
+
block.props.link = c.link;
|
|
2067
|
+
block.props.linkEnabled = true;
|
|
2068
|
+
}
|
|
1673
2069
|
if (beh.openInNewTab === true) block.props.linkTarget = "_blank";
|
|
1674
2070
|
if (asStr(s.width)) block.props.width = s.width;
|
|
1675
2071
|
if (asStr(s.align)) block.props.align = s.align;
|
|
@@ -1791,8 +2187,17 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1791
2187
|
if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
|
|
1792
2188
|
if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
|
|
1793
2189
|
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
1794
|
-
if (c.
|
|
1795
|
-
|
|
2190
|
+
if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
|
|
2191
|
+
if (Array.isArray(c.columns)) {
|
|
2192
|
+
block.props.columns = c.columns.map((col) => ({
|
|
2193
|
+
props: {
|
|
2194
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2195
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2196
|
+
}
|
|
2197
|
+
}));
|
|
2198
|
+
} else if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
|
|
2199
|
+
const ratios = Array.isArray(c.ratios) ? c.ratios : [1];
|
|
2200
|
+
block.props.columns = columnsFromLegacyMap(c.columnStyles, ratios.length);
|
|
1796
2201
|
}
|
|
1797
2202
|
block.props.cells = c.cells.map(
|
|
1798
2203
|
(col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
|
|
@@ -1836,6 +2241,10 @@ function mapBlockToInternal(b, layoutDepth = 0) {
|
|
|
1836
2241
|
}
|
|
1837
2242
|
}
|
|
1838
2243
|
applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
|
|
2244
|
+
if (t === "image") {
|
|
2245
|
+
const link = asStr(block.props.link);
|
|
2246
|
+
if (link && block.props.linkEnabled == null) block.props.linkEnabled = true;
|
|
2247
|
+
}
|
|
1839
2248
|
return block;
|
|
1840
2249
|
}
|
|
1841
2250
|
function rowToInternal(r) {
|
|
@@ -1857,55 +2266,315 @@ function rowToInternal(r) {
|
|
|
1857
2266
|
row.id = typeof r.id === "string" ? r.id : uid();
|
|
1858
2267
|
row.cols = cols;
|
|
1859
2268
|
row.gap = typeof r.layout?.gap === "number" ? r.layout.gap : row.gap;
|
|
1860
|
-
row
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2269
|
+
applyRowPropsToEditorRow(row, r);
|
|
2270
|
+
const lay = r.layout;
|
|
2271
|
+
if (lay && typeof lay === "object" && !Array.isArray(lay)) {
|
|
2272
|
+
if (typeof lay.preset === "string" && lay.preset.trim()) row.preset = lay.preset.trim();
|
|
2273
|
+
if (Array.isArray(lay.ratios) && lay.ratios.length) {
|
|
2274
|
+
const rr = lay.ratios.filter((x) => typeof x === "number" && Number.isFinite(x));
|
|
2275
|
+
if (rr.length) row.ratios = rr;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
1867
2278
|
row.cells = colList.map((c) => {
|
|
1868
2279
|
const blocks = Array.isArray(c.blocks) ? c.blocks : [];
|
|
1869
2280
|
return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
|
|
1870
2281
|
});
|
|
1871
2282
|
const studio = r._reactEmailStudio;
|
|
1872
2283
|
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
|
-
});
|
|
2284
|
+
const columnsFromDoc = colList.map((c) => ({
|
|
2285
|
+
props: columnPropsFromDocColumn(c)
|
|
2286
|
+
}));
|
|
1889
2287
|
if (snap && typeof snap === "object" && !Array.isArray(snap)) {
|
|
1890
|
-
const {
|
|
2288
|
+
const {
|
|
2289
|
+
cells: _omitCells,
|
|
2290
|
+
columnStyles: legacyCs,
|
|
2291
|
+
columns: snapCols,
|
|
2292
|
+
...rest
|
|
2293
|
+
} = snap;
|
|
1891
2294
|
Object.assign(row, rest);
|
|
1892
|
-
const
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2295
|
+
const fromSnap = Array.isArray(snapCols) ? snapCols.map((col) => ({
|
|
2296
|
+
props: {
|
|
2297
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2298
|
+
...col?.props && typeof col.props === "object" ? col.props : {}
|
|
2299
|
+
}
|
|
2300
|
+
})) : columnsFromLegacyMap(legacyCs, cols);
|
|
2301
|
+
row.columns = mergeLayoutColumns(fromSnap, columnsFromDoc) ?? columnsFromDoc;
|
|
2302
|
+
delete row.columnStyles;
|
|
2303
|
+
} else {
|
|
2304
|
+
row.columns = columnsFromDoc;
|
|
1897
2305
|
}
|
|
1898
2306
|
return row;
|
|
1899
2307
|
}
|
|
1900
|
-
function
|
|
1901
|
-
|
|
1902
|
-
if (
|
|
1903
|
-
|
|
1904
|
-
|
|
2308
|
+
function coerceEmailDocumentInput(input) {
|
|
2309
|
+
if (input == null) return input;
|
|
2310
|
+
if (Array.isArray(input)) {
|
|
2311
|
+
if (input.length === 0) return input;
|
|
2312
|
+
return { type: "email_document", settings: {}, blocks: input };
|
|
2313
|
+
}
|
|
2314
|
+
if (typeof input !== "object") return input;
|
|
2315
|
+
const o = input;
|
|
2316
|
+
if (o.type === "email_document") return input;
|
|
2317
|
+
if (Array.isArray(o.blocks)) {
|
|
2318
|
+
return {
|
|
2319
|
+
type: "email_document",
|
|
2320
|
+
settings: o.settings && typeof o.settings === "object" ? o.settings : {},
|
|
2321
|
+
blocks: o.blocks
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
return input;
|
|
2325
|
+
}
|
|
2326
|
+
function rootLayoutBlockToDocRow(block) {
|
|
2327
|
+
const p = block.props && typeof block.props === "object" ? block.props : {};
|
|
2328
|
+
const c = block.content && typeof block.content === "object" && !Array.isArray(block.content) ? block.content : {};
|
|
2329
|
+
const cols = Math.max(
|
|
2330
|
+
1,
|
|
2331
|
+
typeof p.cols === "number" && p.cols > 0 ? Math.floor(p.cols) : 0,
|
|
2332
|
+
typeof c.cols === "number" && c.cols > 0 ? Math.floor(c.cols) : 0,
|
|
2333
|
+
Array.isArray(p.cells) ? p.cells.length : 0,
|
|
2334
|
+
Array.isArray(c.cells) ? c.cells.length : 0
|
|
2335
|
+
);
|
|
2336
|
+
const rawCells = Array.isArray(c.cells) ? c.cells : Array.isArray(p.cells) ? p.cells : [];
|
|
2337
|
+
const contentColumns = Array.isArray(c.columns) ? c.columns : [];
|
|
2338
|
+
const propsColumns = getColumns(p);
|
|
2339
|
+
const columns = Array.from({ length: cols }, (_, ci) => {
|
|
2340
|
+
const cellRaw = rawCells[ci];
|
|
2341
|
+
const blocks = Array.isArray(cellRaw) ? cellRaw.filter((x) => x && typeof x === "object").map((x, bi) => {
|
|
2342
|
+
const blk = x;
|
|
2343
|
+
return {
|
|
2344
|
+
id: ensureBlockId(blk.id, `b_${ci}_${bi}`),
|
|
2345
|
+
type: typeof blk.type === "string" ? blk.type : "text",
|
|
2346
|
+
content: blk.content,
|
|
2347
|
+
...blk.props ? { props: blk.props } : {},
|
|
2348
|
+
...blk.styles ? { styles: blk.styles } : {},
|
|
2349
|
+
...blk.behavior ? { behavior: blk.behavior } : {}
|
|
2350
|
+
};
|
|
2351
|
+
}) : [];
|
|
2352
|
+
const colProps = {
|
|
2353
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2354
|
+
...propsColumns[ci]?.props ?? contentColumns[ci]?.props ?? {}
|
|
2355
|
+
};
|
|
2356
|
+
return {
|
|
2357
|
+
id: `col_${ci + 1}`,
|
|
2358
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2359
|
+
props: cloneJson(colProps),
|
|
2360
|
+
blocks
|
|
2361
|
+
};
|
|
2362
|
+
});
|
|
2363
|
+
const rowProps = {
|
|
2364
|
+
bgColor: typeof p.bgColor === "string" ? p.bgColor : "",
|
|
2365
|
+
bgImage: typeof p.bgImage === "string" ? p.bgImage : "",
|
|
2366
|
+
bgRepeat: p.bgRepeat || "no-repeat",
|
|
2367
|
+
bgSize: p.bgSize || "cover",
|
|
2368
|
+
bgPosition: p.bgPosition || "center",
|
|
2369
|
+
bgGradient: p.bgGradient ?? null,
|
|
2370
|
+
padding: typeof p.padding === "number" ? { top: p.padding, right: p.padding, bottom: p.padding, left: p.padding } : normalizePadding(p.padding)
|
|
2371
|
+
};
|
|
2372
|
+
const ratios = Array.isArray(p.ratios) ? p.ratios : Array.isArray(c.ratios) ? c.ratios : void 0;
|
|
2373
|
+
return {
|
|
2374
|
+
id: typeof block.id === "string" ? block.id : uid(),
|
|
2375
|
+
type: "row",
|
|
2376
|
+
layout: {
|
|
2377
|
+
columns: cols,
|
|
2378
|
+
gap: typeof p.gap === "number" ? p.gap : typeof c.gap === "number" ? c.gap : 0,
|
|
2379
|
+
stackOnMobile: true,
|
|
2380
|
+
align: "center",
|
|
2381
|
+
...typeof p.preset === "string" && p.preset.trim() ? { preset: p.preset.trim() } : typeof c.preset === "string" && c.preset.trim() ? { preset: c.preset.trim() } : {},
|
|
2382
|
+
...ratios?.length ? { ratios: [...ratios] } : {}
|
|
2383
|
+
},
|
|
2384
|
+
props: rowProps,
|
|
2385
|
+
columns
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
function ensureBlockId(id, prefix) {
|
|
2389
|
+
return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
|
|
2390
|
+
}
|
|
2391
|
+
function wrapRootContentBlock(block) {
|
|
2392
|
+
const row = makeLayoutRow(pickPresetByColCount(1));
|
|
2393
|
+
const internal = mapBlockToInternal(block, 0);
|
|
2394
|
+
if (internal) row.cells[0] = [internal];
|
|
2395
|
+
return row;
|
|
2396
|
+
}
|
|
2397
|
+
function rootBlockToEditorRow(block) {
|
|
2398
|
+
const t = block.type === "nestedRow" ? "layout" : block.type;
|
|
2399
|
+
if (t === "layout") {
|
|
2400
|
+
return rowToInternal(rootLayoutBlockToDocRow(block));
|
|
2401
|
+
}
|
|
2402
|
+
return wrapRootContentBlock(block);
|
|
2403
|
+
}
|
|
2404
|
+
function blocksToEditorRows(blocks) {
|
|
2405
|
+
return blocks.map(rootBlockToEditorRow);
|
|
2406
|
+
}
|
|
2407
|
+
function rowPropsToLegacyStyles(p) {
|
|
2408
|
+
return {
|
|
2409
|
+
backgroundColor: p.bgColor ?? "",
|
|
2410
|
+
backgroundImage: p.bgImage ?? "",
|
|
2411
|
+
backgroundRepeat: p.bgRepeat ?? "no-repeat",
|
|
2412
|
+
backgroundSize: p.bgSize ?? "cover",
|
|
2413
|
+
backgroundPosition: p.bgPosition ?? "center",
|
|
2414
|
+
backgroundGradient: p.bgGradient ?? null,
|
|
2415
|
+
padding: p.padding,
|
|
2416
|
+
borderRadius: p.borderRadius ?? 0,
|
|
2417
|
+
borderWidth: p.borderWidth ?? 0,
|
|
2418
|
+
borderColor: p.borderColor ?? "#e5e7eb",
|
|
2419
|
+
textAlign: p.textAlign ?? "left"
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
function columnPropsToLegacyStyles(cp) {
|
|
2423
|
+
return {
|
|
2424
|
+
padding: layoutColumnPaddingForDoc(cp.padding),
|
|
2425
|
+
backgroundColor: cp.bgColor ?? "",
|
|
2426
|
+
backgroundImage: cp.bgImage ?? "",
|
|
2427
|
+
backgroundRepeat: cp.bgRepeat ?? "no-repeat",
|
|
2428
|
+
backgroundSize: cp.bgSize ?? "cover",
|
|
2429
|
+
backgroundPosition: cp.bgPosition ?? "center",
|
|
2430
|
+
backgroundGradient: cp.bgGradient ?? null,
|
|
2431
|
+
borderRadius: layoutColumnBorderRadiusForDoc(cp.borderRadius)
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
function editorRowStudioSnapshot(r) {
|
|
2435
|
+
const p = editorRowToDocProps(r);
|
|
2436
|
+
const columns = getColumns(r).map((col) => ({
|
|
2437
|
+
props: cloneJson(col.props ?? DEFAULT_COLUMN_PROPS)
|
|
2438
|
+
}));
|
|
2439
|
+
return {
|
|
2440
|
+
id: r.id,
|
|
2441
|
+
type: "layout",
|
|
2442
|
+
preset: r.preset,
|
|
2443
|
+
cols: r.cols,
|
|
2444
|
+
ratios: Array.isArray(r.ratios) ? [...r.ratios] : [1],
|
|
2445
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2446
|
+
padding: p.padding,
|
|
2447
|
+
bgColor: p.bgColor ?? "",
|
|
2448
|
+
bgImage: p.bgImage ?? "",
|
|
2449
|
+
bgSize: p.bgSize ?? "cover",
|
|
2450
|
+
bgRepeat: p.bgRepeat ?? "no-repeat",
|
|
2451
|
+
bgPosition: p.bgPosition ?? "center",
|
|
2452
|
+
bgGradient: p.bgGradient ?? null,
|
|
2453
|
+
columns
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
function exportBlockForLegacyRow(b, depth = 0) {
|
|
2457
|
+
if (!b || typeof b !== "object") {
|
|
2458
|
+
return { id: uid(), type: "text", content: {}, props: {} };
|
|
2459
|
+
}
|
|
2460
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? cloneJson(b.props) : {};
|
|
2461
|
+
if (b.type === "layout" && Array.isArray(b.props?.cells)) {
|
|
2462
|
+
rawProps.cells = b.props.cells.map(
|
|
2463
|
+
(col) => Array.isArray(col) ? col.map((child) => exportBlockForLegacyRow(child, depth + 1)) : []
|
|
2464
|
+
);
|
|
2465
|
+
}
|
|
2466
|
+
if (b.type === "html") {
|
|
2467
|
+
const body = typeof b.content === "string" ? b.content : typeof rawProps.content === "string" ? rawProps.content : "";
|
|
2468
|
+
const html = normalizeRichHtmlForStorage(body);
|
|
2469
|
+
rawProps.content = html;
|
|
2470
|
+
return {
|
|
2471
|
+
id: typeof b.id === "string" ? b.id : uid(),
|
|
2472
|
+
type: "html",
|
|
2473
|
+
content: { html },
|
|
2474
|
+
props: rawProps
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
const doc = internalBlockToEmailDoc(b, depth);
|
|
2478
|
+
const out = {
|
|
2479
|
+
id: doc.id,
|
|
2480
|
+
type: doc.type,
|
|
2481
|
+
content: doc.content ?? {},
|
|
2482
|
+
props: rawProps
|
|
2483
|
+
};
|
|
2484
|
+
if (doc.behavior) out.behavior = doc.behavior;
|
|
2485
|
+
return out;
|
|
2486
|
+
}
|
|
2487
|
+
function editorRowToEmailDocumentRow(r, rowIndex) {
|
|
2488
|
+
const rowProps = editorRowToDocProps(r);
|
|
2489
|
+
const cellArrays = Array.isArray(r.cells) ? r.cells : [];
|
|
2490
|
+
const declaredCols = typeof r.cols === "number" && r.cols > 0 ? Math.floor(r.cols) : 0;
|
|
2491
|
+
const colCount = Math.max(1, cellArrays.length, declaredCols);
|
|
2492
|
+
const rowColumns = getColumns(r);
|
|
2493
|
+
const columns = Array.from({ length: colCount }, (_, ci) => {
|
|
2494
|
+
const cp = {
|
|
2495
|
+
...DEFAULT_COLUMN_PROPS,
|
|
2496
|
+
...rowColumns[ci]?.props ?? {}
|
|
2497
|
+
};
|
|
2498
|
+
const cellBlocks = cellArrays[ci];
|
|
2499
|
+
const blocks = Array.isArray(cellBlocks) ? cellBlocks.map((blk) => exportBlockForLegacyRow(blk)).filter((x) => x != null) : [];
|
|
2500
|
+
return {
|
|
2501
|
+
id: `col_${rowIndex + 1}_${ci + 1}`,
|
|
2502
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2503
|
+
styles: columnPropsToLegacyStyles(cp),
|
|
2504
|
+
props: cloneJson(cp),
|
|
2505
|
+
blocks
|
|
2506
|
+
};
|
|
2507
|
+
});
|
|
2508
|
+
const ratios = Array.isArray(r.ratios) ? [...r.ratios] : void 0;
|
|
2509
|
+
return {
|
|
2510
|
+
id: typeof r.id === "string" ? r.id : uid(),
|
|
2511
|
+
type: "row",
|
|
2512
|
+
_reactEmailStudio: { row: editorRowStudioSnapshot(r) },
|
|
2513
|
+
layout: {
|
|
2514
|
+
columns: colCount,
|
|
2515
|
+
gap: typeof r.gap === "number" ? r.gap : 0,
|
|
2516
|
+
stackOnMobile: true,
|
|
2517
|
+
align: "center",
|
|
2518
|
+
...typeof r.preset === "string" && r.preset.trim() ? { preset: r.preset.trim() } : {},
|
|
2519
|
+
...ratios?.length ? { ratios } : {}
|
|
2520
|
+
},
|
|
2521
|
+
props: rowProps,
|
|
2522
|
+
styles: rowPropsToLegacyStyles(rowProps),
|
|
2523
|
+
columns
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
function buildExportSettingsWithStudio(settings) {
|
|
2527
|
+
const base = buildExportSettings(settings) ?? {};
|
|
2528
|
+
const editorSettings = cloneJson(settings);
|
|
2529
|
+
return {
|
|
2530
|
+
...base,
|
|
2531
|
+
_reactEmailStudio: {
|
|
2532
|
+
contentPadding: base.contentPadding ?? 24,
|
|
2533
|
+
contentBorderRadius: base.contentBorderRadius ?? 8,
|
|
2534
|
+
editorSettings
|
|
2535
|
+
}
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
function editorSettingStr(settings, key, fallback = "") {
|
|
2539
|
+
const v = settings[key];
|
|
2540
|
+
return typeof v === "string" ? v : fallback;
|
|
2541
|
+
}
|
|
2542
|
+
function buildExportSettings(settings) {
|
|
2543
|
+
const contentWidth = typeof settings.contentWidth === "number" ? settings.contentWidth : 600;
|
|
2544
|
+
const bgGradient = settings.bgGradient;
|
|
2545
|
+
const contentBgGradient = settings.contentBgGradient;
|
|
2546
|
+
return {
|
|
2547
|
+
width: contentWidth,
|
|
2548
|
+
backgroundColor: editorSettingStr(settings, "bgColor", "#f1f5f9"),
|
|
2549
|
+
backgroundImage: editorSettingStr(settings, "bgImage"),
|
|
2550
|
+
backgroundRepeat: editorSettingStr(settings, "bgRepeat", "no-repeat"),
|
|
2551
|
+
backgroundSize: editorSettingStr(settings, "bgSize", "cover"),
|
|
2552
|
+
backgroundPosition: editorSettingStr(settings, "bgPosition", "center"),
|
|
2553
|
+
backgroundGradient: bgGradient && typeof bgGradient === "object" && !Array.isArray(bgGradient) ? bgGradient : void 0,
|
|
2554
|
+
contentBackgroundColor: editorSettingStr(settings, "contentBg", "#ffffff"),
|
|
2555
|
+
contentBackgroundImage: editorSettingStr(settings, "contentBgImage"),
|
|
2556
|
+
contentBackgroundRepeat: editorSettingStr(settings, "contentBgRepeat", "no-repeat"),
|
|
2557
|
+
contentBackgroundSize: editorSettingStr(settings, "contentBgSize", "cover"),
|
|
2558
|
+
contentBackgroundPosition: editorSettingStr(settings, "contentBgPosition", "center"),
|
|
2559
|
+
contentBackgroundGradient: contentBgGradient && typeof contentBgGradient === "object" && !Array.isArray(contentBgGradient) ? contentBgGradient : void 0,
|
|
2560
|
+
fontFamily: editorSettingStr(settings, "fontFamily") || editorSettingStr(settings, "pageFontFamily", "Arial, Helvetica, sans-serif"),
|
|
2561
|
+
lineHeightBase: settings.pageLineHeight != null && settings.pageLineHeight !== "" ? Number(settings.pageLineHeight) : 1.6,
|
|
2562
|
+
color: editorSettingStr(settings, "pageTextColor", "#111827"),
|
|
2563
|
+
responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
|
|
2564
|
+
rtl: !!settings.pageRtl,
|
|
2565
|
+
contentPadding: typeof settings.padding === "number" ? settings.padding : 24,
|
|
2566
|
+
contentBorderRadius: typeof settings.borderRadius === "number" ? settings.borderRadius : 8
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
function editorSettingsFromDoc(s, rawSettings) {
|
|
2570
|
+
const studioSettings = rawSettings?._reactEmailStudio;
|
|
1905
2571
|
const settings = {};
|
|
1906
2572
|
if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
|
|
1907
2573
|
Object.assign(settings, cloneJson(studioSettings.editorSettings));
|
|
1908
2574
|
}
|
|
2575
|
+
const pageRtlFromEditor = settings.pageRtl;
|
|
2576
|
+
const contentPadding = typeof s.contentPadding === "number" ? s.contentPadding : typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : void 0;
|
|
2577
|
+
const contentBorderRadius = typeof s.contentBorderRadius === "number" ? s.contentBorderRadius : typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : void 0;
|
|
1909
2578
|
Object.assign(settings, {
|
|
1910
2579
|
bgColor: s.backgroundColor || "#f1f5f9",
|
|
1911
2580
|
bgImage: s.backgroundImage || "",
|
|
@@ -1920,103 +2589,67 @@ function normalizeEmailDesignInput(input) {
|
|
|
1920
2589
|
contentBgPosition: s.contentBackgroundPosition || "center",
|
|
1921
2590
|
contentBgGradient: s.contentBackgroundGradient || null,
|
|
1922
2591
|
contentWidth: typeof s.width === "number" ? s.width : 600,
|
|
1923
|
-
padding:
|
|
1924
|
-
borderRadius:
|
|
2592
|
+
padding: contentPadding ?? (typeof settings.padding === "number" ? settings.padding : 24),
|
|
2593
|
+
borderRadius: contentBorderRadius ?? (typeof settings.borderRadius === "number" ? settings.borderRadius : 8),
|
|
1925
2594
|
pageFontFamily: s.fontFamily,
|
|
1926
2595
|
pageTextColor: s.color,
|
|
1927
2596
|
pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
|
|
1928
2597
|
pageResponsive: s.responsive,
|
|
1929
|
-
pageRtl: s.rtl,
|
|
2598
|
+
pageRtl: typeof s.rtl === "boolean" ? s.rtl : pageRtlFromEditor,
|
|
1930
2599
|
fontFamily: s.fontFamily
|
|
1931
2600
|
});
|
|
1932
|
-
|
|
2601
|
+
return settings;
|
|
2602
|
+
}
|
|
2603
|
+
function normalizeEmailDesignInput(input) {
|
|
2604
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2605
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2606
|
+
if (!doc) return null;
|
|
2607
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2608
|
+
const rawSettings = rawDoc?.settings && typeof rawDoc.settings === "object" && !Array.isArray(rawDoc.settings) ? rawDoc.settings : null;
|
|
2609
|
+
const settings = editorSettingsFromDoc(doc.settings || {}, rawSettings);
|
|
2610
|
+
let rows;
|
|
2611
|
+
if (doc.blocks?.length) {
|
|
2612
|
+
rows = blocksToEditorRows(doc.blocks);
|
|
2613
|
+
} else {
|
|
2614
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2615
|
+
rows = (doc.rows || []).map((r, i) => {
|
|
2616
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2617
|
+
if (legacy) {
|
|
2618
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2619
|
+
}
|
|
2620
|
+
return rowToInternal(r);
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
1933
2623
|
return withHydratedRows({ rows, settings, __emailDocument: doc });
|
|
1934
2624
|
}
|
|
2625
|
+
function emailDocumentToEditorRows(input) {
|
|
2626
|
+
const coerced = coerceEmailDocumentInput(input);
|
|
2627
|
+
const doc = normalizeEmailDocument(coerced);
|
|
2628
|
+
if (!doc) return null;
|
|
2629
|
+
const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
2630
|
+
if (doc.blocks?.length) {
|
|
2631
|
+
return blocksToEditorRows(doc.blocks);
|
|
2632
|
+
}
|
|
2633
|
+
const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
|
|
2634
|
+
return (doc.rows || []).map((r, i) => {
|
|
2635
|
+
const legacy = rawRows[i]?._reactEmailStudio;
|
|
2636
|
+
if (legacy) {
|
|
2637
|
+
return rowToInternal({ ...r, _reactEmailStudio: legacy });
|
|
2638
|
+
}
|
|
2639
|
+
return rowToInternal(r);
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
1935
2642
|
function designToEmailDocument(rows, settings) {
|
|
1936
|
-
|
|
2643
|
+
return {
|
|
1937
2644
|
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
|
-
})
|
|
2645
|
+
settings: buildExportSettingsWithStudio(settings),
|
|
2646
|
+
rows: rows.map((r, i) => editorRowToEmailDocumentRow(r, i))
|
|
2018
2647
|
};
|
|
2019
|
-
|
|
2648
|
+
}
|
|
2649
|
+
function canonicalizeEmailDocument(input) {
|
|
2650
|
+
const loaded = normalizeEmailDesignInput(coerceEmailDocumentInput(input) ?? input);
|
|
2651
|
+
if (!loaded?.rows) return null;
|
|
2652
|
+
return designToEmailDocument(loaded.rows, loaded.settings);
|
|
2020
2653
|
}
|
|
2021
2654
|
function hydrateBlock(b, depth = 0) {
|
|
2022
2655
|
if (!b || typeof b !== "object") return null;
|
|
@@ -2030,13 +2663,13 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2030
2663
|
...b,
|
|
2031
2664
|
type: "layout",
|
|
2032
2665
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2033
|
-
props: {
|
|
2666
|
+
props: normalizeLayoutContainerProps({
|
|
2034
2667
|
...defaults2,
|
|
2035
2668
|
...b.props,
|
|
2036
2669
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2037
2670
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2038
2671
|
cells: ratios.map(() => [])
|
|
2039
|
-
}
|
|
2672
|
+
})
|
|
2040
2673
|
};
|
|
2041
2674
|
}
|
|
2042
2675
|
const cells = b.props.cells.map(
|
|
@@ -2046,24 +2679,33 @@ function hydrateBlock(b, depth = 0) {
|
|
|
2046
2679
|
...b,
|
|
2047
2680
|
type: "layout",
|
|
2048
2681
|
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2049
|
-
props: {
|
|
2682
|
+
props: normalizeLayoutContainerProps({
|
|
2050
2683
|
...defaults2,
|
|
2051
2684
|
...b.props,
|
|
2052
2685
|
bgSize: b.props.bgSize ?? defaults2.bgSize,
|
|
2053
2686
|
bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
|
|
2054
2687
|
cells
|
|
2055
|
-
}
|
|
2688
|
+
})
|
|
2056
2689
|
};
|
|
2057
2690
|
}
|
|
2058
2691
|
const defaults = DEFAULT_BLOCK_PROPS[t];
|
|
2059
2692
|
if (typeof defaults !== "object" || defaults == null || Array.isArray(defaults)) return null;
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
)
|
|
2066
|
-
|
|
2693
|
+
if (t === "html") {
|
|
2694
|
+
const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? b.props : {};
|
|
2695
|
+
const { content: legacyPropContent, ...restProps } = rawProps;
|
|
2696
|
+
const merged2 = { ...defaults, ...restProps };
|
|
2697
|
+
normalizeBoxStyles(merged2, t);
|
|
2698
|
+
const src = typeof b.content === "string" ? String(b.content) : typeof legacyPropContent === "string" ? legacyPropContent : "";
|
|
2699
|
+
const htmlNorm = normalizeRichHtmlForStorage(src);
|
|
2700
|
+
merged2.content = htmlNorm;
|
|
2701
|
+
return {
|
|
2702
|
+
...b,
|
|
2703
|
+
id: typeof b.id === "string" && b.id ? b.id : uid(),
|
|
2704
|
+
content: htmlNorm,
|
|
2705
|
+
props: merged2
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
const merged = { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
|
|
2067
2709
|
normalizeBoxStyles(merged, t);
|
|
2068
2710
|
return {
|
|
2069
2711
|
...b,
|
|
@@ -2079,70 +2721,50 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2079
2721
|
}
|
|
2080
2722
|
return mapBlockToInternal(r, layoutDepth);
|
|
2081
2723
|
}
|
|
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
2724
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2103
2725
|
const exp = b.props;
|
|
2104
|
-
if (!exp || typeof exp !== "object" || Array.isArray(exp))
|
|
2726
|
+
if (!exp || typeof exp !== "object" || Array.isArray(exp)) {
|
|
2727
|
+
if (t === "html" && typeof block.content === "string") {
|
|
2728
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2729
|
+
block.props.content = block.content;
|
|
2730
|
+
}
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2105
2733
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2106
|
-
const
|
|
2734
|
+
const columnsFromContent = block.props.columns ?? block.props.columnStyles;
|
|
2735
|
+
const expHasColumns = Array.isArray(exp.columns) || exp.columnStyles && typeof exp.columnStyles === "object" && !Array.isArray(exp.columnStyles);
|
|
2107
2736
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2108
2737
|
if (hb) {
|
|
2109
2738
|
block.props = hb.props;
|
|
2110
|
-
const merged =
|
|
2111
|
-
|
|
2112
|
-
|
|
2739
|
+
const merged = mergeLayoutColumns(
|
|
2740
|
+
columnsFromContent,
|
|
2741
|
+
expHasColumns ? block.props.columns : void 0
|
|
2742
|
+
);
|
|
2743
|
+
if (merged) block.props.columns = merged;
|
|
2744
|
+
else delete block.props.columns;
|
|
2745
|
+
delete block.props.columnStyles;
|
|
2113
2746
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2114
2747
|
}
|
|
2115
2748
|
return;
|
|
2116
2749
|
}
|
|
2750
|
+
if (t === "html") {
|
|
2751
|
+
const { content: expContent, ...expRest } = exp;
|
|
2752
|
+
Object.assign(block.props, expRest);
|
|
2753
|
+
normalizeBoxStyles(block.props, t);
|
|
2754
|
+
if (typeof expContent === "string") {
|
|
2755
|
+
block.content = normalizeRichHtmlForStorage(expContent);
|
|
2756
|
+
} else if (typeof block.content === "string") {
|
|
2757
|
+
block.content = normalizeRichHtmlForStorage(block.content);
|
|
2758
|
+
}
|
|
2759
|
+
block.props.content = typeof block.content === "string" ? block.content : "";
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2117
2762
|
Object.assign(block.props, exp);
|
|
2118
2763
|
normalizeBoxStyles(block.props, t);
|
|
2119
|
-
if (t === "
|
|
2764
|
+
if (t === "text") {
|
|
2120
2765
|
block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
|
|
2121
2766
|
}
|
|
2122
2767
|
}
|
|
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
2768
|
function hydrateLayoutRow(row) {
|
|
2147
2769
|
if (!row || typeof row !== "object") return row;
|
|
2148
2770
|
const cells = (row.cells || []).map(
|
|
@@ -2185,8 +2807,85 @@ function jsonToHtml(designInput, opts = {}) {
|
|
|
2185
2807
|
return designToHtml(d.rows, d.settings, opts);
|
|
2186
2808
|
}
|
|
2187
2809
|
|
|
2810
|
+
// src/lib/htmlToEmailDesign.ts
|
|
2811
|
+
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
2812
|
+
function extractHtmlForDesign(html) {
|
|
2813
|
+
const t = String(html ?? "").trim();
|
|
2814
|
+
if (!t) return "";
|
|
2815
|
+
const head = t.slice(0, 800).toLowerCase();
|
|
2816
|
+
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
2817
|
+
return normalizeRichHtmlForStorage(t);
|
|
2818
|
+
}
|
|
2819
|
+
try {
|
|
2820
|
+
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
2821
|
+
const body = doc.body;
|
|
2822
|
+
if (!body) return normalizeRichHtmlForStorage(t);
|
|
2823
|
+
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
2824
|
+
} catch {
|
|
2825
|
+
return normalizeRichHtmlForStorage(t);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
function htmlToEmailDesignTemplate(html) {
|
|
2829
|
+
const inner = extractHtmlForDesign(html);
|
|
2830
|
+
if (!inner) return null;
|
|
2831
|
+
const doc = {
|
|
2832
|
+
type: "email_document",
|
|
2833
|
+
settings: {
|
|
2834
|
+
width: 600,
|
|
2835
|
+
backgroundColor: "#f1f5f9",
|
|
2836
|
+
contentBackgroundColor: "#ffffff",
|
|
2837
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
2838
|
+
lineHeightBase: 1.6,
|
|
2839
|
+
color: "#111827",
|
|
2840
|
+
responsive: true,
|
|
2841
|
+
rtl: false
|
|
2842
|
+
},
|
|
2843
|
+
rows: [
|
|
2844
|
+
{
|
|
2845
|
+
id: "row_html_import",
|
|
2846
|
+
type: "row",
|
|
2847
|
+
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
2848
|
+
styles: {
|
|
2849
|
+
backgroundColor: "#ffffff",
|
|
2850
|
+
backgroundRepeat: "no-repeat",
|
|
2851
|
+
backgroundSize: "cover",
|
|
2852
|
+
padding: pad(24),
|
|
2853
|
+
textAlign: "left"
|
|
2854
|
+
},
|
|
2855
|
+
columns: [
|
|
2856
|
+
{
|
|
2857
|
+
id: "col_html_import",
|
|
2858
|
+
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
2859
|
+
styles: { padding: pad(0), backgroundColor: "" },
|
|
2860
|
+
blocks: [
|
|
2861
|
+
{
|
|
2862
|
+
id: "block_html_import",
|
|
2863
|
+
type: "html",
|
|
2864
|
+
content: inner,
|
|
2865
|
+
props: { ...DEFAULT_BLOCK_PROPS.html }
|
|
2866
|
+
}
|
|
2867
|
+
]
|
|
2868
|
+
}
|
|
2869
|
+
]
|
|
2870
|
+
}
|
|
2871
|
+
]
|
|
2872
|
+
};
|
|
2873
|
+
return doc;
|
|
2874
|
+
}
|
|
2875
|
+
function htmlToJson(html, pretty = false) {
|
|
2876
|
+
const doc = htmlToEmailDesignTemplate(html);
|
|
2877
|
+
if (!doc) return "";
|
|
2878
|
+
return pretty ? JSON.stringify(doc, null, 2) : JSON.stringify(doc);
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2188
2881
|
// src/editor/canvas/DesignCanvas.tsx
|
|
2189
|
-
import {
|
|
2882
|
+
import {
|
|
2883
|
+
useState,
|
|
2884
|
+
useRef,
|
|
2885
|
+
useCallback,
|
|
2886
|
+
useEffect,
|
|
2887
|
+
Fragment
|
|
2888
|
+
} from "react";
|
|
2190
2889
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2191
2890
|
function useLiveCountdown(endDate) {
|
|
2192
2891
|
const [diff, setDiff] = useState(0);
|
|
@@ -2325,13 +3024,15 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2325
3024
|
)
|
|
2326
3025
|
);
|
|
2327
3026
|
if (type === "html") {
|
|
2328
|
-
const
|
|
3027
|
+
const richHtml = typeof block.content === "string" ? block.content : p.content;
|
|
3028
|
+
const emptyRich = isEffectivelyEmptyRichHtml(richHtml);
|
|
2329
3029
|
return wrap(
|
|
2330
3030
|
/* @__PURE__ */ jsxs2("div", { style: { position: "relative", minHeight: emptyRich && !preview ? "2.75em" : void 0 }, children: [
|
|
3031
|
+
/* @__PURE__ */ jsx3("style", { children: RICH_HTML_CONTENT_CSS }),
|
|
2331
3032
|
/* @__PURE__ */ jsx3(
|
|
2332
3033
|
"div",
|
|
2333
3034
|
{
|
|
2334
|
-
className: "email-editor-rich-canvas",
|
|
3035
|
+
className: "email-editor-rich-canvas email-rich-html-content",
|
|
2335
3036
|
style: {
|
|
2336
3037
|
fontSize: p.fontSize,
|
|
2337
3038
|
color: p.color,
|
|
@@ -2342,7 +3043,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2342
3043
|
wordBreak: "break-word",
|
|
2343
3044
|
minHeight: emptyRich && !preview ? "2.75em" : void 0
|
|
2344
3045
|
},
|
|
2345
|
-
dangerouslySetInnerHTML: { __html:
|
|
3046
|
+
dangerouslySetInnerHTML: { __html: richHtml || "<p></p>" }
|
|
2346
3047
|
}
|
|
2347
3048
|
),
|
|
2348
3049
|
emptyRich && !preview ? /* @__PURE__ */ jsx3(
|
|
@@ -2390,7 +3091,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
|
|
|
2390
3091
|
}
|
|
2391
3092
|
}
|
|
2392
3093
|
);
|
|
2393
|
-
return wrap(/* @__PURE__ */ jsx3("div", { style: { textAlign: p.align }, children: p
|
|
3094
|
+
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
3095
|
}
|
|
2395
3096
|
if (type === "button") return wrap(
|
|
2396
3097
|
/* @__PURE__ */ jsx3("div", { style: { textAlign: p.fullWidth ? "center" : p.align }, children: /* @__PURE__ */ jsx3(
|
|
@@ -2563,7 +3264,7 @@ function NestedRowBlock({
|
|
|
2563
3264
|
);
|
|
2564
3265
|
},
|
|
2565
3266
|
children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2566
|
-
const cS = p
|
|
3267
|
+
const cS = getColumnPropsAt(p, ici);
|
|
2567
3268
|
return /* @__PURE__ */ jsx3(
|
|
2568
3269
|
"div",
|
|
2569
3270
|
{
|
|
@@ -2621,7 +3322,9 @@ function BlockItem({
|
|
|
2621
3322
|
onContextMenu,
|
|
2622
3323
|
preview,
|
|
2623
3324
|
C,
|
|
2624
|
-
Line
|
|
3325
|
+
Line,
|
|
3326
|
+
dragAsRow = false,
|
|
3327
|
+
beginRowDrag
|
|
2625
3328
|
}) {
|
|
2626
3329
|
const wrapperRef = useRef(null);
|
|
2627
3330
|
const bid = editorId ? `${editorId}-block-${cb.id}` : void 0;
|
|
@@ -2669,7 +3372,7 @@ function BlockItem({
|
|
|
2669
3372
|
{
|
|
2670
3373
|
"data-bidx": ci,
|
|
2671
3374
|
draggable: !preview,
|
|
2672
|
-
title: preview ? void 0 : "Click to select \xB7 drag to reorder or move",
|
|
3375
|
+
title: preview ? void 0 : dragAsRow ? "Click to select \xB7 drag to reorder section" : "Click to select \xB7 drag to reorder or move",
|
|
2673
3376
|
onPointerDown: (e) => {
|
|
2674
3377
|
if (preview || e.button !== 0) return;
|
|
2675
3378
|
selectThisBlock(e);
|
|
@@ -2677,6 +3380,10 @@ function BlockItem({
|
|
|
2677
3380
|
onDragStart: (e) => {
|
|
2678
3381
|
if (preview) return;
|
|
2679
3382
|
e.stopPropagation();
|
|
3383
|
+
if (dragAsRow && beginRowDrag) {
|
|
3384
|
+
beginRowDrag(e);
|
|
3385
|
+
return;
|
|
3386
|
+
}
|
|
2680
3387
|
e.dataTransfer.effectAllowed = "move";
|
|
2681
3388
|
e.dataTransfer.setData(
|
|
2682
3389
|
"application/json",
|
|
@@ -2748,7 +3455,9 @@ function Cell({
|
|
|
2748
3455
|
onDeleteContent,
|
|
2749
3456
|
onContextMenu,
|
|
2750
3457
|
preview,
|
|
2751
|
-
C
|
|
3458
|
+
C,
|
|
3459
|
+
dragAsRow = false,
|
|
3460
|
+
beginRowDrag
|
|
2752
3461
|
}) {
|
|
2753
3462
|
const [insertAt, setInsertAt] = useState(null);
|
|
2754
3463
|
const cellRef = useRef(null);
|
|
@@ -2763,6 +3472,14 @@ function Cell({
|
|
|
2763
3472
|
return els.length;
|
|
2764
3473
|
}, [blocks.length]);
|
|
2765
3474
|
const handleDragOver = (e) => {
|
|
3475
|
+
try {
|
|
3476
|
+
const raw = e.dataTransfer.getData("application/json");
|
|
3477
|
+
if (raw) {
|
|
3478
|
+
const data = JSON.parse(raw);
|
|
3479
|
+
if (data.moveRowId) return;
|
|
3480
|
+
}
|
|
3481
|
+
} catch {
|
|
3482
|
+
}
|
|
2766
3483
|
e.preventDefault();
|
|
2767
3484
|
e.stopPropagation();
|
|
2768
3485
|
setInsertAt(calcIdx(e.clientY));
|
|
@@ -2778,6 +3495,7 @@ function Cell({
|
|
|
2778
3495
|
setInsertAt(null);
|
|
2779
3496
|
try {
|
|
2780
3497
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
3498
|
+
if (data.moveRowId) return;
|
|
2781
3499
|
if (data.contentType) {
|
|
2782
3500
|
onDropContent(rowId, cellIdx, { kind: "new", contentType: data.contentType, insertAt: idx, nested: nestedPayload });
|
|
2783
3501
|
} else if (data.moveContent) {
|
|
@@ -2857,7 +3575,9 @@ function Cell({
|
|
|
2857
3575
|
onContextMenu,
|
|
2858
3576
|
preview,
|
|
2859
3577
|
C,
|
|
2860
|
-
Line
|
|
3578
|
+
Line,
|
|
3579
|
+
dragAsRow: dragAsRow && blocks.length === 1 && ci === 0,
|
|
3580
|
+
beginRowDrag
|
|
2861
3581
|
}
|
|
2862
3582
|
) }, cb.id))
|
|
2863
3583
|
]
|
|
@@ -2878,7 +3598,8 @@ function LayoutRow({
|
|
|
2878
3598
|
onLayoutContextMenu,
|
|
2879
3599
|
setSelectedRowId,
|
|
2880
3600
|
preview = false,
|
|
2881
|
-
|
|
3601
|
+
rootContentRow = false,
|
|
3602
|
+
beginRowDrag,
|
|
2882
3603
|
C
|
|
2883
3604
|
}) {
|
|
2884
3605
|
const rowStyle = {
|
|
@@ -2910,16 +3631,9 @@ function LayoutRow({
|
|
|
2910
3631
|
onContextMenu: preview || !onLayoutContextMenu ? void 0 : (e) => {
|
|
2911
3632
|
onLayoutContextMenu(e, row.id);
|
|
2912
3633
|
},
|
|
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
|
-
},
|
|
3634
|
+
style: rowStyle,
|
|
2921
3635
|
children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: row.gap }, children: row.cells.map((cellBlocks, ci) => {
|
|
2922
|
-
const cS = row
|
|
3636
|
+
const cS = getColumnPropsAt(row, ci);
|
|
2923
3637
|
return /* @__PURE__ */ jsx3(
|
|
2924
3638
|
"div",
|
|
2925
3639
|
{
|
|
@@ -2955,7 +3669,9 @@ function LayoutRow({
|
|
|
2955
3669
|
onDeleteContent,
|
|
2956
3670
|
onContextMenu,
|
|
2957
3671
|
preview,
|
|
2958
|
-
C
|
|
3672
|
+
C,
|
|
3673
|
+
dragAsRow: rootContentRow && ci === 0,
|
|
3674
|
+
beginRowDrag
|
|
2959
3675
|
}
|
|
2960
3676
|
)
|
|
2961
3677
|
},
|
|
@@ -2972,10 +3688,11 @@ var CanvasRow = ({
|
|
|
2972
3688
|
selectedRowId,
|
|
2973
3689
|
selContentMeta: rowSelContentMeta,
|
|
2974
3690
|
dragOver,
|
|
3691
|
+
draggingRowId,
|
|
2975
3692
|
C,
|
|
2976
3693
|
setDragOver,
|
|
2977
3694
|
handleRowDrop,
|
|
2978
|
-
|
|
3695
|
+
setRowDrag,
|
|
2979
3696
|
setSelectedRowId,
|
|
2980
3697
|
setSelContentId,
|
|
2981
3698
|
setSelMeta,
|
|
@@ -2988,61 +3705,168 @@ var CanvasRow = ({
|
|
|
2988
3705
|
}) => {
|
|
2989
3706
|
const rowSelected = selectedRowId === row.id;
|
|
2990
3707
|
const rid = (s) => editorId ? `${editorId}-${s}` : void 0;
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3708
|
+
const dropH = dragOver === ri || dragOver === ri + 1 ? 28 : 10;
|
|
3709
|
+
const rowBodyRef = useRef(null);
|
|
3710
|
+
const rootContentRow = isRootContentRow(row);
|
|
3711
|
+
const beginRowDrag = useCallback(
|
|
3712
|
+
(e) => {
|
|
3713
|
+
e.stopPropagation();
|
|
3714
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3715
|
+
e.dataTransfer.setData(
|
|
3716
|
+
"application/json",
|
|
3717
|
+
JSON.stringify({ moveRowId: row.id })
|
|
3718
|
+
);
|
|
3719
|
+
setRowDrag(row.id);
|
|
3720
|
+
},
|
|
3721
|
+
[row.id, setRowDrag]
|
|
3722
|
+
);
|
|
3723
|
+
const rowDragOver = useCallback(
|
|
3724
|
+
(e) => {
|
|
3725
|
+
if (!draggingRowId || draggingRowId === row.id) return;
|
|
3726
|
+
e.preventDefault();
|
|
3727
|
+
e.stopPropagation();
|
|
3728
|
+
const el = rowBodyRef.current;
|
|
3729
|
+
if (!el) {
|
|
3730
|
+
setDragOver(ri);
|
|
3731
|
+
return;
|
|
3006
3732
|
}
|
|
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
|
|
3733
|
+
const rect = el.getBoundingClientRect();
|
|
3734
|
+
setDragOver(e.clientY < rect.top + rect.height / 2 ? ri : ri + 1);
|
|
3735
|
+
},
|
|
3736
|
+
[draggingRowId, row.id, ri, setDragOver]
|
|
3737
|
+
);
|
|
3738
|
+
const rowDrop = useCallback(
|
|
3739
|
+
(e) => {
|
|
3740
|
+
if (!draggingRowId) return;
|
|
3741
|
+
e.preventDefault();
|
|
3742
|
+
e.stopPropagation();
|
|
3743
|
+
const el = rowBodyRef.current;
|
|
3744
|
+
let targetIdx = ri;
|
|
3745
|
+
if (el) {
|
|
3746
|
+
const rect = el.getBoundingClientRect();
|
|
3747
|
+
targetIdx = e.clientY < rect.top + rect.height / 2 ? ri : ri + 1;
|
|
3036
3748
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3749
|
+
handleRowDrop(targetIdx, e);
|
|
3750
|
+
},
|
|
3751
|
+
[draggingRowId, ri, handleRowDrop]
|
|
3752
|
+
);
|
|
3753
|
+
return /* @__PURE__ */ jsxs2(
|
|
3754
|
+
"div",
|
|
3755
|
+
{
|
|
3756
|
+
id: rid(`canvas-row-${row.id}`),
|
|
3757
|
+
style: { display: "flex", alignItems: "stretch", gap: 0, margin: "2px 0" },
|
|
3758
|
+
children: [
|
|
3759
|
+
/* @__PURE__ */ jsx3(
|
|
3760
|
+
"div",
|
|
3761
|
+
{
|
|
3762
|
+
draggable: true,
|
|
3763
|
+
title: "Drag to reorder section",
|
|
3764
|
+
onDragStart: beginRowDrag,
|
|
3765
|
+
onDragEnd: () => setRowDrag(null),
|
|
3766
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3767
|
+
onClick: (e) => {
|
|
3768
|
+
e.stopPropagation();
|
|
3769
|
+
setSelectedRowId(row.id);
|
|
3770
|
+
setSelContentId(null);
|
|
3771
|
+
setSelMeta(null);
|
|
3772
|
+
},
|
|
3773
|
+
onContextMenu: (e) => {
|
|
3774
|
+
e.preventDefault();
|
|
3775
|
+
e.stopPropagation();
|
|
3776
|
+
handleLayoutContextMenu(e, row.id);
|
|
3777
|
+
},
|
|
3778
|
+
style: {
|
|
3779
|
+
width: 20,
|
|
3780
|
+
flexShrink: 0,
|
|
3781
|
+
cursor: "grab",
|
|
3782
|
+
display: "flex",
|
|
3783
|
+
alignItems: "center",
|
|
3784
|
+
justifyContent: "center",
|
|
3785
|
+
color: rowSelected ? C.accent : C.muted,
|
|
3786
|
+
fontSize: 12,
|
|
3787
|
+
fontWeight: 700,
|
|
3788
|
+
letterSpacing: -3,
|
|
3789
|
+
userSelect: "none",
|
|
3790
|
+
opacity: rowSelected ? 1 : 0.5
|
|
3791
|
+
},
|
|
3792
|
+
children: "\u22EE\u22EE"
|
|
3793
|
+
}
|
|
3794
|
+
),
|
|
3795
|
+
/* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3796
|
+
/* @__PURE__ */ jsx3(
|
|
3797
|
+
"div",
|
|
3798
|
+
{
|
|
3799
|
+
id: rid(`row-drop-before-${ri}`),
|
|
3800
|
+
onDragOver: (e) => {
|
|
3801
|
+
e.preventDefault();
|
|
3802
|
+
e.stopPropagation();
|
|
3803
|
+
setDragOver(ri);
|
|
3804
|
+
},
|
|
3805
|
+
onDragLeave: () => setDragOver(null),
|
|
3806
|
+
onDrop: (e) => handleRowDrop(ri, e),
|
|
3807
|
+
style: {
|
|
3808
|
+
height: dropH,
|
|
3809
|
+
background: dragOver === ri ? `${C.accent}25` : "transparent",
|
|
3810
|
+
border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
3811
|
+
borderRadius: 4,
|
|
3812
|
+
transition: "all .12s",
|
|
3813
|
+
boxSizing: "border-box"
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
),
|
|
3817
|
+
/* @__PURE__ */ jsx3(
|
|
3818
|
+
"div",
|
|
3819
|
+
{
|
|
3820
|
+
ref: rowBodyRef,
|
|
3821
|
+
id: rid(`row-body-${row.id}`),
|
|
3822
|
+
onDragOver: rowDragOver,
|
|
3823
|
+
onDrop: rowDrop,
|
|
3824
|
+
style: {
|
|
3825
|
+
position: "relative",
|
|
3826
|
+
width: "100%",
|
|
3827
|
+
minWidth: 0,
|
|
3828
|
+
boxSizing: "border-box",
|
|
3829
|
+
outline: draggingRowId && draggingRowId !== row.id && (dragOver === ri || dragOver === ri + 1) ? `2px dashed ${C.accent}` : void 0,
|
|
3830
|
+
outlineOffset: 2,
|
|
3831
|
+
borderRadius: 6
|
|
3832
|
+
},
|
|
3833
|
+
children: /* @__PURE__ */ jsx3("div", { id: rid(`row-layout-wrap-${row.id}`), style: { width: "100%", minWidth: 0 }, children: /* @__PURE__ */ jsx3(
|
|
3834
|
+
LayoutRow,
|
|
3835
|
+
{
|
|
3836
|
+
editorId,
|
|
3837
|
+
row,
|
|
3838
|
+
selected: rowSelected,
|
|
3839
|
+
setSelectedRowId,
|
|
3840
|
+
onClick: () => {
|
|
3841
|
+
setSelectedRowId(row.id);
|
|
3842
|
+
setSelContentId(null);
|
|
3843
|
+
setSelMeta(null);
|
|
3844
|
+
},
|
|
3845
|
+
selectedContentKey: selContentId,
|
|
3846
|
+
selContentMeta: rowSelContentMeta,
|
|
3847
|
+
onSelectContent: selectContent,
|
|
3848
|
+
onDropContent: dropContent,
|
|
3849
|
+
onDeleteContent: deleteContent,
|
|
3850
|
+
onContextMenu: handleContextMenu,
|
|
3851
|
+
onLayoutContextMenu: handleLayoutContextMenu,
|
|
3852
|
+
rootContentRow,
|
|
3853
|
+
beginRowDrag,
|
|
3854
|
+
C
|
|
3855
|
+
}
|
|
3856
|
+
) })
|
|
3857
|
+
}
|
|
3858
|
+
)
|
|
3859
|
+
] })
|
|
3860
|
+
]
|
|
3861
|
+
}
|
|
3862
|
+
);
|
|
3039
3863
|
};
|
|
3040
3864
|
|
|
3041
3865
|
// src/ReactEmailEditor.tsx
|
|
3042
3866
|
import { Image as ImageIcon2, Repeat as RepeatIcon, Scaling as Scaling2, Crosshair as Crosshair2, Blend as GradientIcon2, Droplets as ColorIcon } from "lucide-react";
|
|
3043
3867
|
|
|
3044
3868
|
// src/editor/properties/PropertyEditors.tsx
|
|
3045
|
-
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo
|
|
3869
|
+
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo } from "react";
|
|
3046
3870
|
import {
|
|
3047
3871
|
AlignCenter as AlignCenter2,
|
|
3048
3872
|
AlignJustify as AlignJustify2,
|
|
@@ -3059,7 +3883,7 @@ import {
|
|
|
3059
3883
|
// src/editor/properties/TextRichEditor.tsx
|
|
3060
3884
|
import { useEffect as useEffect2, useId, useState as useState2 } from "react";
|
|
3061
3885
|
import { createPortal } from "react-dom";
|
|
3062
|
-
import { useEditor, EditorContent } from "@tiptap/react";
|
|
3886
|
+
import { useEditor, EditorContent, useEditorState } from "@tiptap/react";
|
|
3063
3887
|
import StarterKit from "@tiptap/starter-kit";
|
|
3064
3888
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
3065
3889
|
import TextAlign from "@tiptap/extension-text-align";
|
|
@@ -3157,6 +3981,10 @@ function paddingToCss(pad3, fallback) {
|
|
|
3157
3981
|
var FONT_SIZE_PX = [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28, 32, 36, 42, 48, 56, 64, 72];
|
|
3158
3982
|
var LINE_HEIGHT_OPTS = ["1", "1.15", "1.25", "1.5", "1.65", "1.75", "2", "2.5"];
|
|
3159
3983
|
var LETTER_SPACING_OPTS = ["-0.5px", "0px", "0.5px", "1px", "1.5px", "2px", "3px", "4px"];
|
|
3984
|
+
function rgbChannelToHex(n) {
|
|
3985
|
+
const v = Math.max(0, Math.min(255, Math.round(n)));
|
|
3986
|
+
return v.toString(16).padStart(2, "0");
|
|
3987
|
+
}
|
|
3160
3988
|
function hexForColorInput(color) {
|
|
3161
3989
|
if (!color || typeof color !== "string") return "#000000";
|
|
3162
3990
|
const c = color.trim();
|
|
@@ -3169,8 +3997,34 @@ function hexForColorInput(color) {
|
|
|
3169
3997
|
}
|
|
3170
3998
|
return c.slice(0, 7);
|
|
3171
3999
|
}
|
|
4000
|
+
const rgb = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
|
|
4001
|
+
if (rgb) {
|
|
4002
|
+
return `#${rgbChannelToHex(+rgb[1])}${rgbChannelToHex(+rgb[2])}${rgbChannelToHex(+rgb[3])}`;
|
|
4003
|
+
}
|
|
3172
4004
|
return "#000000";
|
|
3173
4005
|
}
|
|
4006
|
+
function textStyleColorFromEditor(editor) {
|
|
4007
|
+
const ts = editor.getAttributes("textStyle");
|
|
4008
|
+
if (ts.color && String(ts.color).trim()) return String(ts.color).trim();
|
|
4009
|
+
const stored = editor.state.storedMarks;
|
|
4010
|
+
if (stored) {
|
|
4011
|
+
for (const m of stored) {
|
|
4012
|
+
if (m.type.name === "textStyle" && m.attrs.color) {
|
|
4013
|
+
return String(m.attrs.color).trim();
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
const { from, to } = editor.state.selection;
|
|
4018
|
+
if (from === to) return null;
|
|
4019
|
+
const domAt = editor.view.domAtPos(from);
|
|
4020
|
+
let el = domAt.node.nodeType === Node.TEXT_NODE ? domAt.node.parentElement : domAt.node;
|
|
4021
|
+
while (el && el !== editor.view.dom) {
|
|
4022
|
+
const inline = el.style?.color?.trim();
|
|
4023
|
+
if (inline) return inline;
|
|
4024
|
+
el = el.parentElement;
|
|
4025
|
+
}
|
|
4026
|
+
return null;
|
|
4027
|
+
}
|
|
3174
4028
|
function parsePxSize(raw) {
|
|
3175
4029
|
if (!raw) return "";
|
|
3176
4030
|
const s = String(raw).trim();
|
|
@@ -3237,13 +4091,22 @@ function toolbarIconBtn(C, active) {
|
|
|
3237
4091
|
}
|
|
3238
4092
|
function ToolbarTypographyControls({ editor, C }) {
|
|
3239
4093
|
const sel = toolbarSelect(C);
|
|
3240
|
-
const ts =
|
|
4094
|
+
const { ts, align, textColor } = useEditorState({
|
|
4095
|
+
editor,
|
|
4096
|
+
selector: ({ editor: ed }) => {
|
|
4097
|
+
const attrs = ed.getAttributes("textStyle");
|
|
4098
|
+
return {
|
|
4099
|
+
ts: attrs,
|
|
4100
|
+
align: currentBlockTextAlign(ed),
|
|
4101
|
+
textColor: textStyleColorFromEditor(ed)
|
|
4102
|
+
};
|
|
4103
|
+
}
|
|
4104
|
+
});
|
|
3241
4105
|
const fontFamily = ts.fontFamily || "";
|
|
3242
4106
|
const sizeVal = parsePxSize(ts.fontSize);
|
|
3243
4107
|
const lhVal = normalizeLineHeightAttr(ts.lineHeight);
|
|
3244
4108
|
const lsVal = normalizeLetterSpacingAttr(ts.letterSpacing);
|
|
3245
4109
|
const lsSelectValue = LETTER_SPACING_OPTS.includes(lsVal) ? lsVal : lsVal || "";
|
|
3246
|
-
const align = currentBlockTextAlign(editor);
|
|
3247
4110
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3248
4111
|
/* @__PURE__ */ jsxs3(
|
|
3249
4112
|
"select",
|
|
@@ -3326,9 +4189,10 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3326
4189
|
{
|
|
3327
4190
|
type: "color",
|
|
3328
4191
|
title: "Text color",
|
|
3329
|
-
value: hexForColorInput(ts.color),
|
|
4192
|
+
value: hexForColorInput(textColor ?? ts.color),
|
|
3330
4193
|
onChange: (e) => {
|
|
3331
|
-
|
|
4194
|
+
const hex = e.target.value;
|
|
4195
|
+
void editor.chain().focus().setColor(hex).run();
|
|
3332
4196
|
},
|
|
3333
4197
|
style: {
|
|
3334
4198
|
width: 28,
|
|
@@ -3701,7 +4565,7 @@ function TextRichEditor({
|
|
|
3701
4565
|
}
|
|
3702
4566
|
}),
|
|
3703
4567
|
TextStyle,
|
|
3704
|
-
Color,
|
|
4568
|
+
Color.configure({ types: ["textStyle"] }),
|
|
3705
4569
|
FontFamily,
|
|
3706
4570
|
FontSize,
|
|
3707
4571
|
LineHeight,
|
|
@@ -4878,11 +5742,141 @@ function BlockSurfaceBgInspector({
|
|
|
4878
5742
|
] }) : null
|
|
4879
5743
|
] });
|
|
4880
5744
|
}
|
|
5745
|
+
function HtmlBlockRichPanel({
|
|
5746
|
+
block,
|
|
5747
|
+
onChange,
|
|
5748
|
+
C
|
|
5749
|
+
}) {
|
|
5750
|
+
const p = block.props;
|
|
5751
|
+
const { IS } = useIS(C);
|
|
5752
|
+
const [mode, setMode] = useState3("visual");
|
|
5753
|
+
const [htmlDraft, setHtmlDraft] = useState3("");
|
|
5754
|
+
useEffect3(() => {
|
|
5755
|
+
setMode("visual");
|
|
5756
|
+
}, [block.id]);
|
|
5757
|
+
const body = normalizeRichHtmlForStorage(typeof block.content === "string" ? block.content : "");
|
|
5758
|
+
const applyHtml = (html) => {
|
|
5759
|
+
const norm = normalizeRichHtmlForStorage(html);
|
|
5760
|
+
onChange({ ...block, content: norm, props: { ...p, content: norm } });
|
|
5761
|
+
};
|
|
5762
|
+
const tabBtn = (active) => ({
|
|
5763
|
+
flex: 1,
|
|
5764
|
+
padding: "7px 10px",
|
|
5765
|
+
borderRadius: 6,
|
|
5766
|
+
border: `1px solid ${C.border}`,
|
|
5767
|
+
background: active ? C.accent : C.surface,
|
|
5768
|
+
color: active ? "#ffffff" : C.text,
|
|
5769
|
+
fontSize: 12,
|
|
5770
|
+
fontWeight: 700,
|
|
5771
|
+
cursor: "pointer"
|
|
5772
|
+
});
|
|
5773
|
+
return /* @__PURE__ */ jsx6(PR, { label: "Content", C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5774
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8 }, children: [
|
|
5775
|
+
/* @__PURE__ */ jsx6(
|
|
5776
|
+
"button",
|
|
5777
|
+
{
|
|
5778
|
+
type: "button",
|
|
5779
|
+
style: tabBtn(mode === "visual"),
|
|
5780
|
+
onClick: () => {
|
|
5781
|
+
if (mode === "html") applyHtml(htmlDraft);
|
|
5782
|
+
setMode("visual");
|
|
5783
|
+
},
|
|
5784
|
+
children: "Rich text"
|
|
5785
|
+
}
|
|
5786
|
+
),
|
|
5787
|
+
/* @__PURE__ */ jsx6(
|
|
5788
|
+
"button",
|
|
5789
|
+
{
|
|
5790
|
+
type: "button",
|
|
5791
|
+
style: tabBtn(mode === "html"),
|
|
5792
|
+
onClick: () => {
|
|
5793
|
+
setHtmlDraft(body);
|
|
5794
|
+
setMode("html");
|
|
5795
|
+
},
|
|
5796
|
+
children: "HTML"
|
|
5797
|
+
}
|
|
5798
|
+
)
|
|
5799
|
+
] }),
|
|
5800
|
+
mode === "visual" ? /* @__PURE__ */ jsx6(
|
|
5801
|
+
TextRichEditor,
|
|
5802
|
+
{
|
|
5803
|
+
value: body,
|
|
5804
|
+
onChange: applyHtml,
|
|
5805
|
+
typography: {
|
|
5806
|
+
fontSize: p.fontSize,
|
|
5807
|
+
color: p.color,
|
|
5808
|
+
align: p.align,
|
|
5809
|
+
fontFamily: p.fontFamily,
|
|
5810
|
+
lineHeight: p.lineHeight,
|
|
5811
|
+
letterSpacing: p.letterSpacing,
|
|
5812
|
+
padding: p.padding
|
|
5813
|
+
},
|
|
5814
|
+
placeholder: "Write your rich content\u2026",
|
|
5815
|
+
headerTitle: "Rich editor",
|
|
5816
|
+
C
|
|
5817
|
+
},
|
|
5818
|
+
block.id
|
|
5819
|
+
) : /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
5820
|
+
/* @__PURE__ */ jsx6(
|
|
5821
|
+
"textarea",
|
|
5822
|
+
{
|
|
5823
|
+
spellCheck: false,
|
|
5824
|
+
value: htmlDraft,
|
|
5825
|
+
onChange: (e) => setHtmlDraft(e.target.value),
|
|
5826
|
+
placeholder: "<p>Your HTML\u2026</p>",
|
|
5827
|
+
style: {
|
|
5828
|
+
...IS,
|
|
5829
|
+
minHeight: 220,
|
|
5830
|
+
resize: "vertical",
|
|
5831
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
5832
|
+
fontSize: 12,
|
|
5833
|
+
lineHeight: 1.45
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
),
|
|
5837
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontSize: 10, color: C.muted, lineHeight: 1.45 }, children: [
|
|
5838
|
+
"Raw HTML. Switch to ",
|
|
5839
|
+
/* @__PURE__ */ jsx6("strong", { children: "Rich text" }),
|
|
5840
|
+
" to apply and preview in the visual editor."
|
|
5841
|
+
] }),
|
|
5842
|
+
/* @__PURE__ */ jsx6(
|
|
5843
|
+
"button",
|
|
5844
|
+
{
|
|
5845
|
+
type: "button",
|
|
5846
|
+
onClick: () => applyHtml(htmlDraft),
|
|
5847
|
+
style: {
|
|
5848
|
+
alignSelf: "flex-start",
|
|
5849
|
+
background: C.surface,
|
|
5850
|
+
border: `1px solid ${C.border}`,
|
|
5851
|
+
color: C.accent,
|
|
5852
|
+
borderRadius: 5,
|
|
5853
|
+
cursor: "pointer",
|
|
5854
|
+
fontSize: 11,
|
|
5855
|
+
padding: "6px 12px",
|
|
5856
|
+
fontWeight: 600
|
|
5857
|
+
},
|
|
5858
|
+
children: "Apply (normalize)"
|
|
5859
|
+
}
|
|
5860
|
+
)
|
|
5861
|
+
] })
|
|
5862
|
+
] }) });
|
|
5863
|
+
}
|
|
4881
5864
|
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4882
5865
|
const { IS, CI } = useIS(C);
|
|
4883
5866
|
const imageFileRef = useRef2(null);
|
|
4884
5867
|
const p = block.props;
|
|
4885
|
-
const set = (k, v) =>
|
|
5868
|
+
const set = (k, v) => {
|
|
5869
|
+
if (block.type === "html" && k === "content") {
|
|
5870
|
+
onChange({ ...block, content: v, props: { ...p, content: v } });
|
|
5871
|
+
return;
|
|
5872
|
+
}
|
|
5873
|
+
if (block.type === "html") {
|
|
5874
|
+
const body = typeof block.content === "string" ? block.content : typeof p.content === "string" ? p.content : "";
|
|
5875
|
+
onChange({ ...block, props: { ...p, [k]: v, content: body } });
|
|
5876
|
+
return;
|
|
5877
|
+
}
|
|
5878
|
+
onChange({ ...block, props: { ...p, [k]: v } });
|
|
5879
|
+
};
|
|
4886
5880
|
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
5881
|
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
5882
|
const Col = (k, lbl) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
@@ -4976,25 +5970,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
4976
5970
|
] });
|
|
4977
5971
|
case "html":
|
|
4978
5972
|
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),
|
|
5973
|
+
/* @__PURE__ */ jsx6(HtmlBlockRichPanel, { block, onChange, C }),
|
|
4998
5974
|
/* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
4999
5975
|
/* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
|
|
5000
5976
|
/* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
|
|
@@ -5014,8 +5990,25 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5014
5990
|
"Object position"
|
|
5015
5991
|
] }), 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
5992
|
Txt("alt", "Alt Text"),
|
|
5017
|
-
|
|
5018
|
-
|
|
5993
|
+
/* @__PURE__ */ jsx6(PR, { label: "Link image", C, children: /* @__PURE__ */ jsxs4("label", { style: { display: "flex", alignItems: "center", gap: 7, cursor: "pointer" }, children: [
|
|
5994
|
+
/* @__PURE__ */ jsx6(
|
|
5995
|
+
"input",
|
|
5996
|
+
{
|
|
5997
|
+
type: "checkbox",
|
|
5998
|
+
checked: !!(p.linkEnabled ?? (p.link && String(p.link).trim())),
|
|
5999
|
+
onChange: (e) => {
|
|
6000
|
+
if (e.target.checked) set("linkEnabled", true);
|
|
6001
|
+
else onChange({ ...block, props: { ...p, linkEnabled: false, link: "" } });
|
|
6002
|
+
},
|
|
6003
|
+
style: { width: 15, height: 15, accentColor: C.accent }
|
|
6004
|
+
}
|
|
6005
|
+
),
|
|
6006
|
+
/* @__PURE__ */ jsx6("span", { style: { color: C.muted, fontSize: 12 }, children: p.linkEnabled ?? (p.link && String(p.link).trim()) ? "On" : "Off" })
|
|
6007
|
+
] }) }),
|
|
6008
|
+
p.linkEnabled ?? (p.link && String(p.link).trim()) ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
6009
|
+
Txt("link", "Link URL"),
|
|
6010
|
+
Sel("linkTarget", "Open In", ["_blank", "_self"])
|
|
6011
|
+
] }) : null,
|
|
5019
6012
|
/* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
|
|
5020
6013
|
/* @__PURE__ */ jsx6(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
|
|
5021
6014
|
/* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
|
|
@@ -5365,7 +6358,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5365
6358
|
bgPosition: p.bgPosition ?? "center",
|
|
5366
6359
|
bgGradient: p.bgGradient ?? null,
|
|
5367
6360
|
cells: p.cells || [],
|
|
5368
|
-
|
|
6361
|
+
columns: getColumns(p)
|
|
5369
6362
|
},
|
|
5370
6363
|
onChange: (row) => onChange({
|
|
5371
6364
|
...block,
|
|
@@ -5383,7 +6376,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
|
5383
6376
|
bgGradient: row.bgGradient,
|
|
5384
6377
|
ratios: [...row.ratios || [1]],
|
|
5385
6378
|
cells: row.cells || [],
|
|
5386
|
-
|
|
6379
|
+
columns: row.columns ?? getColumns(row)
|
|
5387
6380
|
}
|
|
5388
6381
|
}),
|
|
5389
6382
|
onClose,
|
|
@@ -5402,8 +6395,8 @@ function inferLayoutRowBgStep(r) {
|
|
|
5402
6395
|
if (String(r.bgColor || "").trim()) return "solid";
|
|
5403
6396
|
return null;
|
|
5404
6397
|
}
|
|
5405
|
-
function inferColumnBgStep(
|
|
5406
|
-
const cs = (
|
|
6398
|
+
function inferColumnBgStep(container, selCol) {
|
|
6399
|
+
const cs = getColumnPropsAt(container, selCol);
|
|
5407
6400
|
if (cs.bgGradient) return "gradient";
|
|
5408
6401
|
if (String(cs.bgImage || "").trim()) return "image";
|
|
5409
6402
|
if (String(cs.bgColor || "").trim()) return "solid";
|
|
@@ -5415,6 +6408,10 @@ function LayoutRowEditor({
|
|
|
5415
6408
|
onClose,
|
|
5416
6409
|
onUpload,
|
|
5417
6410
|
initialCol = null,
|
|
6411
|
+
rowIndex = -1,
|
|
6412
|
+
rowCount = 1,
|
|
6413
|
+
onMoveUp,
|
|
6414
|
+
onMoveDown,
|
|
5418
6415
|
C,
|
|
5419
6416
|
/** Shown under the inspector header when editing a Layout block */
|
|
5420
6417
|
customizationHint
|
|
@@ -5427,8 +6424,8 @@ function LayoutRowEditor({
|
|
|
5427
6424
|
const [colBgPickerMode, setColBgPickerMode] = useState3(null);
|
|
5428
6425
|
const inferredRowBg = useMemo(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5429
6426
|
const inferredColBg = useMemo(
|
|
5430
|
-
() => inferColumnBgStep(row
|
|
5431
|
-
[row.columnStyles, selCol]
|
|
6427
|
+
() => inferColumnBgStep(row, selCol),
|
|
6428
|
+
[row.columns, row.columnStyles, selCol]
|
|
5432
6429
|
);
|
|
5433
6430
|
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5434
6431
|
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
@@ -5467,13 +6464,12 @@ function LayoutRowEditor({
|
|
|
5467
6464
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5468
6465
|
};
|
|
5469
6466
|
const updCol = (i, upd) => {
|
|
5470
|
-
const
|
|
5471
|
-
|
|
5472
|
-
set("columnStyles", styles);
|
|
6467
|
+
const columns = patchColumnPropsAt(row, i, upd);
|
|
6468
|
+
onChange(withColumnsOnly(row, columns));
|
|
5473
6469
|
};
|
|
5474
6470
|
const setColBgMode = (mode) => {
|
|
5475
6471
|
setColBgPickerMode(mode);
|
|
5476
|
-
const cs = (row
|
|
6472
|
+
const cs = getColumnPropsAt(row, selCol);
|
|
5477
6473
|
if (mode === "solid") {
|
|
5478
6474
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
5479
6475
|
return;
|
|
@@ -5505,7 +6501,51 @@ function LayoutRowEditor({
|
|
|
5505
6501
|
set("bgGradient", { angle: 135, colors: ["#0ea5e9", "#6366f1", "#ec4899"], stops: [0, 50, 100] });
|
|
5506
6502
|
}
|
|
5507
6503
|
};
|
|
6504
|
+
const canMoveUp = rowIndex > 0;
|
|
6505
|
+
const canMoveDown = rowIndex >= 0 && rowIndex < rowCount - 1;
|
|
5508
6506
|
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
6507
|
+
onMoveUp && onMoveDown ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [
|
|
6508
|
+
/* @__PURE__ */ jsx6(
|
|
6509
|
+
"button",
|
|
6510
|
+
{
|
|
6511
|
+
type: "button",
|
|
6512
|
+
disabled: !canMoveUp,
|
|
6513
|
+
onClick: onMoveUp,
|
|
6514
|
+
style: {
|
|
6515
|
+
flex: 1,
|
|
6516
|
+
padding: "8px 10px",
|
|
6517
|
+
fontSize: 12,
|
|
6518
|
+
fontWeight: 600,
|
|
6519
|
+
borderRadius: 8,
|
|
6520
|
+
border: `1px solid ${C.border}`,
|
|
6521
|
+
background: canMoveUp ? C.surface : C.canvas,
|
|
6522
|
+
color: canMoveUp ? C.text : C.muted,
|
|
6523
|
+
cursor: canMoveUp ? "pointer" : "not-allowed"
|
|
6524
|
+
},
|
|
6525
|
+
children: "\u2191 Move section up"
|
|
6526
|
+
}
|
|
6527
|
+
),
|
|
6528
|
+
/* @__PURE__ */ jsx6(
|
|
6529
|
+
"button",
|
|
6530
|
+
{
|
|
6531
|
+
type: "button",
|
|
6532
|
+
disabled: !canMoveDown,
|
|
6533
|
+
onClick: onMoveDown,
|
|
6534
|
+
style: {
|
|
6535
|
+
flex: 1,
|
|
6536
|
+
padding: "8px 10px",
|
|
6537
|
+
fontSize: 12,
|
|
6538
|
+
fontWeight: 600,
|
|
6539
|
+
borderRadius: 8,
|
|
6540
|
+
border: `1px solid ${C.border}`,
|
|
6541
|
+
background: canMoveDown ? C.surface : C.canvas,
|
|
6542
|
+
color: canMoveDown ? C.text : C.muted,
|
|
6543
|
+
cursor: canMoveDown ? "pointer" : "not-allowed"
|
|
6544
|
+
},
|
|
6545
|
+
children: "\u2193 Move section down"
|
|
6546
|
+
}
|
|
6547
|
+
)
|
|
6548
|
+
] }) : null,
|
|
5509
6549
|
customizationHint ? /* @__PURE__ */ jsx6(
|
|
5510
6550
|
"div",
|
|
5511
6551
|
{
|
|
@@ -5655,15 +6695,15 @@ function LayoutRowEditor({
|
|
|
5655
6695
|
/* @__PURE__ */ jsx6(
|
|
5656
6696
|
ColorSwatchGrid,
|
|
5657
6697
|
{
|
|
5658
|
-
value: (row
|
|
6698
|
+
value: getColumnPropsAt(row, selCol)?.bgColor || "",
|
|
5659
6699
|
C,
|
|
5660
6700
|
compact: true,
|
|
5661
6701
|
onPick: (hex) => updCol(selCol, { bgColor: hex === "#ffffff" ? "" : hex })
|
|
5662
6702
|
}
|
|
5663
6703
|
),
|
|
5664
6704
|
/* @__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
|
|
6705
|
+
/* @__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 }) }),
|
|
6706
|
+
/* @__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
6707
|
] })
|
|
5668
6708
|
] }) }) : null,
|
|
5669
6709
|
colBgUiStep === "image" ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
@@ -5671,7 +6711,7 @@ function LayoutRowEditor({
|
|
|
5671
6711
|
/* @__PURE__ */ jsx6(ImageIcon, { size: 13, strokeWidth: 2.25, "aria-hidden": true }),
|
|
5672
6712
|
"bg image"
|
|
5673
6713
|
] }), 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
|
|
6714
|
+
/* @__PURE__ */ jsx6("input", { type: "text", style: IS, placeholder: "https://\u2026", value: getColumnPropsAt(row, selCol)?.bgImage || "", onChange: (e) => updCol(selCol, { bgImage: e.target.value }) }),
|
|
5675
6715
|
onUpload && /* @__PURE__ */ jsxs4("div", { style: { marginTop: 0 }, children: [
|
|
5676
6716
|
/* @__PURE__ */ jsx6(
|
|
5677
6717
|
"input",
|
|
@@ -5709,18 +6749,18 @@ function LayoutRowEditor({
|
|
|
5709
6749
|
] })
|
|
5710
6750
|
] }) }),
|
|
5711
6751
|
/* @__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
|
|
6752
|
+
/* @__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
6753
|
/* @__PURE__ */ jsx6("option", { value: "auto", children: "Auto" }),
|
|
5714
6754
|
/* @__PURE__ */ jsx6("option", { value: "cover", children: "Cover" }),
|
|
5715
6755
|
/* @__PURE__ */ jsx6("option", { value: "contain", children: "Contain" })
|
|
5716
6756
|
] }) }) }),
|
|
5717
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "repeat", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: (row
|
|
6757
|
+
/* @__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
6758
|
/* @__PURE__ */ jsx6("option", { value: "no-repeat", children: "No repeat" }),
|
|
5719
6759
|
/* @__PURE__ */ jsx6("option", { value: "repeat", children: "Repeat" }),
|
|
5720
6760
|
/* @__PURE__ */ jsx6("option", { value: "repeat-x", children: "Repeat X" }),
|
|
5721
6761
|
/* @__PURE__ */ jsx6("option", { value: "repeat-y", children: "Repeat Y" })
|
|
5722
6762
|
] }) }) }),
|
|
5723
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "align", C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: (row
|
|
6763
|
+
/* @__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
6764
|
] })
|
|
5725
6765
|
] }) : null,
|
|
5726
6766
|
colBgUiStep === "gradient" ? /* @__PURE__ */ jsxs4("details", { open: true, style: { marginBottom: 12 }, children: [
|
|
@@ -5729,15 +6769,15 @@ function LayoutRowEditor({
|
|
|
5729
6769
|
DynamicGradientField,
|
|
5730
6770
|
{
|
|
5731
6771
|
label: "gradient",
|
|
5732
|
-
value: (row
|
|
6772
|
+
value: getColumnPropsAt(row, selCol)?.bgGradient,
|
|
5733
6773
|
onChange: (g) => updCol(selCol, { bgGradient: g }),
|
|
5734
6774
|
defaults: { colors: ["#0ea5e9", "#6366f1", "#ec4899"] },
|
|
5735
6775
|
C
|
|
5736
6776
|
}
|
|
5737
6777
|
) })
|
|
5738
6778
|
] }) : null,
|
|
5739
|
-
/* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: (row
|
|
5740
|
-
/* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: (row
|
|
6779
|
+
/* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: getColumnPropsAt(row, selCol).padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
|
|
6780
|
+
/* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: getColumnPropsAt(row, selCol).borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
|
|
5741
6781
|
] })
|
|
5742
6782
|
] })
|
|
5743
6783
|
] });
|
|
@@ -6509,8 +7549,8 @@ import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-ru
|
|
|
6509
7549
|
var RIGHT_RAIL_DEFAULT_W = 328;
|
|
6510
7550
|
var RIGHT_RAIL_MIN_W = 260;
|
|
6511
7551
|
var RIGHT_RAIL_MAX_W = 720;
|
|
6512
|
-
var
|
|
6513
|
-
function
|
|
7552
|
+
var ReactEmailEditorComponent = forwardRef(
|
|
7553
|
+
function ReactEmailEditor({
|
|
6514
7554
|
minHeight = "100vh",
|
|
6515
7555
|
editorId = "email-editor",
|
|
6516
7556
|
onLoad,
|
|
@@ -6534,6 +7574,11 @@ var ReactEmailEditor = forwardRef(
|
|
|
6534
7574
|
const [selContentMeta, setSelMeta] = useState6(null);
|
|
6535
7575
|
const [dragOver, setDragOver] = useState6(null);
|
|
6536
7576
|
const [draggingRowId, setDraggingRowId] = useState6(null);
|
|
7577
|
+
const draggingRowIdRef = useRef6(null);
|
|
7578
|
+
const setRowDrag = useCallback2((id) => {
|
|
7579
|
+
draggingRowIdRef.current = id;
|
|
7580
|
+
setDraggingRowId(id);
|
|
7581
|
+
}, []);
|
|
6537
7582
|
const [history, setHistory] = useState6([]);
|
|
6538
7583
|
const [future, setFuture] = useState6([]);
|
|
6539
7584
|
const [showPreviewModal, setShowPreview] = useState6(false);
|
|
@@ -6568,10 +7613,17 @@ var ReactEmailEditor = forwardRef(
|
|
|
6568
7613
|
});
|
|
6569
7614
|
const [pageBgUiStep, setPageBgUiStep] = useState6(null);
|
|
6570
7615
|
const [contentBgUiStep, setContentBgUiStep] = useState6(null);
|
|
7616
|
+
const pageBgLastSolidRef = useRef6("#f1f5f9");
|
|
7617
|
+
const pageBgIsOff = !String(settings.bgColor ?? "").trim() && !String(settings.bgImage ?? "").trim() && !settings.bgGradient;
|
|
6571
7618
|
const applyPageBgMode = (mode) => {
|
|
6572
7619
|
setPageBgUiStep(mode);
|
|
6573
7620
|
if (mode === "solid") {
|
|
6574
|
-
setSettings((s) =>
|
|
7621
|
+
setSettings((s) => {
|
|
7622
|
+
const next = { ...s, bgGradient: null, bgImage: "" };
|
|
7623
|
+
const raw = typeof next.bgColor === "string" ? next.bgColor.trim() : "";
|
|
7624
|
+
if (!raw) next.bgColor = pageBgLastSolidRef.current || "#f1f5f9";
|
|
7625
|
+
return next;
|
|
7626
|
+
});
|
|
6575
7627
|
return;
|
|
6576
7628
|
}
|
|
6577
7629
|
if (mode === "image") {
|
|
@@ -6681,7 +7733,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
6681
7733
|
return () => window.removeEventListener("keydown", handler);
|
|
6682
7734
|
}, [rows, history, future, selContentMeta]);
|
|
6683
7735
|
const buildApi = useCallback2(() => ({
|
|
6684
|
-
loadJson(input) {
|
|
7736
|
+
loadJson(input, options2) {
|
|
6685
7737
|
setJsonLoading(true);
|
|
6686
7738
|
requestAnimationFrame(() => {
|
|
6687
7739
|
requestAnimationFrame(() => {
|
|
@@ -6694,7 +7746,14 @@ var ReactEmailEditor = forwardRef(
|
|
|
6694
7746
|
return;
|
|
6695
7747
|
}
|
|
6696
7748
|
}
|
|
6697
|
-
const
|
|
7749
|
+
const coerced = coerceEmailDocumentInput(parsed);
|
|
7750
|
+
if (options2?.mode === "append") {
|
|
7751
|
+
const extra = emailDocumentToEditorRows(coerced ?? parsed);
|
|
7752
|
+
if (!extra?.length) return;
|
|
7753
|
+
setRows((prev) => [...prev, ...extra]);
|
|
7754
|
+
return;
|
|
7755
|
+
}
|
|
7756
|
+
const norm = normalizeEmailDesignInput(coerced ?? parsed);
|
|
6698
7757
|
if (!norm) return;
|
|
6699
7758
|
setRows(norm.rows);
|
|
6700
7759
|
const ns = norm.settings;
|
|
@@ -6731,6 +7790,8 @@ var ReactEmailEditor = forwardRef(
|
|
|
6731
7790
|
}, []);
|
|
6732
7791
|
const handleRowDrop = (targetIdx, e) => {
|
|
6733
7792
|
if (e) {
|
|
7793
|
+
e.preventDefault();
|
|
7794
|
+
e.stopPropagation();
|
|
6734
7795
|
try {
|
|
6735
7796
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
6736
7797
|
if (data.layoutPresetKey) {
|
|
@@ -6749,18 +7810,28 @@ var ReactEmailEditor = forwardRef(
|
|
|
6749
7810
|
} catch {
|
|
6750
7811
|
}
|
|
6751
7812
|
}
|
|
6752
|
-
|
|
7813
|
+
const moveId = draggingRowIdRef.current ?? draggingRowId ?? (() => {
|
|
7814
|
+
if (!e) return null;
|
|
7815
|
+
try {
|
|
7816
|
+
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
7817
|
+
return typeof data.moveRowId === "string" ? data.moveRowId : null;
|
|
7818
|
+
} catch {
|
|
7819
|
+
return null;
|
|
7820
|
+
}
|
|
7821
|
+
})();
|
|
7822
|
+
if (moveId) {
|
|
6753
7823
|
mutate((prev) => {
|
|
6754
|
-
const fi = prev.findIndex((r) => r.id ===
|
|
7824
|
+
const fi = prev.findIndex((r) => r.id === moveId);
|
|
6755
7825
|
if (fi === -1) return prev;
|
|
6756
7826
|
const next = [...prev];
|
|
6757
7827
|
const [m] = next.splice(fi, 1);
|
|
6758
|
-
|
|
7828
|
+
const insertAt = targetIdx > fi ? targetIdx - 1 : targetIdx;
|
|
7829
|
+
next.splice(Math.max(0, Math.min(insertAt, next.length)), 0, m);
|
|
6759
7830
|
return next;
|
|
6760
7831
|
});
|
|
6761
7832
|
}
|
|
6762
7833
|
setDragOver(null);
|
|
6763
|
-
|
|
7834
|
+
setRowDrag(null);
|
|
6764
7835
|
};
|
|
6765
7836
|
const addRow = (preset) => mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
6766
7837
|
const deleteRow = (id) => {
|
|
@@ -6890,8 +7961,27 @@ var ReactEmailEditor = forwardRef(
|
|
|
6890
7961
|
}
|
|
6891
7962
|
}
|
|
6892
7963
|
};
|
|
7964
|
+
const rootContentPreset = LAYOUT_PRESETS.find((p) => p.cols === 1) ?? LAYOUT_PRESETS[0];
|
|
7965
|
+
const insertRootContentBlock = (contentType) => {
|
|
7966
|
+
const { row: newRow, block: nb } = makeRootContentRow(contentType, rootContentPreset);
|
|
7967
|
+
const anchorRowId = selContentMeta?.rowId ?? selectedRowId ?? null;
|
|
7968
|
+
mutate((prev) => {
|
|
7969
|
+
if (anchorRowId) {
|
|
7970
|
+
const i = prev.findIndex((r) => r.id === anchorRowId);
|
|
7971
|
+
if (i >= 0) {
|
|
7972
|
+
const next = [...prev];
|
|
7973
|
+
next.splice(i + 1, 0, newRow);
|
|
7974
|
+
return next;
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
return [...prev, newRow];
|
|
7978
|
+
});
|
|
7979
|
+
setSelectedRowId(null);
|
|
7980
|
+
setSelContentId(nb.id);
|
|
7981
|
+
setSelMeta({ rowId: newRow.id, cellIdx: 0, contentIdx: 0 });
|
|
7982
|
+
};
|
|
6893
7983
|
const insertBlockFromLibrary = (contentType) => {
|
|
6894
|
-
|
|
7984
|
+
const isLayoutType = contentType === "layout" || contentType === "nestedRow";
|
|
6895
7985
|
if (selContentMeta?.inner) {
|
|
6896
7986
|
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
6897
7987
|
dropContent(rowId, cellIdx, {
|
|
@@ -6902,30 +7992,26 @@ var ReactEmailEditor = forwardRef(
|
|
|
6902
7992
|
});
|
|
6903
7993
|
return;
|
|
6904
7994
|
}
|
|
6905
|
-
if (
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
7995
|
+
if (isLayoutType) {
|
|
7996
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0 && typeof selContentMeta.contentIdx === "number" && selContentMeta.contentIdx >= 0) {
|
|
7997
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
7998
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
7999
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
8000
|
+
kind: "new",
|
|
8001
|
+
contentType,
|
|
8002
|
+
insertAt: null
|
|
8003
|
+
});
|
|
8004
|
+
return;
|
|
8005
|
+
}
|
|
6914
8006
|
}
|
|
8007
|
+
addRow(rootContentPreset);
|
|
8008
|
+
return;
|
|
6915
8009
|
}
|
|
6916
|
-
if (
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
dropContent(targetRow.id, 0, {
|
|
6920
|
-
kind: "new",
|
|
6921
|
-
contentType,
|
|
6922
|
-
insertAt: null
|
|
6923
|
-
});
|
|
6924
|
-
return;
|
|
6925
|
-
}
|
|
8010
|
+
if (!rows.length) {
|
|
8011
|
+
insertRootContentBlock(contentType);
|
|
8012
|
+
return;
|
|
6926
8013
|
}
|
|
6927
|
-
|
|
6928
|
-
dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
|
|
8014
|
+
insertRootContentBlock(contentType);
|
|
6929
8015
|
};
|
|
6930
8016
|
const deleteContent = (rowId, cellIdx, ci, inner = null) => {
|
|
6931
8017
|
if (!inner) {
|
|
@@ -7520,6 +8606,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
7520
8606
|
e.preventDefault();
|
|
7521
8607
|
try {
|
|
7522
8608
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8609
|
+
if (data.moveRowId) return;
|
|
7523
8610
|
if (data.layoutPresetKey) {
|
|
7524
8611
|
const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
|
|
7525
8612
|
if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
|
|
@@ -7574,6 +8661,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
7574
8661
|
e.stopPropagation();
|
|
7575
8662
|
try {
|
|
7576
8663
|
const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
|
|
8664
|
+
if (data.moveRowId) return;
|
|
7577
8665
|
if (data.contentType && typeof data.contentType === "string") {
|
|
7578
8666
|
insertBlockFromLibrary(data.contentType);
|
|
7579
8667
|
}
|
|
@@ -7595,10 +8683,11 @@ var ReactEmailEditor = forwardRef(
|
|
|
7595
8683
|
selectedRowId,
|
|
7596
8684
|
selContentMeta,
|
|
7597
8685
|
dragOver,
|
|
8686
|
+
draggingRowId,
|
|
7598
8687
|
C,
|
|
7599
8688
|
setDragOver,
|
|
7600
8689
|
handleRowDrop,
|
|
7601
|
-
|
|
8690
|
+
setRowDrag,
|
|
7602
8691
|
setSelectedRowId,
|
|
7603
8692
|
setSelContentId,
|
|
7604
8693
|
setSelMeta,
|
|
@@ -7616,14 +8705,20 @@ var ReactEmailEditor = forwardRef(
|
|
|
7616
8705
|
id: eid("canvas-row-drop-end"),
|
|
7617
8706
|
onDragOver: (e) => {
|
|
7618
8707
|
e.preventDefault();
|
|
8708
|
+
e.stopPropagation();
|
|
7619
8709
|
setDragOver(rows.length);
|
|
7620
8710
|
},
|
|
7621
8711
|
onDragLeave: () => setDragOver(null),
|
|
7622
|
-
onDrop: (e) =>
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
8712
|
+
onDrop: (e) => handleRowDrop(rows.length, e),
|
|
8713
|
+
style: {
|
|
8714
|
+
height: dragOver === rows.length ? 28 : 10,
|
|
8715
|
+
background: dragOver === rows.length ? `${C.accent}25` : "transparent",
|
|
8716
|
+
border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent",
|
|
8717
|
+
borderRadius: 4,
|
|
8718
|
+
transition: "all .12s",
|
|
8719
|
+
margin: "2px 0",
|
|
8720
|
+
boxSizing: "border-box"
|
|
8721
|
+
}
|
|
7627
8722
|
}
|
|
7628
8723
|
)
|
|
7629
8724
|
]
|
|
@@ -7735,6 +8830,10 @@ var ReactEmailEditor = forwardRef(
|
|
|
7735
8830
|
onClose: () => setSelectedRowId(null),
|
|
7736
8831
|
onUpload,
|
|
7737
8832
|
initialCol: selContentMeta?.rowId === selectedRowId ? selContentMeta.cellIdx : null,
|
|
8833
|
+
rowIndex: rows.findIndex((r) => r.id === selectedRowId),
|
|
8834
|
+
rowCount: rows.length,
|
|
8835
|
+
onMoveUp: () => selectedRow && moveRow(selectedRow.id, -1),
|
|
8836
|
+
onMoveDown: () => selectedRow && moveRow(selectedRow.id, 1),
|
|
7738
8837
|
C
|
|
7739
8838
|
}
|
|
7740
8839
|
) })
|
|
@@ -7911,7 +9010,40 @@ var ReactEmailEditor = forwardRef(
|
|
|
7911
9010
|
/* @__PURE__ */ jsx11("summary", { style: railSectionSummary, children: "Background" }),
|
|
7912
9011
|
/* @__PURE__ */ jsxs9("div", { style: railSectionBody, children: [
|
|
7913
9012
|
/* @__PURE__ */ jsx11("div", { style: { fontSize: 10, color: C.muted, fontWeight: 800, textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 }, children: "Page background" }),
|
|
7914
|
-
/* @__PURE__ */ jsx11(
|
|
9013
|
+
/* @__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: [
|
|
9014
|
+
/* @__PURE__ */ jsx11(
|
|
9015
|
+
"input",
|
|
9016
|
+
{
|
|
9017
|
+
type: "checkbox",
|
|
9018
|
+
checked: pageBgIsOff,
|
|
9019
|
+
onChange: (e) => {
|
|
9020
|
+
const off = e.target.checked;
|
|
9021
|
+
if (off) {
|
|
9022
|
+
const cur = String(settings.bgColor ?? "").trim();
|
|
9023
|
+
if (cur) pageBgLastSolidRef.current = cur;
|
|
9024
|
+
setPageBgUiStep(null);
|
|
9025
|
+
setSettings((s) => ({ ...s, bgColor: "", bgImage: "", bgGradient: null }));
|
|
9026
|
+
} else {
|
|
9027
|
+
setPageBgUiStep("solid");
|
|
9028
|
+
setSettings((s) => ({
|
|
9029
|
+
...s,
|
|
9030
|
+
bgColor: pageBgLastSolidRef.current || "#f1f5f9"
|
|
9031
|
+
}));
|
|
9032
|
+
}
|
|
9033
|
+
},
|
|
9034
|
+
style: { width: 15, height: 15, accentColor: C.accent, cursor: "pointer" }
|
|
9035
|
+
}
|
|
9036
|
+
),
|
|
9037
|
+
/* @__PURE__ */ jsx11("span", { style: { color: C.muted, fontSize: 12 }, children: "No page background" })
|
|
9038
|
+
] }) }),
|
|
9039
|
+
/* @__PURE__ */ jsx11(
|
|
9040
|
+
BgModeButtons,
|
|
9041
|
+
{
|
|
9042
|
+
value: pageBgIsOff ? null : pageBgUiStep,
|
|
9043
|
+
onChange: applyPageBgMode,
|
|
9044
|
+
C
|
|
9045
|
+
}
|
|
9046
|
+
),
|
|
7915
9047
|
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
9048
|
pageBgUiStep === "solid" ? /* @__PURE__ */ jsx11(
|
|
7917
9049
|
ColorField,
|
|
@@ -7921,7 +9053,10 @@ var ReactEmailEditor = forwardRef(
|
|
|
7921
9053
|
"bg color"
|
|
7922
9054
|
] }),
|
|
7923
9055
|
value: settings.bgColor,
|
|
7924
|
-
onChange: (v) =>
|
|
9056
|
+
onChange: (v) => {
|
|
9057
|
+
pageBgLastSolidRef.current = v;
|
|
9058
|
+
setSettings((s) => ({ ...s, bgColor: v }));
|
|
9059
|
+
},
|
|
7925
9060
|
placeholder: "#f1f5f9",
|
|
7926
9061
|
C
|
|
7927
9062
|
}
|
|
@@ -8162,79 +9297,17 @@ var ReactEmailEditor = forwardRef(
|
|
|
8162
9297
|
);
|
|
8163
9298
|
}
|
|
8164
9299
|
);
|
|
8165
|
-
|
|
8166
|
-
// src/lib/htmlToEmailDesign.ts
|
|
8167
|
-
var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
|
|
8168
|
-
function extractHtmlForDesign(html) {
|
|
8169
|
-
const t = String(html ?? "").trim();
|
|
8170
|
-
if (!t) return "";
|
|
8171
|
-
const head = t.slice(0, 800).toLowerCase();
|
|
8172
|
-
if (!head.includes("<html") && !head.includes("<!doctype")) {
|
|
8173
|
-
return normalizeRichHtmlForStorage(t);
|
|
8174
|
-
}
|
|
8175
|
-
try {
|
|
8176
|
-
const doc = new DOMParser().parseFromString(t, "text/html");
|
|
8177
|
-
const body = doc.body;
|
|
8178
|
-
if (!body) return normalizeRichHtmlForStorage(t);
|
|
8179
|
-
return normalizeRichHtmlForStorage(body.innerHTML);
|
|
8180
|
-
} catch {
|
|
8181
|
-
return normalizeRichHtmlForStorage(t);
|
|
8182
|
-
}
|
|
8183
|
-
}
|
|
8184
|
-
function htmlToEmailDesignTemplate(html) {
|
|
8185
|
-
const inner = extractHtmlForDesign(html);
|
|
8186
|
-
if (!inner) return null;
|
|
8187
|
-
const doc = {
|
|
8188
|
-
type: "email_document",
|
|
8189
|
-
settings: {
|
|
8190
|
-
width: 600,
|
|
8191
|
-
backgroundColor: "#f1f5f9",
|
|
8192
|
-
contentBackgroundColor: "#ffffff",
|
|
8193
|
-
fontFamily: "Arial, Helvetica, sans-serif",
|
|
8194
|
-
lineHeightBase: 1.6,
|
|
8195
|
-
color: "#111827",
|
|
8196
|
-
responsive: true,
|
|
8197
|
-
rtl: false
|
|
8198
|
-
},
|
|
8199
|
-
rows: [
|
|
8200
|
-
{
|
|
8201
|
-
id: "row_html_import",
|
|
8202
|
-
type: "row",
|
|
8203
|
-
layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
|
|
8204
|
-
styles: {
|
|
8205
|
-
backgroundColor: "#ffffff",
|
|
8206
|
-
backgroundRepeat: "no-repeat",
|
|
8207
|
-
backgroundSize: "cover",
|
|
8208
|
-
padding: pad(24),
|
|
8209
|
-
textAlign: "left"
|
|
8210
|
-
},
|
|
8211
|
-
columns: [
|
|
8212
|
-
{
|
|
8213
|
-
id: "col_html_import",
|
|
8214
|
-
layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
|
|
8215
|
-
styles: { padding: pad(0), backgroundColor: "" },
|
|
8216
|
-
blocks: [
|
|
8217
|
-
{
|
|
8218
|
-
id: "block_html_import",
|
|
8219
|
-
type: "html",
|
|
8220
|
-
content: { html: inner },
|
|
8221
|
-
styles: {}
|
|
8222
|
-
}
|
|
8223
|
-
]
|
|
8224
|
-
}
|
|
8225
|
-
]
|
|
8226
|
-
}
|
|
8227
|
-
]
|
|
8228
|
-
};
|
|
8229
|
-
return doc;
|
|
8230
|
-
}
|
|
9300
|
+
var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml, htmlToJson });
|
|
8231
9301
|
export {
|
|
8232
9302
|
PreviewModal as EmailPreviewModal,
|
|
8233
|
-
ReactEmailEditor,
|
|
9303
|
+
ReactEmailEditor2 as ReactEmailEditor,
|
|
8234
9304
|
base64ToUtf8,
|
|
9305
|
+
canonicalizeEmailDocument,
|
|
9306
|
+
coerceEmailDocumentInput,
|
|
8235
9307
|
DEVICES as emailPreviewDevices,
|
|
8236
9308
|
extractHtmlForDesign,
|
|
8237
9309
|
htmlToEmailDesignTemplate,
|
|
9310
|
+
htmlToJson,
|
|
8238
9311
|
jsonToHtml,
|
|
8239
9312
|
utf8ToBase64
|
|
8240
9313
|
};
|