@savvycal/mjml-editor 0.0.1 → 0.0.2
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 +176 -0
- package/dist/components/editor/BlockIcon.d.ts +7 -0
- package/dist/components/editor/BlockIcon.d.ts.map +1 -0
- package/dist/components/editor/BlockInspector.d.ts +6 -0
- package/dist/components/editor/BlockInspector.d.ts.map +1 -0
- package/dist/components/editor/BlockInspector.js +380 -0
- package/dist/components/editor/EditorCanvas.d.ts +11 -0
- package/dist/components/editor/EditorCanvas.d.ts.map +1 -0
- package/dist/components/editor/EditorCanvas.js +116 -0
- package/dist/components/editor/FontEditor.d.ts +2 -0
- package/dist/components/editor/FontEditor.d.ts.map +1 -0
- package/dist/components/editor/FontEditor.js +227 -0
- package/dist/components/editor/GlobalStylesPanel.d.ts +7 -0
- package/dist/components/editor/GlobalStylesPanel.d.ts.map +1 -0
- package/dist/components/editor/GlobalStylesPanel.js +310 -0
- package/dist/components/editor/InteractivePreview.d.ts +8 -0
- package/dist/components/editor/InteractivePreview.d.ts.map +1 -0
- package/dist/components/editor/InteractivePreview.js +120 -0
- package/dist/components/editor/LiquidAutocomplete.d.ts +10 -0
- package/dist/components/editor/LiquidAutocomplete.d.ts.map +1 -0
- package/dist/components/editor/LiquidAutocomplete.js +70 -0
- package/dist/components/editor/LiquidInput.d.ts +12 -0
- package/dist/components/editor/LiquidInput.d.ts.map +1 -0
- package/dist/components/editor/LiquidInput.js +185 -0
- package/dist/components/editor/MjmlEditor.d.ts +11 -0
- package/dist/components/editor/MjmlEditor.d.ts.map +1 -0
- package/dist/components/editor/MjmlEditor.js +111 -0
- package/dist/components/editor/OutlineTree.d.ts +7 -0
- package/dist/components/editor/OutlineTree.d.ts.map +1 -0
- package/dist/components/editor/OutlineTree.js +282 -0
- package/dist/components/editor/SourceEditor.d.ts +2 -0
- package/dist/components/editor/SourceEditor.d.ts.map +1 -0
- package/dist/components/editor/SourceEditor.js +70 -0
- package/dist/components/editor/SourcePreview.d.ts +7 -0
- package/dist/components/editor/SourcePreview.d.ts.map +1 -0
- package/dist/components/editor/SourcePreview.js +55 -0
- package/dist/components/editor/TiptapEditor.d.ts +12 -0
- package/dist/components/editor/TiptapEditor.d.ts.map +1 -0
- package/dist/components/editor/TiptapEditor.js +330 -0
- package/dist/components/editor/VisualEditor.d.ts +7 -0
- package/dist/components/editor/VisualEditor.d.ts.map +1 -0
- package/dist/components/editor/VisualEditor.js +51 -0
- package/dist/components/editor/visual-blocks/VisualBlock.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualBlock.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualBlock.js +34 -0
- package/dist/components/editor/visual-blocks/VisualButton.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualButton.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualButton.js +111 -0
- package/dist/components/editor/visual-blocks/VisualColumn.d.ts +8 -0
- package/dist/components/editor/visual-blocks/VisualColumn.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualColumn.js +44 -0
- package/dist/components/editor/visual-blocks/VisualDivider.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualDivider.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualDivider.js +41 -0
- package/dist/components/editor/visual-blocks/VisualImage.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualImage.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualImage.js +48 -0
- package/dist/components/editor/visual-blocks/VisualRaw.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualRaw.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualRaw.js +32 -0
- package/dist/components/editor/visual-blocks/VisualSection.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualSection.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualSection.js +131 -0
- package/dist/components/editor/visual-blocks/VisualSocial.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualSocial.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualSocial.js +62 -0
- package/dist/components/editor/visual-blocks/VisualSpacer.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualSpacer.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualSpacer.js +30 -0
- package/dist/components/editor/visual-blocks/VisualText.d.ts +7 -0
- package/dist/components/editor/visual-blocks/VisualText.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/VisualText.js +103 -0
- package/dist/components/editor/visual-blocks/helpers.d.ts +13 -0
- package/dist/components/editor/visual-blocks/helpers.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/helpers.js +44 -0
- package/dist/components/editor/visual-blocks/useResolvedAttributes.d.ts +7 -0
- package/dist/components/editor/visual-blocks/useResolvedAttributes.d.ts.map +1 -0
- package/dist/components/editor/visual-blocks/useResolvedAttributes.js +12 -0
- package/dist/components/ui/badge.d.ts +10 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +26 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +54 -0
- package/dist/components/ui/card.d.ts +10 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/collapsible.d.ts +6 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +7 -0
- package/dist/components/ui/floating-panel.d.ts +12 -0
- package/dist/components/ui/floating-panel.d.ts.map +1 -0
- package/dist/components/ui/floating-panel.js +54 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +26 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +23 -0
- package/dist/components/ui/popover.d.ts +8 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +39 -0
- package/dist/components/ui/resizable-split-pane.d.ts +10 -0
- package/dist/components/ui/resizable-split-pane.d.ts.map +1 -0
- package/dist/components/ui/resizable-split-pane.js +65 -0
- package/dist/components/ui/scroll-area.d.ts +10 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +69 -0
- package/dist/components/ui/select.d.ts +16 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +145 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +8 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tabs.js +68 -0
- package/dist/components/ui/theme-toggle.d.ts +2 -0
- package/dist/components/ui/theme-toggle.d.ts.map +1 -0
- package/dist/components/ui/theme-toggle.js +58 -0
- package/dist/context/EditorContext.d.ts +40 -0
- package/dist/context/EditorContext.d.ts.map +1 -0
- package/dist/context/EditorContext.js +576 -0
- package/dist/context/LiquidSchemaContext.d.ts +10 -0
- package/dist/context/LiquidSchemaContext.d.ts.map +1 -0
- package/dist/context/LiquidSchemaContext.js +16 -0
- package/dist/context/ThemeContext.d.ts +18 -0
- package/dist/context/ThemeContext.d.ts.map +1 -0
- package/dist/context/ThemeContext.js +53 -0
- package/dist/extensions/LiquidHighlight.d.ts +3 -0
- package/dist/extensions/LiquidHighlight.d.ts.map +1 -0
- package/dist/extensions/LiquidHighlight.js +58 -0
- package/dist/extensions/LiquidSuggestion.d.ts +18 -0
- package/dist/extensions/LiquidSuggestion.d.ts.map +1 -0
- package/dist/extensions/LiquidSuggestion.js +119 -0
- package/dist/hooks/useFontLoader.d.ts +6 -0
- package/dist/hooks/useFontLoader.d.ts.map +1 -0
- package/dist/hooks/useFontLoader.js +21 -0
- package/dist/hooks/useStyleLoader.d.ts +11 -0
- package/dist/hooks/useStyleLoader.d.ts.map +1 -0
- package/dist/hooks/useStyleLoader.js +26 -0
- package/dist/index.d.ts +6 -150
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -57452
- package/dist/lib/html-utils.d.ts +23 -0
- package/dist/lib/html-utils.d.ts.map +1 -0
- package/dist/lib/html-utils.js +25 -0
- package/dist/lib/mjml/attributes.d.ts +100 -0
- package/dist/lib/mjml/attributes.d.ts.map +1 -0
- package/dist/lib/mjml/attributes.js +105 -0
- package/dist/lib/mjml/parser.d.ts +67 -0
- package/dist/lib/mjml/parser.d.ts.map +1 -0
- package/dist/lib/mjml/parser.js +184 -0
- package/dist/lib/mjml/parser.test.d.ts +2 -0
- package/dist/lib/mjml/parser.test.d.ts.map +1 -0
- package/dist/lib/mjml/renderer.d.ts +23 -0
- package/dist/lib/mjml/renderer.d.ts.map +1 -0
- package/dist/lib/mjml/renderer.js +72 -0
- package/dist/lib/mjml/schema.d.ts +21 -0
- package/dist/lib/mjml/schema.d.ts.map +1 -0
- package/dist/lib/mjml/schema.js +1307 -0
- package/dist/lib/mjml/scopeCSS.d.ts +21 -0
- package/dist/lib/mjml/scopeCSS.d.ts.map +1 -0
- package/dist/lib/mjml/scopeCSS.js +67 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +8 -0
- package/dist/styles.d.ts +1 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +1 -0
- package/dist/types/liquid.d.ts +28 -0
- package/dist/types/liquid.d.ts.map +1 -0
- package/dist/types/mjml.d.ts +101 -0
- package/dist/types/mjml.d.ts.map +1 -0
- package/package.json +12 -9
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# MJML Visual Email Editor
|
|
2
|
+
|
|
3
|
+
A React-based visual editor for MJML email templates. Built for embedding in applications that need a user-friendly way to edit email templates while keeping MJML markup as the source of truth.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Block-based editing** - Visual representation of MJML structure with sections, columns, and content blocks
|
|
8
|
+
- **Live preview** - Side-by-side HTML preview rendered in real-time
|
|
9
|
+
- **Property inspector** - Edit block attributes through a settings panel
|
|
10
|
+
- **Drag and drop** - Reorder blocks within columns
|
|
11
|
+
- **Undo/redo** - Full history support with keyboard shortcuts
|
|
12
|
+
- **MJML in, MJML out** - Takes MJML markup as input, returns modified MJML on change
|
|
13
|
+
- **Liquid template support** - Autocomplete for Liquid variables and tags
|
|
14
|
+
- **Theme support** - Light, dark, and system theme modes
|
|
15
|
+
|
|
16
|
+
## Supported Components
|
|
17
|
+
|
|
18
|
+
| Component | Description |
|
|
19
|
+
|-----------|-------------|
|
|
20
|
+
| `mj-section` | Row containers with background color/image |
|
|
21
|
+
| `mj-column` | Responsive columns within sections |
|
|
22
|
+
| `mj-text` | Text content with typography settings |
|
|
23
|
+
| `mj-image` | Images with dimensions, alt text, and links |
|
|
24
|
+
| `mj-button` | Call-to-action buttons with styling |
|
|
25
|
+
| `mj-divider` | Horizontal separators |
|
|
26
|
+
| `mj-spacer` | Vertical spacing |
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @savvycal/mjml-editor
|
|
32
|
+
# or
|
|
33
|
+
pnpm add @savvycal/mjml-editor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Peer Dependencies
|
|
37
|
+
|
|
38
|
+
This library requires React 18 or 19:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install react react-dom
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Styles
|
|
45
|
+
|
|
46
|
+
You must import the library's CSS in your application:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import '@savvycal/mjml-editor/styles.css';
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { useState } from 'react';
|
|
56
|
+
import { MjmlEditor } from '@savvycal/mjml-editor';
|
|
57
|
+
import '@savvycal/mjml-editor/styles.css';
|
|
58
|
+
|
|
59
|
+
function App() {
|
|
60
|
+
const [mjml, setMjml] = useState(initialMjml);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<MjmlEditor
|
|
64
|
+
value={mjml}
|
|
65
|
+
onChange={setMjml}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Props
|
|
72
|
+
|
|
73
|
+
| Prop | Type | Description |
|
|
74
|
+
|------|------|-------------|
|
|
75
|
+
| `value` | `string` | MJML markup string (required) |
|
|
76
|
+
| `onChange` | `(mjml: string) => void` | Called when the document changes (required) |
|
|
77
|
+
| `className` | `string` | Optional CSS class for the container |
|
|
78
|
+
| `defaultTheme` | `'light' \| 'dark' \| 'system'` | Theme preference (default: `'system'`) |
|
|
79
|
+
| `liquidSchema` | `LiquidSchema` | Optional schema for Liquid template autocomplete |
|
|
80
|
+
|
|
81
|
+
## Liquid Template Support
|
|
82
|
+
|
|
83
|
+
The editor provides autocomplete for Liquid template variables and tags. Pass a `liquidSchema` prop to enable this feature:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { MjmlEditor, type LiquidSchema } from '@savvycal/mjml-editor';
|
|
87
|
+
import '@savvycal/mjml-editor/styles.css';
|
|
88
|
+
|
|
89
|
+
const liquidSchema: LiquidSchema = {
|
|
90
|
+
variables: [
|
|
91
|
+
{ name: 'user.name', description: 'Recipient name' },
|
|
92
|
+
{ name: 'user.email', description: 'Recipient email' },
|
|
93
|
+
{ name: 'company.name', description: 'Company name' },
|
|
94
|
+
],
|
|
95
|
+
tags: [
|
|
96
|
+
{ name: 'if', description: 'Conditional block' },
|
|
97
|
+
{ name: 'for', description: 'Loop block' },
|
|
98
|
+
{ name: 'unless', description: 'Negative conditional' },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function App() {
|
|
103
|
+
const [mjml, setMjml] = useState(initialMjml);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<MjmlEditor
|
|
107
|
+
value={mjml}
|
|
108
|
+
onChange={setMjml}
|
|
109
|
+
liquidSchema={liquidSchema}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
When editing text content, typing `{{` will trigger variable autocomplete and `{%` will trigger tag autocomplete.
|
|
116
|
+
|
|
117
|
+
## Exported Types
|
|
118
|
+
|
|
119
|
+
The library exports TypeScript types for integration:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import type {
|
|
123
|
+
MjmlNode, // MJML document node structure
|
|
124
|
+
MjmlTagName, // Union of supported MJML tag names
|
|
125
|
+
ContentBlockType, // Union of content block types
|
|
126
|
+
LiquidSchema, // Schema for Liquid autocomplete
|
|
127
|
+
LiquidSchemaItem, // Individual variable/tag definition
|
|
128
|
+
} from '@savvycal/mjml-editor';
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### LiquidSchema
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
interface LiquidSchemaItem {
|
|
135
|
+
name: string; // Variable or tag name (e.g., "user.name")
|
|
136
|
+
description?: string; // Description shown in autocomplete
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface LiquidSchema {
|
|
140
|
+
variables: LiquidSchemaItem[]; // {{ variable }} syntax
|
|
141
|
+
tags: LiquidSchemaItem[]; // {% tag %} syntax
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Theme Utilities
|
|
146
|
+
|
|
147
|
+
The library exports theme utilities if you need to integrate with or control the theme externally:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { ThemeProvider, useTheme, ThemeToggle } from '@savvycal/mjml-editor';
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Export | Description |
|
|
154
|
+
|--------|-------------|
|
|
155
|
+
| `ThemeProvider` | Context provider for theme management |
|
|
156
|
+
| `useTheme()` | Hook returning `{ theme, setTheme }` |
|
|
157
|
+
| `ThemeToggle` | Pre-built UI component for theme switching |
|
|
158
|
+
|
|
159
|
+
Note: `MjmlEditor` includes its own `ThemeProvider`, so you don't need to wrap it. These exports are for advanced use cases where you need theme access outside the editor.
|
|
160
|
+
|
|
161
|
+
## Keyboard Shortcuts
|
|
162
|
+
|
|
163
|
+
| Shortcut | Action |
|
|
164
|
+
|----------|--------|
|
|
165
|
+
| `Cmd/Ctrl + Z` | Undo |
|
|
166
|
+
| `Cmd/Ctrl + Shift + Z` | Redo |
|
|
167
|
+
| `Delete` / `Backspace` | Delete selected block |
|
|
168
|
+
| `Escape` | Deselect block |
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
See [DEVELOPING.md](../../DEVELOPING.md) for development setup and release instructions.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockIcon.d.ts","sourceRoot":"","sources":["../../../src/components/editor/BlockIcon.tsx"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,cAAc,2CAmB5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockInspector.d.ts","sourceRoot":"","sources":["../../../src/components/editor/BlockInspector.tsx"],"names":[],"mappings":"AA8HA,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,EAAE,aAAa,EAAE,EAAE,mBAAmB,2CAuHpE"}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { jsxs as a, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as F } from "react";
|
|
3
|
+
import { PanelRightClose as v, X as O, ChevronRight as _ } from "lucide-react";
|
|
4
|
+
import { useEditor as N } from "../../context/EditorContext.js";
|
|
5
|
+
import { ScrollArea as G } from "../ui/scroll-area.js";
|
|
6
|
+
import { Button as C } from "../ui/button.js";
|
|
7
|
+
import { Label as x } from "../ui/label.js";
|
|
8
|
+
import { Input as b } from "../ui/input.js";
|
|
9
|
+
import { Badge as I } from "../ui/badge.js";
|
|
10
|
+
import { Collapsible as R, CollapsibleTrigger as L, CollapsibleContent as V } from "../ui/collapsible.js";
|
|
11
|
+
import { Select as y, SelectTrigger as j, SelectValue as w, SelectContent as S, SelectItem as A } from "../ui/select.js";
|
|
12
|
+
import { getSchemaForTag as z } from "../../lib/mjml/schema.js";
|
|
13
|
+
import { parseClassNames as E, removeClassFromNode as B, addClassToNode as K } from "../../lib/mjml/attributes.js";
|
|
14
|
+
import { cn as U } from "../../lib/utils.js";
|
|
15
|
+
const P = {
|
|
16
|
+
primary: "Primary",
|
|
17
|
+
background: "Background",
|
|
18
|
+
typography: "Typography",
|
|
19
|
+
border: "Border",
|
|
20
|
+
inner: "Inner Styling",
|
|
21
|
+
sizing: "Sizing",
|
|
22
|
+
spacing: "Spacing",
|
|
23
|
+
link: "Link",
|
|
24
|
+
advanced: "Advanced"
|
|
25
|
+
}, T = [
|
|
26
|
+
"primary",
|
|
27
|
+
"background",
|
|
28
|
+
"typography",
|
|
29
|
+
"border",
|
|
30
|
+
"inner",
|
|
31
|
+
"sizing",
|
|
32
|
+
"spacing",
|
|
33
|
+
"link",
|
|
34
|
+
"advanced"
|
|
35
|
+
];
|
|
36
|
+
function $({ node: n }) {
|
|
37
|
+
const { definedClasses: r, updateAttributes: i } = N(), d = E(n.attributes["mj-class"]);
|
|
38
|
+
if (r.length === 0)
|
|
39
|
+
return null;
|
|
40
|
+
const u = r.filter(
|
|
41
|
+
(t) => !d.includes(t)
|
|
42
|
+
), l = (t) => {
|
|
43
|
+
if (!n._id || !t) return;
|
|
44
|
+
const o = K(n, t);
|
|
45
|
+
i(n._id, { "mj-class": o["mj-class"] || "" });
|
|
46
|
+
}, c = (t) => {
|
|
47
|
+
if (!n._id) return;
|
|
48
|
+
const o = B(n, t);
|
|
49
|
+
i(n._id, {
|
|
50
|
+
"mj-class": o["mj-class"] || ""
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2 pb-4 mb-4 border-b border-border", children: [
|
|
54
|
+
/* @__PURE__ */ e(x, { className: "text-xs font-medium text-foreground-muted", children: "Classes" }),
|
|
55
|
+
d.length > 0 && /* @__PURE__ */ e("div", { className: "flex flex-wrap gap-1", children: d.map((t) => /* @__PURE__ */ a(
|
|
56
|
+
I,
|
|
57
|
+
{
|
|
58
|
+
variant: "secondary",
|
|
59
|
+
className: "text-xs gap-1 pr-1",
|
|
60
|
+
children: [
|
|
61
|
+
t,
|
|
62
|
+
/* @__PURE__ */ e(
|
|
63
|
+
"button",
|
|
64
|
+
{
|
|
65
|
+
type: "button",
|
|
66
|
+
onClick: () => c(t),
|
|
67
|
+
className: "ml-1 hover:text-destructive transition-colors",
|
|
68
|
+
children: /* @__PURE__ */ e(O, { className: "h-3 w-3" })
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
t
|
|
74
|
+
)) }),
|
|
75
|
+
u.length > 0 && /* @__PURE__ */ a(y, { onValueChange: l, value: "", children: [
|
|
76
|
+
/* @__PURE__ */ e(j, { className: "h-8 text-xs border-border-subtle", children: /* @__PURE__ */ e(w, { placeholder: "Add class..." }) }),
|
|
77
|
+
/* @__PURE__ */ e(S, { children: u.map((t) => /* @__PURE__ */ e(A, { value: t, className: "text-xs", children: t }, t)) })
|
|
78
|
+
] })
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
function le({ onTogglePanel: n }) {
|
|
82
|
+
const { selectedBlock: r, updateAttributes: i, getInheritedValue: d, definedClasses: u } = N();
|
|
83
|
+
if (!r)
|
|
84
|
+
return /* @__PURE__ */ a("div", { className: "flex flex-col h-full", children: [
|
|
85
|
+
/* @__PURE__ */ e("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-inspector-header", children: /* @__PURE__ */ a("div", { className: "flex items-center gap-2", children: [
|
|
86
|
+
n && /* @__PURE__ */ e(
|
|
87
|
+
C,
|
|
88
|
+
{
|
|
89
|
+
variant: "ghost",
|
|
90
|
+
size: "icon-sm",
|
|
91
|
+
onClick: n,
|
|
92
|
+
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent",
|
|
93
|
+
title: "Collapse panel",
|
|
94
|
+
children: /* @__PURE__ */ e(v, { className: "h-4 w-4" })
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ e("h3", { className: "text-sm font-semibold text-foreground", children: "Inspector" })
|
|
98
|
+
] }) }),
|
|
99
|
+
/* @__PURE__ */ e("div", { className: "flex-1 flex items-center justify-center p-6", children: /* @__PURE__ */ a("p", { className: "text-sm text-foreground-muted text-center leading-relaxed", children: [
|
|
100
|
+
"Select a block to",
|
|
101
|
+
/* @__PURE__ */ e("br", {}),
|
|
102
|
+
"edit its properties"
|
|
103
|
+
] }) })
|
|
104
|
+
] });
|
|
105
|
+
const l = z(r.tagName), c = r.tagName.replace("mj-", "").charAt(0).toUpperCase() + r.tagName.replace("mj-", "").slice(1), t = (s, p) => {
|
|
106
|
+
r._id && i(r._id, { [s]: p });
|
|
107
|
+
}, o = l ? Object.entries(l).reduce(
|
|
108
|
+
(s, [p, h]) => {
|
|
109
|
+
const f = h.group || "primary";
|
|
110
|
+
return s[f] || (s[f] = []), s[f].push({ key: p, schema: h }), s;
|
|
111
|
+
},
|
|
112
|
+
{}
|
|
113
|
+
) : {}, m = Object.keys(o).some(
|
|
114
|
+
(s) => s !== "primary"
|
|
115
|
+
);
|
|
116
|
+
return /* @__PURE__ */ a("div", { className: "flex flex-col h-full", children: [
|
|
117
|
+
/* @__PURE__ */ e("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-inspector-header", children: /* @__PURE__ */ a("div", { className: "flex items-center gap-2", children: [
|
|
118
|
+
n && /* @__PURE__ */ e(
|
|
119
|
+
C,
|
|
120
|
+
{
|
|
121
|
+
variant: "ghost",
|
|
122
|
+
size: "icon-sm",
|
|
123
|
+
onClick: n,
|
|
124
|
+
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent",
|
|
125
|
+
title: "Collapse panel",
|
|
126
|
+
children: /* @__PURE__ */ e(v, { className: "h-4 w-4" })
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
/* @__PURE__ */ e("h3", { className: "text-sm font-semibold text-foreground", children: c })
|
|
130
|
+
] }) }),
|
|
131
|
+
/* @__PURE__ */ e(G, { className: "flex-1 min-h-0", children: /* @__PURE__ */ a("div", { className: "p-4", children: [
|
|
132
|
+
u.length > 0 && /* @__PURE__ */ e($, { node: r }),
|
|
133
|
+
l ? m ? /* @__PURE__ */ e(
|
|
134
|
+
D,
|
|
135
|
+
{
|
|
136
|
+
groupedAttributes: o,
|
|
137
|
+
selectedBlock: r,
|
|
138
|
+
handleAttributeChange: t
|
|
139
|
+
}
|
|
140
|
+
) : /* @__PURE__ */ e("div", { className: "space-y-5", children: Object.entries(l).map(([s, p]) => /* @__PURE__ */ e(
|
|
141
|
+
g,
|
|
142
|
+
{
|
|
143
|
+
attributeKey: s,
|
|
144
|
+
schema: p,
|
|
145
|
+
value: r.attributes[s] || "",
|
|
146
|
+
onChange: (h) => t(s, h),
|
|
147
|
+
inheritedValue: d(r, s)
|
|
148
|
+
},
|
|
149
|
+
s
|
|
150
|
+
)) }) : /* @__PURE__ */ e("p", { className: "text-sm text-foreground-muted py-4 text-center", children: "No editable properties for this block type." })
|
|
151
|
+
] }) })
|
|
152
|
+
] });
|
|
153
|
+
}
|
|
154
|
+
function D({
|
|
155
|
+
groupedAttributes: n,
|
|
156
|
+
selectedBlock: r,
|
|
157
|
+
handleAttributeChange: i
|
|
158
|
+
}) {
|
|
159
|
+
const [d, u] = F({}), { getInheritedValue: l } = N(), c = (t) => {
|
|
160
|
+
u((o) => ({ ...o, [t]: !o[t] }));
|
|
161
|
+
};
|
|
162
|
+
return /* @__PURE__ */ e("div", { className: "space-y-4", children: T.map((t) => {
|
|
163
|
+
const o = n[t];
|
|
164
|
+
if (!o || o.length === 0) return null;
|
|
165
|
+
if (t === "primary")
|
|
166
|
+
return /* @__PURE__ */ e("div", { className: "space-y-5", children: o.map(({ key: s, schema: p }) => /* @__PURE__ */ e(
|
|
167
|
+
g,
|
|
168
|
+
{
|
|
169
|
+
attributeKey: s,
|
|
170
|
+
schema: p,
|
|
171
|
+
value: r.attributes[s] || "",
|
|
172
|
+
onChange: (h) => i(s, h),
|
|
173
|
+
inheritedValue: l(r, s)
|
|
174
|
+
},
|
|
175
|
+
s
|
|
176
|
+
)) }, t);
|
|
177
|
+
const m = d[t] || !1;
|
|
178
|
+
return /* @__PURE__ */ a(
|
|
179
|
+
R,
|
|
180
|
+
{
|
|
181
|
+
open: m,
|
|
182
|
+
onOpenChange: () => c(t),
|
|
183
|
+
children: [
|
|
184
|
+
/* @__PURE__ */ a(L, { className: "flex items-center gap-1 w-full py-2 text-xs font-medium text-foreground-muted hover:text-foreground transition-colors", children: [
|
|
185
|
+
/* @__PURE__ */ e(
|
|
186
|
+
_,
|
|
187
|
+
{
|
|
188
|
+
className: U(
|
|
189
|
+
"h-3.5 w-3.5 transition-transform",
|
|
190
|
+
m && "rotate-90"
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
P[t]
|
|
195
|
+
] }),
|
|
196
|
+
/* @__PURE__ */ e(V, { children: /* @__PURE__ */ e("div", { className: "space-y-5 pt-2 pb-1", children: o.map(({ key: s, schema: p }) => /* @__PURE__ */ e(
|
|
197
|
+
g,
|
|
198
|
+
{
|
|
199
|
+
attributeKey: s,
|
|
200
|
+
schema: p,
|
|
201
|
+
value: r.attributes[s] || "",
|
|
202
|
+
onChange: (h) => i(s, h),
|
|
203
|
+
inheritedValue: l(r, s)
|
|
204
|
+
},
|
|
205
|
+
s
|
|
206
|
+
)) }) })
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
t
|
|
210
|
+
);
|
|
211
|
+
}) });
|
|
212
|
+
}
|
|
213
|
+
function g({
|
|
214
|
+
attributeKey: n,
|
|
215
|
+
schema: r,
|
|
216
|
+
value: i,
|
|
217
|
+
onChange: d,
|
|
218
|
+
inheritedValue: u
|
|
219
|
+
}) {
|
|
220
|
+
const l = `attr-${n}`, c = u || r.placeholder || r.default || "";
|
|
221
|
+
switch (r.type) {
|
|
222
|
+
case "select": {
|
|
223
|
+
const t = i || u || r.default || r.options?.[0]?.value || "default", o = (m) => {
|
|
224
|
+
d(m === "false" ? "" : m);
|
|
225
|
+
};
|
|
226
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
227
|
+
/* @__PURE__ */ e(
|
|
228
|
+
x,
|
|
229
|
+
{
|
|
230
|
+
htmlFor: l,
|
|
231
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
232
|
+
children: r.label
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
/* @__PURE__ */ a(y, { value: t, onValueChange: o, children: [
|
|
236
|
+
/* @__PURE__ */ e(j, { id: l, className: "h-8 text-xs border-border-subtle", children: /* @__PURE__ */ e(
|
|
237
|
+
w,
|
|
238
|
+
{
|
|
239
|
+
placeholder: `Select ${r.label.toLowerCase()}`
|
|
240
|
+
}
|
|
241
|
+
) }),
|
|
242
|
+
/* @__PURE__ */ e(S, { children: r.options?.map((m) => /* @__PURE__ */ e(
|
|
243
|
+
A,
|
|
244
|
+
{
|
|
245
|
+
value: m.value,
|
|
246
|
+
className: "text-xs",
|
|
247
|
+
children: m.label
|
|
248
|
+
},
|
|
249
|
+
m.value
|
|
250
|
+
)) })
|
|
251
|
+
] })
|
|
252
|
+
] });
|
|
253
|
+
}
|
|
254
|
+
case "color":
|
|
255
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
256
|
+
/* @__PURE__ */ e(
|
|
257
|
+
x,
|
|
258
|
+
{
|
|
259
|
+
htmlFor: l,
|
|
260
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
261
|
+
children: r.label
|
|
262
|
+
}
|
|
263
|
+
),
|
|
264
|
+
/* @__PURE__ */ a("div", { className: "flex gap-2", children: [
|
|
265
|
+
/* @__PURE__ */ e(
|
|
266
|
+
b,
|
|
267
|
+
{
|
|
268
|
+
id: l,
|
|
269
|
+
type: "color",
|
|
270
|
+
value: i || u || r.default || "#000000",
|
|
271
|
+
onChange: (t) => d(t.target.value),
|
|
272
|
+
className: "h-8 w-10 p-0.5 cursor-pointer rounded-md border-border-subtle"
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ e(
|
|
276
|
+
b,
|
|
277
|
+
{
|
|
278
|
+
type: "text",
|
|
279
|
+
value: i || "",
|
|
280
|
+
onChange: (t) => d(t.target.value),
|
|
281
|
+
placeholder: c || "#000000",
|
|
282
|
+
className: "h-8 text-xs flex-1 font-mono border-border-subtle"
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
] })
|
|
286
|
+
] });
|
|
287
|
+
case "padding":
|
|
288
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
289
|
+
/* @__PURE__ */ e(
|
|
290
|
+
x,
|
|
291
|
+
{
|
|
292
|
+
htmlFor: l,
|
|
293
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
294
|
+
children: r.label
|
|
295
|
+
}
|
|
296
|
+
),
|
|
297
|
+
/* @__PURE__ */ e(
|
|
298
|
+
b,
|
|
299
|
+
{
|
|
300
|
+
id: l,
|
|
301
|
+
type: "text",
|
|
302
|
+
value: i || "",
|
|
303
|
+
onChange: (t) => d(t.target.value),
|
|
304
|
+
placeholder: c || "10px 25px",
|
|
305
|
+
className: "h-8 text-xs border-border-subtle"
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
/* @__PURE__ */ e("p", { className: "text-[10px] text-foreground-subtle", children: "Format: top right bottom left (e.g., 10px 20px)" })
|
|
309
|
+
] });
|
|
310
|
+
case "dimension":
|
|
311
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
312
|
+
/* @__PURE__ */ e(
|
|
313
|
+
x,
|
|
314
|
+
{
|
|
315
|
+
htmlFor: l,
|
|
316
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
317
|
+
children: r.label
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
/* @__PURE__ */ e(
|
|
321
|
+
b,
|
|
322
|
+
{
|
|
323
|
+
id: l,
|
|
324
|
+
type: "text",
|
|
325
|
+
value: i || "",
|
|
326
|
+
onChange: (t) => d(t.target.value),
|
|
327
|
+
placeholder: c,
|
|
328
|
+
className: "h-8 text-xs border-border-subtle"
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
] });
|
|
332
|
+
case "url":
|
|
333
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
334
|
+
/* @__PURE__ */ e(
|
|
335
|
+
x,
|
|
336
|
+
{
|
|
337
|
+
htmlFor: l,
|
|
338
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
339
|
+
children: r.label
|
|
340
|
+
}
|
|
341
|
+
),
|
|
342
|
+
/* @__PURE__ */ e(
|
|
343
|
+
b,
|
|
344
|
+
{
|
|
345
|
+
id: l,
|
|
346
|
+
type: "url",
|
|
347
|
+
value: i || "",
|
|
348
|
+
onChange: (t) => d(t.target.value),
|
|
349
|
+
placeholder: c || "https://...",
|
|
350
|
+
className: "h-8 text-xs border-border-subtle"
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
] });
|
|
354
|
+
default:
|
|
355
|
+
return /* @__PURE__ */ a("div", { className: "space-y-2", children: [
|
|
356
|
+
/* @__PURE__ */ e(
|
|
357
|
+
x,
|
|
358
|
+
{
|
|
359
|
+
htmlFor: l,
|
|
360
|
+
className: "text-xs font-medium text-foreground-muted",
|
|
361
|
+
children: r.label
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ e(
|
|
365
|
+
b,
|
|
366
|
+
{
|
|
367
|
+
id: l,
|
|
368
|
+
type: "text",
|
|
369
|
+
value: i || "",
|
|
370
|
+
onChange: (t) => d(t.target.value),
|
|
371
|
+
placeholder: c,
|
|
372
|
+
className: "h-8 text-xs border-border-subtle"
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
] });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
export {
|
|
379
|
+
le as BlockInspector
|
|
380
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PreviewMode = 'desktop' | 'mobile';
|
|
2
|
+
export type EditorTabType = 'edit' | 'preview' | 'source';
|
|
3
|
+
interface EditorCanvasProps {
|
|
4
|
+
activeTab: EditorTabType;
|
|
5
|
+
onTabChange: (tab: EditorTabType) => void;
|
|
6
|
+
leftPanelOpen?: boolean;
|
|
7
|
+
rightPanelOpen?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=EditorCanvas.d.ts.map
|
|
@@ -0,0 +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;CAC1B;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,GACf,EAAE,iBAAiB,2CA0GnB"}
|