@savvycal/mjml-editor 0.0.2 → 0.1.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/README.md +22 -7
- package/dist/components/editor/EditorCanvas.d.ts +2 -1
- package/dist/components/editor/EditorCanvas.d.ts.map +1 -1
- package/dist/components/editor/EditorCanvas.js +28 -27
- package/dist/components/editor/InteractivePreview.d.ts.map +1 -1
- package/dist/components/editor/InteractivePreview.js +50 -40
- package/dist/components/editor/MjmlEditor.d.ts +27 -1
- package/dist/components/editor/MjmlEditor.d.ts.map +1 -1
- package/dist/components/editor/MjmlEditor.js +113 -70
- package/dist/components/editor/SourceEditor.js +1 -1
- package/dist/components/editor/SourcePreview.d.ts.map +1 -1
- package/dist/components/editor/SourcePreview.js +32 -18
- package/dist/components/ui/badge.js +3 -3
- package/dist/components/ui/button.js +2 -2
- package/dist/components/ui/floating-panel.js +11 -11
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/popover.js +3 -3
- package/dist/components/ui/scroll-area.js +9 -9
- package/dist/components/ui/select.js +1 -1
- package/dist/components.css +365 -0
- package/dist/context/ThemeContext.d.ts +12 -1
- package/dist/context/ThemeContext.d.ts.map +1 -1
- package/dist/context/ThemeContext.js +32 -30
- package/dist/lib/mjml/renderer.d.ts +3 -3
- package/dist/lib/mjml/renderer.d.ts.map +1 -1
- package/dist/lib/mjml/renderer.js +25 -22
- package/dist/preset.css +150 -0
- package/package.json +3 -2
- package/dist/styles.css +0 -1
- package/dist/styles.d.ts +0 -1
- package/dist/styles.d.ts.map +0 -1
- package/dist/styles.js +0 -1
package/README.md
CHANGED
|
@@ -35,26 +35,41 @@ pnpm add @savvycal/mjml-editor
|
|
|
35
35
|
|
|
36
36
|
### Peer Dependencies
|
|
37
37
|
|
|
38
|
-
This library requires React 18
|
|
38
|
+
This library requires React 18+ and Tailwind CSS v4:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
npm install react react-dom
|
|
41
|
+
npm install react react-dom tailwindcss @tailwindcss/vite tw-animate-css
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
### Styles
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
This library is designed to work with Tailwind CSS v4. Instead of bundling all styles, the library exports CSS files that integrate with your app's Tailwind build, ensuring no style conflicts and minimal CSS overhead.
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Add the following imports to your app's main CSS file:
|
|
49
|
+
|
|
50
|
+
```css
|
|
51
|
+
@import "@savvycal/mjml-editor/preset.css";
|
|
52
|
+
@import "tailwindcss";
|
|
53
|
+
@import "tw-animate-css";
|
|
54
|
+
@import "@savvycal/mjml-editor/components.css";
|
|
50
55
|
```
|
|
51
56
|
|
|
57
|
+
**Note:** `preset.css` must come before `tailwindcss` so that `@theme` tokens are registered before Tailwind generates its utilities.
|
|
58
|
+
|
|
59
|
+
The `preset.css` file includes:
|
|
60
|
+
- `@source` directive that tells Tailwind to scan the library's dist files for utility classes (works with npm, yarn, and pnpm)
|
|
61
|
+
- `@theme` tokens that map CSS variables to Tailwind utilities
|
|
62
|
+
- Custom utilities (`bg-checkered`, `shadow-framer`, etc.)
|
|
63
|
+
|
|
64
|
+
The `components.css` file includes:
|
|
65
|
+
- Scoped CSS variables for the editor theme (light/dark mode)
|
|
66
|
+
- Tiptap/ProseMirror editor styles
|
|
67
|
+
|
|
52
68
|
## Usage
|
|
53
69
|
|
|
54
70
|
```tsx
|
|
55
71
|
import { useState } from 'react';
|
|
56
72
|
import { MjmlEditor } from '@savvycal/mjml-editor';
|
|
57
|
-
import '@savvycal/mjml-editor/styles.css';
|
|
58
73
|
|
|
59
74
|
function App() {
|
|
60
75
|
const [mjml, setMjml] = useState(initialMjml);
|
|
@@ -77,6 +92,7 @@ function App() {
|
|
|
77
92
|
| `className` | `string` | Optional CSS class for the container |
|
|
78
93
|
| `defaultTheme` | `'light' \| 'dark' \| 'system'` | Theme preference (default: `'system'`) |
|
|
79
94
|
| `liquidSchema` | `LiquidSchema` | Optional schema for Liquid template autocomplete |
|
|
95
|
+
| `applyThemeToDocument` | `boolean` | Whether to apply theme class to `document.documentElement`. Needed for dropdown/popover theming. Set to `false` if your app manages document-level theme classes. (default: `true`) |
|
|
80
96
|
|
|
81
97
|
## Liquid Template Support
|
|
82
98
|
|
|
@@ -84,7 +100,6 @@ The editor provides autocomplete for Liquid template variables and tags. Pass a
|
|
|
84
100
|
|
|
85
101
|
```tsx
|
|
86
102
|
import { MjmlEditor, type LiquidSchema } from '@savvycal/mjml-editor';
|
|
87
|
-
import '@savvycal/mjml-editor/styles.css';
|
|
88
103
|
|
|
89
104
|
const liquidSchema: LiquidSchema = {
|
|
90
105
|
variables: [
|
|
@@ -5,7 +5,8 @@ interface EditorCanvasProps {
|
|
|
5
5
|
onTabChange: (tab: EditorTabType) => void;
|
|
6
6
|
leftPanelOpen?: boolean;
|
|
7
7
|
rightPanelOpen?: boolean;
|
|
8
|
+
showThemeToggle?: boolean;
|
|
8
9
|
}
|
|
9
|
-
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, showThemeToggle, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
10
11
|
export {};
|
|
11
12
|
//# sourceMappingURL=EditorCanvas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorCanvas.d.ts","sourceRoot":"","sources":["../../../src/components/editor/EditorCanvas.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,UAAU,iBAAiB;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorCanvas.d.ts","sourceRoot":"","sources":["../../../src/components/editor/EditorCanvas.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,UAAU,iBAAiB;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAsB,GACvB,EAAE,iBAAiB,2CA0GnB"}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { jsxs as r, jsx as e } from "react/jsx-runtime";
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Monitor as
|
|
4
|
-
import { VisualEditor as
|
|
5
|
-
import { InteractivePreview as
|
|
6
|
-
import { SourceEditor as
|
|
7
|
-
import { useEditor as
|
|
2
|
+
import { useState as x } from "react";
|
|
3
|
+
import { Monitor as v, Smartphone as N, Undo2 as b, Redo2 as w } from "lucide-react";
|
|
4
|
+
import { VisualEditor as k } from "./VisualEditor.js";
|
|
5
|
+
import { InteractivePreview as C } from "./InteractivePreview.js";
|
|
6
|
+
import { SourceEditor as y } from "./SourceEditor.js";
|
|
7
|
+
import { useEditor as E } from "../../context/EditorContext.js";
|
|
8
8
|
import { Button as i } from "../ui/button.js";
|
|
9
|
-
import { Tabs as
|
|
10
|
-
import { ThemeToggle as
|
|
9
|
+
import { Tabs as S, TabsList as M, TabsTrigger as s } from "../ui/tabs.js";
|
|
10
|
+
import { ThemeToggle as P } from "../ui/theme-toggle.js";
|
|
11
11
|
import { cn as d } from "../../lib/utils.js";
|
|
12
|
-
function
|
|
12
|
+
function q({
|
|
13
13
|
activeTab: o,
|
|
14
14
|
onTabChange: t,
|
|
15
15
|
leftPanelOpen: a,
|
|
16
|
-
rightPanelOpen: l
|
|
16
|
+
rightPanelOpen: l,
|
|
17
|
+
showThemeToggle: c = !0
|
|
17
18
|
}) {
|
|
18
|
-
const { undo:
|
|
19
|
+
const { undo: m, redo: u, canUndo: h, canRedo: f } = E(), [n, p] = x("desktop");
|
|
19
20
|
return /* @__PURE__ */ r("div", { className: "flex flex-col h-full", children: [
|
|
20
21
|
/* @__PURE__ */ r("div", { className: "h-11 px-4 flex items-center gap-1 border-b border-border bg-background relative", children: [
|
|
21
22
|
/* @__PURE__ */ e(
|
|
@@ -52,18 +53,18 @@ function L({
|
|
|
52
53
|
}
|
|
53
54
|
),
|
|
54
55
|
o === "preview" && /* @__PURE__ */ e(
|
|
55
|
-
|
|
56
|
+
S,
|
|
56
57
|
{
|
|
57
58
|
value: n,
|
|
58
|
-
onValueChange: (
|
|
59
|
+
onValueChange: (g) => p(g),
|
|
59
60
|
className: "absolute left-1/2 -translate-x-1/2",
|
|
60
|
-
children: /* @__PURE__ */ r(
|
|
61
|
+
children: /* @__PURE__ */ r(M, { className: "h-8", children: [
|
|
61
62
|
/* @__PURE__ */ r(s, { value: "desktop", className: "h-7 px-2 gap-1.5", children: [
|
|
62
|
-
/* @__PURE__ */ e(
|
|
63
|
+
/* @__PURE__ */ e(v, { className: "h-3.5 w-3.5" }),
|
|
63
64
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Desktop" })
|
|
64
65
|
] }),
|
|
65
66
|
/* @__PURE__ */ r(s, { value: "mobile", className: "h-7 px-2 gap-1.5", children: [
|
|
66
|
-
/* @__PURE__ */ e(
|
|
67
|
+
/* @__PURE__ */ e(N, { className: "h-3.5 w-3.5" }),
|
|
67
68
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Mobile" })
|
|
68
69
|
] })
|
|
69
70
|
] })
|
|
@@ -76,11 +77,11 @@ function L({
|
|
|
76
77
|
{
|
|
77
78
|
variant: "ghost",
|
|
78
79
|
size: "icon-sm",
|
|
79
|
-
onClick:
|
|
80
|
-
disabled: !
|
|
80
|
+
onClick: m,
|
|
81
|
+
disabled: !h,
|
|
81
82
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
82
83
|
title: "Undo (Cmd+Z)",
|
|
83
|
-
children: /* @__PURE__ */ e(
|
|
84
|
+
children: /* @__PURE__ */ e(b, { className: "h-4 w-4" })
|
|
84
85
|
}
|
|
85
86
|
),
|
|
86
87
|
/* @__PURE__ */ e(
|
|
@@ -88,29 +89,29 @@ function L({
|
|
|
88
89
|
{
|
|
89
90
|
variant: "ghost",
|
|
90
91
|
size: "icon-sm",
|
|
91
|
-
onClick:
|
|
92
|
-
disabled: !
|
|
92
|
+
onClick: u,
|
|
93
|
+
disabled: !f,
|
|
93
94
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
94
95
|
title: "Redo (Cmd+Shift+Z)",
|
|
95
|
-
children: /* @__PURE__ */ e(
|
|
96
|
+
children: /* @__PURE__ */ e(w, { className: "h-4 w-4" })
|
|
96
97
|
}
|
|
97
98
|
),
|
|
98
|
-
/* @__PURE__ */ e(
|
|
99
|
+
c && /* @__PURE__ */ e(P, {})
|
|
99
100
|
] })
|
|
100
101
|
] }),
|
|
101
102
|
/* @__PURE__ */ r("div", { className: "flex-1 min-h-0 overflow-hidden", children: [
|
|
102
103
|
o === "edit" && /* @__PURE__ */ e(
|
|
103
|
-
|
|
104
|
+
k,
|
|
104
105
|
{
|
|
105
106
|
leftPanelOpen: a,
|
|
106
107
|
rightPanelOpen: l
|
|
107
108
|
}
|
|
108
109
|
),
|
|
109
|
-
o === "preview" && /* @__PURE__ */ e(
|
|
110
|
-
o === "source" && /* @__PURE__ */ e(
|
|
110
|
+
o === "preview" && /* @__PURE__ */ e(C, { showHeader: !1, previewMode: n }),
|
|
111
|
+
o === "source" && /* @__PURE__ */ e(y, {})
|
|
111
112
|
] })
|
|
112
113
|
] });
|
|
113
114
|
}
|
|
114
115
|
export {
|
|
115
|
-
|
|
116
|
+
q as EditorCanvas
|
|
116
117
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InteractivePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/InteractivePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,uBAAuB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,EACjC,UAAiB,EACjB,WAAuB,GACxB,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"InteractivePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/InteractivePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,uBAAuB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,EACjC,UAAiB,EACjB,WAAuB,GACxB,EAAE,uBAAuB,2CAkLzB"}
|
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import { useEditor as
|
|
4
|
-
import { renderMjmlInteractive as
|
|
5
|
-
const
|
|
6
|
-
function
|
|
7
|
-
showHeader:
|
|
8
|
-
previewMode:
|
|
1
|
+
import { jsxs as r, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as w, useState as b, useEffect as c, useCallback as E } from "react";
|
|
3
|
+
import { useEditor as y } from "../../context/EditorContext.js";
|
|
4
|
+
import { renderMjmlInteractive as C } from "../../lib/mjml/renderer.js";
|
|
5
|
+
const I = 375;
|
|
6
|
+
function S({
|
|
7
|
+
showHeader: p = !0,
|
|
8
|
+
previewMode: g = "desktop"
|
|
9
9
|
}) {
|
|
10
|
-
const { state: n, selectBlock: d } =
|
|
11
|
-
|
|
10
|
+
const { state: n, selectBlock: d } = y(), o = w(null), [m, x] = b(n.document), [v, k] = b({
|
|
11
|
+
html: "",
|
|
12
|
+
errors: []
|
|
13
|
+
});
|
|
14
|
+
c(() => {
|
|
12
15
|
const e = setTimeout(() => {
|
|
13
|
-
|
|
16
|
+
x(n.document);
|
|
14
17
|
}, 300);
|
|
15
18
|
return () => clearTimeout(e);
|
|
16
|
-
}, [n.document])
|
|
17
|
-
|
|
19
|
+
}, [n.document]), c(() => {
|
|
20
|
+
let e = !1;
|
|
21
|
+
return C(m).then((t) => {
|
|
22
|
+
e || k(t);
|
|
23
|
+
}), () => {
|
|
24
|
+
e = !0;
|
|
25
|
+
};
|
|
26
|
+
}, [m]);
|
|
27
|
+
const { html: u, errors: l } = v, i = E(
|
|
18
28
|
(e) => {
|
|
19
29
|
e.data?.type === "BLOCK_SELECTED" && d(e.data.blockId);
|
|
20
30
|
},
|
|
21
31
|
[d]
|
|
22
32
|
);
|
|
23
|
-
|
|
24
|
-
const
|
|
33
|
+
c(() => (window.addEventListener("message", i), () => window.removeEventListener("message", i)), [i]);
|
|
34
|
+
const a = n.selectedBlockId ? `.block-${n.selectedBlockId} { outline: 2px solid #6366f1 !important; outline-offset: -2px; position: relative; }` : "", f = `
|
|
25
35
|
(function() {
|
|
26
36
|
document.addEventListener('click', function(e) {
|
|
27
37
|
e.preventDefault();
|
|
@@ -50,15 +60,15 @@ function B({
|
|
|
50
60
|
}, true);
|
|
51
61
|
})();
|
|
52
62
|
`;
|
|
53
|
-
return
|
|
54
|
-
if (
|
|
55
|
-
const e =
|
|
63
|
+
return c(() => {
|
|
64
|
+
if (o.current) {
|
|
65
|
+
const e = o.current.contentDocument;
|
|
56
66
|
if (e) {
|
|
57
67
|
e.open(), e.write(u), e.close();
|
|
58
68
|
const t = e.createElement("script");
|
|
59
69
|
t.textContent = f, e.body.appendChild(t);
|
|
60
|
-
const
|
|
61
|
-
|
|
70
|
+
const h = e.createElement("style");
|
|
71
|
+
h.textContent = `
|
|
62
72
|
[class*="block-"] {
|
|
63
73
|
cursor: pointer;
|
|
64
74
|
transition: outline 0.15s ease;
|
|
@@ -67,44 +77,44 @@ function B({
|
|
|
67
77
|
outline: 1px dashed #94a3b8 !important;
|
|
68
78
|
outline-offset: -1px;
|
|
69
79
|
}
|
|
70
|
-
${
|
|
71
|
-
`, e.head.appendChild(
|
|
80
|
+
${a}
|
|
81
|
+
`, e.head.appendChild(h);
|
|
72
82
|
}
|
|
73
83
|
}
|
|
74
|
-
}, [u,
|
|
75
|
-
if (
|
|
76
|
-
const e =
|
|
84
|
+
}, [u, a, f]), c(() => {
|
|
85
|
+
if (o.current) {
|
|
86
|
+
const e = o.current.contentDocument;
|
|
77
87
|
if (e) {
|
|
78
88
|
let t = e.getElementById("selection-highlight");
|
|
79
|
-
t || (t = e.createElement("style"), t.id = "selection-highlight", e.head.appendChild(t)), t.textContent =
|
|
89
|
+
t || (t = e.createElement("style"), t.id = "selection-highlight", e.head.appendChild(t)), t.textContent = a;
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
|
-
}, [n.selectedBlockId,
|
|
83
|
-
|
|
84
|
-
/* @__PURE__ */
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
}, [n.selectedBlockId, a]), /* @__PURE__ */ r("div", { className: "flex flex-col h-full", children: [
|
|
93
|
+
p && /* @__PURE__ */ r("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-background", children: [
|
|
94
|
+
/* @__PURE__ */ s("span", { className: "text-sm font-semibold text-foreground", children: "Preview" }),
|
|
95
|
+
l.length > 0 && /* @__PURE__ */ r("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-0.5 rounded-md", children: [
|
|
96
|
+
l.length,
|
|
87
97
|
" warning",
|
|
88
|
-
|
|
98
|
+
l.length !== 1 ? "s" : ""
|
|
89
99
|
] })
|
|
90
100
|
] }),
|
|
91
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ s("div", { className: "flex-1 overflow-auto bg-muted flex justify-center", children: /* @__PURE__ */ s(
|
|
92
102
|
"iframe",
|
|
93
103
|
{
|
|
94
|
-
ref:
|
|
104
|
+
ref: o,
|
|
95
105
|
title: "Email Preview",
|
|
96
106
|
className: "h-full border-0 bg-white transition-all duration-200",
|
|
97
107
|
style: {
|
|
98
|
-
width:
|
|
108
|
+
width: g === "mobile" ? I : "100%",
|
|
99
109
|
maxWidth: "100%"
|
|
100
110
|
},
|
|
101
111
|
sandbox: "allow-same-origin allow-scripts"
|
|
102
112
|
}
|
|
103
113
|
) }),
|
|
104
|
-
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
-
/* @__PURE__ */
|
|
114
|
+
l.length > 0 && /* @__PURE__ */ r("div", { className: "max-h-28 overflow-auto border-t border-border bg-amber-50/50 p-3", children: [
|
|
115
|
+
/* @__PURE__ */ s("div", { className: "text-xs font-semibold text-amber-700 mb-2", children: "Warnings" }),
|
|
116
|
+
/* @__PURE__ */ s("div", { className: "space-y-1", children: l.map((e, t) => /* @__PURE__ */ r("div", { className: "text-xs text-amber-600", children: [
|
|
117
|
+
/* @__PURE__ */ r("span", { className: "font-mono", children: [
|
|
108
118
|
"Line ",
|
|
109
119
|
e.line,
|
|
110
120
|
":"
|
|
@@ -116,5 +126,5 @@ function B({
|
|
|
116
126
|
] });
|
|
117
127
|
}
|
|
118
128
|
export {
|
|
119
|
-
|
|
129
|
+
S as InteractivePreview
|
|
120
130
|
};
|
|
@@ -5,7 +5,33 @@ interface MjmlEditorProps {
|
|
|
5
5
|
className?: string;
|
|
6
6
|
defaultTheme?: 'light' | 'dark' | 'system';
|
|
7
7
|
liquidSchema?: LiquidSchema;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to apply the theme class to document.documentElement.
|
|
10
|
+
* This is needed for Radix UI portals (popovers, menus, etc.) which
|
|
11
|
+
* render outside the editor container.
|
|
12
|
+
*
|
|
13
|
+
* Set to false if your app manages document-level theme classes
|
|
14
|
+
* and you want to prevent conflicts.
|
|
15
|
+
*
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
applyThemeToDocument?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to show the theme toggle in the toolbar.
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
showThemeToggle?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the tree panel (left) is open by default.
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
defaultLeftPanelOpen?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the inspector panel (right) is open by default.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
defaultRightPanelOpen?: boolean;
|
|
8
34
|
}
|
|
9
|
-
export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, applyThemeToDocument, showThemeToggle, defaultLeftPanelOpen, defaultRightPanelOpen, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
10
36
|
export {};
|
|
11
37
|
//# sourceMappingURL=MjmlEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MjmlEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/MjmlEditor.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"MjmlEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/MjmlEditor.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAoKD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAuB,EACvB,YAAY,EACZ,oBAA2B,EAC3B,eAAsB,EACtB,oBAA2B,EAC3B,qBAA6B,GAC9B,EAAE,eAAe,2CA4CjB"}
|
|
@@ -1,111 +1,154 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { EditorProvider as
|
|
4
|
-
import { ThemeProvider as
|
|
5
|
-
import { LiquidSchemaProvider as
|
|
6
|
-
import { OutlineTree as
|
|
7
|
-
import { EditorCanvas as
|
|
8
|
-
import { BlockInspector as
|
|
9
|
-
import { GlobalStylesPanel as
|
|
10
|
-
import { FloatingPanel as
|
|
11
|
-
import { createEmptyDocument as
|
|
12
|
-
function
|
|
13
|
-
if (!
|
|
14
|
-
return
|
|
1
|
+
import { jsx as e, jsxs as w, Fragment as L } from "react/jsx-runtime";
|
|
2
|
+
import { useState as c, useEffect as a, useCallback as D, useRef as M } from "react";
|
|
3
|
+
import { EditorProvider as O, useEditor as j } from "../../context/EditorContext.js";
|
|
4
|
+
import { ThemeProvider as x, useTheme as C } from "../../context/ThemeContext.js";
|
|
5
|
+
import { LiquidSchemaProvider as S } from "../../context/LiquidSchemaContext.js";
|
|
6
|
+
import { OutlineTree as K, GLOBAL_STYLES_ID as B } from "./OutlineTree.js";
|
|
7
|
+
import { EditorCanvas as R } from "./EditorCanvas.js";
|
|
8
|
+
import { BlockInspector as A } from "./BlockInspector.js";
|
|
9
|
+
import { GlobalStylesPanel as F } from "./GlobalStylesPanel.js";
|
|
10
|
+
import { FloatingPanel as I } from "../ui/floating-panel.js";
|
|
11
|
+
import { createEmptyDocument as b, parseMjml as N, serializeMjml as $ } from "../../lib/mjml/parser.js";
|
|
12
|
+
function z(n) {
|
|
13
|
+
if (!n || n.trim() === "")
|
|
14
|
+
return b();
|
|
15
15
|
try {
|
|
16
|
-
return
|
|
17
|
-
} catch (
|
|
18
|
-
return console.error("Failed to parse MJML:",
|
|
16
|
+
return N(n);
|
|
17
|
+
} catch (o) {
|
|
18
|
+
return console.error("Failed to parse MJML:", o), b();
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
function G({
|
|
22
|
+
className: n,
|
|
23
|
+
children: o
|
|
24
|
+
}) {
|
|
25
|
+
const { resolvedTheme: l } = C();
|
|
26
|
+
return /* @__PURE__ */ e(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
className: `mjml-editor ${l} relative h-full w-full overflow-hidden bg-background text-foreground antialiased ${n || ""}`,
|
|
30
|
+
children: o
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function H({
|
|
35
|
+
onChange: n,
|
|
36
|
+
showThemeToggle: o = !0,
|
|
37
|
+
defaultLeftPanelOpen: l = !0,
|
|
38
|
+
defaultRightPanelOpen: T = !1
|
|
39
|
+
}) {
|
|
40
|
+
const { state: r, undo: d, redo: u, canUndo: f, canRedo: m, deleteBlock: p, selectBlock: h } = j(), [i, g] = c(l), [s, k] = c(T), [y, P] = c("edit"), E = M(n);
|
|
41
|
+
return a(() => {
|
|
42
|
+
E.current = n;
|
|
43
|
+
}, [n]), a(() => {
|
|
44
|
+
r.selectedBlockId && k(!0);
|
|
45
|
+
}, [r.selectedBlockId]), a(() => {
|
|
46
|
+
const v = $(r.document);
|
|
47
|
+
E.current(v);
|
|
48
|
+
}, [r.document]), a(() => {
|
|
49
|
+
const v = (t) => {
|
|
31
50
|
if (!(t.target instanceof HTMLInputElement || t.target instanceof HTMLTextAreaElement || t.target?.isContentEditable)) {
|
|
32
51
|
if ((t.metaKey || t.ctrlKey) && t.key === "z") {
|
|
33
|
-
t.preventDefault(), t.shiftKey ?
|
|
52
|
+
t.preventDefault(), t.shiftKey ? m && u() : f && d();
|
|
34
53
|
return;
|
|
35
54
|
}
|
|
36
|
-
if ((t.key === "Delete" || t.key === "Backspace") &&
|
|
37
|
-
t.preventDefault(),
|
|
55
|
+
if ((t.key === "Delete" || t.key === "Backspace") && r.selectedBlockId && r.selectedBlockId !== B) {
|
|
56
|
+
t.preventDefault(), p(r.selectedBlockId);
|
|
38
57
|
return;
|
|
39
58
|
}
|
|
40
|
-
if (t.key === "Escape" &&
|
|
41
|
-
t.preventDefault(),
|
|
59
|
+
if (t.key === "Escape" && r.selectedBlockId) {
|
|
60
|
+
t.preventDefault(), h(null);
|
|
42
61
|
return;
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
};
|
|
46
|
-
return window.addEventListener("keydown",
|
|
65
|
+
return window.addEventListener("keydown", v), () => window.removeEventListener("keydown", v);
|
|
47
66
|
}, [
|
|
48
|
-
l,
|
|
49
|
-
i,
|
|
50
|
-
c,
|
|
51
|
-
a,
|
|
52
67
|
d,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
u,
|
|
69
|
+
f,
|
|
70
|
+
m,
|
|
71
|
+
p,
|
|
72
|
+
h,
|
|
73
|
+
r.selectedBlockId
|
|
74
|
+
]), /* @__PURE__ */ w("div", { className: "relative h-full overflow-hidden", children: [
|
|
75
|
+
/* @__PURE__ */ e("div", { className: "absolute inset-0 bg-canvas", children: /* @__PURE__ */ e(
|
|
76
|
+
R,
|
|
58
77
|
{
|
|
59
|
-
activeTab:
|
|
60
|
-
onTabChange:
|
|
61
|
-
leftPanelOpen:
|
|
62
|
-
rightPanelOpen:
|
|
78
|
+
activeTab: y,
|
|
79
|
+
onTabChange: P,
|
|
80
|
+
leftPanelOpen: i,
|
|
81
|
+
rightPanelOpen: s,
|
|
82
|
+
showThemeToggle: o
|
|
63
83
|
}
|
|
64
84
|
) }),
|
|
65
|
-
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
|
|
85
|
+
y === "edit" && /* @__PURE__ */ w(L, { children: [
|
|
86
|
+
/* @__PURE__ */ e(
|
|
87
|
+
I,
|
|
68
88
|
{
|
|
69
89
|
side: "left",
|
|
70
|
-
isOpen:
|
|
71
|
-
onToggle: () => g(!
|
|
90
|
+
isOpen: i,
|
|
91
|
+
onToggle: () => g(!i),
|
|
72
92
|
width: 256,
|
|
73
|
-
children: /* @__PURE__ */
|
|
93
|
+
children: /* @__PURE__ */ e(K, { onTogglePanel: () => g(!1) })
|
|
74
94
|
}
|
|
75
95
|
),
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
|
|
96
|
+
/* @__PURE__ */ e(
|
|
97
|
+
I,
|
|
78
98
|
{
|
|
79
99
|
side: "right",
|
|
80
|
-
isOpen:
|
|
81
|
-
onToggle: () =>
|
|
100
|
+
isOpen: s,
|
|
101
|
+
onToggle: () => k(!s),
|
|
82
102
|
width: 300,
|
|
83
|
-
children:
|
|
84
|
-
|
|
103
|
+
children: r.selectedBlockId === B ? /* @__PURE__ */ e(
|
|
104
|
+
F,
|
|
85
105
|
{
|
|
86
|
-
onTogglePanel: () =>
|
|
106
|
+
onTogglePanel: () => k(!1)
|
|
87
107
|
}
|
|
88
|
-
) : /* @__PURE__ */
|
|
108
|
+
) : /* @__PURE__ */ e(A, { onTogglePanel: () => k(!1) })
|
|
89
109
|
}
|
|
90
110
|
)
|
|
91
111
|
] })
|
|
92
112
|
] });
|
|
93
113
|
}
|
|
94
|
-
function
|
|
95
|
-
value:
|
|
96
|
-
onChange:
|
|
114
|
+
function te({
|
|
115
|
+
value: n,
|
|
116
|
+
onChange: o,
|
|
97
117
|
className: l,
|
|
98
|
-
defaultTheme:
|
|
99
|
-
liquidSchema:
|
|
118
|
+
defaultTheme: T = "system",
|
|
119
|
+
liquidSchema: r,
|
|
120
|
+
applyThemeToDocument: d = !0,
|
|
121
|
+
showThemeToggle: u = !0,
|
|
122
|
+
defaultLeftPanelOpen: f = !0,
|
|
123
|
+
defaultRightPanelOpen: m = !1
|
|
100
124
|
}) {
|
|
101
|
-
const [
|
|
125
|
+
const [p, h] = c(!1);
|
|
126
|
+
a(() => {
|
|
127
|
+
h(!0);
|
|
128
|
+
}, []);
|
|
129
|
+
const [i] = c(() => z(n)), g = D(
|
|
102
130
|
(s) => {
|
|
103
|
-
|
|
131
|
+
o(s);
|
|
104
132
|
},
|
|
105
|
-
[
|
|
133
|
+
[o]
|
|
106
134
|
);
|
|
107
|
-
return
|
|
135
|
+
return p ? /* @__PURE__ */ e(
|
|
136
|
+
x,
|
|
137
|
+
{
|
|
138
|
+
defaultTheme: T,
|
|
139
|
+
applyToDocument: d,
|
|
140
|
+
children: /* @__PURE__ */ e(S, { schema: r, children: /* @__PURE__ */ e(G, { className: l, children: /* @__PURE__ */ e(O, { initialDocument: i, children: /* @__PURE__ */ e(
|
|
141
|
+
H,
|
|
142
|
+
{
|
|
143
|
+
onChange: g,
|
|
144
|
+
showThemeToggle: u,
|
|
145
|
+
defaultLeftPanelOpen: f,
|
|
146
|
+
defaultRightPanelOpen: m
|
|
147
|
+
}
|
|
148
|
+
) }) }) })
|
|
149
|
+
}
|
|
150
|
+
) : /* @__PURE__ */ e("div", { className: `h-full w-full bg-background ${l || ""}` });
|
|
108
151
|
}
|
|
109
152
|
export {
|
|
110
|
-
|
|
153
|
+
te as MjmlEditor
|
|
111
154
|
};
|
|
@@ -42,7 +42,7 @@ function E() {
|
|
|
42
42
|
{
|
|
43
43
|
value: a,
|
|
44
44
|
onChange: (e) => f(e.target.value),
|
|
45
|
-
className: "w-full h-full p-4 font-mono text-sm bg-muted border border-border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-ring",
|
|
45
|
+
className: "w-full h-full p-4 font-mono text-sm bg-muted text-foreground border border-border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-ring",
|
|
46
46
|
spellCheck: !1
|
|
47
47
|
}
|
|
48
48
|
) }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SourcePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourcePreview.tsx"],"names":[],"mappings":"AAGA,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAgB,GACjB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"SourcePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourcePreview.tsx"],"names":[],"mappings":"AAGA,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAgB,GACjB,EAAE,kBAAkB,2CAkFpB"}
|