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.js CHANGED
@@ -138,7 +138,8 @@ var I18N = {
138
138
  blockPaletteGroupWidgets: "Widgets",
139
139
  closePanel: "Close",
140
140
  canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
141
- emailContentSettings: "Email content"
141
+ emailContentSettings: "Email content",
142
+ loadingDesign: "Loading design\u2026"
142
143
  },
143
144
  fr: {
144
145
  layouts: "Mises en page",
@@ -191,6 +192,7 @@ var I18N = {
191
192
  blockPaletteGroupWidgets: "Widgets",
192
193
  closePanel: "Fermer",
193
194
  canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
195
+ loadingDesign: "Chargement du design\u2026",
194
196
  emailContentSettings: "Contenu de l\u2019e-mail",
195
197
  zoomIn: "Zoom +",
196
198
  zoomOut: "Zoom -",
@@ -250,6 +252,7 @@ var I18N = {
250
252
  blockPaletteGroupWidgets: "Widgets",
251
253
  closePanel: "Schlie\xDFen",
252
254
  canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
255
+ loadingDesign: "Design wird geladen\u2026",
253
256
  emailContentSettings: "E-Mail-Inhalt",
254
257
  zoomIn: "Zoom +",
255
258
  zoomOut: "Zoom -",
@@ -310,6 +313,7 @@ var I18N = {
310
313
  blockPaletteGroupWidgets: "Widgets",
311
314
  closePanel: "Cerrar",
312
315
  canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
316
+ loadingDesign: "Cargando dise\xF1o\u2026",
313
317
  emailContentSettings: "Contenido del correo",
314
318
  resizeSidebar: "Redimensionar panel"
315
319
  }
@@ -2079,13 +2083,37 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
2079
2083
  }
2080
2084
  return mapBlockToInternal(r, layoutDepth);
2081
2085
  }
2086
+ function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
2087
+ const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2088
+ if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
2089
+ const a = isObj(fromContent) ? fromContent : {};
2090
+ const b = isObj(fromHydrated) ? fromHydrated : {};
2091
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
2092
+ const out = {};
2093
+ for (const k of keys) {
2094
+ const va = a[k];
2095
+ const vb = b[k];
2096
+ if (isObj(va) && isObj(vb)) {
2097
+ out[k] = { ...va, ...vb };
2098
+ } else if (vb !== void 0) {
2099
+ out[k] = vb;
2100
+ } else {
2101
+ out[k] = va;
2102
+ }
2103
+ }
2104
+ return Object.keys(out).length ? out : void 0;
2105
+ }
2082
2106
  function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
2083
2107
  const exp = b.props;
2084
2108
  if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
2085
2109
  if (t === "layout" && Array.isArray(exp.cells)) {
2110
+ const columnStylesFromContent = block.props.columnStyles;
2086
2111
  const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
2087
2112
  if (hb) {
2088
2113
  block.props = hb.props;
2114
+ const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
2115
+ if (merged) block.props.columnStyles = merged;
2116
+ else delete block.props.columnStyles;
2089
2117
  if (typeof hb.id === "string" && hb.id) block.id = hb.id;
2090
2118
  }
2091
2119
  return;
@@ -6503,6 +6531,7 @@ var ReactEmailEditor = forwardRef(
6503
6531
  const [previewDevice, setPreviewDevice] = useState6("desktop");
6504
6532
  const [activeTab, setActiveTab] = useState6("blocks");
6505
6533
  const [templatesOpen, setTemplatesOpen] = useState6(false);
6534
+ const [jsonLoading, setJsonLoading] = useState6(false);
6506
6535
  const [autoSaveMsg, setAutoSaveMsg] = useState6("");
6507
6536
  const [locale, setLocale] = useState6(
6508
6537
  typeof opt.locale === "string" ? opt.locale : "en"
@@ -6645,31 +6674,40 @@ var ReactEmailEditor = forwardRef(
6645
6674
  }, [rows, history, future, selContentMeta]);
6646
6675
  const buildApi = useCallback2(() => ({
6647
6676
  loadJson(input) {
6648
- let parsed = input;
6649
- if (typeof input === "string") {
6650
- try {
6651
- parsed = JSON.parse(input);
6652
- } catch {
6653
- return;
6654
- }
6655
- }
6656
- const norm = normalizeEmailDesignInput(parsed);
6657
- if (!norm) return;
6658
- setRows(norm.rows);
6659
- const ns = norm.settings;
6660
- setSettings((s) => ({ ...s, ...ns }));
6661
- const inferPage = () => {
6662
- if (ns?.bgGradient) return "gradient";
6663
- if (String(ns?.bgImage ?? "").trim()) return "image";
6664
- return "solid";
6665
- };
6666
- const inferContent = () => {
6667
- if (ns?.contentBgGradient) return "gradient";
6668
- if (String(ns?.contentBgImage ?? "").trim()) return "image";
6669
- return "solid";
6670
- };
6671
- setPageBgUiStep(inferPage());
6672
- setContentBgUiStep(inferContent());
6677
+ setJsonLoading(true);
6678
+ requestAnimationFrame(() => {
6679
+ requestAnimationFrame(() => {
6680
+ try {
6681
+ let parsed = input;
6682
+ if (typeof input === "string") {
6683
+ try {
6684
+ parsed = JSON.parse(input);
6685
+ } catch {
6686
+ return;
6687
+ }
6688
+ }
6689
+ const norm = normalizeEmailDesignInput(parsed);
6690
+ if (!norm) return;
6691
+ setRows(norm.rows);
6692
+ const ns = norm.settings;
6693
+ setSettings((s) => ({ ...s, ...ns }));
6694
+ const inferPage = () => {
6695
+ if (ns?.bgGradient) return "gradient";
6696
+ if (String(ns?.bgImage ?? "").trim()) return "image";
6697
+ return "solid";
6698
+ };
6699
+ const inferContent = () => {
6700
+ if (ns?.contentBgGradient) return "gradient";
6701
+ if (String(ns?.contentBgImage ?? "").trim()) return "image";
6702
+ return "solid";
6703
+ };
6704
+ setPageBgUiStep(inferPage());
6705
+ setContentBgUiStep(inferContent());
6706
+ } finally {
6707
+ setJsonLoading(false);
6708
+ }
6709
+ });
6710
+ });
6673
6711
  },
6674
6712
  exportJson(cb, pretty) {
6675
6713
  const design = designToEmailDocument(rowsRef.current, settingsRef.current);
@@ -7056,6 +7094,7 @@ var ReactEmailEditor = forwardRef(
7056
7094
  ::-webkit-scrollbar-track{background:transparent;}
7057
7095
  ::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
7058
7096
  ::-webkit-scrollbar-thumb:hover{background:${C.muted};}
7097
+ @keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
7059
7098
  ` }),
7060
7099
  /* @__PURE__ */ jsxs9("div", { id: eid("workspace"), style: {
7061
7100
  display: "flex",
@@ -7984,7 +8023,47 @@ var ReactEmailEditor = forwardRef(
7984
8023
  ) })
7985
8024
  ]
7986
8025
  }
7987
- )
8026
+ ),
8027
+ jsonLoading ? /* @__PURE__ */ jsxs9(
8028
+ "div",
8029
+ {
8030
+ id: eid("json-loading"),
8031
+ role: "status",
8032
+ "aria-live": "polite",
8033
+ "aria-busy": true,
8034
+ style: {
8035
+ position: "absolute",
8036
+ inset: 0,
8037
+ zIndex: 180,
8038
+ background: `${C.bg}f2`,
8039
+ backdropFilter: "blur(2px)",
8040
+ WebkitBackdropFilter: "blur(2px)",
8041
+ display: "flex",
8042
+ flexDirection: "column",
8043
+ alignItems: "center",
8044
+ justifyContent: "center",
8045
+ gap: 14,
8046
+ pointerEvents: "auto"
8047
+ },
8048
+ children: [
8049
+ /* @__PURE__ */ jsx11(
8050
+ "div",
8051
+ {
8052
+ "aria-hidden": true,
8053
+ style: {
8054
+ width: 40,
8055
+ height: 40,
8056
+ borderRadius: "50%",
8057
+ border: `3px solid ${C.border}`,
8058
+ borderTopColor: C.accent,
8059
+ animation: "email-editor-json-spin 0.65s linear infinite"
8060
+ }
8061
+ }
8062
+ ),
8063
+ /* @__PURE__ */ jsx11("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
8064
+ ]
8065
+ }
8066
+ ) : null
7988
8067
  ] }),
7989
8068
  /* @__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
8069
  /* @__PURE__ */ jsxs9("span", { children: [