@parathantl/react-email-editor 0.1.20 → 0.2.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/README.md CHANGED
@@ -8,8 +8,9 @@ A visual drag-and-drop email template editor for React, powered by MJML. Build r
8
8
  ## Features
9
9
 
10
10
  - 12 block types: Text, Heading, Button, Image, Video, Divider, Spacer, Social, HTML, Countdown, Menu, Hero
11
- - Drag-and-drop block reordering and section management
11
+ - Drag-and-drop block reordering and section management (with keyboard reordering via Arrow keys)
12
12
  - Rich text editing (TipTap) with formatting toolbar
13
+ - Inline `{{` autocomplete — type `{{` in any Text/Heading block to search variables and insert at the cursor; create new variables on the fly without leaving the editor
13
14
  - MJML generation, parsing, and HTML compilation
14
15
  - Template variable support (`{{ variable }}` syntax)
15
16
  - Built-in persistence with localStorage or custom adapters
@@ -68,19 +69,22 @@ Templates auto-save and restore when you provide a `persistenceKey`. Each key st
68
69
  import type { PersistenceAdapter } from '@parathantl/react-email-editor';
69
70
 
70
71
  const serverAdapter: PersistenceAdapter = {
71
- save(key, template) {
72
- fetch(`/api/templates/${key}`, {
72
+ async save(key, template) {
73
+ await fetch(`/api/templates/${key}`, {
73
74
  method: 'PUT',
74
75
  body: JSON.stringify(template),
75
76
  headers: { 'Content-Type': 'application/json' },
76
77
  });
77
78
  },
78
- load(key) {
79
- // Must be synchronous preload data before rendering the editor
80
- return window.__PRELOADED_TEMPLATES__?.[key] ?? null;
79
+ async load(key) {
80
+ // `load` may return synchronously or as a Promise the editor handles both.
81
+ // When async, the editor renders with `initialTemplate` first, then swaps in
82
+ // the loaded data once it resolves.
83
+ const res = await fetch(`/api/templates/${key}`);
84
+ return res.ok ? res.json() : null;
81
85
  },
82
- remove(key) {
83
- fetch(`/api/templates/${key}`, { method: 'DELETE' });
86
+ async remove(key) {
87
+ await fetch(`/api/templates/${key}`, { method: 'DELETE' });
84
88
  },
85
89
  };
86
90
 
@@ -186,6 +190,7 @@ Every key is optional. If you skip a key, the editor uses its default emoji/icon
186
190
  sectionDrag: '↕️',
187
191
  sectionDuplicate: '📄',
188
192
  sectionRemove: '🗑️',
193
+ blockDrag: '↕️',
189
194
  blockDuplicate: '📄',
190
195
  blockRemove: '🗑️',
191
196
  previewDesktop: '🖥️',
@@ -223,6 +228,7 @@ Every key is optional. If you skip a key, the editor uses its default emoji/icon
223
228
  | `sectionDrag` | Section drag handle |
224
229
  | `sectionDuplicate` | Section duplicate action |
225
230
  | `sectionRemove` | Section remove action |
231
+ | `blockDrag` | Block drag handle |
226
232
  | `blockDuplicate` | Block duplicate action |
227
233
  | `blockRemove` | Block remove action |
228
234
  | `previewDesktop` | Preview panel desktop toggle |
@@ -259,7 +265,13 @@ Every key is optional. If you skip a key, the editor uses its default emoji/icon
259
265
 
260
266
  ## Template Variables
261
267
 
262
- Define variables and insert them into text blocks as `{{ variable_name }}`. Variables appear as insertable chips in the sidebar, grouped by category. Users can click or drag them into any text/heading block.
268
+ Define variables and insert them into text blocks as `{{ variable_name }}`. There are three insertion paths:
269
+
270
+ - **Inline autocomplete** — type `{{` in any Text or Heading block to open a popup at the cursor. Continue typing to filter by key or label, use ↑/↓ to navigate, `Enter`/`Tab` to insert, `Esc` to cancel.
271
+ - **Sidebar click** — click a variable chip in the sidebar to insert it at the cursor in the focused editor.
272
+ - **Sidebar drag** — drag a chip from the sidebar into a text/heading block.
273
+
274
+ Variables in the sidebar are grouped by their `group` field; in the autocomplete popup, group headers appear when no query is typed and the list flattens into a search-result view as you type.
263
275
 
264
276
  ```tsx
265
277
  <EmailEditor
@@ -290,7 +302,7 @@ const keys = editorRef.current?.getVariables();
290
302
 
291
303
  ### Listening for custom variable changes
292
304
 
293
- Users can create custom variables at runtime via the sidebar. Use `onVariablesChange` to sync these back to your backend:
305
+ Users can create custom variables at runtime two ways: via the sidebar form, or inline by picking the "+ Create variable" entry at the bottom of the `{{` autocomplete popup. Inline-created variables are added with `group: "Custom"` and a label derived from the key (underscores become spaces). Both paths fire `onVariablesChange`:
294
306
 
295
307
  ```tsx
296
308
  <EmailEditor
@@ -445,6 +457,8 @@ See `src/styles/variables.css` for the full list of 70+ customizable tokens.
445
457
 
446
458
  ## Keyboard Shortcuts
447
459
 
460
+ ### Editor
461
+
448
462
  | Shortcut | Action |
449
463
  |----------|--------|
450
464
  | `Ctrl/Cmd + Z` | Undo |
@@ -453,6 +467,20 @@ See `src/styles/variables.css` for the full list of 70+ customizable tokens.
453
467
  | `Escape` | Deselect block/section |
454
468
  | `Delete` / `Backspace` | Remove selected block or section |
455
469
 
470
+ ### Drag handles (focused)
471
+
472
+ | Shortcut | Action |
473
+ |----------|--------|
474
+ | `↑` / `↓` | Reorder the focused section or block within its container |
475
+
476
+ ### `{{` autocomplete (open popup)
477
+
478
+ | Shortcut | Action |
479
+ |----------|--------|
480
+ | `↑` / `↓` | Move selection through matches |
481
+ | `Enter` / `Tab` | Insert the selected variable, or create the typed key as a new custom variable when the "+ Create variable" row is highlighted |
482
+ | `Escape` | Close without inserting |
483
+
456
484
  ## Responsive Editor
457
485
 
458
486
  The editor automatically adapts to smaller screens: