react-email-studio 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -170,7 +170,8 @@ var I18N = {
170
170
  blockPaletteGroupWidgets: "Widgets",
171
171
  closePanel: "Close",
172
172
  canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
173
- emailContentSettings: "Email content"
173
+ emailContentSettings: "Email content",
174
+ loadingDesign: "Loading design\u2026"
174
175
  },
175
176
  fr: {
176
177
  layouts: "Mises en page",
@@ -223,6 +224,7 @@ var I18N = {
223
224
  blockPaletteGroupWidgets: "Widgets",
224
225
  closePanel: "Fermer",
225
226
  canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
227
+ loadingDesign: "Chargement du design\u2026",
226
228
  emailContentSettings: "Contenu de l\u2019e-mail",
227
229
  zoomIn: "Zoom +",
228
230
  zoomOut: "Zoom -",
@@ -282,6 +284,7 @@ var I18N = {
282
284
  blockPaletteGroupWidgets: "Widgets",
283
285
  closePanel: "Schlie\xDFen",
284
286
  canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
287
+ loadingDesign: "Design wird geladen\u2026",
285
288
  emailContentSettings: "E-Mail-Inhalt",
286
289
  zoomIn: "Zoom +",
287
290
  zoomOut: "Zoom -",
@@ -342,6 +345,7 @@ var I18N = {
342
345
  blockPaletteGroupWidgets: "Widgets",
343
346
  closePanel: "Cerrar",
344
347
  canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
348
+ loadingDesign: "Cargando dise\xF1o\u2026",
345
349
  emailContentSettings: "Contenido del correo",
346
350
  resizeSidebar: "Redimensionar panel"
347
351
  }
@@ -2111,13 +2115,37 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
2111
2115
  }
2112
2116
  return mapBlockToInternal(r, layoutDepth);
2113
2117
  }
2118
+ function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
2119
+ const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2120
+ if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
2121
+ const a = isObj(fromContent) ? fromContent : {};
2122
+ const b = isObj(fromHydrated) ? fromHydrated : {};
2123
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
2124
+ const out = {};
2125
+ for (const k of keys) {
2126
+ const va = a[k];
2127
+ const vb = b[k];
2128
+ if (isObj(va) && isObj(vb)) {
2129
+ out[k] = { ...va, ...vb };
2130
+ } else if (vb !== void 0) {
2131
+ out[k] = vb;
2132
+ } else {
2133
+ out[k] = va;
2134
+ }
2135
+ }
2136
+ return Object.keys(out).length ? out : void 0;
2137
+ }
2114
2138
  function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
2115
2139
  const exp = b.props;
2116
2140
  if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
2117
2141
  if (t === "layout" && Array.isArray(exp.cells)) {
2142
+ const columnStylesFromContent = block.props.columnStyles;
2118
2143
  const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
2119
2144
  if (hb) {
2120
2145
  block.props = hb.props;
2146
+ const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
2147
+ if (merged) block.props.columnStyles = merged;
2148
+ else delete block.props.columnStyles;
2121
2149
  if (typeof hb.id === "string" && hb.id) block.id = hb.id;
2122
2150
  }
2123
2151
  return;
@@ -6488,6 +6516,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6488
6516
  const [previewDevice, setPreviewDevice] = (0, import_react8.useState)("desktop");
6489
6517
  const [activeTab, setActiveTab] = (0, import_react8.useState)("blocks");
6490
6518
  const [templatesOpen, setTemplatesOpen] = (0, import_react8.useState)(false);
6519
+ const [jsonLoading, setJsonLoading] = (0, import_react8.useState)(false);
6491
6520
  const [autoSaveMsg, setAutoSaveMsg] = (0, import_react8.useState)("");
6492
6521
  const [locale, setLocale] = (0, import_react8.useState)(
6493
6522
  typeof opt.locale === "string" ? opt.locale : "en"
@@ -6630,31 +6659,40 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6630
6659
  }, [rows, history, future, selContentMeta]);
6631
6660
  const buildApi = (0, import_react8.useCallback)(() => ({
6632
6661
  loadJson(input) {
6633
- let parsed = input;
6634
- if (typeof input === "string") {
6635
- try {
6636
- parsed = JSON.parse(input);
6637
- } catch {
6638
- return;
6639
- }
6640
- }
6641
- const norm = normalizeEmailDesignInput(parsed);
6642
- if (!norm) return;
6643
- setRows(norm.rows);
6644
- const ns = norm.settings;
6645
- setSettings((s) => ({ ...s, ...ns }));
6646
- const inferPage = () => {
6647
- if (ns?.bgGradient) return "gradient";
6648
- if (String(ns?.bgImage ?? "").trim()) return "image";
6649
- return "solid";
6650
- };
6651
- const inferContent = () => {
6652
- if (ns?.contentBgGradient) return "gradient";
6653
- if (String(ns?.contentBgImage ?? "").trim()) return "image";
6654
- return "solid";
6655
- };
6656
- setPageBgUiStep(inferPage());
6657
- setContentBgUiStep(inferContent());
6662
+ setJsonLoading(true);
6663
+ requestAnimationFrame(() => {
6664
+ requestAnimationFrame(() => {
6665
+ try {
6666
+ let parsed = input;
6667
+ if (typeof input === "string") {
6668
+ try {
6669
+ parsed = JSON.parse(input);
6670
+ } catch {
6671
+ return;
6672
+ }
6673
+ }
6674
+ const norm = normalizeEmailDesignInput(parsed);
6675
+ if (!norm) return;
6676
+ setRows(norm.rows);
6677
+ const ns = norm.settings;
6678
+ setSettings((s) => ({ ...s, ...ns }));
6679
+ const inferPage = () => {
6680
+ if (ns?.bgGradient) return "gradient";
6681
+ if (String(ns?.bgImage ?? "").trim()) return "image";
6682
+ return "solid";
6683
+ };
6684
+ const inferContent = () => {
6685
+ if (ns?.contentBgGradient) return "gradient";
6686
+ if (String(ns?.contentBgImage ?? "").trim()) return "image";
6687
+ return "solid";
6688
+ };
6689
+ setPageBgUiStep(inferPage());
6690
+ setContentBgUiStep(inferContent());
6691
+ } finally {
6692
+ setJsonLoading(false);
6693
+ }
6694
+ });
6695
+ });
6658
6696
  },
6659
6697
  exportJson(cb, pretty) {
6660
6698
  const design = designToEmailDocument(rowsRef.current, settingsRef.current);
@@ -7041,6 +7079,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7041
7079
  ::-webkit-scrollbar-track{background:transparent;}
7042
7080
  ::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
7043
7081
  ::-webkit-scrollbar-thumb:hover{background:${C.muted};}
7082
+ @keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
7044
7083
  ` }),
7045
7084
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("workspace"), style: {
7046
7085
  display: "flex",
@@ -7969,7 +8008,47 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7969
8008
  ) })
7970
8009
  ]
7971
8010
  }
7972
- )
8011
+ ),
8012
+ jsonLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
8013
+ "div",
8014
+ {
8015
+ id: eid("json-loading"),
8016
+ role: "status",
8017
+ "aria-live": "polite",
8018
+ "aria-busy": true,
8019
+ style: {
8020
+ position: "absolute",
8021
+ inset: 0,
8022
+ zIndex: 180,
8023
+ background: `${C.bg}f2`,
8024
+ backdropFilter: "blur(2px)",
8025
+ WebkitBackdropFilter: "blur(2px)",
8026
+ display: "flex",
8027
+ flexDirection: "column",
8028
+ alignItems: "center",
8029
+ justifyContent: "center",
8030
+ gap: 14,
8031
+ pointerEvents: "auto"
8032
+ },
8033
+ children: [
8034
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8035
+ "div",
8036
+ {
8037
+ "aria-hidden": true,
8038
+ style: {
8039
+ width: 40,
8040
+ height: 40,
8041
+ borderRadius: "50%",
8042
+ border: `3px solid ${C.border}`,
8043
+ borderTopColor: C.accent,
8044
+ animation: "email-editor-json-spin 0.65s linear infinite"
8045
+ }
8046
+ }
8047
+ ),
8048
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
8049
+ ]
8050
+ }
8051
+ ) : null
7973
8052
  ] }),
7974
8053
  /* @__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
8054
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [