react-email-studio 3.0.0 → 3.2.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 +27 -0
- package/README.md +5 -1
- package/TUTORIAL.md +1 -2
- package/dist/index.cjs +209 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -4
- package/dist/index.d.ts +0 -4
- package/dist/index.js +215 -140
- package/dist/index.js.map +1 -1
- package/package.json +63 -62
package/dist/index.d.cts
CHANGED
|
@@ -108,10 +108,6 @@ type ReactEmailEditorOptions = EmailHtmlOptions & {
|
|
|
108
108
|
customThemes?: Record<string, ThemeColors>;
|
|
109
109
|
};
|
|
110
110
|
locale?: string;
|
|
111
|
-
mergeTags?: {
|
|
112
|
-
name: string;
|
|
113
|
-
value: string;
|
|
114
|
-
}[];
|
|
115
111
|
features?: {
|
|
116
112
|
autoSave?: {
|
|
117
113
|
enabled?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -108,10 +108,6 @@ type ReactEmailEditorOptions = EmailHtmlOptions & {
|
|
|
108
108
|
customThemes?: Record<string, ThemeColors>;
|
|
109
109
|
};
|
|
110
110
|
locale?: string;
|
|
111
|
-
mergeTags?: {
|
|
112
|
-
name: string;
|
|
113
|
-
value: string;
|
|
114
|
-
}[];
|
|
115
111
|
features?: {
|
|
116
112
|
autoSave?: {
|
|
117
113
|
enabled?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
useRef as useRef6,
|
|
5
5
|
useEffect as useEffect6,
|
|
6
6
|
useCallback as useCallback2,
|
|
7
|
-
useMemo as
|
|
7
|
+
useMemo as useMemo3,
|
|
8
8
|
forwardRef,
|
|
9
9
|
useImperativeHandle,
|
|
10
10
|
Fragment as Fragment6
|
|
@@ -120,7 +120,6 @@ var I18N = {
|
|
|
120
120
|
delete: "Delete",
|
|
121
121
|
moveUp: "Move Up",
|
|
122
122
|
moveDown: "Move Down",
|
|
123
|
-
mergeTags: "Merge Tags",
|
|
124
123
|
autoSaved: "Auto-saved",
|
|
125
124
|
search: "Search blocks\u2026",
|
|
126
125
|
zoomIn: "Zoom In",
|
|
@@ -138,7 +137,8 @@ var I18N = {
|
|
|
138
137
|
blockPaletteGroupWidgets: "Widgets",
|
|
139
138
|
closePanel: "Close",
|
|
140
139
|
canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
|
|
141
|
-
emailContentSettings: "Email content"
|
|
140
|
+
emailContentSettings: "Email content",
|
|
141
|
+
loadingDesign: "Loading design\u2026"
|
|
142
142
|
},
|
|
143
143
|
fr: {
|
|
144
144
|
layouts: "Mises en page",
|
|
@@ -178,7 +178,6 @@ var I18N = {
|
|
|
178
178
|
delete: "Supprimer",
|
|
179
179
|
moveUp: "Monter",
|
|
180
180
|
moveDown: "Descendre",
|
|
181
|
-
mergeTags: "Balises fusion",
|
|
182
181
|
autoSaved: "Auto-sauvegard\xE9",
|
|
183
182
|
search: "Rechercher\u2026",
|
|
184
183
|
colorScheme: "Sch\xE9ma de couleurs",
|
|
@@ -191,6 +190,7 @@ var I18N = {
|
|
|
191
190
|
blockPaletteGroupWidgets: "Widgets",
|
|
192
191
|
closePanel: "Fermer",
|
|
193
192
|
canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
|
|
193
|
+
loadingDesign: "Chargement du design\u2026",
|
|
194
194
|
emailContentSettings: "Contenu de l\u2019e-mail",
|
|
195
195
|
zoomIn: "Zoom +",
|
|
196
196
|
zoomOut: "Zoom -",
|
|
@@ -237,7 +237,6 @@ var I18N = {
|
|
|
237
237
|
delete: "L\xF6schen",
|
|
238
238
|
moveUp: "Nach oben",
|
|
239
239
|
moveDown: "Nach unten",
|
|
240
|
-
mergeTags: "Merge-Tags",
|
|
241
240
|
autoSaved: "Auto-gespeichert",
|
|
242
241
|
search: "Bl\xF6cke suchen\u2026",
|
|
243
242
|
colorScheme: "Farbschema",
|
|
@@ -250,6 +249,7 @@ var I18N = {
|
|
|
250
249
|
blockPaletteGroupWidgets: "Widgets",
|
|
251
250
|
closePanel: "Schlie\xDFen",
|
|
252
251
|
canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
|
|
252
|
+
loadingDesign: "Design wird geladen\u2026",
|
|
253
253
|
emailContentSettings: "E-Mail-Inhalt",
|
|
254
254
|
zoomIn: "Zoom +",
|
|
255
255
|
zoomOut: "Zoom -",
|
|
@@ -294,7 +294,6 @@ var I18N = {
|
|
|
294
294
|
delete: "Eliminar",
|
|
295
295
|
moveUp: "Subir",
|
|
296
296
|
moveDown: "Bajar",
|
|
297
|
-
mergeTags: "Etiquetas de fusi\xF3n",
|
|
298
297
|
autoSaved: "Auto-guardado",
|
|
299
298
|
search: "Buscar bloques\u2026",
|
|
300
299
|
zoomIn: "Zoom +",
|
|
@@ -310,6 +309,7 @@ var I18N = {
|
|
|
310
309
|
blockPaletteGroupWidgets: "Widgets",
|
|
311
310
|
closePanel: "Cerrar",
|
|
312
311
|
canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
|
|
312
|
+
loadingDesign: "Cargando dise\xF1o\u2026",
|
|
313
313
|
emailContentSettings: "Contenido del correo",
|
|
314
314
|
resizeSidebar: "Redimensionar panel"
|
|
315
315
|
}
|
|
@@ -2079,13 +2079,37 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2079
2079
|
}
|
|
2080
2080
|
return mapBlockToInternal(r, layoutDepth);
|
|
2081
2081
|
}
|
|
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
|
+
}
|
|
2082
2102
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2083
2103
|
const exp = b.props;
|
|
2084
2104
|
if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
|
|
2085
2105
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2106
|
+
const columnStylesFromContent = block.props.columnStyles;
|
|
2086
2107
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2087
2108
|
if (hb) {
|
|
2088
2109
|
block.props = hb.props;
|
|
2110
|
+
const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
|
|
2111
|
+
if (merged) block.props.columnStyles = merged;
|
|
2112
|
+
else delete block.props.columnStyles;
|
|
2089
2113
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2090
2114
|
}
|
|
2091
2115
|
return;
|
|
@@ -2178,9 +2202,8 @@ function useLiveCountdown(endDate) {
|
|
|
2178
2202
|
const s = Math.floor(diff % 6e4 / 1e3);
|
|
2179
2203
|
return [d, h, m, s];
|
|
2180
2204
|
}
|
|
2181
|
-
function editChromeBorder(
|
|
2182
|
-
|
|
2183
|
-
return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
|
|
2205
|
+
function editChromeBorder(_C, _preview) {
|
|
2206
|
+
return {};
|
|
2184
2207
|
}
|
|
2185
2208
|
function blockLinkCaptureHandler(preview, e) {
|
|
2186
2209
|
if (preview) return;
|
|
@@ -2482,6 +2505,8 @@ function NestedRowBlock({
|
|
|
2482
2505
|
rowId,
|
|
2483
2506
|
cellIdx,
|
|
2484
2507
|
parentBlockIdx,
|
|
2508
|
+
/** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
|
|
2509
|
+
selectionInner,
|
|
2485
2510
|
editorId,
|
|
2486
2511
|
selectedKey,
|
|
2487
2512
|
selContentMeta,
|
|
@@ -2493,7 +2518,7 @@ function NestedRowBlock({
|
|
|
2493
2518
|
C
|
|
2494
2519
|
}) {
|
|
2495
2520
|
const p = block.props;
|
|
2496
|
-
const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner &&
|
|
2521
|
+
const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selectedKey === block.id && (selectionInner == null ? selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner : selContentMeta.contentIdx === parentBlockIdx && selContentMeta.inner && selContentMeta.inner.cellIdx === selectionInner.cellIdx && selContentMeta.inner.contentIdx === selectionInner.contentIdx);
|
|
2497
2522
|
const rowStyle = {
|
|
2498
2523
|
padding: p.padding ?? 8,
|
|
2499
2524
|
position: "relative",
|
|
@@ -2517,7 +2542,13 @@ function NestedRowBlock({
|
|
|
2517
2542
|
style: rowStyle,
|
|
2518
2543
|
onClick: preview ? void 0 : (e) => {
|
|
2519
2544
|
e.stopPropagation();
|
|
2520
|
-
onSelectContent(
|
|
2545
|
+
onSelectContent(
|
|
2546
|
+
block.id,
|
|
2547
|
+
rowId,
|
|
2548
|
+
cellIdx,
|
|
2549
|
+
parentBlockIdx,
|
|
2550
|
+
selectionInner ?? null
|
|
2551
|
+
);
|
|
2521
2552
|
},
|
|
2522
2553
|
children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2523
2554
|
const cS = p.columnStyles && p.columnStyles[ici] || {};
|
|
@@ -2583,7 +2614,13 @@ function BlockItem({
|
|
|
2583
2614
|
if (!onSelectContent) return;
|
|
2584
2615
|
e.stopPropagation();
|
|
2585
2616
|
if (isSplitLayoutBlock(cb)) {
|
|
2586
|
-
onSelectContent(
|
|
2617
|
+
onSelectContent(
|
|
2618
|
+
cb.id,
|
|
2619
|
+
rowId,
|
|
2620
|
+
cellIdx,
|
|
2621
|
+
nest ? nest.parentBlockIdx : ci,
|
|
2622
|
+
nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
|
|
2623
|
+
);
|
|
2587
2624
|
} else {
|
|
2588
2625
|
onSelectContent(
|
|
2589
2626
|
cb.id,
|
|
@@ -2654,6 +2691,7 @@ function BlockItem({
|
|
|
2654
2691
|
rowId,
|
|
2655
2692
|
cellIdx,
|
|
2656
2693
|
parentBlockIdx: nest ? nest.parentBlockIdx : ci,
|
|
2694
|
+
selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
|
|
2657
2695
|
editorId,
|
|
2658
2696
|
selectedKey,
|
|
2659
2697
|
selContentMeta,
|
|
@@ -2961,7 +2999,7 @@ var CanvasRow = ({
|
|
|
2961
2999
|
import { Image as ImageIcon2, Repeat as RepeatIcon, Scaling as Scaling2, Crosshair as Crosshair2, Blend as GradientIcon2, Droplets as ColorIcon } from "lucide-react";
|
|
2962
3000
|
|
|
2963
3001
|
// src/editor/properties/PropertyEditors.tsx
|
|
2964
|
-
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, Fragment as Fragment3 } from "react";
|
|
3002
|
+
import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo, Fragment as Fragment3 } from "react";
|
|
2965
3003
|
import {
|
|
2966
3004
|
AlignCenter as AlignCenter2,
|
|
2967
3005
|
AlignJustify as AlignJustify2,
|
|
@@ -3282,11 +3320,8 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3282
3320
|
}
|
|
3283
3321
|
function FormattingToolbar({
|
|
3284
3322
|
editor,
|
|
3285
|
-
C
|
|
3286
|
-
mergeTags,
|
|
3287
|
-
onMergeTagInsert
|
|
3323
|
+
C
|
|
3288
3324
|
}) {
|
|
3289
|
-
const [mergeSel, setMergeSel] = useState2("");
|
|
3290
3325
|
const [linkPanelOpen, setLinkPanelOpen] = useState2(false);
|
|
3291
3326
|
const [linkUrlValue, setLinkUrlValue] = useState2("");
|
|
3292
3327
|
const ibtn = (active) => toolbarIconBtn(C, active);
|
|
@@ -3606,8 +3641,6 @@ function TextRichEditor({
|
|
|
3606
3641
|
value,
|
|
3607
3642
|
onChange,
|
|
3608
3643
|
typography,
|
|
3609
|
-
mergeTags,
|
|
3610
|
-
onMergeTagInsert,
|
|
3611
3644
|
placeholder = "Write your message\u2026",
|
|
3612
3645
|
headerTitle = "Rich editor",
|
|
3613
3646
|
C
|
|
@@ -3751,15 +3784,7 @@ function TextRichEditor({
|
|
|
3751
3784
|
]
|
|
3752
3785
|
}
|
|
3753
3786
|
),
|
|
3754
|
-
/* @__PURE__ */ jsx4(
|
|
3755
|
-
FormattingToolbar,
|
|
3756
|
-
{
|
|
3757
|
-
editor,
|
|
3758
|
-
C,
|
|
3759
|
-
mergeTags,
|
|
3760
|
-
onMergeTagInsert
|
|
3761
|
-
}
|
|
3762
|
-
),
|
|
3787
|
+
/* @__PURE__ */ jsx4(FormattingToolbar, { editor, C }),
|
|
3763
3788
|
/* @__PURE__ */ jsx4("style", { children: proseStyle }),
|
|
3764
3789
|
/* @__PURE__ */ jsx4(
|
|
3765
3790
|
"div",
|
|
@@ -3810,15 +3835,7 @@ function TextRichEditor({
|
|
|
3810
3835
|
background: C.inputBg
|
|
3811
3836
|
},
|
|
3812
3837
|
children: [
|
|
3813
|
-
/* @__PURE__ */ jsx4(
|
|
3814
|
-
FormattingToolbar,
|
|
3815
|
-
{
|
|
3816
|
-
editor,
|
|
3817
|
-
C,
|
|
3818
|
-
mergeTags,
|
|
3819
|
-
onMergeTagInsert
|
|
3820
|
-
}
|
|
3821
|
-
),
|
|
3838
|
+
/* @__PURE__ */ jsx4(FormattingToolbar, { editor, C }),
|
|
3822
3839
|
/* @__PURE__ */ jsx4("style", { children: proseStyle }),
|
|
3823
3840
|
/* @__PURE__ */ jsx4(
|
|
3824
3841
|
"div",
|
|
@@ -4818,7 +4835,7 @@ function BlockSurfaceBgInspector({
|
|
|
4818
4835
|
] }) : null
|
|
4819
4836
|
] });
|
|
4820
4837
|
}
|
|
4821
|
-
function ContentBlockEditor({ block, onChange,
|
|
4838
|
+
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4822
4839
|
const { IS, CI } = useIS(C);
|
|
4823
4840
|
const imageFileRef = useRef2(null);
|
|
4824
4841
|
const p = block.props;
|
|
@@ -4837,7 +4854,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4837
4854
|
/* @__PURE__ */ jsx6("input", { type: "checkbox", checked: !!p[k], onChange: (e) => set(k, e.target.checked), style: { width: 15, height: 15, accentColor: C.accent } }),
|
|
4838
4855
|
/* @__PURE__ */ jsx6("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
|
|
4839
4856
|
] }) });
|
|
4840
|
-
const TagPicker = (_field) => null;
|
|
4841
4857
|
const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
|
|
4842
4858
|
const ImgUpload = (key) => {
|
|
4843
4859
|
if (!onUpload) return null;
|
|
@@ -4891,7 +4907,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4891
4907
|
] });
|
|
4892
4908
|
case "text":
|
|
4893
4909
|
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
4894
|
-
/* @__PURE__ */ jsx6(PR, { label: "Content
|
|
4910
|
+
/* @__PURE__ */ jsx6(PR, { label: "Content", C, children: /* @__PURE__ */ jsx6(
|
|
4895
4911
|
"textarea",
|
|
4896
4912
|
{
|
|
4897
4913
|
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
@@ -4902,24 +4918,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4902
4918
|
},
|
|
4903
4919
|
block.id
|
|
4904
4920
|
) }),
|
|
4905
|
-
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ jsx6(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ jsxs4(
|
|
4906
|
-
"select",
|
|
4907
|
-
{
|
|
4908
|
-
style: IS,
|
|
4909
|
-
defaultValue: "",
|
|
4910
|
-
onChange: (e) => {
|
|
4911
|
-
const v = e.target.value;
|
|
4912
|
-
if (v) {
|
|
4913
|
-
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4914
|
-
e.target.value = "";
|
|
4915
|
-
}
|
|
4916
|
-
},
|
|
4917
|
-
children: [
|
|
4918
|
-
/* @__PURE__ */ jsx6("option", { value: "", children: "Choose\u2026" }),
|
|
4919
|
-
mergeTags.map((t) => /* @__PURE__ */ jsx6("option", { value: t.value, children: t.name }, t.name))
|
|
4920
|
-
]
|
|
4921
|
-
}
|
|
4922
|
-
) }) : null,
|
|
4923
4921
|
/* @__PURE__ */ jsx6(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
|
|
4924
4922
|
Col("color", "Color"),
|
|
4925
4923
|
/* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
@@ -4949,8 +4947,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4949
4947
|
letterSpacing: p.letterSpacing,
|
|
4950
4948
|
padding: p.padding
|
|
4951
4949
|
},
|
|
4952
|
-
mergeTags,
|
|
4953
|
-
onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
|
|
4954
4950
|
placeholder: "Write your rich content\u2026",
|
|
4955
4951
|
headerTitle: "Rich editor",
|
|
4956
4952
|
C
|
|
@@ -5357,6 +5353,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
5357
5353
|
return null;
|
|
5358
5354
|
}
|
|
5359
5355
|
}
|
|
5356
|
+
function inferLayoutRowBgStep(r) {
|
|
5357
|
+
if (r.bgGradient) return "gradient";
|
|
5358
|
+
if (String(r.bgImage || "").trim()) return "image";
|
|
5359
|
+
if (String(r.bgColor || "").trim()) return "solid";
|
|
5360
|
+
return null;
|
|
5361
|
+
}
|
|
5362
|
+
function inferColumnBgStep(columnStyles, selCol) {
|
|
5363
|
+
const cs = (columnStyles || {})[selCol] || {};
|
|
5364
|
+
if (cs.bgGradient) return "gradient";
|
|
5365
|
+
if (String(cs.bgImage || "").trim()) return "image";
|
|
5366
|
+
if (String(cs.bgColor || "").trim()) return "solid";
|
|
5367
|
+
return null;
|
|
5368
|
+
}
|
|
5360
5369
|
function LayoutRowEditor({
|
|
5361
5370
|
row,
|
|
5362
5371
|
onChange,
|
|
@@ -5368,49 +5377,37 @@ function LayoutRowEditor({
|
|
|
5368
5377
|
customizationHint
|
|
5369
5378
|
}) {
|
|
5370
5379
|
const { IS, CI } = useIS(C);
|
|
5371
|
-
const
|
|
5372
|
-
const
|
|
5373
|
-
const [
|
|
5380
|
+
const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
|
|
5381
|
+
const ratioList = row.ratios?.length ? row.ratios : [1];
|
|
5382
|
+
const [selCol, setSelCol] = useState3(0);
|
|
5383
|
+
const [rowBgPickerMode, setRowBgPickerMode] = useState3(null);
|
|
5384
|
+
const [colBgPickerMode, setColBgPickerMode] = useState3(null);
|
|
5385
|
+
const inferredRowBg = useMemo(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5386
|
+
const inferredColBg = useMemo(
|
|
5387
|
+
() => inferColumnBgStep(row.columnStyles, selCol),
|
|
5388
|
+
[row.columnStyles, selCol]
|
|
5389
|
+
);
|
|
5390
|
+
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5391
|
+
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
5374
5392
|
useEffect3(() => {
|
|
5375
|
-
if (initialCol !== null && initialCol !== void 0) {
|
|
5376
|
-
setSelCol(initialCol);
|
|
5393
|
+
if (initialCol !== null && initialCol !== void 0 && initialCol >= 0) {
|
|
5394
|
+
setSelCol(Math.min(initialCol, colCount - 1));
|
|
5395
|
+
} else {
|
|
5396
|
+
setSelCol((c) => Math.min(Math.max(0, c), colCount - 1));
|
|
5377
5397
|
}
|
|
5378
|
-
}, [initialCol]);
|
|
5398
|
+
}, [initialCol, row.id, colCount]);
|
|
5379
5399
|
useEffect3(() => {
|
|
5380
|
-
|
|
5381
|
-
setRowBgUiStep("gradient");
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
if (String(row.bgImage || "").trim()) {
|
|
5385
|
-
setRowBgUiStep("image");
|
|
5386
|
-
return;
|
|
5387
|
-
}
|
|
5388
|
-
if (String(row.bgColor || "").trim()) {
|
|
5389
|
-
setRowBgUiStep("solid");
|
|
5390
|
-
return;
|
|
5391
|
-
}
|
|
5392
|
-
setRowBgUiStep(null);
|
|
5400
|
+
setRowBgPickerMode(null);
|
|
5393
5401
|
}, [row.id]);
|
|
5394
5402
|
useEffect3(() => {
|
|
5395
|
-
|
|
5396
|
-
setColBgUiStep(null);
|
|
5397
|
-
return;
|
|
5398
|
-
}
|
|
5399
|
-
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5400
|
-
if (cs.bgGradient) {
|
|
5401
|
-
setColBgUiStep("gradient");
|
|
5402
|
-
return;
|
|
5403
|
-
}
|
|
5404
|
-
if (String(cs.bgImage || "").trim()) {
|
|
5405
|
-
setColBgUiStep("image");
|
|
5406
|
-
return;
|
|
5407
|
-
}
|
|
5408
|
-
if (String(cs.bgColor || "").trim()) {
|
|
5409
|
-
setColBgUiStep("solid");
|
|
5410
|
-
return;
|
|
5411
|
-
}
|
|
5412
|
-
setColBgUiStep(null);
|
|
5403
|
+
setColBgPickerMode(null);
|
|
5413
5404
|
}, [selCol, row.id]);
|
|
5405
|
+
useEffect3(() => {
|
|
5406
|
+
if (inferredRowBg) setRowBgPickerMode(null);
|
|
5407
|
+
}, [inferredRowBg]);
|
|
5408
|
+
useEffect3(() => {
|
|
5409
|
+
if (inferredColBg) setColBgPickerMode(null);
|
|
5410
|
+
}, [inferredColBg]);
|
|
5414
5411
|
const set = (k, v) => onChange({ ...row, [k]: v });
|
|
5415
5412
|
const applyColumnCount = (n) => {
|
|
5416
5413
|
const prevCells = [...row.cells || []];
|
|
@@ -5426,16 +5423,13 @@ function LayoutRowEditor({
|
|
|
5426
5423
|
}
|
|
5427
5424
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5428
5425
|
};
|
|
5429
|
-
const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
|
|
5430
|
-
const ratioList = row.ratios?.length ? row.ratios : [1];
|
|
5431
5426
|
const updCol = (i, upd) => {
|
|
5432
5427
|
const styles = { ...row.columnStyles || {} };
|
|
5433
5428
|
styles[i] = { ...styles[i] || {}, ...upd };
|
|
5434
5429
|
set("columnStyles", styles);
|
|
5435
5430
|
};
|
|
5436
5431
|
const setColBgMode = (mode) => {
|
|
5437
|
-
|
|
5438
|
-
setColBgUiStep(mode);
|
|
5432
|
+
setColBgPickerMode(mode);
|
|
5439
5433
|
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5440
5434
|
if (mode === "solid") {
|
|
5441
5435
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
@@ -5453,7 +5447,7 @@ function LayoutRowEditor({
|
|
|
5453
5447
|
const total = ratioList.reduce((a, b) => a + b, 0) || 1;
|
|
5454
5448
|
const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
|
|
5455
5449
|
const setRowBgMode = (mode) => {
|
|
5456
|
-
|
|
5450
|
+
setRowBgPickerMode(mode);
|
|
5457
5451
|
if (mode === "solid") {
|
|
5458
5452
|
set("bgGradient", null);
|
|
5459
5453
|
set("bgImage", "");
|
|
@@ -5611,7 +5605,7 @@ function LayoutRowEditor({
|
|
|
5611
5605
|
},
|
|
5612
5606
|
i
|
|
5613
5607
|
)) }),
|
|
5614
|
-
|
|
5608
|
+
colCount > 0 && /* @__PURE__ */ jsxs4("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
|
|
5615
5609
|
/* @__PURE__ */ jsx6(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
|
|
5616
5610
|
colBgUiStep === null ? /* @__PURE__ */ jsx6("div", { style: { fontSize: 11, color: C.muted, marginBottom: 10, lineHeight: 1.45 }, children: "Choose Color, Gradient, or Image \u2014 then edit column background details." }) : null,
|
|
5617
5611
|
colBgUiStep === "solid" ? /* @__PURE__ */ jsx6(PR, { label: `col ${selCol + 1} bg`, C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
@@ -5710,7 +5704,7 @@ function LayoutRowEditor({
|
|
|
5710
5704
|
import {
|
|
5711
5705
|
useState as useState4,
|
|
5712
5706
|
useEffect as useEffect4,
|
|
5713
|
-
useMemo,
|
|
5707
|
+
useMemo as useMemo2,
|
|
5714
5708
|
useRef as useRef3,
|
|
5715
5709
|
useLayoutEffect
|
|
5716
5710
|
} from "react";
|
|
@@ -6088,8 +6082,8 @@ function PreviewModal({
|
|
|
6088
6082
|
const iframeHDesktop = "min(720px, calc(100vh - 200px))";
|
|
6089
6083
|
const mobileIframeW = previewIframeWidthPx("mobile", mobileVariant);
|
|
6090
6084
|
const tabletIframeW = previewIframeWidthPx("tablet");
|
|
6091
|
-
const srcDocMobile =
|
|
6092
|
-
const srcDocTablet =
|
|
6085
|
+
const srcDocMobile = useMemo2(() => previewEmailSrcDoc(html, mobileIframeW), [html, mobileIframeW]);
|
|
6086
|
+
const srcDocTablet = useMemo2(() => previewEmailSrcDoc(html, tabletIframeW), [html, tabletIframeW]);
|
|
6093
6087
|
const IframeContent = ({
|
|
6094
6088
|
srcDoc,
|
|
6095
6089
|
title: iframeTitle,
|
|
@@ -6485,7 +6479,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
6485
6479
|
style = {}
|
|
6486
6480
|
}, ref) {
|
|
6487
6481
|
const opt = options;
|
|
6488
|
-
const mergedThemes =
|
|
6482
|
+
const mergedThemes = useMemo3(
|
|
6489
6483
|
() => ({ ...THEMES, ...opt.appearance?.customThemes || {} }),
|
|
6490
6484
|
[opt.appearance?.customThemes]
|
|
6491
6485
|
);
|
|
@@ -6503,11 +6497,11 @@ var ReactEmailEditor = forwardRef(
|
|
|
6503
6497
|
const [previewDevice, setPreviewDevice] = useState6("desktop");
|
|
6504
6498
|
const [activeTab, setActiveTab] = useState6("blocks");
|
|
6505
6499
|
const [templatesOpen, setTemplatesOpen] = useState6(false);
|
|
6500
|
+
const [jsonLoading, setJsonLoading] = useState6(false);
|
|
6506
6501
|
const [autoSaveMsg, setAutoSaveMsg] = useState6("");
|
|
6507
6502
|
const [locale, setLocale] = useState6(
|
|
6508
6503
|
typeof opt.locale === "string" ? opt.locale : "en"
|
|
6509
6504
|
);
|
|
6510
|
-
const [mergeTags, setMergeTagsState] = useState6(() => opt.mergeTags ?? []);
|
|
6511
6505
|
const [zoom, setZoom] = useState6(100);
|
|
6512
6506
|
const [rightRailWidth, setRightRailWidth] = useState6(RIGHT_RAIL_DEFAULT_W);
|
|
6513
6507
|
const [ctxMenu, setCtxMenu] = useState6(null);
|
|
@@ -6645,31 +6639,40 @@ var ReactEmailEditor = forwardRef(
|
|
|
6645
6639
|
}, [rows, history, future, selContentMeta]);
|
|
6646
6640
|
const buildApi = useCallback2(() => ({
|
|
6647
6641
|
loadJson(input) {
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6642
|
+
setJsonLoading(true);
|
|
6643
|
+
requestAnimationFrame(() => {
|
|
6644
|
+
requestAnimationFrame(() => {
|
|
6645
|
+
try {
|
|
6646
|
+
let parsed = input;
|
|
6647
|
+
if (typeof input === "string") {
|
|
6648
|
+
try {
|
|
6649
|
+
parsed = JSON.parse(input);
|
|
6650
|
+
} catch {
|
|
6651
|
+
return;
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
const norm = normalizeEmailDesignInput(parsed);
|
|
6655
|
+
if (!norm) return;
|
|
6656
|
+
setRows(norm.rows);
|
|
6657
|
+
const ns = norm.settings;
|
|
6658
|
+
setSettings((s) => ({ ...s, ...ns }));
|
|
6659
|
+
const inferPage = () => {
|
|
6660
|
+
if (ns?.bgGradient) return "gradient";
|
|
6661
|
+
if (String(ns?.bgImage ?? "").trim()) return "image";
|
|
6662
|
+
return "solid";
|
|
6663
|
+
};
|
|
6664
|
+
const inferContent = () => {
|
|
6665
|
+
if (ns?.contentBgGradient) return "gradient";
|
|
6666
|
+
if (String(ns?.contentBgImage ?? "").trim()) return "image";
|
|
6667
|
+
return "solid";
|
|
6668
|
+
};
|
|
6669
|
+
setPageBgUiStep(inferPage());
|
|
6670
|
+
setContentBgUiStep(inferContent());
|
|
6671
|
+
} finally {
|
|
6672
|
+
setJsonLoading(false);
|
|
6673
|
+
}
|
|
6674
|
+
});
|
|
6675
|
+
});
|
|
6673
6676
|
},
|
|
6674
6677
|
exportJson(cb, pretty) {
|
|
6675
6678
|
const design = designToEmailDocument(rowsRef.current, settingsRef.current);
|
|
@@ -6893,7 +6896,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
6893
6896
|
}));
|
|
6894
6897
|
return;
|
|
6895
6898
|
}
|
|
6896
|
-
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx:
|
|
6899
|
+
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
|
|
6897
6900
|
mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
|
|
6898
6901
|
};
|
|
6899
6902
|
const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
|
|
@@ -7056,6 +7059,7 @@ var ReactEmailEditor = forwardRef(
|
|
|
7056
7059
|
::-webkit-scrollbar-track{background:transparent;}
|
|
7057
7060
|
::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
|
|
7058
7061
|
::-webkit-scrollbar-thumb:hover{background:${C.muted};}
|
|
7062
|
+
@keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
|
|
7059
7063
|
` }),
|
|
7060
7064
|
/* @__PURE__ */ jsxs9("div", { id: eid("workspace"), style: {
|
|
7061
7065
|
display: "flex",
|
|
@@ -7623,7 +7627,6 @@ var ReactEmailEditor = forwardRef(
|
|
|
7623
7627
|
setSelMeta(null);
|
|
7624
7628
|
setSelectedRowId(null);
|
|
7625
7629
|
},
|
|
7626
|
-
mergeTags,
|
|
7627
7630
|
onUpload,
|
|
7628
7631
|
C
|
|
7629
7632
|
}
|
|
@@ -7692,11 +7695,43 @@ var ReactEmailEditor = forwardRef(
|
|
|
7692
7695
|
e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
|
|
7693
7696
|
},
|
|
7694
7697
|
onClick: () => {
|
|
7695
|
-
|
|
7696
|
-
if (
|
|
7697
|
-
|
|
7698
|
+
if (!rows.length) return;
|
|
7699
|
+
if (selContentMeta?.inner) {
|
|
7700
|
+
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
7701
|
+
dropContent(rowId, cellIdx, {
|
|
7702
|
+
kind: "new",
|
|
7703
|
+
contentType: bt.type,
|
|
7704
|
+
insertAt: null,
|
|
7705
|
+
nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
|
|
7706
|
+
});
|
|
7707
|
+
return;
|
|
7708
|
+
}
|
|
7709
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
|
|
7710
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
7711
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
7712
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
7713
|
+
kind: "new",
|
|
7714
|
+
contentType: bt.type,
|
|
7715
|
+
insertAt: null
|
|
7716
|
+
});
|
|
7717
|
+
return;
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
if (selectedRowId) {
|
|
7721
|
+
const targetRow = rows.find((r) => r.id === selectedRowId);
|
|
7722
|
+
if (targetRow) {
|
|
7723
|
+
dropContent(targetRow.id, 0, {
|
|
7724
|
+
kind: "new",
|
|
7725
|
+
contentType: bt.type,
|
|
7726
|
+
insertAt: null
|
|
7727
|
+
});
|
|
7728
|
+
return;
|
|
7729
|
+
}
|
|
7730
|
+
}
|
|
7731
|
+
const main = rows[0];
|
|
7732
|
+
dropContent(main.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
|
|
7698
7733
|
},
|
|
7699
|
-
title:
|
|
7734
|
+
title: selContentMeta?.inner ? "Click to add inside the selected nested column" : selContentMeta?.rowId ? "Click to add in the selected column" : selectedRowId ? "Click to add to column 1 of the selected row" : "Click to add to the first row, first column",
|
|
7700
7735
|
style: {
|
|
7701
7736
|
display: "flex",
|
|
7702
7737
|
flexDirection: "row",
|
|
@@ -7984,7 +8019,47 @@ var ReactEmailEditor = forwardRef(
|
|
|
7984
8019
|
) })
|
|
7985
8020
|
]
|
|
7986
8021
|
}
|
|
7987
|
-
)
|
|
8022
|
+
),
|
|
8023
|
+
jsonLoading ? /* @__PURE__ */ jsxs9(
|
|
8024
|
+
"div",
|
|
8025
|
+
{
|
|
8026
|
+
id: eid("json-loading"),
|
|
8027
|
+
role: "status",
|
|
8028
|
+
"aria-live": "polite",
|
|
8029
|
+
"aria-busy": true,
|
|
8030
|
+
style: {
|
|
8031
|
+
position: "absolute",
|
|
8032
|
+
inset: 0,
|
|
8033
|
+
zIndex: 180,
|
|
8034
|
+
background: `${C.bg}f2`,
|
|
8035
|
+
backdropFilter: "blur(2px)",
|
|
8036
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
8037
|
+
display: "flex",
|
|
8038
|
+
flexDirection: "column",
|
|
8039
|
+
alignItems: "center",
|
|
8040
|
+
justifyContent: "center",
|
|
8041
|
+
gap: 14,
|
|
8042
|
+
pointerEvents: "auto"
|
|
8043
|
+
},
|
|
8044
|
+
children: [
|
|
8045
|
+
/* @__PURE__ */ jsx11(
|
|
8046
|
+
"div",
|
|
8047
|
+
{
|
|
8048
|
+
"aria-hidden": true,
|
|
8049
|
+
style: {
|
|
8050
|
+
width: 40,
|
|
8051
|
+
height: 40,
|
|
8052
|
+
borderRadius: "50%",
|
|
8053
|
+
border: `3px solid ${C.border}`,
|
|
8054
|
+
borderTopColor: C.accent,
|
|
8055
|
+
animation: "email-editor-json-spin 0.65s linear infinite"
|
|
8056
|
+
}
|
|
8057
|
+
}
|
|
8058
|
+
),
|
|
8059
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
|
|
8060
|
+
]
|
|
8061
|
+
}
|
|
8062
|
+
) : null
|
|
7988
8063
|
] }),
|
|
7989
8064
|
/* @__PURE__ */ jsxs9("div", { id: eid("status-bar"), style: { display: "flex", alignItems: "center", gap: 12, padding: "3px 14px", background: C.toolbarBg, borderTop: `1px solid ${C.border}`, fontSize: 10, color: C.muted, flexShrink: 0, position: "sticky", bottom: 0, zIndex: 100 }, children: [
|
|
7990
8065
|
/* @__PURE__ */ jsxs9("span", { children: [
|