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