react-email-studio 3.1.1 → 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 +103 -107
- 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 +109 -113
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [3.1.1] - 2026-04-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Loading state for `loadJson`**: `ReactEmailEditor` shows a full-workspace overlay (spinner + localized “Loading design…”) while a design JSON is applied, including invalid-parse early exit.
|
|
13
|
+
- **Lossless studio JSON**: document `settings._reactEmailStudio.editorSettings` stores a snapshot of editor settings; each row may include `_reactEmailStudio.row` (row fields without `cells`) for presets, ratios, `columnStyles`, etc.
|
|
14
|
+
- **i18n**: `loadingDesign` string in `en`, `fr`, `de`, `es`.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Settings round-trip**: exported document `rtl` / `responsive` now follow `pageRtl` / `pageResponsive` so normalization no longer overwrites them from stale canonical defaults.
|
|
19
|
+
- **Social blocks**: canonical `content.networks` includes URLs from `socialUrls` even when not listed in `networks`; import supports `networks` as an array plus `content.socialUrls`; `styles.padding` is exported/imported; legacy flat-merge skips `socialUrls` where appropriate.
|
|
20
|
+
- **Nested layout `columnStyles`**: importing a layout whose `props.cells` is hydrated no longer drops `columnStyles` that exist only on `content` (merge after hydrate).
|
|
21
|
+
- **Nested layout inspector**: `LayoutRowEditor` background-step UI (row + per-column) re-syncs when `bgColor` / `bgImage` / `bgGradient` / `columnStyles` change, not only when `row.id` changes.
|
|
22
|
+
- **Block palette (click)**: new blocks go to the focused column, a focused nested column, column 0 of a row selected in the rails, or **first row / first column** by default (instead of always column 0 of the last row).
|
|
23
|
+
|
|
24
|
+
### Notes for integrators
|
|
25
|
+
|
|
26
|
+
- Prefer **`loadJson` / `exportJson`** (or `designToEmailDocument` + `normalizeEmailDesignInput`) for full fidelity; blocks may include a `props` object alongside `content` / `styles` for editor-only fields.
|
|
27
|
+
- **`jsonToHtml`** nested layouts already receive `columnStyles` when present on hydrated blocks.
|
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/react-email-studio)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
+
## Release notes
|
|
9
|
+
|
|
10
|
+
Version history and migration hints: **[CHANGELOG.md](./CHANGELOG.md)** (also included in the published npm tarball under `node_modules/react-email-studio/CHANGELOG.md`).
|
|
11
|
+
|
|
8
12
|
---
|
|
9
13
|
|
|
10
14
|
## Features
|
|
@@ -13,7 +17,7 @@
|
|
|
13
17
|
- **Page & content** backgrounds (solid, gradient, image), responsive preview
|
|
14
18
|
- **Undo / redo**, zoom, device preview modal
|
|
15
19
|
- **Internationalized** UI strings (`en`, `fr`, `de`, `es`)
|
|
16
|
-
- **
|
|
20
|
+
- **Template placeholders** (e.g. `{{first_name}}`) can be typed directly in HTML / text blocks
|
|
17
21
|
- Tree-shakeable ESM + CJS, TypeScript types
|
|
18
22
|
|
|
19
23
|
---
|
package/TUTORIAL.md
CHANGED
|
@@ -134,7 +134,7 @@ Adjust the import path to match your structure.
|
|
|
134
134
|
| Prop | Type | Description |
|
|
135
135
|
|------|------|-------------|
|
|
136
136
|
| `ref` | `ReactEmailEditorRef` | Imperative API: `loadJson`, `exportJson` (see below). |
|
|
137
|
-
| `options` | `ReactEmailEditorOptions` | Editor chrome, locale,
|
|
137
|
+
| `options` | `ReactEmailEditorOptions` | Editor chrome, locale, feature flags, etc. (see §6). |
|
|
138
138
|
| `onUpload` | `(file: File) => Promise<string>` | Required for good image/video UX; return public URL string. |
|
|
139
139
|
| `onLoad` / `onReady` | `(api: ReactEmailEditorRef) => void` | Fired once with the same API object; use to call `loadJson` with server data. |
|
|
140
140
|
| `hideTemplates` | `boolean` | Hide built-in templates UI; use when you only load via `loadJson`. |
|
|
@@ -163,7 +163,6 @@ Adjust the import path to match your structure.
|
|
|
163
163
|
| **`customCSS`** | Extra `<style>` content for generated HTML (preview / export parity). |
|
|
164
164
|
| **`appearance`** | `theme`, `colors`, `customThemes` — chrome theming. |
|
|
165
165
|
| **`locale`** | `"en"` \| `"fr"` \| `"de"` \| `"es"` for UI strings. |
|
|
166
|
-
| **`mergeTags`** | `{ name: string; value: string }[]` for rich-text insertion. |
|
|
167
166
|
| **`features.autoSave`** | `{ enabled?: boolean; interval?: number }` — UI autosave hint. |
|
|
168
167
|
| **`tools`** | `{ [blockType: string]: { enabled?: boolean } }` — toggle blocks. |
|
|
169
168
|
| **`blockLibrary`** | `groupHeadings`, `paletteGroupLabels` — library copy overrides. |
|
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",
|
|
@@ -211,7 +210,6 @@ var I18N = {
|
|
|
211
210
|
delete: "Supprimer",
|
|
212
211
|
moveUp: "Monter",
|
|
213
212
|
moveDown: "Descendre",
|
|
214
|
-
mergeTags: "Balises fusion",
|
|
215
213
|
autoSaved: "Auto-sauvegard\xE9",
|
|
216
214
|
search: "Rechercher\u2026",
|
|
217
215
|
colorScheme: "Sch\xE9ma de couleurs",
|
|
@@ -271,7 +269,6 @@ var I18N = {
|
|
|
271
269
|
delete: "L\xF6schen",
|
|
272
270
|
moveUp: "Nach oben",
|
|
273
271
|
moveDown: "Nach unten",
|
|
274
|
-
mergeTags: "Merge-Tags",
|
|
275
272
|
autoSaved: "Auto-gespeichert",
|
|
276
273
|
search: "Bl\xF6cke suchen\u2026",
|
|
277
274
|
colorScheme: "Farbschema",
|
|
@@ -329,7 +326,6 @@ var I18N = {
|
|
|
329
326
|
delete: "Eliminar",
|
|
330
327
|
moveUp: "Subir",
|
|
331
328
|
moveDown: "Bajar",
|
|
332
|
-
mergeTags: "Etiquetas de fusi\xF3n",
|
|
333
329
|
autoSaved: "Auto-guardado",
|
|
334
330
|
search: "Buscar bloques\u2026",
|
|
335
331
|
zoomIn: "Zoom +",
|
|
@@ -2238,9 +2234,8 @@ function useLiveCountdown(endDate) {
|
|
|
2238
2234
|
const s = Math.floor(diff % 6e4 / 1e3);
|
|
2239
2235
|
return [d, h, m, s];
|
|
2240
2236
|
}
|
|
2241
|
-
function editChromeBorder(
|
|
2242
|
-
|
|
2243
|
-
return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
|
|
2237
|
+
function editChromeBorder(_C, _preview) {
|
|
2238
|
+
return {};
|
|
2244
2239
|
}
|
|
2245
2240
|
function blockLinkCaptureHandler(preview, e) {
|
|
2246
2241
|
if (preview) return;
|
|
@@ -2542,6 +2537,8 @@ function NestedRowBlock({
|
|
|
2542
2537
|
rowId,
|
|
2543
2538
|
cellIdx,
|
|
2544
2539
|
parentBlockIdx,
|
|
2540
|
+
/** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
|
|
2541
|
+
selectionInner,
|
|
2545
2542
|
editorId,
|
|
2546
2543
|
selectedKey,
|
|
2547
2544
|
selContentMeta,
|
|
@@ -2553,7 +2550,7 @@ function NestedRowBlock({
|
|
|
2553
2550
|
C
|
|
2554
2551
|
}) {
|
|
2555
2552
|
const p = block.props;
|
|
2556
|
-
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);
|
|
2557
2554
|
const rowStyle = {
|
|
2558
2555
|
padding: p.padding ?? 8,
|
|
2559
2556
|
position: "relative",
|
|
@@ -2577,7 +2574,13 @@ function NestedRowBlock({
|
|
|
2577
2574
|
style: rowStyle,
|
|
2578
2575
|
onClick: preview ? void 0 : (e) => {
|
|
2579
2576
|
e.stopPropagation();
|
|
2580
|
-
onSelectContent(
|
|
2577
|
+
onSelectContent(
|
|
2578
|
+
block.id,
|
|
2579
|
+
rowId,
|
|
2580
|
+
cellIdx,
|
|
2581
|
+
parentBlockIdx,
|
|
2582
|
+
selectionInner ?? null
|
|
2583
|
+
);
|
|
2581
2584
|
},
|
|
2582
2585
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
|
|
2583
2586
|
const cS = p.columnStyles && p.columnStyles[ici] || {};
|
|
@@ -2643,7 +2646,13 @@ function BlockItem({
|
|
|
2643
2646
|
if (!onSelectContent) return;
|
|
2644
2647
|
e.stopPropagation();
|
|
2645
2648
|
if (isSplitLayoutBlock(cb)) {
|
|
2646
|
-
onSelectContent(
|
|
2649
|
+
onSelectContent(
|
|
2650
|
+
cb.id,
|
|
2651
|
+
rowId,
|
|
2652
|
+
cellIdx,
|
|
2653
|
+
nest ? nest.parentBlockIdx : ci,
|
|
2654
|
+
nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
|
|
2655
|
+
);
|
|
2647
2656
|
} else {
|
|
2648
2657
|
onSelectContent(
|
|
2649
2658
|
cb.id,
|
|
@@ -2714,6 +2723,7 @@ function BlockItem({
|
|
|
2714
2723
|
rowId,
|
|
2715
2724
|
cellIdx,
|
|
2716
2725
|
parentBlockIdx: nest ? nest.parentBlockIdx : ci,
|
|
2726
|
+
selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
|
|
2717
2727
|
editorId,
|
|
2718
2728
|
selectedKey,
|
|
2719
2729
|
selContentMeta,
|
|
@@ -3301,11 +3311,8 @@ function ToolbarTypographyControls({ editor, C }) {
|
|
|
3301
3311
|
}
|
|
3302
3312
|
function FormattingToolbar({
|
|
3303
3313
|
editor,
|
|
3304
|
-
C
|
|
3305
|
-
mergeTags,
|
|
3306
|
-
onMergeTagInsert
|
|
3314
|
+
C
|
|
3307
3315
|
}) {
|
|
3308
|
-
const [mergeSel, setMergeSel] = (0, import_react2.useState)("");
|
|
3309
3316
|
const [linkPanelOpen, setLinkPanelOpen] = (0, import_react2.useState)(false);
|
|
3310
3317
|
const [linkUrlValue, setLinkUrlValue] = (0, import_react2.useState)("");
|
|
3311
3318
|
const ibtn = (active) => toolbarIconBtn(C, active);
|
|
@@ -3625,8 +3632,6 @@ function TextRichEditor({
|
|
|
3625
3632
|
value,
|
|
3626
3633
|
onChange,
|
|
3627
3634
|
typography,
|
|
3628
|
-
mergeTags,
|
|
3629
|
-
onMergeTagInsert,
|
|
3630
3635
|
placeholder = "Write your message\u2026",
|
|
3631
3636
|
headerTitle = "Rich editor",
|
|
3632
3637
|
C
|
|
@@ -3770,15 +3775,7 @@ function TextRichEditor({
|
|
|
3770
3775
|
]
|
|
3771
3776
|
}
|
|
3772
3777
|
),
|
|
3773
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3774
|
-
FormattingToolbar,
|
|
3775
|
-
{
|
|
3776
|
-
editor,
|
|
3777
|
-
C,
|
|
3778
|
-
mergeTags,
|
|
3779
|
-
onMergeTagInsert
|
|
3780
|
-
}
|
|
3781
|
-
),
|
|
3778
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
|
|
3782
3779
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
|
|
3783
3780
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3784
3781
|
"div",
|
|
@@ -3829,15 +3826,7 @@ function TextRichEditor({
|
|
|
3829
3826
|
background: C.inputBg
|
|
3830
3827
|
},
|
|
3831
3828
|
children: [
|
|
3832
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3833
|
-
FormattingToolbar,
|
|
3834
|
-
{
|
|
3835
|
-
editor,
|
|
3836
|
-
C,
|
|
3837
|
-
mergeTags,
|
|
3838
|
-
onMergeTagInsert
|
|
3839
|
-
}
|
|
3840
|
-
),
|
|
3829
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
|
|
3841
3830
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
|
|
3842
3831
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3843
3832
|
"div",
|
|
@@ -4837,7 +4826,7 @@ function BlockSurfaceBgInspector({
|
|
|
4837
4826
|
] }) : null
|
|
4838
4827
|
] });
|
|
4839
4828
|
}
|
|
4840
|
-
function ContentBlockEditor({ block, onChange,
|
|
4829
|
+
function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
|
|
4841
4830
|
const { IS, CI } = useIS(C);
|
|
4842
4831
|
const imageFileRef = (0, import_react4.useRef)(null);
|
|
4843
4832
|
const p = block.props;
|
|
@@ -4856,7 +4845,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4856
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 } }),
|
|
4857
4846
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
|
|
4858
4847
|
] }) });
|
|
4859
|
-
const TagPicker = (_field) => null;
|
|
4860
4848
|
const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
|
|
4861
4849
|
const ImgUpload = (key) => {
|
|
4862
4850
|
if (!onUpload) return null;
|
|
@@ -4910,7 +4898,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4910
4898
|
] });
|
|
4911
4899
|
case "text":
|
|
4912
4900
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4913
|
-
/* @__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)(
|
|
4914
4902
|
"textarea",
|
|
4915
4903
|
{
|
|
4916
4904
|
style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
|
|
@@ -4921,24 +4909,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4921
4909
|
},
|
|
4922
4910
|
block.id
|
|
4923
4911
|
) }),
|
|
4924
|
-
mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4925
|
-
"select",
|
|
4926
|
-
{
|
|
4927
|
-
style: IS,
|
|
4928
|
-
defaultValue: "",
|
|
4929
|
-
onChange: (e) => {
|
|
4930
|
-
const v = e.target.value;
|
|
4931
|
-
if (v) {
|
|
4932
|
-
set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
|
|
4933
|
-
e.target.value = "";
|
|
4934
|
-
}
|
|
4935
|
-
},
|
|
4936
|
-
children: [
|
|
4937
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
|
|
4938
|
-
mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
|
|
4939
|
-
]
|
|
4940
|
-
}
|
|
4941
|
-
) }) : null,
|
|
4942
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 }),
|
|
4943
4913
|
Col("color", "Color"),
|
|
4944
4914
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
|
|
@@ -4968,8 +4938,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
4968
4938
|
letterSpacing: p.letterSpacing,
|
|
4969
4939
|
padding: p.padding
|
|
4970
4940
|
},
|
|
4971
|
-
mergeTags,
|
|
4972
|
-
onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
|
|
4973
4941
|
placeholder: "Write your rich content\u2026",
|
|
4974
4942
|
headerTitle: "Rich editor",
|
|
4975
4943
|
C
|
|
@@ -5376,6 +5344,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
|
|
|
5376
5344
|
return null;
|
|
5377
5345
|
}
|
|
5378
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
|
+
}
|
|
5379
5360
|
function LayoutRowEditor({
|
|
5380
5361
|
row,
|
|
5381
5362
|
onChange,
|
|
@@ -5387,49 +5368,37 @@ function LayoutRowEditor({
|
|
|
5387
5368
|
customizationHint
|
|
5388
5369
|
}) {
|
|
5389
5370
|
const { IS, CI } = useIS(C);
|
|
5390
|
-
const
|
|
5391
|
-
const
|
|
5392
|
-
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;
|
|
5393
5383
|
(0, import_react4.useEffect)(() => {
|
|
5394
|
-
if (initialCol !== null && initialCol !== void 0) {
|
|
5395
|
-
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));
|
|
5396
5388
|
}
|
|
5397
|
-
}, [initialCol]);
|
|
5389
|
+
}, [initialCol, row.id, colCount]);
|
|
5398
5390
|
(0, import_react4.useEffect)(() => {
|
|
5399
|
-
|
|
5400
|
-
setRowBgUiStep("gradient");
|
|
5401
|
-
return;
|
|
5402
|
-
}
|
|
5403
|
-
if (String(row.bgImage || "").trim()) {
|
|
5404
|
-
setRowBgUiStep("image");
|
|
5405
|
-
return;
|
|
5406
|
-
}
|
|
5407
|
-
if (String(row.bgColor || "").trim()) {
|
|
5408
|
-
setRowBgUiStep("solid");
|
|
5409
|
-
return;
|
|
5410
|
-
}
|
|
5411
|
-
setRowBgUiStep(null);
|
|
5391
|
+
setRowBgPickerMode(null);
|
|
5412
5392
|
}, [row.id]);
|
|
5413
5393
|
(0, import_react4.useEffect)(() => {
|
|
5414
|
-
|
|
5415
|
-
setColBgUiStep(null);
|
|
5416
|
-
return;
|
|
5417
|
-
}
|
|
5418
|
-
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5419
|
-
if (cs.bgGradient) {
|
|
5420
|
-
setColBgUiStep("gradient");
|
|
5421
|
-
return;
|
|
5422
|
-
}
|
|
5423
|
-
if (String(cs.bgImage || "").trim()) {
|
|
5424
|
-
setColBgUiStep("image");
|
|
5425
|
-
return;
|
|
5426
|
-
}
|
|
5427
|
-
if (String(cs.bgColor || "").trim()) {
|
|
5428
|
-
setColBgUiStep("solid");
|
|
5429
|
-
return;
|
|
5430
|
-
}
|
|
5431
|
-
setColBgUiStep(null);
|
|
5394
|
+
setColBgPickerMode(null);
|
|
5432
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]);
|
|
5433
5402
|
const set = (k, v) => onChange({ ...row, [k]: v });
|
|
5434
5403
|
const applyColumnCount = (n) => {
|
|
5435
5404
|
const prevCells = [...row.cells || []];
|
|
@@ -5445,16 +5414,13 @@ function LayoutRowEditor({
|
|
|
5445
5414
|
}
|
|
5446
5415
|
onChange({ ...row, cols: n, preset: "custom", cells, ratios });
|
|
5447
5416
|
};
|
|
5448
|
-
const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
|
|
5449
|
-
const ratioList = row.ratios?.length ? row.ratios : [1];
|
|
5450
5417
|
const updCol = (i, upd) => {
|
|
5451
5418
|
const styles = { ...row.columnStyles || {} };
|
|
5452
5419
|
styles[i] = { ...styles[i] || {}, ...upd };
|
|
5453
5420
|
set("columnStyles", styles);
|
|
5454
5421
|
};
|
|
5455
5422
|
const setColBgMode = (mode) => {
|
|
5456
|
-
|
|
5457
|
-
setColBgUiStep(mode);
|
|
5423
|
+
setColBgPickerMode(mode);
|
|
5458
5424
|
const cs = (row.columnStyles || {})[selCol] || {};
|
|
5459
5425
|
if (mode === "solid") {
|
|
5460
5426
|
updCol(selCol, { bgGradient: null, bgImage: "" });
|
|
@@ -5472,7 +5438,7 @@ function LayoutRowEditor({
|
|
|
5472
5438
|
const total = ratioList.reduce((a, b) => a + b, 0) || 1;
|
|
5473
5439
|
const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
|
|
5474
5440
|
const setRowBgMode = (mode) => {
|
|
5475
|
-
|
|
5441
|
+
setRowBgPickerMode(mode);
|
|
5476
5442
|
if (mode === "solid") {
|
|
5477
5443
|
set("bgGradient", null);
|
|
5478
5444
|
set("bgImage", "");
|
|
@@ -5630,7 +5596,7 @@ function LayoutRowEditor({
|
|
|
5630
5596
|
},
|
|
5631
5597
|
i
|
|
5632
5598
|
)) }),
|
|
5633
|
-
|
|
5599
|
+
colCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
|
|
5634
5600
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
|
|
5635
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,
|
|
5636
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: [
|
|
@@ -6521,7 +6487,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6521
6487
|
const [locale, setLocale] = (0, import_react8.useState)(
|
|
6522
6488
|
typeof opt.locale === "string" ? opt.locale : "en"
|
|
6523
6489
|
);
|
|
6524
|
-
const [mergeTags, setMergeTagsState] = (0, import_react8.useState)(() => opt.mergeTags ?? []);
|
|
6525
6490
|
const [zoom, setZoom] = (0, import_react8.useState)(100);
|
|
6526
6491
|
const [rightRailWidth, setRightRailWidth] = (0, import_react8.useState)(RIGHT_RAIL_DEFAULT_W);
|
|
6527
6492
|
const [ctxMenu, setCtxMenu] = (0, import_react8.useState)(null);
|
|
@@ -6916,7 +6881,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
6916
6881
|
}));
|
|
6917
6882
|
return;
|
|
6918
6883
|
}
|
|
6919
|
-
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx:
|
|
6884
|
+
const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
|
|
6920
6885
|
mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
|
|
6921
6886
|
};
|
|
6922
6887
|
const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
|
|
@@ -7647,7 +7612,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7647
7612
|
setSelMeta(null);
|
|
7648
7613
|
setSelectedRowId(null);
|
|
7649
7614
|
},
|
|
7650
|
-
mergeTags,
|
|
7651
7615
|
onUpload,
|
|
7652
7616
|
C
|
|
7653
7617
|
}
|
|
@@ -7716,11 +7680,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
|
|
|
7716
7680
|
e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
|
|
7717
7681
|
},
|
|
7718
7682
|
onClick: () => {
|
|
7719
|
-
|
|
7720
|
-
if (
|
|
7721
|
-
|
|
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 });
|
|
7722
7718
|
},
|
|
7723
|
-
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",
|
|
7724
7720
|
style: {
|
|
7725
7721
|
display: "flex",
|
|
7726
7722
|
flexDirection: "row",
|