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.cjs
CHANGED
|
@@ -152,7 +152,6 @@ var I18N = {
|
|
|
152
152
|
delete: "Delete",
|
|
153
153
|
moveUp: "Move Up",
|
|
154
154
|
moveDown: "Move Down",
|
|
155
|
-
mergeTags: "Merge Tags",
|
|
156
155
|
autoSaved: "Auto-saved",
|
|
157
156
|
search: "Search blocks\u2026",
|
|
158
157
|
zoomIn: "Zoom In",
|
|
@@ -170,7 +169,8 @@ var I18N = {
|
|
|
170
169
|
blockPaletteGroupWidgets: "Widgets",
|
|
171
170
|
closePanel: "Close",
|
|
172
171
|
canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
|
|
173
|
-
emailContentSettings: "Email content"
|
|
172
|
+
emailContentSettings: "Email content",
|
|
173
|
+
loadingDesign: "Loading design\u2026"
|
|
174
174
|
},
|
|
175
175
|
fr: {
|
|
176
176
|
layouts: "Mises en page",
|
|
@@ -210,7 +210,6 @@ var I18N = {
|
|
|
210
210
|
delete: "Supprimer",
|
|
211
211
|
moveUp: "Monter",
|
|
212
212
|
moveDown: "Descendre",
|
|
213
|
-
mergeTags: "Balises fusion",
|
|
214
213
|
autoSaved: "Auto-sauvegard\xE9",
|
|
215
214
|
search: "Rechercher\u2026",
|
|
216
215
|
colorScheme: "Sch\xE9ma de couleurs",
|
|
@@ -223,6 +222,7 @@ var I18N = {
|
|
|
223
222
|
blockPaletteGroupWidgets: "Widgets",
|
|
224
223
|
closePanel: "Fermer",
|
|
225
224
|
canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
|
|
225
|
+
loadingDesign: "Chargement du design\u2026",
|
|
226
226
|
emailContentSettings: "Contenu de l\u2019e-mail",
|
|
227
227
|
zoomIn: "Zoom +",
|
|
228
228
|
zoomOut: "Zoom -",
|
|
@@ -269,7 +269,6 @@ var I18N = {
|
|
|
269
269
|
delete: "L\xF6schen",
|
|
270
270
|
moveUp: "Nach oben",
|
|
271
271
|
moveDown: "Nach unten",
|
|
272
|
-
mergeTags: "Merge-Tags",
|
|
273
272
|
autoSaved: "Auto-gespeichert",
|
|
274
273
|
search: "Bl\xF6cke suchen\u2026",
|
|
275
274
|
colorScheme: "Farbschema",
|
|
@@ -282,6 +281,7 @@ var I18N = {
|
|
|
282
281
|
blockPaletteGroupWidgets: "Widgets",
|
|
283
282
|
closePanel: "Schlie\xDFen",
|
|
284
283
|
canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
|
|
284
|
+
loadingDesign: "Design wird geladen\u2026",
|
|
285
285
|
emailContentSettings: "E-Mail-Inhalt",
|
|
286
286
|
zoomIn: "Zoom +",
|
|
287
287
|
zoomOut: "Zoom -",
|
|
@@ -326,7 +326,6 @@ var I18N = {
|
|
|
326
326
|
delete: "Eliminar",
|
|
327
327
|
moveUp: "Subir",
|
|
328
328
|
moveDown: "Bajar",
|
|
329
|
-
mergeTags: "Etiquetas de fusi\xF3n",
|
|
330
329
|
autoSaved: "Auto-guardado",
|
|
331
330
|
search: "Buscar bloques\u2026",
|
|
332
331
|
zoomIn: "Zoom +",
|
|
@@ -342,6 +341,7 @@ var I18N = {
|
|
|
342
341
|
blockPaletteGroupWidgets: "Widgets",
|
|
343
342
|
closePanel: "Cerrar",
|
|
344
343
|
canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
|
|
344
|
+
loadingDesign: "Cargando dise\xF1o\u2026",
|
|
345
345
|
emailContentSettings: "Contenido del correo",
|
|
346
346
|
resizeSidebar: "Redimensionar panel"
|
|
347
347
|
}
|
|
@@ -2111,13 +2111,37 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
|
|
|
2111
2111
|
}
|
|
2112
2112
|
return mapBlockToInternal(r, layoutDepth);
|
|
2113
2113
|
}
|
|
2114
|
+
function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
|
|
2115
|
+
const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
2116
|
+
if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
|
|
2117
|
+
const a = isObj(fromContent) ? fromContent : {};
|
|
2118
|
+
const b = isObj(fromHydrated) ? fromHydrated : {};
|
|
2119
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
2120
|
+
const out = {};
|
|
2121
|
+
for (const k of keys) {
|
|
2122
|
+
const va = a[k];
|
|
2123
|
+
const vb = b[k];
|
|
2124
|
+
if (isObj(va) && isObj(vb)) {
|
|
2125
|
+
out[k] = { ...va, ...vb };
|
|
2126
|
+
} else if (vb !== void 0) {
|
|
2127
|
+
out[k] = vb;
|
|
2128
|
+
} else {
|
|
2129
|
+
out[k] = va;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
return Object.keys(out).length ? out : void 0;
|
|
2133
|
+
}
|
|
2114
2134
|
function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
|
|
2115
2135
|
const exp = b.props;
|
|
2116
2136
|
if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
|
|
2117
2137
|
if (t === "layout" && Array.isArray(exp.cells)) {
|
|
2138
|
+
const columnStylesFromContent = block.props.columnStyles;
|
|
2118
2139
|
const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
|
|
2119
2140
|
if (hb) {
|
|
2120
2141
|
block.props = hb.props;
|
|
2142
|
+
const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
|
|
2143
|
+
if (merged) block.props.columnStyles = merged;
|
|
2144
|
+
else delete block.props.columnStyles;
|
|
2121
2145
|
if (typeof hb.id === "string" && hb.id) block.id = hb.id;
|
|
2122
2146
|
}
|
|
2123
2147
|
return;
|
|
@@ -2210,9 +2234,8 @@ function useLiveCountdown(endDate) {
|
|
|
2210
2234
|
const s = Math.floor(diff % 6e4 / 1e3);
|
|
2211
2235
|
return [d, h, m, s];
|
|
2212
2236
|
}
|
|
2213
|
-
function editChromeBorder(
|
|
2214
|
-
|
|
2215
|
-
return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
|
|
2237
|
+
function editChromeBorder(_C, _preview) {
|
|
2238
|
+
return {};
|
|
2216
2239
|
}
|
|
2217
2240
|
function blockLinkCaptureHandler(preview, e) {
|
|
2218
2241
|
if (preview) return;
|
|
@@ -2514,6 +2537,8 @@ function NestedRowBlock({
|
|
|
2514
2537
|
rowId,
|
|
2515
2538
|
cellIdx,
|
|
2516
2539
|
parentBlockIdx,
|
|
2540
|
+
/** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
|
|
2541
|
+
selectionInner,
|
|
2517
2542
|
editorId,
|
|
2518
2543
|
selectedKey,
|
|
2519
2544
|
selContentMeta,
|
|
@@ -2525,7 +2550,7 @@ function NestedRowBlock({
|
|
|
2525
2550
|
C
|
|
2526
2551
|
}) {
|
|
2527
2552
|
const p = block.props;
|
|
2528
|
-
const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner &&
|
|
2553
|
+
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);
|
|
2529
2554
|
const rowStyle = {
|
|
2530
2555
|
padding: p.padding ?? 8,
|
|
2531
2556
|
position: "relative",
|
|
@@ -2549,7 +2574,13 @@ function NestedRowBlock({
|
|
|
2549
2574
|
style: rowStyle,
|
|
2550
2575
|
onClick: preview ? void 0 : (e) => {
|
|
2551
2576
|
e.stopPropagation();
|
|
2552
|
-
onSelectContent(
|
|
2577
|
+
onSelectContent(
|
|
2578
|
+
block.id,
|
|
2579
|
+
rowId,
|
|
2580
|
+
cellIdx,
|
|
2581
|
+
parentBlockIdx,
|
|
2582
|
+
selectionInner ?? null
|
|
2583
|
+
);
|
|
2553
2584
|
},
|
|
2554
2585
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2555
2586
|
const cS = p.columnStyles && p.columnStyles[ici] || {};
|
|
@@ -2615,7 +2646,13 @@ function BlockItem({
|
|
|
2615
2646
|
if (!onSelectContent) return;
|
|
2616
2647
|
e.stopPropagation();
|
|
2617
2648
|
if (isSplitLayoutBlock(cb)) {
|
|
2618
|
-
onSelectContent(
|
|
2649
|
+
onSelectContent(
|
|
2650
|
+
cb.id,
|
|
2651
|
+
rowId,
|
|
2652
|
+
cellIdx,
|
|
2653
|
+
nest ? nest.parentBlockIdx : ci,
|
|
2654
|
+
nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
|
|
2655
|
+
);
|
|
2619
2656
|
} else {
|
|
2620
2657
|
onSelectContent(
|
|
2621
2658
|
cb.id,
|
|
@@ -2686,6 +2723,7 @@ function BlockItem({
|
|
|
2686
2723
|
rowId,
|
|
2687
2724
|
cellIdx,
|
|
2688
2725
|
parentBlockIdx: nest ? nest.parentBlockIdx : ci,
|
|
2726
|
+
selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
|
|
2689
2727
|
editorId,
|
|
2690
2728
|
selectedKey,
|
|
2691
2729
|
selContentMeta,
|
|
@@ -3273,11 +3311,8 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3273
3311
|
}
|
|
3274
3312
|
function FormattingToolbar({
|
|
3275
3313
|
editor,
|
|
3276
|
-
C
|
|
3277
|
-
mergeTags,
|
|
3278
|
-
onMergeTagInsert
|
|
3314
|
+
C
|
|
3279
3315
|
}) {
|
|
3280
|
-
const [mergeSel, setMergeSel] = (0, import_react2.useState)("");
|
|
3281
3316
|
const [linkPanelOpen, setLinkPanelOpen] = (0, import_react2.useState)(false);
|
|
3282
3317
|
const [linkUrlValue, setLinkUrlValue] = (0, import_react2.useState)("");
|
|
3283
3318
|
const ibtn = (active) => toolbarIconBtn(C, active);
|
|
@@ -3597,8 +3632,6 @@ function TextRichEditor({
|
|
|
3597
3632
|
value,
|
|
3598
3633
|
onChange,
|
|
3599
3634
|
typography,
|
|
3600
|
-
mergeTags,
|
|
3601
|
-
onMergeTagInsert,
|
|
3602
3635
|
placeholder = "Write your message\u2026",
|
|
3603
3636
|
headerTitle = "Rich editor",
|
|
3604
3637
|
C
|
|
@@ -3742,15 +3775,7 @@ function TextRichEditor({
|
|
|
3742
3775
|
]
|
|
3743
3776
|
}
|
|
3744
3777
|
),
|
|
3745
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3746
|
-
FormattingToolbar,
|
|
3747
|
-
{
|
|
3748
|
-
editor,
|
|
3749
|
-
C,
|
|
3750
|
-
mergeTags,
|
|
3751
|
-
onMergeTagInsert
|
|
3752
|
-
}
|
|
3753
|
-
),
|
|
3778
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
|
|
3754
3779
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
|
|
3755
3780
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3756
3781
|
"div",
|
|
@@ -3801,15 +3826,7 @@ function TextRichEditor({
|
|
|
3801
3826
|
background: C.inputBg
|
|
3802
3827
|
},
|
|
3803
3828
|
children: [
|
|
3804
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3805
|
-
FormattingToolbar,
|
|
3806
|
-
{
|
|
3807
|
-
editor,
|
|
3808
|
-
C,
|
|
3809
|
-
mergeTags,
|
|
3810
|
-
onMergeTagInsert
|
|
3811
|
-
}
|
|
3812
|
-
),
|
|
3829
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
|
|
3813
3830
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
|
|
3814
3831
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3815
3832
|
"div",
|
|
@@ -4809,7 +4826,7 @@ function BlockSurfaceBgInspector({
|
|
|
4809
4826
|
] }) : null
|
|
4810
4827
|
] });
|
|
4811
4828
|
}
|
|
4812
|
-
function ContentBlockEditor({ block, onChange,
|
|
4829
|
+
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4813
4830
|
const { IS, CI } = useIS(C);
|
|
4814
4831
|
const imageFileRef = (0, import_react4.useRef)(null);
|
|
4815
4832
|
const p = block.props;
|
|
@@ -4828,7 +4845,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4828
4845
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: !!p[k], onChange: (e) => set(k, e.target.checked), style: { width: 15, height: 15, accentColor: C.accent } }),
|
|
4829
4846
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
|
|
4830
4847
|
] }) });
|
|
4831
|
-
const TagPicker = (_field) => null;
|
|
4832
4848
|
const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
|
|
4833
4849
|
const ImgUpload = (key) => {
|
|
4834
4850
|
if (!onUpload) return null;
|
|
@@ -4882,7 +4898,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4882
4898
|
] });
|
|
4883
4899
|
case "text":
|
|
4884
4900
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4885
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content
|
|
4901
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4886
4902
|
"textarea",
|
|
4887
4903
|
{
|
|
4888
4904
|
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
@@ -4893,24 +4909,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4893
4909
|
},
|
|
4894
4910
|
block.id
|
|
4895
4911
|
) }),
|
|
4896
|
-
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4897
|
-
"select",
|
|
4898
|
-
{
|
|
4899
|
-
style: IS,
|
|
4900
|
-
defaultValue: "",
|
|
4901
|
-
onChange: (e) => {
|
|
4902
|
-
const v = e.target.value;
|
|
4903
|
-
if (v) {
|
|
4904
|
-
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4905
|
-
e.target.value = "";
|
|
4906
|
-
}
|
|
4907
|
-
},
|
|
4908
|
-
children: [
|
|
4909
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
|
|
4910
|
-
mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
|
|
4911
|
-
]
|
|
4912
|
-
}
|
|
4913
|
-
) }) : null,
|
|
4914
4912
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
|
|
4915
4913
|
Col("color", "Color"),
|
|
4916
4914
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
@@ -4940,8 +4938,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4940
4938
|
letterSpacing: p.letterSpacing,
|
|
4941
4939
|
padding: p.padding
|
|
4942
4940
|
},
|
|
4943
|
-
mergeTags,
|
|
4944
|
-
onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
|
|
4945
4941
|
placeholder: "Write your rich content\u2026",
|
|
4946
4942
|
headerTitle: "Rich editor",
|
|
4947
4943
|
C
|
|
@@ -5348,6 +5344,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
5348
5344
|
return null;
|
|
5349
5345
|
}
|
|
5350
5346
|
}
|
|
5347
|
+
function inferLayoutRowBgStep(r) {
|
|
5348
|
+
if (r.bgGradient) return "gradient";
|
|
5349
|
+
if (String(r.bgImage || "").trim()) return "image";
|
|
5350
|
+
if (String(r.bgColor || "").trim()) return "solid";
|
|
5351
|
+
return null;
|
|
5352
|
+
}
|
|
5353
|
+
function inferColumnBgStep(columnStyles, selCol) {
|
|
5354
|
+
const cs = (columnStyles || {})[selCol] || {};
|
|
5355
|
+
if (cs.bgGradient) return "gradient";
|
|
5356
|
+
if (String(cs.bgImage || "").trim()) return "image";
|
|
5357
|
+
if (String(cs.bgColor || "").trim()) return "solid";
|
|
5358
|
+
return null;
|
|
5359
|
+
}
|
|
5351
5360
|
function LayoutRowEditor({
|
|
5352
5361
|
row,
|
|
5353
5362
|
onChange,
|
|
@@ -5359,49 +5368,37 @@ function LayoutRowEditor({
|
|
|
5359
5368
|
customizationHint
|
|
5360
5369
|
}) {
|
|
5361
5370
|
const { IS, CI } = useIS(C);
|
|
5362
|
-
const
|
|
5363
|
-
const
|
|
5364
|
-
const [
|
|
5371
|
+
const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
|
|
5372
|
+
const ratioList = row.ratios?.length ? row.ratios : [1];
|
|
5373
|
+
const [selCol, setSelCol] = (0, import_react4.useState)(0);
|
|
5374
|
+
const [rowBgPickerMode, setRowBgPickerMode] = (0, import_react4.useState)(null);
|
|
5375
|
+
const [colBgPickerMode, setColBgPickerMode] = (0, import_react4.useState)(null);
|
|
5376
|
+
const inferredRowBg = (0, import_react4.useMemo)(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
|
|
5377
|
+
const inferredColBg = (0, import_react4.useMemo)(
|
|
5378
|
+
() => inferColumnBgStep(row.columnStyles, selCol),
|
|
5379
|
+
[row.columnStyles, selCol]
|
|
5380
|
+
);
|
|
5381
|
+
const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
|
|
5382
|
+
const colBgUiStep = inferredColBg ?? colBgPickerMode;
|
|
5365
5383
|
(0, import_react4.useEffect)(() => {
|
|
5366
|
-
if (initialCol !== null && initialCol !== void 0) {
|
|
5367
|
-
setSelCol(initialCol);
|
|
5384
|
+
if (initialCol !== null && initialCol !== void 0 && initialCol >= 0) {
|
|
5385
|
+
setSelCol(Math.min(initialCol, colCount - 1));
|
|
5386
|
+
} else {
|
|
5387
|
+
setSelCol((c) => Math.min(Math.max(0, c), colCount - 1));
|
|
5368
5388
|
}
|
|
5369
|
-
}, [initialCol]);
|
|
5389
|
+
}, [initialCol, row.id, colCount]);
|
|
5370
5390
|
(0, import_react4.useEffect)(() => {
|
|
5371
|
-
|
|
5372
|
-
setRowBgUiStep("gradient");
|
|
5373
|
-
return;
|
|
5374
|
-
}
|
|
5375
|
-
if (String(row.bgImage || "").trim()) {
|
|
5376
|
-
setRowBgUiStep("image");
|
|
5377
|
-
return;
|
|
5378
|
-
}
|
|
5379
|
-
if (String(row.bgColor || "").trim()) {
|
|
5380
|
-
setRowBgUiStep("solid");
|
|
5381
|
-
return;
|
|
5382
|
-
}
|
|
5383
|
-
setRowBgUiStep(null);
|
|
5391
|
+
setRowBgPickerMode(null);
|
|
5384
5392
|
}, [row.id]);
|
|
5385
5393
|
(0, import_react4.useEffect)(() => {
|
|
5386
|
-
|
|
5387
|
-
setColBgUiStep(null);
|
|
5388
|
-
return;
|
|
5389
|
-
}
|
|
5390
|
-
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5391
|
-
if (cs.bgGradient) {
|
|
5392
|
-
setColBgUiStep("gradient");
|
|
5393
|
-
return;
|
|
5394
|
-
}
|
|
5395
|
-
if (String(cs.bgImage || "").trim()) {
|
|
5396
|
-
setColBgUiStep("image");
|
|
5397
|
-
return;
|
|
5398
|
-
}
|
|
5399
|
-
if (String(cs.bgColor || "").trim()) {
|
|
5400
|
-
setColBgUiStep("solid");
|
|
5401
|
-
return;
|
|
5402
|
-
}
|
|
5403
|
-
setColBgUiStep(null);
|
|
5394
|
+
setColBgPickerMode(null);
|
|
5404
5395
|
}, [selCol, row.id]);
|
|
5396
|
+
(0, import_react4.useEffect)(() => {
|
|
5397
|
+
if (inferredRowBg) setRowBgPickerMode(null);
|
|
5398
|
+
}, [inferredRowBg]);
|
|
5399
|
+
(0, import_react4.useEffect)(() => {
|
|
5400
|
+
if (inferredColBg) setColBgPickerMode(null);
|
|
5401
|
+
}, [inferredColBg]);
|
|
5405
5402
|
const set = (k, v) => onChange({ ...row, [k]: v });
|
|
5406
5403
|
const applyColumnCount = (n) => {
|
|
5407
5404
|
const prevCells = [...row.cells || []];
|
|
@@ -5417,16 +5414,13 @@ function LayoutRowEditor({
|
|
|
5417
5414
|
}
|
|
5418
5415
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5419
5416
|
};
|
|
5420
|
-
const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
|
|
5421
|
-
const ratioList = row.ratios?.length ? row.ratios : [1];
|
|
5422
5417
|
const updCol = (i, upd) => {
|
|
5423
5418
|
const styles = { ...row.columnStyles || {} };
|
|
5424
5419
|
styles[i] = { ...styles[i] || {}, ...upd };
|
|
5425
5420
|
set("columnStyles", styles);
|
|
5426
5421
|
};
|
|
5427
5422
|
const setColBgMode = (mode) => {
|
|
5428
|
-
|
|
5429
|
-
setColBgUiStep(mode);
|
|
5423
|
+
setColBgPickerMode(mode);
|
|
5430
5424
|
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5431
5425
|
if (mode === "solid") {
|
|
5432
5426
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
@@ -5444,7 +5438,7 @@ function LayoutRowEditor({
|
|
|
5444
5438
|
const total = ratioList.reduce((a, b) => a + b, 0) || 1;
|
|
5445
5439
|
const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
|
|
5446
5440
|
const setRowBgMode = (mode) => {
|
|
5447
|
-
|
|
5441
|
+
setRowBgPickerMode(mode);
|
|
5448
5442
|
if (mode === "solid") {
|
|
5449
5443
|
set("bgGradient", null);
|
|
5450
5444
|
set("bgImage", "");
|
|
@@ -5602,7 +5596,7 @@ function LayoutRowEditor({
|
|
|
5602
5596
|
},
|
|
5603
5597
|
i
|
|
5604
5598
|
)) }),
|
|
5605
|
-
|
|
5599
|
+
colCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
|
|
5606
5600
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
|
|
5607
5601
|
colBgUiStep === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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,
|
|
5608
5602
|
colBgUiStep === "solid" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: `col ${selCol + 1} bg`, C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
@@ -6488,11 +6482,11 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6488
6482
|
const [previewDevice, setPreviewDevice] = (0, import_react8.useState)("desktop");
|
|
6489
6483
|
const [activeTab, setActiveTab] = (0, import_react8.useState)("blocks");
|
|
6490
6484
|
const [templatesOpen, setTemplatesOpen] = (0, import_react8.useState)(false);
|
|
6485
|
+
const [jsonLoading, setJsonLoading] = (0, import_react8.useState)(false);
|
|
6491
6486
|
const [autoSaveMsg, setAutoSaveMsg] = (0, import_react8.useState)("");
|
|
6492
6487
|
const [locale, setLocale] = (0, import_react8.useState)(
|
|
6493
6488
|
typeof opt.locale === "string" ? opt.locale : "en"
|
|
6494
6489
|
);
|
|
6495
|
-
const [mergeTags, setMergeTagsState] = (0, import_react8.useState)(() => opt.mergeTags ?? []);
|
|
6496
6490
|
const [zoom, setZoom] = (0, import_react8.useState)(100);
|
|
6497
6491
|
const [rightRailWidth, setRightRailWidth] = (0, import_react8.useState)(RIGHT_RAIL_DEFAULT_W);
|
|
6498
6492
|
const [ctxMenu, setCtxMenu] = (0, import_react8.useState)(null);
|
|
@@ -6630,31 +6624,40 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6630
6624
|
}, [rows, history, future, selContentMeta]);
|
|
6631
6625
|
const buildApi = (0, import_react8.useCallback)(() => ({
|
|
6632
6626
|
loadJson(input) {
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6627
|
+
setJsonLoading(true);
|
|
6628
|
+
requestAnimationFrame(() => {
|
|
6629
|
+
requestAnimationFrame(() => {
|
|
6630
|
+
try {
|
|
6631
|
+
let parsed = input;
|
|
6632
|
+
if (typeof input === "string") {
|
|
6633
|
+
try {
|
|
6634
|
+
parsed = JSON.parse(input);
|
|
6635
|
+
} catch {
|
|
6636
|
+
return;
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6639
|
+
const norm = normalizeEmailDesignInput(parsed);
|
|
6640
|
+
if (!norm) return;
|
|
6641
|
+
setRows(norm.rows);
|
|
6642
|
+
const ns = norm.settings;
|
|
6643
|
+
setSettings((s) => ({ ...s, ...ns }));
|
|
6644
|
+
const inferPage = () => {
|
|
6645
|
+
if (ns?.bgGradient) return "gradient";
|
|
6646
|
+
if (String(ns?.bgImage ?? "").trim()) return "image";
|
|
6647
|
+
return "solid";
|
|
6648
|
+
};
|
|
6649
|
+
const inferContent = () => {
|
|
6650
|
+
if (ns?.contentBgGradient) return "gradient";
|
|
6651
|
+
if (String(ns?.contentBgImage ?? "").trim()) return "image";
|
|
6652
|
+
return "solid";
|
|
6653
|
+
};
|
|
6654
|
+
setPageBgUiStep(inferPage());
|
|
6655
|
+
setContentBgUiStep(inferContent());
|
|
6656
|
+
} finally {
|
|
6657
|
+
setJsonLoading(false);
|
|
6658
|
+
}
|
|
6659
|
+
});
|
|
6660
|
+
});
|
|
6658
6661
|
},
|
|
6659
6662
|
exportJson(cb, pretty) {
|
|
6660
6663
|
const design = designToEmailDocument(rowsRef.current, settingsRef.current);
|
|
@@ -6878,7 +6881,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6878
6881
|
}));
|
|
6879
6882
|
return;
|
|
6880
6883
|
}
|
|
6881
|
-
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx:
|
|
6884
|
+
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
|
|
6882
6885
|
mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
|
|
6883
6886
|
};
|
|
6884
6887
|
const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
|
|
@@ -7041,6 +7044,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7041
7044
|
::-webkit-scrollbar-track{background:transparent;}
|
|
7042
7045
|
::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
|
|
7043
7046
|
::-webkit-scrollbar-thumb:hover{background:${C.muted};}
|
|
7047
|
+
@keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
|
|
7044
7048
|
` }),
|
|
7045
7049
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("workspace"), style: {
|
|
7046
7050
|
display: "flex",
|
|
@@ -7608,7 +7612,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7608
7612
|
setSelMeta(null);
|
|
7609
7613
|
setSelectedRowId(null);
|
|
7610
7614
|
},
|
|
7611
|
-
mergeTags,
|
|
7612
7615
|
onUpload,
|
|
7613
7616
|
C
|
|
7614
7617
|
}
|
|
@@ -7677,11 +7680,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7677
7680
|
e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
|
|
7678
7681
|
},
|
|
7679
7682
|
onClick: () => {
|
|
7680
|
-
|
|
7681
|
-
if (
|
|
7682
|
-
|
|
7683
|
+
if (!rows.length) return;
|
|
7684
|
+
if (selContentMeta?.inner) {
|
|
7685
|
+
const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
|
|
7686
|
+
dropContent(rowId, cellIdx, {
|
|
7687
|
+
kind: "new",
|
|
7688
|
+
contentType: bt.type,
|
|
7689
|
+
insertAt: null,
|
|
7690
|
+
nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
|
|
7691
|
+
});
|
|
7692
|
+
return;
|
|
7693
|
+
}
|
|
7694
|
+
if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
|
|
7695
|
+
const r = rows.find((x) => x.id === selContentMeta.rowId);
|
|
7696
|
+
if (r && selContentMeta.cellIdx < r.cells.length) {
|
|
7697
|
+
dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
|
|
7698
|
+
kind: "new",
|
|
7699
|
+
contentType: bt.type,
|
|
7700
|
+
insertAt: null
|
|
7701
|
+
});
|
|
7702
|
+
return;
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
if (selectedRowId) {
|
|
7706
|
+
const targetRow = rows.find((r) => r.id === selectedRowId);
|
|
7707
|
+
if (targetRow) {
|
|
7708
|
+
dropContent(targetRow.id, 0, {
|
|
7709
|
+
kind: "new",
|
|
7710
|
+
contentType: bt.type,
|
|
7711
|
+
insertAt: null
|
|
7712
|
+
});
|
|
7713
|
+
return;
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
const main = rows[0];
|
|
7717
|
+
dropContent(main.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
|
|
7683
7718
|
},
|
|
7684
|
-
title:
|
|
7719
|
+
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",
|
|
7685
7720
|
style: {
|
|
7686
7721
|
display: "flex",
|
|
7687
7722
|
flexDirection: "row",
|
|
@@ -7969,7 +8004,47 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7969
8004
|
) })
|
|
7970
8005
|
]
|
|
7971
8006
|
}
|
|
7972
|
-
)
|
|
8007
|
+
),
|
|
8008
|
+
jsonLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
8009
|
+
"div",
|
|
8010
|
+
{
|
|
8011
|
+
id: eid("json-loading"),
|
|
8012
|
+
role: "status",
|
|
8013
|
+
"aria-live": "polite",
|
|
8014
|
+
"aria-busy": true,
|
|
8015
|
+
style: {
|
|
8016
|
+
position: "absolute",
|
|
8017
|
+
inset: 0,
|
|
8018
|
+
zIndex: 180,
|
|
8019
|
+
background: `${C.bg}f2`,
|
|
8020
|
+
backdropFilter: "blur(2px)",
|
|
8021
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
8022
|
+
display: "flex",
|
|
8023
|
+
flexDirection: "column",
|
|
8024
|
+
alignItems: "center",
|
|
8025
|
+
justifyContent: "center",
|
|
8026
|
+
gap: 14,
|
|
8027
|
+
pointerEvents: "auto"
|
|
8028
|
+
},
|
|
8029
|
+
children: [
|
|
8030
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
8031
|
+
"div",
|
|
8032
|
+
{
|
|
8033
|
+
"aria-hidden": true,
|
|
8034
|
+
style: {
|
|
8035
|
+
width: 40,
|
|
8036
|
+
height: 40,
|
|
8037
|
+
borderRadius: "50%",
|
|
8038
|
+
border: `3px solid ${C.border}`,
|
|
8039
|
+
borderTopColor: C.accent,
|
|
8040
|
+
animation: "email-editor-json-spin 0.65s linear infinite"
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
),
|
|
8044
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
|
|
8045
|
+
]
|
|
8046
|
+
}
|
|
8047
|
+
) : null
|
|
7973
8048
|
] }),
|
|
7974
8049
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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: [
|
|
7975
8050
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
|