@marlinjai/email-editor-ui 0.2.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 +21 -0
- package/README.md +20 -0
- package/dist/index.d.mts +514 -0
- package/dist/index.d.ts +514 -0
- package/dist/index.js +5556 -0
- package/dist/index.mjs +5525 -0
- package/dist/styles.css +3 -0
- package/package.json +87 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 marlinjai
|
|
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,20 @@
|
|
|
1
|
+
# @marlinjai/email-editor-ui
|
|
2
|
+
|
|
3
|
+
The React interface of [`@marlinjai/email-editor`](https://www.npmjs.com/package/@marlinjai/email-editor): toolbar, block sidebar, canvas and property inspector, plus the prebuilt stylesheet (`@marlinjai/email-editor-ui/styles.css`).
|
|
4
|
+
|
|
5
|
+
Most apps should use `@marlinjai/email-editor` instead, which wires this UI to the standard blocks and pre-built sections. Use this package directly only to supply your own block registry:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EmailEditor } from '@marlinjai/email-editor-ui';
|
|
9
|
+
import '@marlinjai/email-editor-ui/styles.css';
|
|
10
|
+
|
|
11
|
+
<EmailEditor blockRegistry={myRegistry} onChange={save} onRequestImage={pickImage} />;
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The stylesheet is scoped under `.ee-root` (the element `EmailEditor` renders) and is safe next to Tailwind CSS 4. Theming, the `onRequestImage` contract and the Next.js setup are documented in the [editor README](https://github.com/marlinjai/email-editor/tree/main/packages/editor#readme).
|
|
15
|
+
|
|
16
|
+
Requires `react` and `react-dom` 18 or 19.
|
|
17
|
+
|
|
18
|
+
## License
|
|
19
|
+
|
|
20
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import React, { Provider } from 'react';
|
|
2
|
+
import { TemplateSnapshotIn, TemplateSnapshotOut, BlockRegistryImpl, PrebuiltTemplateRegistry, RootStoreInstance, EditorUIInstance, BlockInstance, ColumnInstance, SectionInstance, TemplateInstance, BackgroundGradient, SubColumnInstance, BlockDefinition, PrebuiltTemplate } from '@marlinjai/email-editor-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What the editor tells the host when it asks for an image.
|
|
6
|
+
*/
|
|
7
|
+
interface ImageRequest {
|
|
8
|
+
/** The id of the block the image is for (or the section or wrapper, for a background image) */
|
|
9
|
+
blockId: string;
|
|
10
|
+
/** What is asking: a block type (`image`), or `section` or `wrapper` for a background image */
|
|
11
|
+
blockType: string;
|
|
12
|
+
/** The image URL the block currently shows, if any */
|
|
13
|
+
currentUrl?: string;
|
|
14
|
+
/** The alt text the block currently has, if any */
|
|
15
|
+
currentAlt?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The image the host hands back: a URL the email can load (it must be
|
|
19
|
+
* publicly reachable from a recipient's mail client), and optional alt text.
|
|
20
|
+
*/
|
|
21
|
+
interface RequestedImage {
|
|
22
|
+
url: string;
|
|
23
|
+
alt?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Called when the user asks to choose an image. Open your own picker or
|
|
27
|
+
* uploader, then resolve with the chosen image, or with `null` when the user
|
|
28
|
+
* cancels (the block is left unchanged). A rejected promise is shown to the
|
|
29
|
+
* user as an inline error next to the button, using the error's message.
|
|
30
|
+
*/
|
|
31
|
+
type OnRequestImage = (request: ImageRequest) => Promise<RequestedImage | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Called when somebody asks to keep a section for reuse. The editor has already
|
|
34
|
+
* asked for the name and checked it against `savedSectionNames`; what is handed
|
|
35
|
+
* over is a plain snapshot of the section, ids and all.
|
|
36
|
+
*
|
|
37
|
+
* Resolve when it is stored. A rejected promise is shown in the dialog, using
|
|
38
|
+
* the error's message, and the dialog stays open so the name is not lost.
|
|
39
|
+
*
|
|
40
|
+
* Without it no "Save as a section" action appears, rather than an action that
|
|
41
|
+
* fails: a host that cannot store one should not offer to.
|
|
42
|
+
*/
|
|
43
|
+
type OnSaveSection = (section: Record<string, unknown>, name: string) => Promise<void>;
|
|
44
|
+
interface EditorHostHooks {
|
|
45
|
+
onRequestImage?: OnRequestImage;
|
|
46
|
+
onSaveSection?: OnSaveSection;
|
|
47
|
+
/**
|
|
48
|
+
* The names this workspace has already used, so the dialog can say that a
|
|
49
|
+
* name is taken before the save is attempted rather than after it comes back
|
|
50
|
+
* as a conflict.
|
|
51
|
+
*/
|
|
52
|
+
savedSectionNames?: readonly string[];
|
|
53
|
+
/**
|
|
54
|
+
* The editor's root element. Dialogs portal into it rather than into
|
|
55
|
+
* `document.body`, so they stay inside the scoped stylesheet and inherit
|
|
56
|
+
* the theme tokens set on the root.
|
|
57
|
+
*/
|
|
58
|
+
portalContainer?: HTMLElement | null;
|
|
59
|
+
}
|
|
60
|
+
declare function EditorHostProvider({ onRequestImage, onSaveSection, savedSectionNames, portalContainer, children, }: EditorHostHooks & {
|
|
61
|
+
children: React.ReactNode;
|
|
62
|
+
}): React.JSX.Element;
|
|
63
|
+
/**
|
|
64
|
+
* The hooks the host supplied. Outside an editor every hook is undefined, so
|
|
65
|
+
* components fall back to their built-in behavior.
|
|
66
|
+
*/
|
|
67
|
+
declare function useEditorHost(): EditorHostHooks;
|
|
68
|
+
|
|
69
|
+
interface EmailEditorProps {
|
|
70
|
+
/** Initial template data */
|
|
71
|
+
initialTemplate?: TemplateSnapshotIn;
|
|
72
|
+
/** Called when template changes */
|
|
73
|
+
onChange?: (template: TemplateSnapshotOut) => void;
|
|
74
|
+
/** Block registry for available block types */
|
|
75
|
+
blockRegistry: BlockRegistryImpl;
|
|
76
|
+
/** Pre-built template registry */
|
|
77
|
+
prebuiltRegistry?: PrebuiltTemplateRegistry;
|
|
78
|
+
/** Called when save button is clicked */
|
|
79
|
+
onSave?: () => void;
|
|
80
|
+
/** Called when export is requested */
|
|
81
|
+
onExport?: (template: TemplateSnapshotOut) => void;
|
|
82
|
+
/** Called when back button is clicked to navigate away from editor */
|
|
83
|
+
onNavigateBack?: () => void;
|
|
84
|
+
/**
|
|
85
|
+
* Supply images from the host's own picker or uploader. When set, the image
|
|
86
|
+
* block's inspector shows a "Choose image" button that calls it instead of a
|
|
87
|
+
* URL field. Resolve with `null` to cancel; a rejection is shown inline.
|
|
88
|
+
*/
|
|
89
|
+
onRequestImage?: OnRequestImage;
|
|
90
|
+
/**
|
|
91
|
+
* Keep a section for reuse. When set, a selected section's controls carry a
|
|
92
|
+
* "Save as a section" action, which asks for a name and calls this. Without
|
|
93
|
+
* it no action appears, rather than one that fails.
|
|
94
|
+
*/
|
|
95
|
+
onSaveSection?: OnSaveSection;
|
|
96
|
+
/** The names already saved, so the dialog can catch a clash before the save. */
|
|
97
|
+
savedSectionNames?: readonly string[];
|
|
98
|
+
/** Extra class names for the editor's root element (which carries `ee-root`) */
|
|
99
|
+
className?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Inline styles for the editor's root element. Use it to set design tokens,
|
|
102
|
+
* e.g. `{ '--ee-accent': '#0f766e' }`. The editor fills its container's
|
|
103
|
+
* height, so give the container one.
|
|
104
|
+
*/
|
|
105
|
+
style?: React.CSSProperties;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* EmailEditor - Main email editor component
|
|
109
|
+
*
|
|
110
|
+
* Uses MobX State Tree for instant visual feedback on property changes.
|
|
111
|
+
* MJML compilation only happens on export, not during editing.
|
|
112
|
+
*/
|
|
113
|
+
declare const EmailEditor: React.FunctionComponent<EmailEditorProps>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Provider component for the store
|
|
117
|
+
*/
|
|
118
|
+
declare const StoreProvider: Provider<RootStoreInstance | null>;
|
|
119
|
+
/**
|
|
120
|
+
* Hook to access the full store
|
|
121
|
+
* @throws Error if used outside of StoreProvider
|
|
122
|
+
*/
|
|
123
|
+
declare function useStore(): RootStoreInstance;
|
|
124
|
+
/**
|
|
125
|
+
* Hook to access just the template
|
|
126
|
+
*/
|
|
127
|
+
declare function useTemplate(): TemplateInstance;
|
|
128
|
+
/**
|
|
129
|
+
* Hook to access just the editor UI store
|
|
130
|
+
*/
|
|
131
|
+
declare function useEditorUI(): EditorUIInstance;
|
|
132
|
+
/**
|
|
133
|
+
* Hook to get the selected block (convenience)
|
|
134
|
+
*/
|
|
135
|
+
declare function useSelectedBlock(): BlockInstance | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Hook to get the selected section (convenience)
|
|
138
|
+
*/
|
|
139
|
+
declare function useSelectedSection(): SectionInstance | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Hook to get the selected column (convenience)
|
|
142
|
+
*/
|
|
143
|
+
declare function useSelectedColumn(): ColumnInstance | undefined;
|
|
144
|
+
|
|
145
|
+
interface EmailRendererProps {
|
|
146
|
+
/** Additional class names */
|
|
147
|
+
className?: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* EmailRenderer - Root renderer for email templates
|
|
151
|
+
*
|
|
152
|
+
* This is the main preview component that renders the entire email template
|
|
153
|
+
* using React components instead of MJML compilation.
|
|
154
|
+
*
|
|
155
|
+
* Key benefits:
|
|
156
|
+
* - Instant updates (<16ms) on property changes
|
|
157
|
+
* - Fine-grained reactivity (only changed blocks re-render)
|
|
158
|
+
* - No iframe needed
|
|
159
|
+
* - MJML compilation only happens on export
|
|
160
|
+
*/
|
|
161
|
+
declare const EmailRenderer: (({ className }: EmailRendererProps) => React.JSX.Element) & {
|
|
162
|
+
displayName: string;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Hook to get the email renderer width
|
|
166
|
+
*/
|
|
167
|
+
declare function usePreviewWidth(): number;
|
|
168
|
+
|
|
169
|
+
interface SectionRendererProps {
|
|
170
|
+
section: SectionInstance;
|
|
171
|
+
sectionIndex: number;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* SectionRenderer - Renders a section with its columns
|
|
175
|
+
*
|
|
176
|
+
* Key features:
|
|
177
|
+
* - Table-based layout for email compatibility
|
|
178
|
+
* - Section selection and hover states
|
|
179
|
+
* - Column distribution
|
|
180
|
+
* - Background styling
|
|
181
|
+
*/
|
|
182
|
+
declare const SectionRenderer: (({ section, sectionIndex }: SectionRendererProps) => React.JSX.Element) & {
|
|
183
|
+
displayName: string;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
interface ColumnRendererProps {
|
|
187
|
+
column: ColumnInstance;
|
|
188
|
+
section: SectionInstance;
|
|
189
|
+
columnIndex: number;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* ColumnRenderer - Renders a column within a section
|
|
193
|
+
*
|
|
194
|
+
* Key features:
|
|
195
|
+
* - Renders blocks in order
|
|
196
|
+
* - Handles column selection
|
|
197
|
+
* - Shows drop zones during drag
|
|
198
|
+
* - Applies column styling
|
|
199
|
+
*/
|
|
200
|
+
declare const ColumnRenderer: (({ column, section, columnIndex }: ColumnRendererProps) => React.JSX.Element) & {
|
|
201
|
+
displayName: string;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
interface BlockRendererProps {
|
|
205
|
+
block: BlockInstance;
|
|
206
|
+
/** Whether this block is in inline editing mode */
|
|
207
|
+
isEditing?: boolean;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* BlockRenderer - Routes blocks to their specific renderer components
|
|
211
|
+
*
|
|
212
|
+
* Key features:
|
|
213
|
+
* - Wraps blocks with selection/hover UI
|
|
214
|
+
* - Handles click to select
|
|
215
|
+
* - Shows hidden block placeholder
|
|
216
|
+
* - Routes to appropriate block renderer
|
|
217
|
+
*/
|
|
218
|
+
declare const BlockRenderer: (({ block, isEditing }: BlockRendererProps) => React.JSX.Element) & {
|
|
219
|
+
displayName: string;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
interface TextBlockProps {
|
|
223
|
+
block: BlockInstance;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* TextBlock - Renders a text block with inline WYSIWYG editing
|
|
227
|
+
*
|
|
228
|
+
* Key implementation details:
|
|
229
|
+
* - Uses contenteditable for direct text editing when selected
|
|
230
|
+
* - Does NOT use dangerouslySetInnerHTML when editing (causes cursor reset)
|
|
231
|
+
* - Syncs content to MST only on blur (not on every keystroke)
|
|
232
|
+
* - Stops keyboard event propagation to prevent block deletion on backspace
|
|
233
|
+
*/
|
|
234
|
+
declare const TextBlock: (({ block }: TextBlockProps) => React.JSX.Element) & {
|
|
235
|
+
displayName: string;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
interface ImageBlockProps {
|
|
239
|
+
block: BlockInstance;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* ImageBlock - Renders an image block in the email preview
|
|
243
|
+
*/
|
|
244
|
+
declare const ImageBlock: (({ block }: ImageBlockProps) => React.JSX.Element) & {
|
|
245
|
+
displayName: string;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
interface ButtonBlockProps {
|
|
249
|
+
block: BlockInstance;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* ButtonBlock - Renders a button/CTA block in the email preview
|
|
253
|
+
*/
|
|
254
|
+
declare const ButtonBlock: (({ block }: ButtonBlockProps) => React.JSX.Element) & {
|
|
255
|
+
displayName: string;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
interface DividerBlockProps {
|
|
259
|
+
block: BlockInstance;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* DividerBlock - Renders a horizontal divider in the email preview
|
|
263
|
+
*/
|
|
264
|
+
declare const DividerBlock: (({ block }: DividerBlockProps) => React.JSX.Element) & {
|
|
265
|
+
displayName: string;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
interface SpacerBlockProps {
|
|
269
|
+
block: BlockInstance;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* SpacerBlock - Renders vertical spacing in the email preview
|
|
273
|
+
*/
|
|
274
|
+
declare const SpacerBlock: (({ block }: SpacerBlockProps) => React.JSX.Element) & {
|
|
275
|
+
displayName: string;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
interface SocialBlockProps {
|
|
279
|
+
block: BlockInstance;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* SocialBlock - Renders social media icons in the email preview
|
|
283
|
+
*/
|
|
284
|
+
declare const SocialBlock: (({ block }: SocialBlockProps) => React.JSX.Element) & {
|
|
285
|
+
displayName: string;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
interface HeroBlockProps {
|
|
289
|
+
block: BlockInstance;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* HeroBlock - Renders a hero section with background image
|
|
293
|
+
*/
|
|
294
|
+
declare const HeroBlock: (({ block }: HeroBlockProps) => React.JSX.Element) & {
|
|
295
|
+
displayName: string;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
interface RawBlockProps {
|
|
299
|
+
block: BlockInstance;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* RawBlock - Renders custom HTML content
|
|
303
|
+
*/
|
|
304
|
+
declare const RawBlock: (({ block }: RawBlockProps) => React.JSX.Element) & {
|
|
305
|
+
displayName: string;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
interface PropertyInspectorProps {
|
|
309
|
+
onDeleteBlock?: (blockId: string) => void;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* PropertyInspector - Property editor panel
|
|
313
|
+
*
|
|
314
|
+
* Updates MST models directly for instant visual feedback.
|
|
315
|
+
* No intermediate state or MJML recompilation needed.
|
|
316
|
+
*/
|
|
317
|
+
declare const PropertyInspector: React.FunctionComponent<PropertyInspectorProps>;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Normalize spacing values to ensure valid CSS units.
|
|
321
|
+
*
|
|
322
|
+
* CSS ignores unitless values (except 0), so we auto-append "px" for bare numbers.
|
|
323
|
+
* MJML supports: px, % (em/rem have poor email client support)
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* normalizeSpacingValue("10") // "10px" - bare number gets px
|
|
327
|
+
* normalizeSpacingValue("10px") // "10px" - already has unit
|
|
328
|
+
* normalizeSpacingValue("5%") // "5%" - percent is valid
|
|
329
|
+
* normalizeSpacingValue("0") // "0" - zero works without unit
|
|
330
|
+
* normalizeSpacingValue("") // undefined - empty clears value
|
|
331
|
+
*/
|
|
332
|
+
declare function normalizeSpacingValue(value: string): string | undefined;
|
|
333
|
+
/**
|
|
334
|
+
* Formatting toolbar for text editing
|
|
335
|
+
* Uses execCommand for contenteditable WYSIWYG editing
|
|
336
|
+
*
|
|
337
|
+
* Important: Uses onMouseDown with preventDefault to keep focus
|
|
338
|
+
* in the contenteditable text block while clicking buttons.
|
|
339
|
+
*/
|
|
340
|
+
declare function FormattingToolbar(): React.JSX.Element;
|
|
341
|
+
/**
|
|
342
|
+
* Text input field
|
|
343
|
+
*/
|
|
344
|
+
declare function TextField({ label, value, onChange, placeholder, hint, error, }: {
|
|
345
|
+
label: string;
|
|
346
|
+
value: string;
|
|
347
|
+
onChange: (value: string) => void;
|
|
348
|
+
placeholder?: string;
|
|
349
|
+
/** A line under the field saying what it does. */
|
|
350
|
+
hint?: string;
|
|
351
|
+
/** Shown instead of the hint, and marks the field invalid. */
|
|
352
|
+
error?: string;
|
|
353
|
+
}): React.JSX.Element;
|
|
354
|
+
/**
|
|
355
|
+
* Theme color swatch type
|
|
356
|
+
*/
|
|
357
|
+
interface ThemeColorSwatch {
|
|
358
|
+
name: string;
|
|
359
|
+
value: string;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Color picker with text input and theme color swatches
|
|
363
|
+
*/
|
|
364
|
+
declare function ColorField({ label, value, onChange, allowEmpty, themeColors, }: {
|
|
365
|
+
label: string;
|
|
366
|
+
value: string;
|
|
367
|
+
onChange: (value: string) => void;
|
|
368
|
+
allowEmpty?: boolean;
|
|
369
|
+
themeColors?: ThemeColorSwatch[];
|
|
370
|
+
}): React.JSX.Element;
|
|
371
|
+
/**
|
|
372
|
+
* Select dropdown field
|
|
373
|
+
*/
|
|
374
|
+
declare function SelectField({ label, value, options, onChange, }: {
|
|
375
|
+
label: string;
|
|
376
|
+
value: string;
|
|
377
|
+
options: string[];
|
|
378
|
+
onChange: (value: string) => void;
|
|
379
|
+
}): React.JSX.Element;
|
|
380
|
+
/**
|
|
381
|
+
* Text alignment buttons
|
|
382
|
+
*/
|
|
383
|
+
declare function AlignmentField({ value, onChange, }: {
|
|
384
|
+
value: string;
|
|
385
|
+
onChange: (value: string) => void;
|
|
386
|
+
}): React.JSX.Element;
|
|
387
|
+
/**
|
|
388
|
+
* Checkbox field
|
|
389
|
+
*/
|
|
390
|
+
declare function CheckboxField({ label, checked, onChange, disabled, hint, }: {
|
|
391
|
+
label: string;
|
|
392
|
+
checked: boolean;
|
|
393
|
+
onChange: () => void;
|
|
394
|
+
disabled?: boolean;
|
|
395
|
+
/** A line under the checkbox, e.g. why it is disabled. */
|
|
396
|
+
hint?: string;
|
|
397
|
+
}): React.JSX.Element;
|
|
398
|
+
/**
|
|
399
|
+
* 4-sided spacing input (top, right, bottom, left)
|
|
400
|
+
*
|
|
401
|
+
* Automatically normalizes values to ensure valid CSS units.
|
|
402
|
+
* Bare numbers like "10" become "10px".
|
|
403
|
+
*/
|
|
404
|
+
declare function SpacingField({ label, top, right, bottom, left, onChange, placeholders, hint, }: {
|
|
405
|
+
label: string;
|
|
406
|
+
top?: string;
|
|
407
|
+
right?: string;
|
|
408
|
+
bottom?: string;
|
|
409
|
+
left?: string;
|
|
410
|
+
onChange: (side: 'top' | 'right' | 'bottom' | 'left', value: string | undefined) => void;
|
|
411
|
+
/** Placeholders per side, e.g. what the mail uses when a side is not set. */
|
|
412
|
+
placeholders?: Partial<Record<'top' | 'right' | 'bottom' | 'left', string>>;
|
|
413
|
+
hint?: string;
|
|
414
|
+
}): React.JSX.Element;
|
|
415
|
+
/**
|
|
416
|
+
* Range slider with value display
|
|
417
|
+
*/
|
|
418
|
+
declare function RangeField({ label, value, min, max, onChange, unit, }: {
|
|
419
|
+
label: string;
|
|
420
|
+
value: number;
|
|
421
|
+
min: number;
|
|
422
|
+
max: number;
|
|
423
|
+
onChange: (value: number) => void;
|
|
424
|
+
unit?: string;
|
|
425
|
+
}): React.JSX.Element;
|
|
426
|
+
/**
|
|
427
|
+
* Button group for selecting from options
|
|
428
|
+
*/
|
|
429
|
+
declare function ButtonGroupField({ label, value, options, onChange, }: {
|
|
430
|
+
label: string;
|
|
431
|
+
value: string | number;
|
|
432
|
+
options: (string | number)[];
|
|
433
|
+
onChange: (value: string | number) => void;
|
|
434
|
+
}): React.JSX.Element;
|
|
435
|
+
/**
|
|
436
|
+
* Gradient editor field
|
|
437
|
+
*/
|
|
438
|
+
declare function GradientField({ value, onChange, }: {
|
|
439
|
+
value: BackgroundGradient | undefined;
|
|
440
|
+
onChange: (gradient: BackgroundGradient | undefined) => void;
|
|
441
|
+
}): React.JSX.Element;
|
|
442
|
+
|
|
443
|
+
interface BlockPropertiesProps {
|
|
444
|
+
block: BlockInstance;
|
|
445
|
+
onDelete: () => void;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Main block properties panel - routes to type-specific panels
|
|
449
|
+
*/
|
|
450
|
+
declare const BlockProperties: React.FunctionComponent<BlockPropertiesProps>;
|
|
451
|
+
|
|
452
|
+
interface SectionPropertiesProps {
|
|
453
|
+
section: SectionInstance;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Section properties panel
|
|
457
|
+
*/
|
|
458
|
+
declare const SectionProperties: React.FunctionComponent<SectionPropertiesProps>;
|
|
459
|
+
|
|
460
|
+
interface ColumnPropertiesProps {
|
|
461
|
+
column: ColumnInstance;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Column properties panel
|
|
465
|
+
*/
|
|
466
|
+
declare const ColumnProperties: React.FunctionComponent<ColumnPropertiesProps>;
|
|
467
|
+
|
|
468
|
+
interface SubColumnPropertiesProps {
|
|
469
|
+
subColumn: SubColumnInstance;
|
|
470
|
+
parentColumn: ColumnInstance;
|
|
471
|
+
}
|
|
472
|
+
declare const SubColumnProperties: React.FunctionComponent<SubColumnPropertiesProps>;
|
|
473
|
+
|
|
474
|
+
interface ElementsPanelProps {
|
|
475
|
+
blocks: BlockDefinition[];
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Panel showing draggable base elements (MJML blocks)
|
|
479
|
+
* Matches Mailjet's "Elements > Content" panel
|
|
480
|
+
*/
|
|
481
|
+
declare const ElementsPanel: React.FunctionComponent<ElementsPanelProps>;
|
|
482
|
+
|
|
483
|
+
interface LayoutPanelProps {
|
|
484
|
+
templates: PrebuiltTemplate[];
|
|
485
|
+
onAddSection: (columns: 1 | 2 | 3) => void;
|
|
486
|
+
onAddPrebuilt: (template: PrebuiltTemplate) => void;
|
|
487
|
+
/** Adds a container (MJML mj-wrapper) around one empty section. */
|
|
488
|
+
onAddWrapper: () => void;
|
|
489
|
+
}
|
|
490
|
+
declare function LayoutPanel({ templates, onAddSection, onAddPrebuilt, onAddWrapper }: LayoutPanelProps): React.JSX.Element;
|
|
491
|
+
|
|
492
|
+
declare const LayersPanel: React.FunctionComponent<object>;
|
|
493
|
+
|
|
494
|
+
interface LeftSidebarProps {
|
|
495
|
+
blockRegistry: BlockRegistryImpl;
|
|
496
|
+
prebuiltRegistry?: PrebuiltTemplateRegistry;
|
|
497
|
+
onAddSection: (columnCount: 1 | 2 | 3) => void;
|
|
498
|
+
onAddPrebuilt: (template: PrebuiltTemplate) => void;
|
|
499
|
+
onAddWrapper: () => void;
|
|
500
|
+
}
|
|
501
|
+
declare const LeftSidebar: React.FunctionComponent<LeftSidebarProps>;
|
|
502
|
+
|
|
503
|
+
declare const TemplateSettingsPanel: React.FunctionComponent<object>;
|
|
504
|
+
|
|
505
|
+
interface DragOverlayContentProps {
|
|
506
|
+
item: {
|
|
507
|
+
type: 'block' | 'template';
|
|
508
|
+
id: string;
|
|
509
|
+
label: string;
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
declare function DragOverlayContent({ item }: DragOverlayContentProps): React.JSX.Element;
|
|
513
|
+
|
|
514
|
+
export { AlignmentField, BlockProperties, BlockRenderer, ButtonBlock, ButtonGroupField, CheckboxField, ColorField, ColumnProperties, ColumnRenderer, DividerBlock, DragOverlayContent, type EditorHostHooks, EditorHostProvider, ElementsPanel, EmailEditor, type EmailEditorProps, EmailRenderer, FormattingToolbar, GradientField, HeroBlock, ImageBlock, type ImageRequest, LayersPanel, LayoutPanel, LeftSidebar, type OnRequestImage, type OnSaveSection, PropertyInspector, RangeField, RawBlock, type RequestedImage, SectionProperties, SectionRenderer, SelectField, SocialBlock, SpacerBlock, SpacingField, StoreProvider, SubColumnProperties, TemplateSettingsPanel, TextBlock, TextField, normalizeSpacingValue, useEditorHost, useEditorUI, usePreviewWidth, useSelectedBlock, useSelectedColumn, useSelectedSection, useStore, useTemplate };
|