@parathantl/react-email-editor 0.1.0 → 0.1.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/dist/index.d.ts CHANGED
@@ -1,10 +1,33 @@
1
- import * as React$1 from 'react';
2
- import React__default, { ReactNode, ComponentType } from 'react';
1
+ import * as react from 'react';
2
+ import { ReactNode, ComponentType } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as _tiptap_core from '@tiptap/core';
5
5
  import { Editor, Node } from '@tiptap/core';
6
6
  import * as _tiptap_extension_underline from '@tiptap/extension-underline';
7
7
 
8
+ declare const DEFAULT_SOCIAL_PROPERTIES: SocialBlockProperties;
9
+ declare const DEFAULT_BLOCK_PROPERTIES: {
10
+ [K in BlockType]: BlockPropertiesMap[K];
11
+ };
12
+ declare const DEFAULT_SECTION_PROPERTIES: SectionProperties;
13
+ declare const DEFAULT_HEAD_METADATA: HeadMetadata;
14
+ declare const DEFAULT_GLOBAL_STYLES: GlobalStyles;
15
+ declare const DEFAULT_VARIABLE_CHIP_STYLE: VariableChipStyle;
16
+ interface BlockDefinition {
17
+ type: BlockType;
18
+ label: string;
19
+ icon: string;
20
+ description: string;
21
+ }
22
+ declare const BLOCK_DEFINITIONS: BlockDefinition[];
23
+ declare const DEFAULT_FONT_SIZES: string[];
24
+ declare const FONT_OPTIONS: string[];
25
+ declare const COLOR_PRESETS: string[];
26
+ declare const COLUMN_LAYOUTS: {
27
+ label: string;
28
+ widths: string[];
29
+ }[];
30
+
8
31
  type BlockType = 'text' | 'button' | 'image' | 'divider' | 'spacer' | 'social' | 'html' | 'video' | 'heading' | 'countdown' | 'menu' | 'hero';
9
32
  interface TextBlockProperties {
10
33
  content: string;
@@ -15,7 +38,7 @@ interface TextBlockProperties {
15
38
  padding: string;
16
39
  align: 'left' | 'center' | 'right' | 'justify';
17
40
  fontWeight: string;
18
- textTransform: string;
41
+ textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
19
42
  letterSpacing: string;
20
43
  }
21
44
  interface ButtonBlockProperties {
@@ -31,7 +54,7 @@ interface ButtonBlockProperties {
31
54
  align: 'left' | 'center' | 'right';
32
55
  width: string;
33
56
  fontWeight: string;
34
- textTransform: string;
57
+ textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
35
58
  letterSpacing: string;
36
59
  }
37
60
  interface ImageBlockProperties {
@@ -55,6 +78,7 @@ interface SpacerBlockProperties {
55
78
  height: string;
56
79
  }
57
80
  interface SocialElement {
81
+ id?: string;
58
82
  name: string;
59
83
  href: string;
60
84
  src?: string;
@@ -94,7 +118,7 @@ interface HeadingBlockProperties {
94
118
  fontWeight: string;
95
119
  padding: string;
96
120
  align: 'left' | 'center' | 'right' | 'justify';
97
- textTransform: string;
121
+ textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
98
122
  letterSpacing: string;
99
123
  }
100
124
  interface CountdownBlockProperties {
@@ -108,6 +132,7 @@ interface CountdownBlockProperties {
108
132
  align: 'left' | 'center' | 'right';
109
133
  }
110
134
  interface MenuItem {
135
+ id?: string;
111
136
  text: string;
112
137
  href: string;
113
138
  }
@@ -135,6 +160,8 @@ interface HeroBlockProperties {
135
160
  buttonBorderRadius: string;
136
161
  align: 'left' | 'center' | 'right';
137
162
  padding: string;
163
+ backgroundImage: string;
164
+ backgroundColor: string;
138
165
  }
139
166
  type BlockProperties = TextBlockProperties | ButtonBlockProperties | ImageBlockProperties | DividerBlockProperties | SpacerBlockProperties | SocialBlockProperties | HtmlBlockProperties | VideoBlockProperties | HeadingBlockProperties | CountdownBlockProperties | MenuBlockProperties | HeroBlockProperties;
140
167
  interface BlockPropertiesMap {
@@ -151,11 +178,17 @@ interface BlockPropertiesMap {
151
178
  menu: MenuBlockProperties;
152
179
  hero: HeroBlockProperties;
153
180
  }
154
- interface Block {
181
+ interface Block<T extends BlockType = BlockType> {
155
182
  id: string;
156
- type: BlockType;
183
+ type: T;
157
184
  properties: Record<string, any>;
158
185
  }
186
+ /** A Block with type-safe properties, returned by narrowBlock */
187
+ interface TypedBlock<T extends BlockType> extends Block<T> {
188
+ properties: BlockPropertiesMap[T];
189
+ }
190
+ /** Narrow a Block to a specific type for type-safe property access */
191
+ declare function narrowBlock<T extends BlockType>(block: Block, type: T): block is TypedBlock<T>;
159
192
  interface Column {
160
193
  id: string;
161
194
  width: string;
@@ -193,7 +226,7 @@ interface EmailTemplate {
193
226
  interface Variable {
194
227
  key: string;
195
228
  icon?: string;
196
- sample: string;
229
+ sample?: string;
197
230
  label?: string;
198
231
  group?: string;
199
232
  }
@@ -205,9 +238,9 @@ interface VariableChipStyle {
205
238
  borderRadius: string;
206
239
  }
207
240
  interface PersistenceAdapter {
208
- save(key: string, template: EmailTemplate): void;
209
- load(key: string): EmailTemplate | null;
210
- remove(key: string): void;
241
+ save(key: string, template: EmailTemplate): void | Promise<void>;
242
+ load(key: string): EmailTemplate | null | Promise<EmailTemplate | null>;
243
+ remove(key: string): void | Promise<void>;
211
244
  }
212
245
  interface UploadOptions {
213
246
  context: string;
@@ -253,6 +286,10 @@ interface EditorState {
253
286
  history: EmailTemplate[];
254
287
  historyIndex: number;
255
288
  isDirty: boolean;
289
+ blockIndex: Map<string, {
290
+ sectionId: string;
291
+ columnId: string;
292
+ }>;
256
293
  }
257
294
  type EditorAction = {
258
295
  type: 'SET_TEMPLATE';
@@ -344,10 +381,29 @@ type EditorAction = {
344
381
  payload: {
345
382
  sectionId: string;
346
383
  };
384
+ } | {
385
+ type: 'ADD_BLOCK_AND_SELECT';
386
+ payload: {
387
+ sectionId: string;
388
+ columnId: string;
389
+ block: Block;
390
+ index?: number;
391
+ };
392
+ } | {
393
+ type: 'ADD_SECTION_WITH_BLOCK';
394
+ payload: {
395
+ section: Section;
396
+ block: Block;
397
+ index?: number;
398
+ };
399
+ } | {
400
+ type: 'DESELECT_ALL';
347
401
  } | {
348
402
  type: 'UNDO';
349
403
  } | {
350
404
  type: 'REDO';
405
+ } | {
406
+ type: 'PUSH_HISTORY';
351
407
  };
352
408
  interface EmailEditorProps {
353
409
  initialTemplate?: EmailTemplate;
@@ -357,16 +413,40 @@ interface EmailEditorProps {
357
413
  onChange?: (template: EmailTemplate) => void;
358
414
  onSave?: (mjml: string, html: string) => void;
359
415
  onReady?: () => void;
416
+ /** Called when custom variables are added or removed by the user. Receives all custom variables. */
417
+ onVariablesChange?: (customVariables: Variable[]) => void;
360
418
  /** Custom font family options for the rich text toolbar. Falls back to FONT_OPTIONS constant. */
361
419
  fontFamilies?: string[];
362
420
  /** Custom font size options for the rich text toolbar (e.g. ['12px', '14px', '16px']). Falls back to DEFAULT_FONT_SIZES constant. */
363
421
  fontSizes?: string[];
422
+ /** Custom block definitions for the sidebar palette. Override icons, labels, descriptions, or hide specific blocks. Falls back to BLOCK_DEFINITIONS constant. */
423
+ blockDefinitions?: BlockDefinition[];
364
424
  /** Key for auto-persisting the template. Different keys allow multiple editor instances to coexist. */
365
425
  persistenceKey?: string;
366
426
  /** Custom persistence adapter. Defaults to localStorage when persistenceKey is set. */
367
427
  persistenceAdapter?: PersistenceAdapter;
368
428
  className?: string;
369
429
  style?: React.CSSProperties;
430
+ /** Called when a block is added to the template */
431
+ onBlockAdd?: (block: Block, sectionId: string, columnId: string) => void;
432
+ /** Called when a block is removed from the template */
433
+ onBlockRemove?: (blockId: string, sectionId: string, columnId: string) => void;
434
+ /** Called when block properties are updated */
435
+ onBlockUpdate?: (blockId: string, properties: Partial<BlockProperties>) => void;
436
+ /** Called when a block is moved to a new position */
437
+ onBlockMove?: (blockId: string, toSectionId: string, toColumnId: string, toIndex: number) => void;
438
+ /** Called when a section is added to the template */
439
+ onSectionAdd?: (section: Section, index?: number) => void;
440
+ /** Called when a section is removed from the template */
441
+ onSectionRemove?: (sectionId: string) => void;
442
+ /** Called when a section is moved to a new position */
443
+ onSectionMove?: (sectionId: string, toIndex: number) => void;
444
+ /** Called when the selection changes */
445
+ onSelectionChange?: (selection: SelectionState) => void;
446
+ /** Called when a template is loaded via SET_TEMPLATE */
447
+ onTemplateLoad?: (template: EmailTemplate) => void;
448
+ /** Called when history state changes (undo/redo) */
449
+ onHistoryChange?: (canUndo: boolean, canRedo: boolean) => void;
370
450
  }
371
451
  interface EmailEditorRef {
372
452
  getMJML: () => string;
@@ -384,49 +464,107 @@ interface EmailEditorRef {
384
464
  clearPersisted: () => void;
385
465
  }
386
466
 
387
- declare const EmailEditor: React$1.ForwardRefExoticComponent<EmailEditorProps & React$1.RefAttributes<EmailEditorRef>>;
467
+ declare const EmailEditor: react.ForwardRefExoticComponent<EmailEditorProps & react.RefAttributes<EmailEditorRef>>;
388
468
 
389
- interface EditorContextValue {
390
- state: EditorState;
391
- dispatch: React__default.Dispatch<EditorAction>;
392
- /** All variables: pre-defined (props) + custom (user-created) */
469
+ /** @deprecated Use focused hooks (useTemplateContext, useSelectionContext, etc.) instead */
470
+ declare function useEditorState(): {
471
+ template: EmailTemplate;
472
+ isDirty: boolean;
473
+ activeTab: ActiveTab;
474
+ selection: SelectionState;
475
+ canUndo: boolean;
476
+ canRedo: boolean;
477
+ };
478
+ declare function useEditorDispatch(): React.Dispatch<EditorAction>;
479
+ declare function useSelectedBlock(): Block | null;
480
+ declare function useSelectedSection(): Section | null;
481
+ declare function useEditorVariables(): {
393
482
  variables: Variable[];
394
- /** Pre-defined variables from props (read-only) */
395
483
  predefinedVariables: Variable[];
396
- /** User-created custom variables */
397
484
  customVariables: Variable[];
398
- imageUploadAdapter?: ImageUploadAdapter;
399
- setActiveEditor: (editor: Editor | null) => void;
400
- getActiveEditor: () => Editor | null;
485
+ addCustomVariable: (variable: Variable) => void;
486
+ removeCustomVariable: (key: string) => void;
401
487
  insertVariable: (key: string) => boolean;
488
+ variableChipStyle: VariableChipStyle;
489
+ updateVariableChipStyle: (style: Partial<VariableChipStyle>) => void;
490
+ };
491
+ declare function useEditorFonts(): {
492
+ fontFamilies: string[];
493
+ fontSizes: string[];
494
+ };
495
+ declare function useImageAdapter(): {
496
+ imageUploadAdapter: ImageUploadAdapter | undefined;
497
+ };
498
+
499
+ declare function useDispatchContext(): React.Dispatch<EditorAction>;
500
+
501
+ interface TemplateContextValue {
502
+ template: EmailTemplate;
503
+ isDirty: boolean;
504
+ activeTab: ActiveTab;
505
+ }
506
+ declare function useTemplateContext(): TemplateContextValue;
507
+
508
+ declare function useSelectionContext(): SelectionState;
509
+
510
+ interface ConfigContextValue {
511
+ variables: Variable[];
512
+ predefinedVariables: Variable[];
513
+ customVariables: Variable[];
514
+ imageUploadAdapter?: ImageUploadAdapter;
402
515
  addCustomVariable: (variable: Variable) => void;
403
516
  removeCustomVariable: (key: string) => void;
404
517
  variableChipStyle: VariableChipStyle;
405
518
  updateVariableChipStyle: (style: Partial<VariableChipStyle>) => void;
406
- /** Font family options for the rich text toolbar */
407
519
  fontFamilies: string[];
408
- /** Font size options for the rich text toolbar */
409
520
  fontSizes: string[];
410
- /** Remove persisted template for the current key. No-op if no persistenceKey. */
411
521
  clearPersisted: () => void;
412
522
  }
413
- declare function useEditor(): EditorContextValue;
414
- declare function useEditorState(): EditorState;
415
- declare function useEditorDispatch(): React__default.Dispatch<EditorAction>;
416
- declare function useSelectedBlock(): Block | null;
417
- declare function useSelectedSection(): Section | null;
523
+ declare function useConfigContext(): ConfigContextValue;
524
+
525
+ interface MethodsContextValue {
526
+ setActiveEditor: (editor: Editor | null) => void;
527
+ getActiveEditor: () => Editor | null;
528
+ insertVariable: (key: string) => boolean;
529
+ }
530
+ declare function useMethodsContext(): MethodsContextValue;
531
+
532
+ interface HistoryContextValue {
533
+ canUndo: boolean;
534
+ canRedo: boolean;
535
+ }
536
+ declare function useHistoryContext(): HistoryContextValue;
537
+
538
+ type BlockIndex = Map<string, {
539
+ sectionId: string;
540
+ columnId: string;
541
+ }>;
542
+
543
+ declare function useBlockIndexContext(): BlockIndex;
544
+
418
545
  interface EditorProviderProps {
419
546
  children: ReactNode;
420
547
  initialTemplate?: EmailTemplate;
421
548
  variables?: Variable[];
422
549
  imageUploadAdapter?: ImageUploadAdapter;
423
550
  onChange?: (template: EmailTemplate) => void;
551
+ onVariablesChange?: (customVariables: Variable[]) => void;
424
552
  fontFamilies?: string[];
425
553
  fontSizes?: string[];
426
554
  persistenceKey?: string;
427
555
  persistenceAdapter?: PersistenceAdapter;
428
- }
429
- declare function EditorProvider({ children, initialTemplate, variables: predefinedVariables, imageUploadAdapter, onChange, fontFamilies: fontFamiliesProp, fontSizes: fontSizesProp, persistenceKey, persistenceAdapter, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
556
+ onBlockAdd?: (block: Block, sectionId: string, columnId: string) => void;
557
+ onBlockRemove?: (blockId: string, sectionId: string, columnId: string) => void;
558
+ onBlockUpdate?: (blockId: string, properties: Partial<BlockProperties>) => void;
559
+ onBlockMove?: (blockId: string, toSectionId: string, toColumnId: string, toIndex: number) => void;
560
+ onSectionAdd?: (section: Section, index?: number) => void;
561
+ onSectionRemove?: (sectionId: string) => void;
562
+ onSectionMove?: (sectionId: string, toIndex: number) => void;
563
+ onSelectionChange?: (selection: SelectionState) => void;
564
+ onTemplateLoad?: (template: EmailTemplate) => void;
565
+ onHistoryChange?: (canUndo: boolean, canRedo: boolean) => void;
566
+ }
567
+ declare function EditorProvider({ children, initialTemplate, variables: predefinedVariables, imageUploadAdapter, onChange, onVariablesChange, fontFamilies: fontFamiliesProp, fontSizes: fontSizesProp, persistenceKey, persistenceAdapter, onBlockAdd, onBlockRemove, onBlockUpdate, onBlockMove, onSectionAdd, onSectionRemove, onSectionMove, onSelectionChange, onTemplateLoad, onHistoryChange, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
430
568
 
431
569
  declare function parseMJML(mjmlString: string): EmailTemplate;
432
570
 
@@ -465,6 +603,27 @@ declare function groupVariables(variables: Variable[]): Map<string, Variable[]>;
465
603
 
466
604
  declare function sanitizeHTML(html: string): string;
467
605
  declare function escapeHTML(str: string): string;
606
+ /**
607
+ * Returns true if the URL scheme is safe (http, https, mailto, tel, fragment, relative path).
608
+ * Rejects javascript:, data:, vbscript:, and other dangerous schemes.
609
+ */
610
+ declare function isSafeURL(url: string): boolean;
611
+
612
+ interface ValidationResult {
613
+ valid: boolean;
614
+ errors: string[];
615
+ }
616
+ /**
617
+ * Validate raw data as an EmailTemplate.
618
+ * Returns { valid, errors } with descriptive error messages.
619
+ */
620
+ declare function validateTemplate(data: unknown): ValidationResult;
621
+ /**
622
+ * Sanitize and coerce raw data into a valid EmailTemplate.
623
+ * Fixes common issues (missing globalStyles, headMetadata, malformed sections).
624
+ * Returns a safe-to-use template even if input is partially invalid.
625
+ */
626
+ declare function sanitizeTemplate(data: unknown): EmailTemplate;
468
627
 
469
628
  /**
470
629
  * Default persistence adapter using localStorage.
@@ -480,36 +639,24 @@ declare const localStorageAdapter: PersistenceAdapter;
480
639
  * Adding a new block type requires only adding entries here (Open/Closed Principle).
481
640
  */
482
641
 
483
- declare function registerBlockRenderer(type: BlockType, component: ComponentType<{
642
+ /**
643
+ * Register a block renderer. Accepts BlockType for built-in types, or any string for custom blocks.
644
+ */
645
+ declare function registerBlockRenderer(type: BlockType | (string & {}), component: ComponentType<{
484
646
  block: Block;
485
647
  }>): void;
486
- declare function registerBlockProperties(type: BlockType, component: ComponentType<{
648
+ /**
649
+ * Register a block properties panel. Accepts BlockType for built-in types, or any string for custom blocks.
650
+ */
651
+ declare function registerBlockProperties(type: BlockType | (string & {}), component: ComponentType<{
487
652
  block: Block;
488
653
  }>): void;
489
- declare function registerBlockGenerator(type: BlockType, generator: (block: Block, indent: string) => string): void;
654
+ /**
655
+ * Register a MJML generator. Accepts BlockType for built-in types, or any string for custom blocks.
656
+ */
657
+ declare function registerBlockGenerator(type: BlockType | (string & {}), generator: (block: Block, indent: string) => string): void;
490
658
  declare function registerBlockParser(mjmlTag: string, parser: (el: Element) => Block): void;
659
+ /** Returns set of all registered block types (built-in + custom). */
660
+ declare function getRegisteredBlockTypes(): Set<string>;
491
661
 
492
- declare const DEFAULT_SOCIAL_PROPERTIES: SocialBlockProperties;
493
- declare const DEFAULT_BLOCK_PROPERTIES: {
494
- [K in BlockType]: BlockPropertiesMap[K];
495
- };
496
- declare const DEFAULT_SECTION_PROPERTIES: SectionProperties;
497
- declare const DEFAULT_HEAD_METADATA: HeadMetadata;
498
- declare const DEFAULT_GLOBAL_STYLES: GlobalStyles;
499
- declare const DEFAULT_VARIABLE_CHIP_STYLE: VariableChipStyle;
500
- interface BlockDefinition {
501
- type: BlockType;
502
- label: string;
503
- icon: string;
504
- description: string;
505
- }
506
- declare const BLOCK_DEFINITIONS: BlockDefinition[];
507
- declare const DEFAULT_FONT_SIZES: string[];
508
- declare const FONT_OPTIONS: string[];
509
- declare const COLOR_PRESETS: string[];
510
- declare const COLUMN_LAYOUTS: {
511
- label: string;
512
- widths: string[];
513
- }[];
514
-
515
- export { type ActiveTab, BLOCK_DEFINITIONS, type Block, type BlockProperties, type BlockPropertiesMap, type BlockType, type BrowseResult, type ButtonBlockProperties, COLOR_PRESETS, COLUMN_LAYOUTS, type Column, type CountdownBlockProperties, DEFAULT_BLOCK_PROPERTIES, DEFAULT_FONT_SIZES, DEFAULT_GLOBAL_STYLES, DEFAULT_HEAD_METADATA, DEFAULT_SECTION_PROPERTIES, DEFAULT_SOCIAL_PROPERTIES, DEFAULT_VARIABLE_CHIP_STYLE, type DividerBlockProperties, type EditorAction, EditorProvider, type EditorState, EmailEditor, type EmailEditorProps, type EmailEditorRef, type EmailTemplate, FONT_OPTIONS, type GlobalStyles, type HeadMetadata, type HeroBlockProperties, type ImageBlockProperties, type ImageUploadAdapter, type MenuBlockProperties, type MenuItem, type PersistenceAdapter, type Section, type SectionProperties, type SelectionState, type SocialBlockProperties, type SocialElement, type SpacerBlockProperties, type TextBlockProperties, type TransformOptions, type UploadOptions, type UploadResult, type Variable, type VariableChipStyle, VariableNode, compileMJMLToHTML, escapeHTML, extractVariableKeys, generateBlockId, generateColumnId, generateId, generateMJML, generateSectionId, getExtensions, groupVariables, localStorageAdapter, parseMJML, registerBlockGenerator, registerBlockParser, registerBlockProperties, registerBlockRenderer, replaceVariables, sanitizeHTML, useEditor, useEditorDispatch, useEditorState, useSelectedBlock, useSelectedSection };
662
+ export { type ActiveTab, BLOCK_DEFINITIONS, type Block, type BlockDefinition, type BlockProperties, type BlockPropertiesMap, type BlockType, type BrowseResult, type ButtonBlockProperties, COLOR_PRESETS, COLUMN_LAYOUTS, type Column, type CountdownBlockProperties, DEFAULT_BLOCK_PROPERTIES, DEFAULT_FONT_SIZES, DEFAULT_GLOBAL_STYLES, DEFAULT_HEAD_METADATA, DEFAULT_SECTION_PROPERTIES, DEFAULT_SOCIAL_PROPERTIES, DEFAULT_VARIABLE_CHIP_STYLE, type DividerBlockProperties, type EditorAction, EditorProvider, type EditorState, EmailEditor, type EmailEditorProps, type EmailEditorRef, type EmailTemplate, FONT_OPTIONS, type GlobalStyles, type HeadMetadata, type HeadingBlockProperties, type HeroBlockProperties, type HtmlBlockProperties, type ImageBlockProperties, type ImageUploadAdapter, type MenuBlockProperties, type MenuItem, type PersistenceAdapter, type Section, type SectionProperties, type SelectionState, type SocialBlockProperties, type SocialElement, type SpacerBlockProperties, type TextBlockProperties, type TransformOptions, type TypedBlock, type UploadOptions, type UploadResult, type ValidationResult, type Variable, type VariableChipStyle, VariableNode, type VideoBlockProperties, compileMJMLToHTML, escapeHTML, extractVariableKeys, generateBlockId, generateColumnId, generateId, generateMJML, generateSectionId, getExtensions, getRegisteredBlockTypes, groupVariables, isSafeURL, localStorageAdapter, narrowBlock, parseMJML, registerBlockGenerator, registerBlockParser, registerBlockProperties, registerBlockRenderer, replaceVariables, sanitizeHTML, sanitizeTemplate, useBlockIndexContext, useConfigContext, useDispatchContext, useEditorDispatch, useEditorFonts, useEditorState, useEditorVariables, useHistoryContext, useImageAdapter, useMethodsContext, useSelectedBlock, useSelectedSection, useSelectionContext, useTemplateContext, validateTemplate };