better-react-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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/dist/index.cjs +2 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.js +4190 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/styles.css +1634 -0
  8. package/dist/types/components/BetterEmailEditor.d.ts +34 -0
  9. package/dist/types/components/BlockProperties.d.ts +7 -0
  10. package/dist/types/components/BlocksTab.d.ts +13 -0
  11. package/dist/types/components/BodyTab.d.ts +9 -0
  12. package/dist/types/components/Canvas.d.ts +15 -0
  13. package/dist/types/components/CanvasOverlay.d.ts +10 -0
  14. package/dist/types/components/ContentPanel.d.ts +19 -0
  15. package/dist/types/components/ContentTab.d.ts +17 -0
  16. package/dist/types/components/LayersPanel.d.ts +9 -0
  17. package/dist/types/components/PreviewDialog.d.ts +15 -0
  18. package/dist/types/components/PropertiesPanel.d.ts +12 -0
  19. package/dist/types/components/Sidebar.d.ts +14 -0
  20. package/dist/types/components/TextProperties.d.ts +7 -0
  21. package/dist/types/components/TopBar.d.ts +37 -0
  22. package/dist/types/components/fields.d.ts +103 -0
  23. package/dist/types/components/icons.d.ts +21 -0
  24. package/dist/types/components/inspectors/ButtonInspector.d.ts +2 -0
  25. package/dist/types/components/inspectors/ColumnsInspector.d.ts +3 -0
  26. package/dist/types/components/inspectors/DefaultInspector.d.ts +3 -0
  27. package/dist/types/components/inspectors/HeadingInspector.d.ts +2 -0
  28. package/dist/types/components/inspectors/ImageInspector.d.ts +2 -0
  29. package/dist/types/components/inspectors/SimpleInspectors.d.ts +6 -0
  30. package/dist/types/components/inspectors/SocialLinksInspector.d.ts +2 -0
  31. package/dist/types/components/inspectors/groups.d.ts +22 -0
  32. package/dist/types/components/useDraggableBlock.d.ts +17 -0
  33. package/dist/types/context.d.ts +115 -0
  34. package/dist/types/core/content.d.ts +28 -0
  35. package/dist/types/core/cx.d.ts +8 -0
  36. package/dist/types/core/dnd.d.ts +73 -0
  37. package/dist/types/core/layers.d.ts +27 -0
  38. package/dist/types/core/paste-handler.d.ts +9 -0
  39. package/dist/types/core/paste.d.ts +22 -0
  40. package/dist/types/core/stores.d.ts +19 -0
  41. package/dist/types/core/style-utils.d.ts +3 -0
  42. package/dist/types/i18n/en.d.ts +247 -0
  43. package/dist/types/i18n/fr.d.ts +3 -0
  44. package/dist/types/i18n/index.d.ts +509 -0
  45. package/dist/types/index.d.ts +37 -0
  46. package/dist/types/nodes/custom-nodes.d.ts +19 -0
  47. package/dist/types/registry/default-nodes.d.ts +7 -0
  48. package/dist/types/registry/default-palette.d.ts +7 -0
  49. package/dist/types/registry/default-templates.d.ts +10 -0
  50. package/dist/types/registry/types.d.ts +56 -0
  51. package/package.json +89 -0
@@ -0,0 +1,73 @@
1
+ import type { Editor, JSONContent } from '@tiptap/core';
2
+ import { Fragment, type Node as PMNode } from '@tiptap/pm/model';
3
+ import { type Transaction } from '@tiptap/pm/state';
4
+ import type { EditorView } from '@tiptap/pm/view';
5
+ /** Nodes whose children are free-flowing blocks (the "drop zones"). */
6
+ export declare const BLOCK_CONTAINERS: Set<string>;
7
+ /** Row-like nodes whose children are columns laid out horizontally. */
8
+ export declare const COLUMN_PARENTS: Set<string>;
9
+ export declare const DND_MIME = "application/x-email-block";
10
+ export type DragPayload = {
11
+ kind: 'new';
12
+ label: string;
13
+ content: () => JSONContent[];
14
+ } | {
15
+ kind: 'move';
16
+ pos: number;
17
+ label: string;
18
+ };
19
+ export interface DropTarget {
20
+ /** Document position where the content will be inserted. */
21
+ pos: number;
22
+ /** When set, the range [pos, replaceTo) is replaced (empty placeholder paragraph). */
23
+ replaceTo?: number;
24
+ /** Indicator geometry in client coordinates (canvas drops only). */
25
+ line?: {
26
+ top: number;
27
+ left: number;
28
+ width: number;
29
+ };
30
+ /** Whether the target is an empty drop zone (shows a filled zone instead of a line). */
31
+ emptyZone?: {
32
+ top: number;
33
+ left: number;
34
+ width: number;
35
+ height: number;
36
+ };
37
+ }
38
+ export declare function isEmptyParagraph(node: PMNode): boolean;
39
+ export declare function findRootContainer(doc: PMNode): {
40
+ node: PMNode;
41
+ pos: number;
42
+ } | null;
43
+ /**
44
+ * Finds where content dragged at (x, y) should land. Walks down from the email
45
+ * container into sections and columns, then picks the gap between children
46
+ * closest to the pointer — the same mental model as Unlayer's rows/contents.
47
+ */
48
+ export declare function findDropTarget(view: EditorView, x: number, y: number, fragment: Fragment, exclude?: {
49
+ from: number;
50
+ to: number;
51
+ }): DropTarget | null;
52
+ /** Builds the PM fragment for a drag payload. */
53
+ export declare function payloadFragment(view: EditorView, payload: DragPayload): Fragment | null;
54
+ /** Applies a drop. Returns true when the document changed. */
55
+ export declare function applyDrop(editor: Editor, payload: DragPayload, rawTarget: DropTarget): boolean;
56
+ /** Deletes a node; keeps its parent valid by leaving an empty paragraph if needed. */
57
+ export declare function removeNode(tr: Transaction, from: number, to: number): void;
58
+ export interface BlockUnit {
59
+ pos: number;
60
+ node: PMNode;
61
+ }
62
+ /** The innermost node at `pos` (or one of its ancestors) that sits directly in a drop zone. */
63
+ export declare function unitAt(doc: PMNode, pos: number, includeSelf?: boolean): BlockUnit | null;
64
+ /** Columns live in fixed-size rows: they can be selected but not moved, duplicated or deleted. */
65
+ export declare function isFixedUnit(unit: BlockUnit): boolean;
66
+ /** The unit enclosing `unit` (e.g. paragraph -> its columns row). */
67
+ export declare function parentUnit(doc: PMNode, unit: BlockUnit): BlockUnit | null;
68
+ export declare function selectedUnit(editor: Editor): BlockUnit | null;
69
+ export declare function selectUnit(editor: Editor, unit: BlockUnit): void;
70
+ export declare function duplicateUnit(editor: Editor, unit: BlockUnit): void;
71
+ export declare function deleteUnit(editor: Editor, unit: BlockUnit): void;
72
+ /** Appends blocks at the end of the email (click-to-insert from the palette). */
73
+ export declare function appendBlocks(editor: Editor, content: JSONContent[]): void;
@@ -0,0 +1,27 @@
1
+ import type { Fragment, Node as PMNode } from '@tiptap/pm/model';
2
+ import { type DropTarget } from './dnd';
3
+ export interface LayerItem {
4
+ /** Stable-ish key built from the child indexes ("0.2.1"). */
5
+ key: string;
6
+ pos: number;
7
+ node: PMNode;
8
+ depth: number;
9
+ /** 1-based index among siblings (used to name columns). */
10
+ index: number;
11
+ children: LayerItem[];
12
+ /** Accepts blocks dropped "inside" (section, column). */
13
+ accepts: boolean;
14
+ /** Can't be moved (columns are locked in their row). */
15
+ fixed: boolean;
16
+ /** Drop zone holding nothing but its placeholder paragraph. */
17
+ empty: boolean;
18
+ }
19
+ export declare function buildLayers(doc: PMNode): LayerItem[];
20
+ export type LayerZone = 'before' | 'after' | 'inside';
21
+ /** Which part of a row the pointer is over, given what the row can accept. */
22
+ export declare function zoneFor(item: LayerItem, ratio: number): LayerZone;
23
+ /** Translates a layers-panel drop into a document position, validated by the schema. */
24
+ export declare function layerDropTarget(doc: PMNode, item: LayerItem, zone: LayerZone, fragment: Fragment, exclude?: {
25
+ from: number;
26
+ to: number;
27
+ }): DropTarget | null;
@@ -0,0 +1,9 @@
1
+ import type { Extensions } from '@tiptap/core';
2
+ import type { Slice } from '@tiptap/pm/model';
3
+ import type { EditorView } from '@tiptap/pm/view';
4
+ /**
5
+ * Drops dangerous elements and URL schemes. Content copied from the editor
6
+ * (node-* classes) keeps its attributes; external content keeps semantic HTML only.
7
+ */
8
+ export declare function sanitizePastedHtml(html: string): string;
9
+ export declare function createPasteHandler(extensions: Extensions): (view: EditorView, event: ClipboardEvent, slice: Slice) => boolean;
@@ -0,0 +1,22 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ /**
3
+ * The package's Button renders `data-href` and wraps the link in
4
+ * `div.align-*`, but its parseHTML reads neither: a copied button came back
5
+ * with href "#" and left-aligned. Read them back so copy/paste round-trips.
6
+ */
7
+ export declare const EmailButton: import("@react-email/editor/core").EmailNode<import("@react-email/editor/extensions").EditorButtonOptions, Record<string, never>>;
8
+ /**
9
+ * Block-friendly paste, wired as a capture-phase `paste` listener on the canvas:
10
+ *
11
+ * 1. With a block selected (NodeSelection), ProseMirror would *replace* it.
12
+ * Builders (Unlayer, Elementor) paste *after* it instead, so copy + paste on
13
+ * the same block duplicates it. We open an empty paragraph after the block
14
+ * and paste there.
15
+ * 2. Content copied from this editor (`data-pm-slice`) is replayed through
16
+ * `view.pasteHTML` without clipboard data, which makes the package's paste
17
+ * handler step aside and lets ProseMirror restore the exact slice.
18
+ * External HTML still goes through the package's sanitizer.
19
+ *
20
+ * Returns true when the event was handled.
21
+ */
22
+ export declare function handleCanvasPaste(editor: Editor, event: ClipboardEvent): boolean;
@@ -0,0 +1,19 @@
1
+ import type { DragPayload } from './dnd';
2
+ /**
3
+ * Per-editor state shared by the canvas, the palette and the layers panel.
4
+ * One instance lives in each `EditorRoot`, so several editors can coexist on a page.
5
+ */
6
+ /** Only one drag happens at a time; `dragover` can't read dataTransfer payloads, so the active payload lives here. */
7
+ export interface DragSession {
8
+ get: () => DragPayload | null;
9
+ start: (payload: DragPayload) => void;
10
+ end: () => void;
11
+ }
12
+ export declare function createDragSession(): DragSession;
13
+ /** Hovered block position: the canvas and the layers panel highlight the same block. */
14
+ export interface HoverStore {
15
+ get: () => number | null;
16
+ set: (pos: number | null) => void;
17
+ subscribe: (listener: () => void) => () => void;
18
+ }
19
+ export declare function createHoverStore(): HoverStore;
@@ -0,0 +1,3 @@
1
+ import type { CSSProperties } from 'react';
2
+ /** "padding-top: 4px; color: red" -> { paddingTop: '4px', color: 'red' } */
3
+ export declare function inlineStyleToObject(style: unknown): CSSProperties;
@@ -0,0 +1,247 @@
1
+ /** English UI strings (default locale). Every other locale implements the same shape. */
2
+ export declare const en: {
3
+ topBar: {
4
+ layers: string;
5
+ toggleLayers: string;
6
+ undo: string;
7
+ redo: string;
8
+ canvasWidth: string;
9
+ desktop: string;
10
+ mobile: string;
11
+ template: string;
12
+ confirmTemplate: string;
13
+ import: string;
14
+ exportJson: string;
15
+ preview: string;
16
+ exportHtml: string;
17
+ invalidFile: (message: string) => string;
18
+ };
19
+ preview: {
20
+ dialogLabel: string;
21
+ desktop: string;
22
+ mobile: string;
23
+ html: string;
24
+ text: string;
25
+ copy: string;
26
+ copied: string;
27
+ download: string;
28
+ close: string;
29
+ rendering: string;
30
+ renderError: (message: string) => string;
31
+ iframeTitle: string;
32
+ };
33
+ sidebar: {
34
+ label: string;
35
+ tabContent: string;
36
+ tabBlocks: string;
37
+ tabBody: string;
38
+ layoutTitle: string;
39
+ contentTitle: string;
40
+ dragHint: string;
41
+ tileTitle: (label: string) => string;
42
+ propertiesEyebrow: string;
43
+ closeProperties: string;
44
+ breadcrumb: string;
45
+ selectedText: string;
46
+ };
47
+ body: {
48
+ general: string;
49
+ previewText: string;
50
+ previewTextPlaceholder: string;
51
+ background: string;
52
+ backgroundColor: string;
53
+ outerPadding: string;
54
+ content: string;
55
+ width: string;
56
+ contentColor: string;
57
+ innerPadding: string;
58
+ radius: string;
59
+ defaultStyles: string;
60
+ text: string;
61
+ headings: string;
62
+ links: string;
63
+ buttonBackground: string;
64
+ buttonText: string;
65
+ };
66
+ inspector: {
67
+ typography: string;
68
+ color: string;
69
+ size: string;
70
+ weight: string;
71
+ weightLight: string;
72
+ weightNormal: string;
73
+ weightSemibold: string;
74
+ weightBold: string;
75
+ weightExtrabold: string;
76
+ lineHeight: string;
77
+ alignment: string;
78
+ spacing: string;
79
+ padding: string;
80
+ box: string;
81
+ backgroundColor: string;
82
+ radius: string;
83
+ border: string;
84
+ borderColor: string;
85
+ image: string;
86
+ uploadImage: string;
87
+ imageUrl: string;
88
+ altText: string;
89
+ altPlaceholder: string;
90
+ layout: string;
91
+ autoWidth: string;
92
+ width: string;
93
+ action: string;
94
+ clickLink: string;
95
+ linkUrl: string;
96
+ button: string;
97
+ textColor: string;
98
+ textSize: string;
99
+ buttonPadding: string;
100
+ networks: string;
101
+ removeNetwork: string;
102
+ addNetwork: string;
103
+ appearance: string;
104
+ iconSize: string;
105
+ gap: string;
106
+ columns: string;
107
+ distribution: string;
108
+ equal: string;
109
+ columnGap: string;
110
+ columnsHint: string;
111
+ spacer: string;
112
+ height: string;
113
+ htmlCode: string;
114
+ htmlHint: string;
115
+ line: string;
116
+ thickness: string;
117
+ lineStyle: string;
118
+ heading: string;
119
+ level: string;
120
+ formatting: string;
121
+ bold: string;
122
+ italic: string;
123
+ underline: string;
124
+ strike: string;
125
+ link: string;
126
+ url: string;
127
+ linkColor: string;
128
+ };
129
+ fields: {
130
+ colorDefault: string;
131
+ chooseColor: (value: string) => string;
132
+ hexCode: string;
133
+ eyedropper: string;
134
+ documentColors: string;
135
+ palette: string;
136
+ resetColor: string;
137
+ alignLeft: string;
138
+ alignCenter: string;
139
+ alignRight: string;
140
+ top: string;
141
+ right: string;
142
+ bottom: string;
143
+ left: string;
144
+ moreOptions: string;
145
+ auto: string;
146
+ };
147
+ layers: {
148
+ title: string;
149
+ expandAll: string;
150
+ collapseAll: string;
151
+ close: string;
152
+ tree: string;
153
+ emptyEmail: string;
154
+ emptyZone: string;
155
+ column: (index: number) => string;
156
+ fixedHint: string;
157
+ itemHint: string;
158
+ expand: string;
159
+ collapse: string;
160
+ uploadedImage: string;
161
+ empty: string;
162
+ };
163
+ canvas: {
164
+ dropHere: string;
165
+ dropLabel: (label: string) => string;
166
+ emptyZone: string;
167
+ move: string;
168
+ moveLabel: (label: string) => string;
169
+ selectParent: (label: string) => string;
170
+ duplicate: string;
171
+ delete: string;
172
+ };
173
+ placeholder: {
174
+ heading: string;
175
+ paragraph: string;
176
+ };
177
+ nodes: Record<string, string>;
178
+ palette: {
179
+ oneColumn: string;
180
+ twoColumns: string;
181
+ threeColumns: string;
182
+ fourColumns: string;
183
+ heading: string;
184
+ text: string;
185
+ button: string;
186
+ image: string;
187
+ divider: string;
188
+ spacer: string;
189
+ social: string;
190
+ menu: string;
191
+ list: string;
192
+ quote: string;
193
+ html: string;
194
+ card: string;
195
+ };
196
+ templates: {
197
+ header: string;
198
+ headerDescription: string;
199
+ hero: string;
200
+ heroDescription: string;
201
+ products: string;
202
+ productsDescription: string;
203
+ features: string;
204
+ featuresDescription: string;
205
+ testimonial: string;
206
+ testimonialDescription: string;
207
+ footer: string;
208
+ footerDescription: string;
209
+ };
210
+ /** Default text inserted by the palette, the templates and the starter email. */
211
+ content: {
212
+ heading: string;
213
+ text: string;
214
+ button: string;
215
+ image: string;
216
+ menuHome: string;
217
+ menuShop: string;
218
+ menuBlog: string;
219
+ menuContact: string;
220
+ listItems: string[];
221
+ quote: string;
222
+ html: string;
223
+ cardTitle: string;
224
+ cardText: string;
225
+ logo: string;
226
+ heroTitle: string;
227
+ heroText: string;
228
+ heroButton: string;
229
+ productA: string;
230
+ productB: string;
231
+ productText: string;
232
+ productButton: string;
233
+ features: string[];
234
+ featureText: string;
235
+ testimonialQuote: string;
236
+ testimonialAuthor: string;
237
+ footerAddress: string;
238
+ unsubscribe: string;
239
+ starterTitle: string;
240
+ };
241
+ /** Translations of the package's “/” commands, keyed by their English title. */
242
+ slashCommands: Record<string, {
243
+ title: string;
244
+ description: string;
245
+ }>;
246
+ };
247
+ export type Messages = typeof en;
@@ -0,0 +1,3 @@
1
+ import type { Messages } from './en';
2
+ /** Textes de l'interface en français. */
3
+ export declare const fr: Messages;