@lab2view/vue-email-editor 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lab2View
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,386 @@
1
+ # @lab2view/vue-email-editor
2
+
3
+ A professional, extensible drag-and-drop email editor built with **Vue 3** and **MJML**. Design responsive HTML emails visually with 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.
4
+
5
+ ## Screenshots
6
+
7
+ | Blocks & Layout | Ready-made Templates |
8
+ |:---:|:---:|
9
+ | ![Blocks panel with layout and content blocks](src/assets/capture_blocs_layouts_1.png) | ![Ready-made composite blocks](src/assets/capture_blocs_ready_made_1.png) |
10
+
11
+ | Property Editing | Layers Panel |
12
+ |:---:|:---:|
13
+ | ![Image properties and styling](src/assets/capture_blocs_styes_image_1.png) | ![Document tree layers view](src/assets/capture_layers_1.png) |
14
+
15
+ ## Features
16
+
17
+ ### Visual Drag & Drop Builder
18
+ - Intuitive block-based editor with live preview
19
+ - Drag blocks from the sidebar onto the canvas
20
+ - Reorder and nest elements with visual drop indicators
21
+ - Responsive preview in Desktop, Tablet, and Mobile modes
22
+ - Iframe-isolated canvas for accurate email rendering
23
+
24
+ ### 43 Pre-built Blocks
25
+
26
+ | Category | Count | Examples |
27
+ |----------|-------|---------|
28
+ | **Layout** | 6 | 1-4 columns, sidebar left/right |
29
+ | **Content** | 7 | Text, Image, Button, Divider, Spacer, Social, Hero |
30
+ | **Composite** | 30 | Header, Hero Banner, Pricing, Testimonial, FAQ, Product Card, Footer, and more |
31
+
32
+ ### Rich Text Editing
33
+ Powered by [TipTap](https://tiptap.dev), with inline formatting:
34
+ - Bold, Italic, Underline, Strikethrough
35
+ - Text alignment (left, center, right)
36
+ - Text color picker
37
+ - Link insertion and editing
38
+
39
+ ### MJML-Powered Rendering
40
+ - Real-time MJML to HTML compilation via [mjml-browser](https://github.com/mjmlio/mjml)
41
+ - 13 supported MJML node types
42
+ - Export as MJML, compiled HTML, or design JSON
43
+ - Legacy GrapesJS format detection for migration
44
+
45
+ ### Undo / Redo
46
+ - Full history with keyboard shortcuts (`Ctrl+Z` / `Ctrl+Shift+Z`)
47
+ - Reactive `canUndo` / `canRedo` state
48
+
49
+ ### Property Editing
50
+ - 40+ editable MJML attributes across 11 property types
51
+ - Color pickers, padding controls, alignment selectors
52
+ - Global styles panel (email background, default font, preview text)
53
+ - Custom fonts support
54
+
55
+ ---
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ npm install @lab2view/vue-email-editor
61
+ ```
62
+
63
+ **Peer dependencies:**
64
+ ```bash
65
+ npm install vue@^3.4 mjml-browser@^4.15
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ```vue
71
+ <script setup lang="ts">
72
+ import { ref } from 'vue'
73
+ import { EmailEditor, FR_LABELS } from '@lab2view/vue-email-editor'
74
+
75
+ const editorRef = ref()
76
+ const mjml = ref('')
77
+ const html = ref('')
78
+ const designJson = ref()
79
+ </script>
80
+
81
+ <template>
82
+ <EmailEditor
83
+ ref="editorRef"
84
+ v-model="mjml"
85
+ :design-json="designJson"
86
+ :labels="FR_LABELS"
87
+ @update:compiled-html="html = $event"
88
+ @update:design-json="designJson = $event"
89
+ />
90
+ </template>
91
+ ```
92
+
93
+ ## Theming
94
+
95
+ Customize the editor appearance with the `theme` prop:
96
+
97
+ ```vue
98
+ <EmailEditor
99
+ :theme="{
100
+ primaryColor: '#7C3AED',
101
+ primaryHover: '#6D28D9',
102
+ borderRadius: '8px',
103
+ fontFamily: 'Inter, sans-serif',
104
+ }"
105
+ />
106
+ ```
107
+
108
+ ### Available Theme Properties
109
+
110
+ | Property | Default | Description |
111
+ |----------|---------|-------------|
112
+ | `primaryColor` | `#01A8AB` | Main accent color |
113
+ | `primaryHover` | `#018F91` | Hover state |
114
+ | `primaryActive` | `#017375` | Active/pressed state |
115
+ | `backgroundColor` | `#ffffff` | Panel backgrounds |
116
+ | `borderColor` | `#e5e7eb` | Border color |
117
+ | `textPrimary` | `#1f2937` | Primary text |
118
+ | `textSecondary` | `#6b7280` | Secondary text |
119
+ | `canvasBg` | `#e5e7eb` | Canvas background |
120
+ | `selectionColor` | `#01A8AB` | Selected node outline |
121
+ | `fontFamily` | System stack | UI font family |
122
+ | `fontSize` | `13px` | UI font size |
123
+ | `borderRadius` | `6px` | UI border radius |
124
+
125
+ See `ThemeConfig` in [src/types.ts](src/types.ts) for the full list of 25 properties.
126
+
127
+ ## Internationalization (i18n)
128
+
129
+ The editor ships with English defaults and a complete French translation. Pass custom labels via the `labels` prop:
130
+
131
+ ```vue
132
+ <script setup>
133
+ import { EmailEditor, FR_LABELS } from '@lab2view/vue-email-editor'
134
+ </script>
135
+
136
+ <!-- French UI -->
137
+ <EmailEditor :labels="FR_LABELS" />
138
+
139
+ <!-- Custom labels (partial override) -->
140
+ <EmailEditor :labels="{ editor_title: 'Mein Editor', undo: 'Ruckgangig' }" />
141
+ ```
142
+
143
+ 175+ label keys cover all UI elements: toolbar, sidebar tabs, block names, property labels, option values, and status messages. See `EditorLabels` in [src/labels.ts](src/labels.ts).
144
+
145
+ ## Imperative API
146
+
147
+ Access the editor programmatically via template ref:
148
+
149
+ ```vue
150
+ <script setup>
151
+ import { ref, onMounted } from 'vue'
152
+ import { EmailEditor } from '@lab2view/vue-email-editor'
153
+
154
+ const editor = ref()
155
+
156
+ onMounted(() => {
157
+ // Export
158
+ const mjml = editor.value.getMjml()
159
+ const html = editor.value.getHtml()
160
+ const json = editor.value.getDesignJson()
161
+
162
+ // History
163
+ editor.value.undo()
164
+ editor.value.redo()
165
+
166
+ // Selection
167
+ editor.value.selectNode('node-id')
168
+ const selected = editor.value.getSelectedNode()
169
+ editor.value.clearSelection()
170
+
171
+ // Manipulation
172
+ editor.value.deleteNode('node-id')
173
+ editor.value.duplicateNode('node-id')
174
+ editor.value.updateNodeAttribute('node-id', 'color', '#ff0000')
175
+
176
+ // Events
177
+ editor.value.on('editor:change', ({ document }) => {
178
+ console.log('Document changed', document)
179
+ })
180
+ })
181
+ </script>
182
+ ```
183
+
184
+ ### Full API Reference
185
+
186
+ | Method | Returns | Description |
187
+ |--------|---------|-------------|
188
+ | `getDocument()` | `EmailDocument` | Current document tree |
189
+ | `setDocument(doc)` | `void` | Replace entire document |
190
+ | `getMjml()` | `string` | Export as MJML string |
191
+ | `getHtml()` | `string` | Export as compiled HTML |
192
+ | `getDesignJson()` | `EmailDesignJson` | Export persisted format |
193
+ | `undo()` | `void` | Undo last change |
194
+ | `redo()` | `void` | Redo last undone change |
195
+ | `canUndo()` | `boolean` | Whether undo is available |
196
+ | `canRedo()` | `boolean` | Whether redo is available |
197
+ | `selectNode(id)` | `void` | Select a node by ID |
198
+ | `getSelectedNode()` | `EmailNode \| null` | Get selected node |
199
+ | `clearSelection()` | `void` | Deselect current node |
200
+ | `deleteNode(id)` | `void` | Remove a node |
201
+ | `duplicateNode(id)` | `NodeId \| null` | Clone a node |
202
+ | `insertBlock(block, parentId, index?)` | `NodeId \| null` | Insert a block |
203
+ | `updateNodeAttribute(id, key, value)` | `void` | Update an attribute |
204
+ | `loadTemplate(doc)` | `void` | Load an EmailDocument |
205
+ | `on(event, handler)` | `void` | Subscribe to event |
206
+ | `off(event, handler)` | `void` | Unsubscribe from event |
207
+
208
+ ## Events
209
+
210
+ Subscribe to editor events for real-time notifications:
211
+
212
+ ```ts
213
+ editor.value.on('node:selected', ({ nodeId, node }) => {
214
+ console.log('Selected:', node.type)
215
+ })
216
+
217
+ editor.value.on('editor:change', ({ document }) => {
218
+ // Auto-save
219
+ saveToServer(document)
220
+ })
221
+ ```
222
+
223
+ | Event | Payload | Trigger |
224
+ |-------|---------|---------|
225
+ | `editor:ready` | `{ document }` | Editor initialized |
226
+ | `editor:change` | `{ document }` | Any document change |
227
+ | `node:selected` | `{ nodeId, node }` | Node selected |
228
+ | `node:deselected` | `{ nodeId }` | Selection cleared |
229
+ | `node:deleted` | `{ nodeId }` | Node removed |
230
+ | `node:moved` | `{ nodeId, fromParentId, toParentId }` | Node repositioned |
231
+ | `node:duplicated` | `{ originalId, newId }` | Node cloned |
232
+ | `block:dropped` | `{ blockId, parentId }` | Block added from panel |
233
+ | `history:undo` | `{ canUndo, canRedo }` | Undo performed |
234
+ | `history:redo` | `{ canUndo, canRedo }` | Redo performed |
235
+ | `property:changed` | `{ nodeId, key, value }` | Attribute updated |
236
+
237
+ ## Plugin System
238
+
239
+ Extend the editor with custom blocks, property editors, toolbar actions, and sidebar panels:
240
+
241
+ ```ts
242
+ import type { Plugin } from '@lab2view/vue-email-editor'
243
+ import { createText } from '@lab2view/vue-email-editor'
244
+
245
+ const myPlugin: Plugin = (ctx) => {
246
+ // Add a custom block
247
+ ctx.registerBlock({
248
+ id: 'custom-banner',
249
+ label: 'Custom Banner',
250
+ category: 'content',
251
+ icon: 'Sparkles',
252
+ factory: () => createText('<h1>My Custom Block</h1>', {
253
+ align: 'center',
254
+ 'font-size': '24px',
255
+ }),
256
+ })
257
+
258
+ // Add a new block category
259
+ ctx.registerBlockCategory({
260
+ id: 'my-category',
261
+ label: 'My Blocks',
262
+ icon: 'Package',
263
+ order: 50,
264
+ })
265
+
266
+ // Add a toolbar button
267
+ ctx.registerToolbarAction({
268
+ id: 'save',
269
+ label: 'Save',
270
+ icon: 'Save',
271
+ handler: () => console.log('Saving...'),
272
+ position: 'right',
273
+ })
274
+
275
+ // Listen to events
276
+ ctx.on('editor:change', ({ document }) => {
277
+ console.log('Auto-saving...')
278
+ })
279
+ }
280
+ ```
281
+
282
+ ```vue
283
+ <EmailEditor :plugins="[myPlugin]" />
284
+ ```
285
+
286
+ ### Plugin Context API
287
+
288
+ | Method | Description |
289
+ |--------|-------------|
290
+ | `registerBlock(block)` | Add a custom block to the blocks panel |
291
+ | `registerBlockCategory(category)` | Define a new block category |
292
+ | `registerPropertyEditor(type, component)` | Override a property editor component |
293
+ | `registerToolbarAction(action)` | Add a button to the toolbar |
294
+ | `registerSidebarPanel(panel)` | Add a custom sidebar tab/panel |
295
+ | `on(event, handler)` | Subscribe to editor events |
296
+ | `off(event, handler)` | Unsubscribe from events |
297
+ | `labels` | Reactive reference to current labels |
298
+
299
+ ## Exports
300
+
301
+ The package provides 35+ exports for advanced usage:
302
+
303
+ ```ts
304
+ // Component
305
+ import { EmailEditor } from '@lab2view/vue-email-editor'
306
+
307
+ // Labels & i18n
308
+ import { DEFAULT_LABELS, FR_LABELS } from '@lab2view/vue-email-editor'
309
+
310
+ // Types
311
+ import type {
312
+ EmailDocument, EmailNode, EmailDesignJson, EmailEditorAPI,
313
+ Plugin, PluginContext, ThemeConfig, EditorEventMap,
314
+ BlockDefinition, PropertyDefinition,
315
+ } from '@lab2view/vue-email-editor'
316
+
317
+ // Serialization
318
+ import { compileMjml, documentToMjml, mjmlToDocument } from '@lab2view/vue-email-editor'
319
+
320
+ // Node factories
321
+ import {
322
+ createDefaultDocument, createSection, createColumn,
323
+ createText, createImage, createButton, createDivider,
324
+ createSpacer, createSocial, createHero, createWrapper,
325
+ } from '@lab2view/vue-email-editor'
326
+
327
+ // Tree utilities
328
+ import { findNode, findParent, removeNode, moveNode, cloneSubtree } from '@lab2view/vue-email-editor'
329
+
330
+ // Constants
331
+ import {
332
+ DEFAULT_THEME, STATIC_BLOCKS,
333
+ CONTENT_NODE_TYPES, CONTAINER_NODE_TYPES, SELF_CLOSING_NODE_TYPES,
334
+ } from '@lab2view/vue-email-editor'
335
+ ```
336
+
337
+ ## Keyboard Shortcuts
338
+
339
+ | Shortcut | Action |
340
+ |----------|--------|
341
+ | `Ctrl+Z` | Undo |
342
+ | `Ctrl+Shift+Z` | Redo |
343
+ | `Ctrl+D` | Duplicate selected node |
344
+ | `Delete` / `Backspace` | Delete selected node |
345
+ | `Escape` | Deselect / close inline editor |
346
+
347
+ ## Props
348
+
349
+ | Prop | Type | Default | Description |
350
+ |------|------|---------|-------------|
351
+ | `modelValue` | `string` | `''` | MJML content (v-model) |
352
+ | `designJson` | `Record<string, unknown>` | — | Persisted design JSON |
353
+ | `variables` | `string[]` | `[]` | Available merge variables |
354
+ | `labels` | `Partial<EditorLabels>` | `DEFAULT_LABELS` | i18n label overrides |
355
+ | `label` | `string` | — | Form field label |
356
+ | `required` | `boolean` | `false` | Form validation flag |
357
+ | `theme` | `Partial<ThemeConfig>` | `DEFAULT_THEME` | Visual customization |
358
+ | `plugins` | `Plugin[]` | `[]` | Editor extensions |
359
+
360
+ ## Development
361
+
362
+ ```bash
363
+ # Type check
364
+ npm run typecheck
365
+
366
+ # Run tests (64 tests)
367
+ npm test
368
+
369
+ # Build library
370
+ npm run build
371
+ ```
372
+
373
+ ## Tech Stack
374
+
375
+ - **[Vue 3](https://vuejs.org)** — Reactive component framework
376
+ - **[MJML](https://mjml.io)** — Email markup language
377
+ - **[TipTap](https://tiptap.dev)** — Rich text editing
378
+ - **[Lucide](https://lucide.dev)** — Icon system (400+ icons)
379
+ - **[VueUse](https://vueuse.org)** — Vue composable utilities
380
+ - **[TypeScript](https://www.typescriptlang.org)** — Full type safety
381
+ - **[Vite](https://vite.dev)** — Build tooling
382
+ - **[Vitest](https://vitest.dev)** — Testing framework
383
+
384
+ ## License
385
+
386
+ [MIT](LICENSE)