@react-email/editor 0.0.0-experimental.4 → 0.0.0-experimental.40
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/core/index.cjs +9 -0
- package/dist/core/index.d.cts +2 -0
- package/dist/core/index.d.mts +3 -0
- package/dist/core/index.mjs +4 -0
- package/dist/create-paste-handler-B8BtjBk3.d.cts +14 -0
- package/dist/create-paste-handler-B8BtjBk3.d.cts.map +1 -0
- package/dist/create-paste-handler-CGR738bC.d.mts +14 -0
- package/dist/create-paste-handler-CGR738bC.d.mts.map +1 -0
- package/dist/event-bus-CHEzOS_O.mjs +329 -0
- package/dist/event-bus-CHEzOS_O.mjs.map +1 -0
- package/dist/event-bus-fb8U7hrl.cjs +450 -0
- package/dist/extension-DyY8_bh4.mjs +1110 -0
- package/dist/extension-DyY8_bh4.mjs.map +1 -0
- package/dist/extension-w5VaUeSw.cjs +1235 -0
- package/dist/extensions/index.cjs +51 -0
- package/dist/extensions/index.d.cts +399 -0
- package/dist/extensions/index.d.cts.map +1 -0
- package/dist/extensions/index.d.mts +400 -0
- package/dist/extensions/index.d.mts.map +1 -0
- package/dist/extensions/index.mjs +5 -0
- package/dist/extensions-BvfmaKCn.mjs +2088 -0
- package/dist/extensions-BvfmaKCn.mjs.map +1 -0
- package/dist/extensions-CkjPj2JO.cjs +2369 -0
- package/dist/global-content-D_WYaFgX.mjs +78 -0
- package/dist/global-content-D_WYaFgX.mjs.map +1 -0
- package/dist/global-content-bJgotqmA.cjs +89 -0
- package/dist/index-C4KcMQ0R.d.cts +161 -0
- package/dist/index-C4KcMQ0R.d.cts.map +1 -0
- package/dist/index-CxX7W63O.d.mts +161 -0
- package/dist/index-CxX7W63O.d.mts.map +1 -0
- package/dist/index.cjs +74 -0
- package/dist/index.css +832 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +31 -277
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +64 -1377
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/index.cjs +23 -0
- package/dist/plugins/index.d.cts +191 -0
- package/dist/plugins/index.d.cts.map +1 -0
- package/dist/plugins/index.d.mts +191 -0
- package/dist/plugins/index.d.mts.map +1 -0
- package/dist/plugins/index.mjs +3 -0
- package/dist/root-CkYaJZpj.mjs +2316 -0
- package/dist/root-CkYaJZpj.mjs.map +1 -0
- package/dist/root-Gu08xybW.cjs +2832 -0
- package/dist/set-text-alignment-Cv72txmv.cjs +24 -0
- package/dist/set-text-alignment-OA8IMWmO.mjs +19 -0
- package/dist/set-text-alignment-OA8IMWmO.mjs.map +1 -0
- package/dist/styles-C-cCyJCn.cjs +211 -0
- package/dist/styles-_TMw3YxC.mjs +194 -0
- package/dist/styles-_TMw3YxC.mjs.map +1 -0
- package/dist/ui/bubble-menu/bubble-menu.css +285 -0
- package/dist/ui/index.cjs +147 -0
- package/dist/ui/index.d.cts +939 -0
- package/dist/ui/index.d.cts.map +1 -0
- package/dist/ui/index.d.mts +939 -0
- package/dist/ui/index.d.mts.map +1 -0
- package/dist/ui/index.mjs +60 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/slash-command/slash-command.css +44 -0
- package/dist/ui/themes/default.css +830 -0
- package/dist/utils/index.cjs +3 -0
- package/dist/utils/index.d.cts +7 -0
- package/dist/utils/index.d.cts.map +1 -0
- package/dist/utils/index.d.mts +7 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +3 -0
- package/package.json +109 -21
- package/dist/index.d.ts +0 -279
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -1436
package/dist/index.mjs
CHANGED
|
@@ -1,1387 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* @param config - Node configuration object or a function that returns a configuration object
|
|
20
|
-
*/
|
|
21
|
-
static create(config) {
|
|
22
|
-
return new EmailNode(typeof config === "function" ? config() : config);
|
|
23
|
-
}
|
|
24
|
-
static from(node, renderToReactEmail) {
|
|
25
|
-
const customNode = EmailNode.create({});
|
|
26
|
-
Object.assign(customNode, { ...node });
|
|
27
|
-
customNode.config = {
|
|
28
|
-
...node.config,
|
|
29
|
-
renderToReactEmail
|
|
30
|
-
};
|
|
31
|
-
return customNode;
|
|
32
|
-
}
|
|
33
|
-
configure(options) {
|
|
34
|
-
return super.configure(options);
|
|
35
|
-
}
|
|
36
|
-
extend(extendedConfig) {
|
|
37
|
-
const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
|
|
38
|
-
return super.extend(resolvedConfig);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/extensions/alignment-attribute.tsx
|
|
44
|
-
const AlignmentAttribute = Extension.create({
|
|
45
|
-
name: "alignmentAttribute",
|
|
46
|
-
addOptions() {
|
|
47
|
-
return {
|
|
48
|
-
types: [],
|
|
49
|
-
alignments: [
|
|
50
|
-
"left",
|
|
51
|
-
"center",
|
|
52
|
-
"right",
|
|
53
|
-
"justify"
|
|
54
|
-
]
|
|
55
|
-
};
|
|
56
|
-
},
|
|
57
|
-
addGlobalAttributes() {
|
|
58
|
-
return [{
|
|
59
|
-
types: this.options.types,
|
|
60
|
-
attributes: { alignment: {
|
|
61
|
-
parseHTML: (element) => {
|
|
62
|
-
const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
|
|
63
|
-
if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
|
|
64
|
-
return null;
|
|
65
|
-
},
|
|
66
|
-
renderHTML: (attributes) => {
|
|
67
|
-
if (attributes.alignment === "left") return {};
|
|
68
|
-
return { alignment: attributes.alignment };
|
|
69
|
-
}
|
|
70
|
-
} }
|
|
71
|
-
}];
|
|
72
|
-
},
|
|
73
|
-
addCommands() {
|
|
74
|
-
return { setAlignment: (alignment) => ({ commands }) => {
|
|
75
|
-
if (!this.options.alignments.includes(alignment)) return false;
|
|
76
|
-
return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
|
|
77
|
-
} };
|
|
78
|
-
},
|
|
79
|
-
addKeyboardShortcuts() {
|
|
80
|
-
return {
|
|
81
|
-
Enter: () => {
|
|
82
|
-
const { from } = this.editor.state.selection;
|
|
83
|
-
const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
|
|
84
|
-
if (currentAlignment) requestAnimationFrame(() => {
|
|
85
|
-
this.editor.commands.setAlignment(currentAlignment);
|
|
86
|
-
});
|
|
87
|
-
return false;
|
|
88
|
-
},
|
|
89
|
-
"Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
|
|
90
|
-
"Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
|
|
91
|
-
"Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
|
|
92
|
-
"Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region src/utils/attribute-helpers.ts
|
|
99
|
-
/**
|
|
100
|
-
* Creates TipTap attribute definitions for a list of HTML attributes.
|
|
101
|
-
* Each attribute will have the same pattern:
|
|
102
|
-
* - default: null
|
|
103
|
-
* - parseHTML: extracts the attribute from the element
|
|
104
|
-
* - renderHTML: conditionally renders the attribute if it has a value
|
|
105
|
-
*
|
|
106
|
-
* @param attributeNames - Array of HTML attribute names to create definitions for
|
|
107
|
-
* @returns Object with TipTap attribute definitions
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* const attrs = createStandardAttributes(['class', 'id', 'title']);
|
|
111
|
-
* // Returns:
|
|
112
|
-
* // {
|
|
113
|
-
* // class: {
|
|
114
|
-
* // default: null,
|
|
115
|
-
* // parseHTML: (element) => element.getAttribute('class'),
|
|
116
|
-
* // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
|
|
117
|
-
* // },
|
|
118
|
-
* // ...
|
|
119
|
-
* // }
|
|
120
|
-
*/
|
|
121
|
-
function createStandardAttributes(attributeNames) {
|
|
122
|
-
return Object.fromEntries(attributeNames.map((attr) => [attr, {
|
|
123
|
-
default: null,
|
|
124
|
-
parseHTML: (element) => element.getAttribute(attr),
|
|
125
|
-
renderHTML: (attributes) => {
|
|
126
|
-
if (!attributes[attr]) return {};
|
|
127
|
-
return { [attr]: attributes[attr] };
|
|
128
|
-
}
|
|
129
|
-
}]));
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Common HTML attributes used across multiple extensions.
|
|
133
|
-
* These preserve attributes during HTML import and editing for better
|
|
134
|
-
* fidelity when importing existing email templates.
|
|
135
|
-
*/
|
|
136
|
-
const COMMON_HTML_ATTRIBUTES = [
|
|
137
|
-
"id",
|
|
138
|
-
"class",
|
|
139
|
-
"title",
|
|
140
|
-
"lang",
|
|
141
|
-
"dir",
|
|
142
|
-
"data-id"
|
|
143
|
-
];
|
|
144
|
-
/**
|
|
145
|
-
* Layout-specific HTML attributes used for positioning and sizing.
|
|
146
|
-
*/
|
|
147
|
-
const LAYOUT_ATTRIBUTES = [
|
|
148
|
-
"align",
|
|
149
|
-
"width",
|
|
150
|
-
"height"
|
|
151
|
-
];
|
|
152
|
-
/**
|
|
153
|
-
* Table-specific HTML attributes used for table layout and styling.
|
|
154
|
-
*/
|
|
155
|
-
const TABLE_ATTRIBUTES = [
|
|
156
|
-
"border",
|
|
157
|
-
"cellpadding",
|
|
158
|
-
"cellspacing"
|
|
159
|
-
];
|
|
160
|
-
/**
|
|
161
|
-
* Table cell-specific HTML attributes.
|
|
162
|
-
*/
|
|
163
|
-
const TABLE_CELL_ATTRIBUTES = [
|
|
164
|
-
"valign",
|
|
165
|
-
"bgcolor",
|
|
166
|
-
"colspan",
|
|
167
|
-
"rowspan"
|
|
168
|
-
];
|
|
169
|
-
/**
|
|
170
|
-
* Table header cell-specific HTML attributes.
|
|
171
|
-
* These are additional attributes that only apply to <th> elements.
|
|
172
|
-
*/
|
|
173
|
-
const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
|
|
174
|
-
|
|
175
|
-
//#endregion
|
|
176
|
-
//#region src/utils/styles.ts
|
|
177
|
-
const WHITE_SPACE_REGEX = /\s+/;
|
|
178
|
-
const inlineCssToJs = (inlineStyle, options = {}) => {
|
|
179
|
-
const styleObject = {};
|
|
180
|
-
if (!inlineStyle || inlineStyle === "" || typeof inlineStyle === "object") return styleObject;
|
|
181
|
-
inlineStyle.split(";").forEach((style) => {
|
|
182
|
-
if (style.trim()) {
|
|
183
|
-
const [key, value] = style.split(":");
|
|
184
|
-
const valueTrimmed = value?.trim();
|
|
185
|
-
if (!valueTrimmed) return;
|
|
186
|
-
const formattedKey = key.trim().replace(/-\w/g, (match) => match[1].toUpperCase());
|
|
187
|
-
styleObject[formattedKey] = options?.removeUnit ? valueTrimmed.replace(/px|%/g, "") : valueTrimmed;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
return styleObject;
|
|
191
|
-
};
|
|
192
|
-
/**
|
|
193
|
-
* Expands CSS shorthand properties (margin, padding) into their longhand equivalents.
|
|
194
|
-
* This prevents shorthand properties from overriding specific longhand properties in email clients.
|
|
195
|
-
*
|
|
196
|
-
* @param styles - Style object that may contain shorthand properties
|
|
197
|
-
* @returns New style object with shorthand properties expanded to longhand
|
|
198
|
-
*
|
|
199
|
-
* @example
|
|
200
|
-
* expandShorthandProperties({ margin: '0', paddingTop: '10px' })
|
|
201
|
-
* // Returns: { marginTop: '0', marginRight: '0', marginBottom: '0', marginLeft: '0', paddingTop: '10px' }
|
|
202
|
-
*/
|
|
203
|
-
function expandShorthandProperties(styles) {
|
|
204
|
-
if (!styles || typeof styles !== "object") return {};
|
|
205
|
-
const expanded = {};
|
|
206
|
-
for (const key in styles) {
|
|
207
|
-
const value = styles[key];
|
|
208
|
-
if (value === void 0 || value === null || value === "") continue;
|
|
209
|
-
switch (key) {
|
|
210
|
-
case "margin": {
|
|
211
|
-
const values = parseShorthandValue(value);
|
|
212
|
-
expanded.marginTop = values.top;
|
|
213
|
-
expanded.marginRight = values.right;
|
|
214
|
-
expanded.marginBottom = values.bottom;
|
|
215
|
-
expanded.marginLeft = values.left;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
case "padding": {
|
|
219
|
-
const values = parseShorthandValue(value);
|
|
220
|
-
expanded.paddingTop = values.top;
|
|
221
|
-
expanded.paddingRight = values.right;
|
|
222
|
-
expanded.paddingBottom = values.bottom;
|
|
223
|
-
expanded.paddingLeft = values.left;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
case "border": {
|
|
227
|
-
const values = convertBorderValue(value);
|
|
228
|
-
expanded.borderStyle = values.style;
|
|
229
|
-
expanded.borderWidth = values.width;
|
|
230
|
-
expanded.borderColor = values.color;
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case "borderTopLeftRadius":
|
|
234
|
-
case "borderTopRightRadius":
|
|
235
|
-
case "borderBottomLeftRadius":
|
|
236
|
-
case "borderBottomRightRadius":
|
|
237
|
-
expanded[key] = value;
|
|
238
|
-
if (styles.borderTopLeftRadius && styles.borderTopRightRadius && styles.borderBottomLeftRadius && styles.borderBottomRightRadius) {
|
|
239
|
-
const values = [
|
|
240
|
-
styles.borderTopLeftRadius,
|
|
241
|
-
styles.borderTopRightRadius,
|
|
242
|
-
styles.borderBottomLeftRadius,
|
|
243
|
-
styles.borderBottomRightRadius
|
|
244
|
-
];
|
|
245
|
-
if (new Set(values).size === 1) expanded.borderRadius = values[0];
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
default: expanded[key] = value;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return expanded;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Parses CSS shorthand value (1-4 values) into individual side values.
|
|
255
|
-
* Follows CSS specification for shorthand property value parsing.
|
|
256
|
-
*
|
|
257
|
-
* @param value - Shorthand value string (e.g., '0', '10px 20px', '5px 10px 15px 20px')
|
|
258
|
-
* @returns Object with top, right, bottom, left values
|
|
259
|
-
*/
|
|
260
|
-
function parseShorthandValue(value) {
|
|
261
|
-
const stringValue = String(value).trim();
|
|
262
|
-
const parts = stringValue.split(WHITE_SPACE_REGEX);
|
|
263
|
-
const len = parts.length;
|
|
264
|
-
if (len === 1) return {
|
|
265
|
-
top: parts[0],
|
|
266
|
-
right: parts[0],
|
|
267
|
-
bottom: parts[0],
|
|
268
|
-
left: parts[0]
|
|
269
|
-
};
|
|
270
|
-
if (len === 2) return {
|
|
271
|
-
top: parts[0],
|
|
272
|
-
right: parts[1],
|
|
273
|
-
bottom: parts[0],
|
|
274
|
-
left: parts[1]
|
|
275
|
-
};
|
|
276
|
-
if (len === 3) return {
|
|
277
|
-
top: parts[0],
|
|
278
|
-
right: parts[1],
|
|
279
|
-
bottom: parts[2],
|
|
280
|
-
left: parts[1]
|
|
281
|
-
};
|
|
282
|
-
if (len === 4) return {
|
|
283
|
-
top: parts[0],
|
|
284
|
-
right: parts[1],
|
|
285
|
-
bottom: parts[2],
|
|
286
|
-
left: parts[3]
|
|
287
|
-
};
|
|
288
|
-
return {
|
|
289
|
-
top: stringValue,
|
|
290
|
-
right: stringValue,
|
|
291
|
-
bottom: stringValue,
|
|
292
|
-
left: stringValue
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
function convertBorderValue(value) {
|
|
296
|
-
const stringValue = String(value).trim();
|
|
297
|
-
const parts = stringValue.split(WHITE_SPACE_REGEX);
|
|
298
|
-
switch (parts.length) {
|
|
299
|
-
case 1: return {
|
|
300
|
-
style: "solid",
|
|
301
|
-
width: parts[0],
|
|
302
|
-
color: "black"
|
|
303
|
-
};
|
|
304
|
-
case 2: return {
|
|
305
|
-
style: parts[1],
|
|
306
|
-
width: parts[0],
|
|
307
|
-
color: "black"
|
|
308
|
-
};
|
|
309
|
-
case 3: return {
|
|
310
|
-
style: parts[1],
|
|
311
|
-
width: parts[0],
|
|
312
|
-
color: parts[2]
|
|
313
|
-
};
|
|
314
|
-
case 4: return {
|
|
315
|
-
style: parts[1],
|
|
316
|
-
width: parts[0],
|
|
317
|
-
color: parts[2]
|
|
318
|
-
};
|
|
319
|
-
default: return {
|
|
320
|
-
style: "solid",
|
|
321
|
-
width: stringValue,
|
|
322
|
-
color: "black"
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Resolves conflicts between reset styles and inline styles by expanding
|
|
328
|
-
* shorthand properties (margin, padding) to longhand before merging.
|
|
329
|
-
* This prevents shorthand properties from overriding specific longhand properties.
|
|
330
|
-
*
|
|
331
|
-
* @param resetStyles - Base reset styles that may contain shorthand properties
|
|
332
|
-
* @param inlineStyles - Inline styles that should override reset styles
|
|
333
|
-
* @returns Merged styles with inline styles taking precedence
|
|
334
|
-
*/
|
|
335
|
-
function resolveConflictingStyles(resetStyles, inlineStyles) {
|
|
336
|
-
const expandedResetStyles = expandShorthandProperties(resetStyles);
|
|
337
|
-
const expandedInlineStyles = expandShorthandProperties(inlineStyles);
|
|
338
|
-
return {
|
|
339
|
-
...expandedResetStyles,
|
|
340
|
-
...expandedInlineStyles
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
//#endregion
|
|
345
|
-
//#region src/extensions/body.tsx
|
|
346
|
-
const Body = EmailNode.create({
|
|
347
|
-
name: "body",
|
|
348
|
-
group: "block",
|
|
349
|
-
content: "block+",
|
|
350
|
-
defining: true,
|
|
351
|
-
isolating: true,
|
|
352
|
-
addAttributes() {
|
|
353
|
-
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
354
|
-
},
|
|
355
|
-
parseHTML() {
|
|
356
|
-
return [{
|
|
357
|
-
tag: "body",
|
|
358
|
-
getAttrs: (node) => {
|
|
359
|
-
if (typeof node === "string") return false;
|
|
360
|
-
const element = node;
|
|
361
|
-
const attrs = {};
|
|
362
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
363
|
-
attrs[attr.name] = attr.value;
|
|
364
|
-
});
|
|
365
|
-
return attrs;
|
|
366
|
-
}
|
|
367
|
-
}];
|
|
368
|
-
},
|
|
369
|
-
renderHTML({ HTMLAttributes }) {
|
|
370
|
-
return [
|
|
371
|
-
"div",
|
|
372
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
373
|
-
0
|
|
374
|
-
];
|
|
375
|
-
},
|
|
376
|
-
renderToReactEmail({ children, node, styles }) {
|
|
377
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
378
|
-
return /* @__PURE__ */ jsx("div", {
|
|
379
|
-
className: node.attrs?.class || void 0,
|
|
380
|
-
style: {
|
|
381
|
-
...styles.reset,
|
|
382
|
-
...inlineStyles
|
|
383
|
-
},
|
|
384
|
-
children
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
//#endregion
|
|
390
|
-
//#region src/extensions/bold.ts
|
|
391
|
-
/**
|
|
392
|
-
* Matches bold text via `**` as input.
|
|
393
|
-
*/
|
|
394
|
-
const starInputRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))$/;
|
|
395
|
-
/**
|
|
396
|
-
* Matches bold text via `**` while pasting.
|
|
397
|
-
*/
|
|
398
|
-
const starPasteRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))/g;
|
|
399
|
-
/**
|
|
400
|
-
* Matches bold text via `__` as input.
|
|
401
|
-
*/
|
|
402
|
-
const underscoreInputRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))$/;
|
|
403
|
-
/**
|
|
404
|
-
* Matches bold text via `__` while pasting.
|
|
405
|
-
*/
|
|
406
|
-
const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
|
|
407
|
-
/**
|
|
408
|
-
* This extension allows you to mark text as bold.
|
|
409
|
-
* @see https://tiptap.dev/api/marks/bold
|
|
410
|
-
*/
|
|
411
|
-
const Bold = Mark.create({
|
|
412
|
-
name: "bold",
|
|
413
|
-
addOptions() {
|
|
414
|
-
return { HTMLAttributes: {} };
|
|
415
|
-
},
|
|
416
|
-
parseHTML() {
|
|
417
|
-
return [
|
|
418
|
-
{ tag: "strong" },
|
|
419
|
-
{
|
|
420
|
-
tag: "b",
|
|
421
|
-
getAttrs: (node) => node.style.fontWeight !== "normal" && null
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
style: "font-weight=400",
|
|
425
|
-
clearMark: (mark) => mark.type.name === this.name
|
|
426
|
-
}
|
|
427
|
-
];
|
|
428
|
-
},
|
|
429
|
-
renderHTML({ HTMLAttributes }) {
|
|
430
|
-
return [
|
|
431
|
-
"strong",
|
|
432
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
433
|
-
0
|
|
434
|
-
];
|
|
435
|
-
},
|
|
436
|
-
addCommands() {
|
|
437
|
-
return {
|
|
438
|
-
setBold: () => ({ commands }) => {
|
|
439
|
-
return commands.setMark(this.name);
|
|
440
|
-
},
|
|
441
|
-
toggleBold: () => ({ commands }) => {
|
|
442
|
-
return commands.toggleMark(this.name);
|
|
443
|
-
},
|
|
444
|
-
unsetBold: () => ({ commands }) => {
|
|
445
|
-
return commands.unsetMark(this.name);
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
},
|
|
449
|
-
addKeyboardShortcuts() {
|
|
450
|
-
return {
|
|
451
|
-
"Mod-b": () => this.editor.commands.toggleBold(),
|
|
452
|
-
"Mod-B": () => this.editor.commands.toggleBold()
|
|
453
|
-
};
|
|
454
|
-
},
|
|
455
|
-
addInputRules() {
|
|
456
|
-
return [markInputRule({
|
|
457
|
-
find: starInputRegex,
|
|
458
|
-
type: this.type
|
|
459
|
-
}), markInputRule({
|
|
460
|
-
find: underscoreInputRegex,
|
|
461
|
-
type: this.type
|
|
462
|
-
})];
|
|
463
|
-
},
|
|
464
|
-
addPasteRules() {
|
|
465
|
-
return [markPasteRule({
|
|
466
|
-
find: starPasteRegex,
|
|
467
|
-
type: this.type
|
|
468
|
-
}), markPasteRule({
|
|
469
|
-
find: underscorePasteRegex,
|
|
470
|
-
type: this.type
|
|
471
|
-
})];
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
//#endregion
|
|
476
|
-
//#region src/extensions/button.tsx
|
|
477
|
-
const Button = EmailNode.create({
|
|
478
|
-
name: "button",
|
|
479
|
-
group: "block",
|
|
480
|
-
content: "inline*",
|
|
481
|
-
defining: true,
|
|
482
|
-
draggable: true,
|
|
483
|
-
marks: "bold",
|
|
484
|
-
addAttributes() {
|
|
485
|
-
return {
|
|
486
|
-
class: { default: "button" },
|
|
487
|
-
href: { default: "#" },
|
|
488
|
-
alignment: { default: "left" }
|
|
489
|
-
};
|
|
490
|
-
},
|
|
491
|
-
parseHTML() {
|
|
492
|
-
return [{
|
|
493
|
-
tag: "a[data-id=\"react-email-button\"]",
|
|
494
|
-
getAttrs: (node) => {
|
|
495
|
-
if (typeof node === "string") return false;
|
|
496
|
-
const element = node;
|
|
497
|
-
const attrs = {};
|
|
498
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
499
|
-
attrs[attr.name] = attr.value;
|
|
500
|
-
});
|
|
501
|
-
return attrs;
|
|
502
|
-
}
|
|
503
|
-
}];
|
|
504
|
-
},
|
|
505
|
-
renderHTML({ HTMLAttributes }) {
|
|
506
|
-
return [
|
|
507
|
-
"div",
|
|
508
|
-
mergeAttributes({ class: `align-${HTMLAttributes?.alignment}` }),
|
|
509
|
-
[
|
|
510
|
-
"a",
|
|
511
|
-
mergeAttributes({
|
|
512
|
-
class: `node-button ${HTMLAttributes?.class}`,
|
|
513
|
-
style: HTMLAttributes?.style,
|
|
514
|
-
"data-id": "react-email-button",
|
|
515
|
-
"data-href": HTMLAttributes?.href
|
|
516
|
-
}),
|
|
517
|
-
0
|
|
518
|
-
]
|
|
519
|
-
];
|
|
520
|
-
},
|
|
521
|
-
addCommands() {
|
|
522
|
-
return {
|
|
523
|
-
updateButton: (attributes) => ({ commands }) => {
|
|
524
|
-
return commands.updateAttributes("button", attributes);
|
|
525
|
-
},
|
|
526
|
-
setButton: () => ({ commands }) => {
|
|
527
|
-
return commands.insertContent({
|
|
528
|
-
type: "button",
|
|
529
|
-
content: [{
|
|
530
|
-
type: "text",
|
|
531
|
-
text: "Button"
|
|
532
|
-
}]
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
},
|
|
537
|
-
renderToReactEmail({ children, node, styles }) {
|
|
538
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
539
|
-
return /* @__PURE__ */ jsx(Row, { children: /* @__PURE__ */ jsx(Column, {
|
|
540
|
-
align: node.attrs?.align || node.attrs?.alignment,
|
|
541
|
-
children: /* @__PURE__ */ jsx(Button$1, {
|
|
542
|
-
className: node.attrs?.class || void 0,
|
|
543
|
-
href: node.attrs?.href,
|
|
544
|
-
style: {
|
|
545
|
-
...styles.reset,
|
|
546
|
-
...styles.button,
|
|
547
|
-
...inlineStyles
|
|
548
|
-
},
|
|
549
|
-
children
|
|
550
|
-
})
|
|
551
|
-
}) });
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
//#endregion
|
|
556
|
-
//#region src/extensions/class-attribute.tsx
|
|
557
|
-
const ClassAttribute = Extension.create({
|
|
558
|
-
name: "classAttribute",
|
|
559
|
-
addOptions() {
|
|
560
|
-
return {
|
|
561
|
-
types: [],
|
|
562
|
-
class: []
|
|
563
|
-
};
|
|
564
|
-
},
|
|
565
|
-
addGlobalAttributes() {
|
|
566
|
-
return [{
|
|
567
|
-
types: this.options.types,
|
|
568
|
-
attributes: { class: {
|
|
569
|
-
default: "",
|
|
570
|
-
parseHTML: (element) => element.className || "",
|
|
571
|
-
renderHTML: (attributes) => {
|
|
572
|
-
return attributes.class ? { class: attributes.class } : {};
|
|
573
|
-
}
|
|
574
|
-
} }
|
|
575
|
-
}];
|
|
576
|
-
},
|
|
577
|
-
addCommands() {
|
|
578
|
-
return {
|
|
579
|
-
unsetClass: () => ({ commands }) => {
|
|
580
|
-
return this.options.types.every((type) => commands.resetAttributes(type, "class"));
|
|
581
|
-
},
|
|
582
|
-
setClass: (classList) => ({ commands }) => {
|
|
583
|
-
return this.options.types.every((type) => commands.updateAttributes(type, { class: classList }));
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
},
|
|
587
|
-
addKeyboardShortcuts() {
|
|
588
|
-
return { Enter: ({ editor }) => {
|
|
589
|
-
requestAnimationFrame(() => {
|
|
590
|
-
editor.commands.resetAttributes("paragraph", "class");
|
|
591
|
-
});
|
|
592
|
-
return false;
|
|
593
|
-
} };
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
//#endregion
|
|
598
|
-
//#region src/utils/prism-utils.ts
|
|
599
|
-
const publicURL = "/styles/prism";
|
|
600
|
-
function loadPrismTheme(theme) {
|
|
601
|
-
const link = document.createElement("link");
|
|
602
|
-
link.rel = "stylesheet";
|
|
603
|
-
link.href = `${publicURL}/prism-${theme}.css`;
|
|
604
|
-
link.setAttribute("data-prism-theme", "");
|
|
605
|
-
document.head.appendChild(link);
|
|
606
|
-
}
|
|
607
|
-
function removePrismTheme() {
|
|
608
|
-
const existingTheme = document.querySelectorAll("link[rel=\"stylesheet\"][data-prism-theme]");
|
|
609
|
-
if (existingTheme.length > 0) existingTheme.forEach((cssLinkTag) => {
|
|
610
|
-
cssLinkTag.remove();
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
function hasPrismThemeLoaded(theme) {
|
|
614
|
-
return !!document.querySelector(`link[rel="stylesheet"][data-prism-theme][href="${publicURL}/prism-${theme}.css"]`);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region src/extensions/prism-plugin.ts
|
|
619
|
-
const PRISM_LANGUAGE_LOADED_META = "prismLanguageLoaded";
|
|
620
|
-
function parseNodes(nodes, className = []) {
|
|
621
|
-
return nodes.flatMap((node) => {
|
|
622
|
-
const classes = [...className, ...node.properties ? node.properties.className : []];
|
|
623
|
-
if (node.children) return parseNodes(node.children, classes);
|
|
624
|
-
return {
|
|
625
|
-
text: node.value ?? "",
|
|
626
|
-
classes
|
|
627
|
-
};
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
function getHighlightNodes(html) {
|
|
631
|
-
return fromHtml(html, { fragment: true }).children;
|
|
632
|
-
}
|
|
633
|
-
function registeredLang(aliasOrLanguage) {
|
|
634
|
-
const allSupportLang = Object.keys(Prism.languages).filter((id) => typeof Prism.languages[id] === "object");
|
|
635
|
-
return Boolean(allSupportLang.find((x) => x === aliasOrLanguage));
|
|
636
|
-
}
|
|
637
|
-
function getDecorations({ doc, name, defaultLanguage, defaultTheme, loadingLanguages, onLanguageLoaded }) {
|
|
638
|
-
const decorations = [];
|
|
639
|
-
findChildren(doc, (node) => node.type.name === name).forEach((block) => {
|
|
640
|
-
let from = block.pos + 1;
|
|
641
|
-
const language = block.node.attrs.language || defaultLanguage;
|
|
642
|
-
const theme = block.node.attrs.theme || defaultTheme;
|
|
643
|
-
let html = "";
|
|
644
|
-
try {
|
|
645
|
-
if (!registeredLang(language) && !loadingLanguages.has(language)) {
|
|
646
|
-
loadingLanguages.add(language);
|
|
647
|
-
import(`prismjs/components/prism-${language}`).then(() => {
|
|
648
|
-
loadingLanguages.delete(language);
|
|
649
|
-
onLanguageLoaded(language);
|
|
650
|
-
}).catch(() => {
|
|
651
|
-
loadingLanguages.delete(language);
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
if (!hasPrismThemeLoaded(theme)) loadPrismTheme(theme);
|
|
655
|
-
html = Prism.highlight(block.node.textContent, Prism.languages[language], language);
|
|
656
|
-
} catch {
|
|
657
|
-
html = Prism.highlight(block.node.textContent, Prism.languages.javascript, "js");
|
|
658
|
-
}
|
|
659
|
-
parseNodes(getHighlightNodes(html)).forEach((node) => {
|
|
660
|
-
const to = from + node.text.length;
|
|
661
|
-
if (node.classes.length) {
|
|
662
|
-
const decoration = Decoration.inline(from, to, { class: node.classes.join(" ") });
|
|
663
|
-
decorations.push(decoration);
|
|
664
|
-
}
|
|
665
|
-
from = to;
|
|
666
|
-
});
|
|
667
|
-
});
|
|
668
|
-
return DecorationSet.create(doc, decorations);
|
|
669
|
-
}
|
|
670
|
-
function PrismPlugin({ name, defaultLanguage, defaultTheme }) {
|
|
671
|
-
if (!defaultLanguage) throw Error("You must specify the defaultLanguage parameter");
|
|
672
|
-
const loadingLanguages = /* @__PURE__ */ new Set();
|
|
673
|
-
let pluginView = null;
|
|
674
|
-
const onLanguageLoaded = (language) => {
|
|
675
|
-
if (pluginView) pluginView.dispatch(pluginView.state.tr.setMeta(PRISM_LANGUAGE_LOADED_META, language));
|
|
676
|
-
};
|
|
677
|
-
const prismjsPlugin = new Plugin({
|
|
678
|
-
key: new PluginKey("prism"),
|
|
679
|
-
view(view) {
|
|
680
|
-
pluginView = view;
|
|
681
|
-
return { destroy() {
|
|
682
|
-
pluginView = null;
|
|
683
|
-
} };
|
|
1
|
+
import { U as createPasteHandler, V as composeReactEmail, W as createDropHandler, t as StarterKit } from "./extensions-BvfmaKCn.mjs";
|
|
2
|
+
import { t as EmailTheming } from "./extension-DyY8_bh4.mjs";
|
|
3
|
+
import { J as BubbleMenuButtonDefault, O as BubbleMenuImageDefault, S as BubbleMenuLinkDefault, j as BubbleMenuDefault, t as SlashCommandRoot } from "./root-CkYaJZpj.mjs";
|
|
4
|
+
import { UndoRedo } from "@tiptap/extensions";
|
|
5
|
+
import { EditorProvider, useCurrentEditor } from "@tiptap/react";
|
|
6
|
+
import { forwardRef, useImperativeHandle, useMemo } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
|
|
9
|
+
//#region src/email-editor/email-editor.tsx
|
|
10
|
+
function RefBridge({ editorRef }) {
|
|
11
|
+
const { editor } = useCurrentEditor();
|
|
12
|
+
useImperativeHandle(editorRef, () => ({
|
|
13
|
+
export: async () => {
|
|
14
|
+
if (!editor) return {
|
|
15
|
+
html: "",
|
|
16
|
+
text: ""
|
|
17
|
+
};
|
|
18
|
+
return composeReactEmail({ editor });
|
|
684
19
|
},
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
doc,
|
|
689
|
-
name,
|
|
690
|
-
defaultLanguage,
|
|
691
|
-
defaultTheme,
|
|
692
|
-
loadingLanguages,
|
|
693
|
-
onLanguageLoaded
|
|
694
|
-
});
|
|
695
|
-
},
|
|
696
|
-
apply: (transaction, decorationSet, oldState, newState) => {
|
|
697
|
-
const oldNodeName = oldState.selection.$head.parent.type.name;
|
|
698
|
-
const newNodeName = newState.selection.$head.parent.type.name;
|
|
699
|
-
const oldNodes = findChildren(oldState.doc, (node) => node.type.name === name);
|
|
700
|
-
const newNodes = findChildren(newState.doc, (node) => node.type.name === name);
|
|
701
|
-
if (transaction.getMeta(PRISM_LANGUAGE_LOADED_META) || transaction.docChanged && ([oldNodeName, newNodeName].includes(name) || newNodes.length !== oldNodes.length || transaction.steps.some((step) => {
|
|
702
|
-
const rangeStep = step;
|
|
703
|
-
return rangeStep.from !== void 0 && rangeStep.to !== void 0 && oldNodes.some((node) => {
|
|
704
|
-
return node.pos >= rangeStep.from && node.pos + node.node.nodeSize <= rangeStep.to;
|
|
705
|
-
});
|
|
706
|
-
}))) return getDecorations({
|
|
707
|
-
doc: transaction.doc,
|
|
708
|
-
name,
|
|
709
|
-
defaultLanguage,
|
|
710
|
-
defaultTheme,
|
|
711
|
-
loadingLanguages,
|
|
712
|
-
onLanguageLoaded
|
|
713
|
-
});
|
|
714
|
-
return decorationSet.map(transaction.mapping, transaction.doc);
|
|
715
|
-
}
|
|
20
|
+
getJSON: () => editor?.getJSON() ?? {
|
|
21
|
+
type: "doc",
|
|
22
|
+
content: []
|
|
716
23
|
},
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
return prismjsPlugin;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
//#endregion
|
|
729
|
-
//#region src/extensions/code-block.tsx
|
|
730
|
-
const CodeBlockPrism = EmailNode.from(CodeBlock$1.extend({
|
|
731
|
-
addOptions() {
|
|
732
|
-
return {
|
|
733
|
-
languageClassPrefix: "language-",
|
|
734
|
-
exitOnTripleEnter: false,
|
|
735
|
-
exitOnArrowDown: false,
|
|
736
|
-
enableTabIndentation: true,
|
|
737
|
-
tabSize: 2,
|
|
738
|
-
defaultLanguage: "javascript",
|
|
739
|
-
defaultTheme: "default",
|
|
740
|
-
HTMLAttributes: {}
|
|
741
|
-
};
|
|
742
|
-
},
|
|
743
|
-
addAttributes() {
|
|
744
|
-
return {
|
|
745
|
-
...this.parent?.(),
|
|
746
|
-
language: {
|
|
747
|
-
default: this.options.defaultLanguage,
|
|
748
|
-
parseHTML: (element) => {
|
|
749
|
-
if (!element) return null;
|
|
750
|
-
const { languageClassPrefix } = this.options;
|
|
751
|
-
if (!languageClassPrefix) return null;
|
|
752
|
-
const language = [...element.firstElementChild?.classList || []].filter((className) => className.startsWith(languageClassPrefix || "")).map((className) => className.replace(languageClassPrefix, ""))[0];
|
|
753
|
-
if (!language) return null;
|
|
754
|
-
return language;
|
|
755
|
-
},
|
|
756
|
-
rendered: false
|
|
757
|
-
},
|
|
758
|
-
theme: {
|
|
759
|
-
default: this.options.defaultTheme,
|
|
760
|
-
rendered: false
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
},
|
|
764
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
24
|
+
getHTML: () => editor?.getHTML() ?? "",
|
|
25
|
+
editor
|
|
26
|
+
}), [editor]);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const EmailEditor = forwardRef(({ content, onChange, onUploadImage, onReady, theme = "basic", editable = true, placeholder, bubbleMenu, extensions: extensionsProp, className }, ref) => {
|
|
30
|
+
const extensions = useMemo(() => {
|
|
31
|
+
if (extensionsProp) return extensionsProp;
|
|
765
32
|
return [
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
"code",
|
|
770
|
-
{ class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language} node-codeTag` : "node-codeTag" },
|
|
771
|
-
0
|
|
772
|
-
]
|
|
33
|
+
StarterKit.configure({ Placeholder: placeholder ? { placeholder } : void 0 }),
|
|
34
|
+
UndoRedo,
|
|
35
|
+
EmailTheming.configure({ theme })
|
|
773
36
|
];
|
|
774
|
-
},
|
|
775
|
-
|
|
776
|
-
return [...this.parent?.() || [], PrismPlugin({
|
|
777
|
-
name: this.name,
|
|
778
|
-
defaultLanguage: this.options.defaultLanguage,
|
|
779
|
-
defaultTheme: this.options.defaultTheme
|
|
780
|
-
})];
|
|
781
|
-
}
|
|
782
|
-
}), ({ node, styles }) => {
|
|
783
|
-
const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
|
|
784
|
-
const userTheme = ReactEmailComponents[node.attrs?.theme];
|
|
785
|
-
const theme = userTheme ? {
|
|
786
|
-
...userTheme,
|
|
787
|
-
base: {
|
|
788
|
-
...userTheme.base,
|
|
789
|
-
borderRadius: "0.125rem",
|
|
790
|
-
padding: "0.75rem 1rem"
|
|
791
|
-
}
|
|
792
|
-
} : { base: {
|
|
793
|
-
color: "#1e293b",
|
|
794
|
-
background: "#f1f5f9",
|
|
795
|
-
lineHeight: "1.5",
|
|
796
|
-
fontFamily: "\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace",
|
|
797
|
-
padding: "0.75rem 1rem",
|
|
798
|
-
borderRadius: "0.125rem"
|
|
799
|
-
} };
|
|
800
|
-
return /* @__PURE__ */ jsx(CodeBlock, {
|
|
801
|
-
code: node.content?.[0]?.text ?? "",
|
|
802
|
-
language,
|
|
37
|
+
}, [
|
|
38
|
+
extensionsProp,
|
|
803
39
|
theme,
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
group: "block",
|
|
816
|
-
content: "block+",
|
|
817
|
-
defining: true,
|
|
818
|
-
isolating: true,
|
|
819
|
-
parseHTML() {
|
|
820
|
-
return [{
|
|
821
|
-
tag: "div:not([data-type])",
|
|
822
|
-
getAttrs: (node) => {
|
|
823
|
-
if (typeof node === "string") return false;
|
|
824
|
-
const element = node;
|
|
825
|
-
const attrs = {};
|
|
826
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
827
|
-
attrs[attr.name] = attr.value;
|
|
828
|
-
});
|
|
829
|
-
return attrs;
|
|
830
|
-
}
|
|
831
|
-
}];
|
|
832
|
-
},
|
|
833
|
-
renderHTML({ HTMLAttributes }) {
|
|
834
|
-
return [
|
|
835
|
-
"div",
|
|
836
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
837
|
-
0
|
|
838
|
-
];
|
|
839
|
-
},
|
|
840
|
-
addAttributes() {
|
|
841
|
-
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
842
|
-
},
|
|
843
|
-
renderToReactEmail({ children, node, styles }) {
|
|
844
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
845
|
-
return /* @__PURE__ */ jsx("div", {
|
|
846
|
-
className: node.attrs?.class || void 0,
|
|
847
|
-
style: {
|
|
848
|
-
...styles.reset,
|
|
849
|
-
...inlineStyles
|
|
850
|
-
},
|
|
851
|
-
children
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
//#endregion
|
|
857
|
-
//#region src/extensions/max-nesting.ts
|
|
858
|
-
const MaxNesting = Extension.create({
|
|
859
|
-
name: "maxNesting",
|
|
860
|
-
addOptions() {
|
|
861
|
-
return {
|
|
862
|
-
maxDepth: 3,
|
|
863
|
-
nodeTypes: void 0
|
|
864
|
-
};
|
|
865
|
-
},
|
|
866
|
-
addProseMirrorPlugins() {
|
|
867
|
-
const { maxDepth, nodeTypes } = this.options;
|
|
868
|
-
if (typeof maxDepth !== "number" || maxDepth < 1) throw new Error("maxDepth must be a positive number");
|
|
869
|
-
return [new Plugin({
|
|
870
|
-
key: new PluginKey("maxNesting"),
|
|
871
|
-
appendTransaction(transactions, _oldState, newState) {
|
|
872
|
-
if (!transactions.some((tr$1) => tr$1.docChanged)) return null;
|
|
873
|
-
const rangesToLift = [];
|
|
874
|
-
newState.doc.descendants((node, pos) => {
|
|
875
|
-
let depth = 0;
|
|
876
|
-
let currentPos = pos;
|
|
877
|
-
let currentNode = node;
|
|
878
|
-
while (currentNode && depth <= maxDepth) {
|
|
879
|
-
if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
|
|
880
|
-
const $pos = newState.doc.resolve(currentPos);
|
|
881
|
-
if ($pos.depth === 0) break;
|
|
882
|
-
currentPos = $pos.before($pos.depth);
|
|
883
|
-
currentNode = newState.doc.nodeAt(currentPos);
|
|
884
|
-
}
|
|
885
|
-
if (depth > maxDepth) {
|
|
886
|
-
const $pos = newState.doc.resolve(pos);
|
|
887
|
-
if ($pos.depth > 0) {
|
|
888
|
-
const range = $pos.blockRange();
|
|
889
|
-
if (range && "canReplace" in newState.schema.nodes.doc && typeof newState.schema.nodes.doc.canReplace === "function" && newState.schema.nodes.doc.canReplace(range.start - 1, range.end + 1, newState.doc.slice(range.start, range.end).content)) rangesToLift.push({
|
|
890
|
-
range,
|
|
891
|
-
target: range.start - 1
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
if (rangesToLift.length === 0) return null;
|
|
897
|
-
const tr = newState.tr;
|
|
898
|
-
for (let i = rangesToLift.length - 1; i >= 0; i--) {
|
|
899
|
-
const { range, target } = rangesToLift[i];
|
|
900
|
-
tr.lift(range, target);
|
|
901
|
-
}
|
|
902
|
-
return tr;
|
|
903
|
-
},
|
|
904
|
-
filterTransaction(tr) {
|
|
905
|
-
if (!tr.docChanged) return true;
|
|
906
|
-
let wouldCreateDeepNesting = false;
|
|
907
|
-
const newDoc = tr.doc;
|
|
908
|
-
newDoc.descendants((node, pos) => {
|
|
909
|
-
if (wouldCreateDeepNesting) return false;
|
|
910
|
-
let depth = 0;
|
|
911
|
-
let currentPos = pos;
|
|
912
|
-
let currentNode = node;
|
|
913
|
-
while (currentNode && depth <= maxDepth) {
|
|
914
|
-
if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
|
|
915
|
-
const $pos = newDoc.resolve(currentPos);
|
|
916
|
-
if ($pos.depth === 0) break;
|
|
917
|
-
currentPos = $pos.before($pos.depth);
|
|
918
|
-
currentNode = newDoc.nodeAt(currentPos);
|
|
919
|
-
}
|
|
920
|
-
if (depth > maxDepth) {
|
|
921
|
-
wouldCreateDeepNesting = true;
|
|
922
|
-
return false;
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
return !wouldCreateDeepNesting;
|
|
926
|
-
}
|
|
927
|
-
})];
|
|
928
|
-
}
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
//#endregion
|
|
932
|
-
//#region src/extensions/placeholder.ts
|
|
933
|
-
const Placeholder = TipTapPlaceholder.configure({
|
|
934
|
-
placeholder: ({ node }) => {
|
|
935
|
-
if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
|
|
936
|
-
return "Press '/' for commands";
|
|
937
|
-
},
|
|
938
|
-
includeChildren: true
|
|
939
|
-
});
|
|
940
|
-
|
|
941
|
-
//#endregion
|
|
942
|
-
//#region src/extensions/preserved-style.ts
|
|
943
|
-
const PreservedStyle = Mark.create({
|
|
944
|
-
name: "preservedStyle",
|
|
945
|
-
addAttributes() {
|
|
946
|
-
return { style: {
|
|
947
|
-
default: null,
|
|
948
|
-
parseHTML: (element) => element.getAttribute("style"),
|
|
949
|
-
renderHTML: (attributes) => {
|
|
950
|
-
if (!attributes.style) return {};
|
|
951
|
-
return { style: attributes.style };
|
|
952
|
-
}
|
|
953
|
-
} };
|
|
954
|
-
},
|
|
955
|
-
parseHTML() {
|
|
956
|
-
return [{
|
|
957
|
-
tag: "span[style]",
|
|
958
|
-
getAttrs: (element) => {
|
|
959
|
-
if (typeof element === "string") return false;
|
|
960
|
-
const style = element.getAttribute("style");
|
|
961
|
-
if (style && hasPreservableStyles(style)) return { style };
|
|
962
|
-
return false;
|
|
963
|
-
}
|
|
964
|
-
}];
|
|
965
|
-
},
|
|
966
|
-
renderHTML({ HTMLAttributes }) {
|
|
967
|
-
return [
|
|
968
|
-
"span",
|
|
969
|
-
mergeAttributes(HTMLAttributes),
|
|
970
|
-
0
|
|
971
|
-
];
|
|
972
|
-
}
|
|
973
|
-
});
|
|
974
|
-
const LINK_INDICATOR_STYLES = [
|
|
975
|
-
"color",
|
|
976
|
-
"text-decoration",
|
|
977
|
-
"text-decoration-line",
|
|
978
|
-
"text-decoration-color",
|
|
979
|
-
"text-decoration-style"
|
|
980
|
-
];
|
|
981
|
-
function parseStyleString(styleString) {
|
|
982
|
-
const temp = document.createElement("div");
|
|
983
|
-
temp.style.cssText = styleString;
|
|
984
|
-
return temp.style;
|
|
985
|
-
}
|
|
986
|
-
function hasBackground(style) {
|
|
987
|
-
const bgColor = style.backgroundColor;
|
|
988
|
-
const bg = style.background;
|
|
989
|
-
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") return true;
|
|
990
|
-
if (bg && bg !== "transparent" && bg !== "none" && bg !== "rgba(0, 0, 0, 0)") return true;
|
|
991
|
-
return false;
|
|
992
|
-
}
|
|
993
|
-
function hasPreservableStyles(styleString) {
|
|
994
|
-
return processStylesForUnlink(styleString) !== null;
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Processes styles when unlinking:
|
|
998
|
-
* - Has background (button-like): preserve all styles
|
|
999
|
-
* - No background: strip link-indicator styles (color, text-decoration), keep the rest
|
|
1000
|
-
*/
|
|
1001
|
-
function processStylesForUnlink(styleString) {
|
|
1002
|
-
if (!styleString) return null;
|
|
1003
|
-
const style = parseStyleString(styleString);
|
|
1004
|
-
if (hasBackground(style)) return styleString;
|
|
1005
|
-
const filtered = [];
|
|
1006
|
-
for (let i = 0; i < style.length; i++) {
|
|
1007
|
-
const prop = style[i];
|
|
1008
|
-
if (LINK_INDICATOR_STYLES.includes(prop)) continue;
|
|
1009
|
-
const value = style.getPropertyValue(prop);
|
|
1010
|
-
if (value) filtered.push(`${prop}: ${value}`);
|
|
1011
|
-
}
|
|
1012
|
-
return filtered.length > 0 ? filtered.join("; ") : null;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
//#endregion
|
|
1016
|
-
//#region src/extensions/preview-text.ts
|
|
1017
|
-
const PreviewText = Node.create({
|
|
1018
|
-
name: "previewText",
|
|
1019
|
-
group: "block",
|
|
1020
|
-
selectable: false,
|
|
1021
|
-
draggable: false,
|
|
1022
|
-
atom: true,
|
|
1023
|
-
addOptions() {
|
|
1024
|
-
return { HTMLAttributes: {} };
|
|
1025
|
-
},
|
|
1026
|
-
addStorage() {
|
|
1027
|
-
return { previewText: null };
|
|
1028
|
-
},
|
|
1029
|
-
renderHTML() {
|
|
1030
|
-
return ["div", { style: "display: none" }];
|
|
1031
|
-
},
|
|
1032
|
-
parseHTML() {
|
|
1033
|
-
return [{
|
|
1034
|
-
tag: "div[data-skip-in-text=\"true\"]",
|
|
1035
|
-
getAttrs: (node) => {
|
|
1036
|
-
if (typeof node === "string") return false;
|
|
1037
|
-
const element = node;
|
|
1038
|
-
let directText = "";
|
|
1039
|
-
for (const child of element.childNodes) if (child.nodeType === 3) directText += child.textContent || "";
|
|
1040
|
-
const cleanText = directText.trim();
|
|
1041
|
-
if (cleanText) this.storage.previewText = cleanText;
|
|
1042
|
-
return false;
|
|
1043
|
-
}
|
|
1044
|
-
}, {
|
|
1045
|
-
tag: "span.preheader",
|
|
1046
|
-
getAttrs: (node) => {
|
|
1047
|
-
if (typeof node === "string") return false;
|
|
1048
|
-
const preheaderText = node.textContent?.trim();
|
|
1049
|
-
if (preheaderText) this.storage.previewText = preheaderText;
|
|
1050
|
-
return false;
|
|
1051
|
-
}
|
|
1052
|
-
}];
|
|
1053
|
-
}
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
//#endregion
|
|
1057
|
-
//#region src/utils/get-text-alignment.ts
|
|
1058
|
-
function getTextAlignment(alignment) {
|
|
1059
|
-
switch (alignment) {
|
|
1060
|
-
case "left": return { textAlign: "left" };
|
|
1061
|
-
case "center": return { textAlign: "center" };
|
|
1062
|
-
case "right": return { textAlign: "right" };
|
|
1063
|
-
default: return {};
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
//#endregion
|
|
1068
|
-
//#region src/extensions/section.tsx
|
|
1069
|
-
const Section = EmailNode.create({
|
|
1070
|
-
name: "section",
|
|
1071
|
-
group: "block",
|
|
1072
|
-
content: "block+",
|
|
1073
|
-
isolating: true,
|
|
1074
|
-
defining: true,
|
|
1075
|
-
parseHTML() {
|
|
1076
|
-
return [{ tag: "section[data-type=\"section\"]" }];
|
|
1077
|
-
},
|
|
1078
|
-
renderHTML({ HTMLAttributes }) {
|
|
1079
|
-
return [
|
|
1080
|
-
"section",
|
|
1081
|
-
mergeAttributes({
|
|
1082
|
-
"data-type": "section",
|
|
1083
|
-
class: "node-section"
|
|
1084
|
-
}, HTMLAttributes),
|
|
1085
|
-
0
|
|
1086
|
-
];
|
|
1087
|
-
},
|
|
1088
|
-
addCommands() {
|
|
1089
|
-
return { insertSection: () => ({ commands }) => {
|
|
1090
|
-
return commands.insertContent({
|
|
1091
|
-
type: this.name,
|
|
1092
|
-
content: [{
|
|
1093
|
-
type: "paragraph",
|
|
1094
|
-
content: []
|
|
1095
|
-
}]
|
|
1096
|
-
});
|
|
1097
|
-
} };
|
|
1098
|
-
},
|
|
1099
|
-
renderToReactEmail({ children, node, styles }) {
|
|
1100
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1101
|
-
const textAlign = node.attrs?.align || node.attrs?.alignment;
|
|
1102
|
-
return /* @__PURE__ */ jsx(Section$1, {
|
|
1103
|
-
className: node.attrs?.class || void 0,
|
|
1104
|
-
align: textAlign,
|
|
1105
|
-
style: {
|
|
1106
|
-
...styles.section,
|
|
1107
|
-
...inlineStyles,
|
|
1108
|
-
...getTextAlignment(textAlign)
|
|
1109
|
-
},
|
|
1110
|
-
children
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
|
-
//#endregion
|
|
1116
|
-
//#region src/extensions/style-attribute.tsx
|
|
1117
|
-
const StyleAttribute = Extension.create({
|
|
1118
|
-
name: "styleAttribute",
|
|
1119
|
-
priority: 101,
|
|
1120
|
-
addOptions() {
|
|
1121
|
-
return {
|
|
1122
|
-
types: [],
|
|
1123
|
-
style: []
|
|
1124
|
-
};
|
|
1125
|
-
},
|
|
1126
|
-
addGlobalAttributes() {
|
|
1127
|
-
return [{
|
|
1128
|
-
types: this.options.types,
|
|
1129
|
-
attributes: { style: {
|
|
1130
|
-
default: "",
|
|
1131
|
-
parseHTML: (element) => element.getAttribute("style") || "",
|
|
1132
|
-
renderHTML: (attributes) => {
|
|
1133
|
-
return { style: attributes.style ?? "" };
|
|
1134
|
-
}
|
|
1135
|
-
} }
|
|
1136
|
-
}];
|
|
1137
|
-
},
|
|
1138
|
-
addCommands() {
|
|
1139
|
-
return {
|
|
1140
|
-
unsetStyle: () => ({ commands }) => {
|
|
1141
|
-
return this.options.types.every((type) => commands.resetAttributes(type, "style"));
|
|
1142
|
-
},
|
|
1143
|
-
setStyle: (style) => ({ commands }) => {
|
|
1144
|
-
return this.options.types.every((type) => commands.updateAttributes(type, { style }));
|
|
1145
|
-
}
|
|
1146
|
-
};
|
|
1147
|
-
},
|
|
1148
|
-
addKeyboardShortcuts() {
|
|
1149
|
-
return { Enter: ({ editor }) => {
|
|
1150
|
-
const { state } = editor.view;
|
|
1151
|
-
const { selection } = state;
|
|
1152
|
-
const { $from } = selection;
|
|
1153
|
-
const textBefore = $from.nodeBefore?.text || "";
|
|
1154
|
-
if (textBefore.includes("{{") || textBefore.includes("{{{")) return false;
|
|
1155
|
-
requestAnimationFrame(() => {
|
|
1156
|
-
editor.commands.resetAttributes("paragraph", "style");
|
|
1157
|
-
});
|
|
1158
|
-
return false;
|
|
1159
|
-
} };
|
|
1160
|
-
}
|
|
1161
|
-
});
|
|
1162
|
-
|
|
1163
|
-
//#endregion
|
|
1164
|
-
//#region src/extensions/sup.ts
|
|
1165
|
-
/**
|
|
1166
|
-
* This extension allows you to mark text as superscript.
|
|
1167
|
-
* @see https://tiptap.dev/api/marks/superscript
|
|
1168
|
-
*/
|
|
1169
|
-
const Sup = Mark.create({
|
|
1170
|
-
name: "sup",
|
|
1171
|
-
addOptions() {
|
|
1172
|
-
return { HTMLAttributes: {} };
|
|
1173
|
-
},
|
|
1174
|
-
parseHTML() {
|
|
1175
|
-
return [{ tag: "sup" }];
|
|
1176
|
-
},
|
|
1177
|
-
renderHTML({ HTMLAttributes }) {
|
|
1178
|
-
return [
|
|
1179
|
-
"sup",
|
|
1180
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
1181
|
-
0
|
|
1182
|
-
];
|
|
1183
|
-
},
|
|
1184
|
-
addCommands() {
|
|
1185
|
-
return {
|
|
1186
|
-
setSup: () => ({ commands }) => {
|
|
1187
|
-
return commands.setMark(this.name);
|
|
1188
|
-
},
|
|
1189
|
-
toggleSup: () => ({ commands }) => {
|
|
1190
|
-
return commands.toggleMark(this.name);
|
|
1191
|
-
},
|
|
1192
|
-
unsetSup: () => ({ commands }) => {
|
|
1193
|
-
return commands.unsetMark(this.name);
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
});
|
|
1198
|
-
|
|
1199
|
-
//#endregion
|
|
1200
|
-
//#region src/extensions/table.tsx
|
|
1201
|
-
const Table = EmailNode.create({
|
|
1202
|
-
name: "table",
|
|
1203
|
-
group: "block",
|
|
1204
|
-
content: "tableRow+",
|
|
1205
|
-
isolating: true,
|
|
1206
|
-
tableRole: "table",
|
|
1207
|
-
addAttributes() {
|
|
1208
|
-
return { ...createStandardAttributes([
|
|
1209
|
-
...TABLE_ATTRIBUTES,
|
|
1210
|
-
...LAYOUT_ATTRIBUTES,
|
|
1211
|
-
...COMMON_HTML_ATTRIBUTES
|
|
1212
|
-
]) };
|
|
1213
|
-
},
|
|
1214
|
-
parseHTML() {
|
|
1215
|
-
return [{
|
|
1216
|
-
tag: "table",
|
|
1217
|
-
getAttrs: (node) => {
|
|
1218
|
-
if (typeof node === "string") return false;
|
|
1219
|
-
const element = node;
|
|
1220
|
-
const attrs = {};
|
|
1221
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
1222
|
-
attrs[attr.name] = attr.value;
|
|
1223
|
-
});
|
|
1224
|
-
return attrs;
|
|
1225
|
-
}
|
|
1226
|
-
}];
|
|
1227
|
-
},
|
|
1228
|
-
renderHTML({ HTMLAttributes }) {
|
|
1229
|
-
return [
|
|
1230
|
-
"table",
|
|
1231
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
1232
|
-
[
|
|
1233
|
-
"tbody",
|
|
1234
|
-
{},
|
|
1235
|
-
0
|
|
1236
|
-
]
|
|
1237
|
-
];
|
|
1238
|
-
},
|
|
1239
|
-
renderToReactEmail({ children, node, styles }) {
|
|
1240
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1241
|
-
const alignment = node.attrs?.align || node.attrs?.alignment;
|
|
1242
|
-
const width = node.attrs?.width;
|
|
1243
|
-
const centeringStyles = alignment === "center" ? {
|
|
1244
|
-
marginLeft: "auto",
|
|
1245
|
-
marginRight: "auto"
|
|
1246
|
-
} : {};
|
|
1247
|
-
return /* @__PURE__ */ jsx(Section$1, {
|
|
1248
|
-
className: node.attrs?.class || void 0,
|
|
1249
|
-
align: alignment,
|
|
1250
|
-
style: resolveConflictingStyles(styles.reset, {
|
|
1251
|
-
...inlineStyles,
|
|
1252
|
-
...centeringStyles
|
|
40
|
+
placeholder
|
|
41
|
+
]);
|
|
42
|
+
return /* @__PURE__ */ jsxs(EditorProvider, {
|
|
43
|
+
extensions,
|
|
44
|
+
content,
|
|
45
|
+
editable,
|
|
46
|
+
immediatelyRender: false,
|
|
47
|
+
editorProps: useMemo(() => ({
|
|
48
|
+
handlePaste: createPasteHandler({
|
|
49
|
+
onUploadImage,
|
|
50
|
+
extensions
|
|
1253
51
|
}),
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
})
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
return [{
|
|
1272
|
-
tag: "tr",
|
|
1273
|
-
getAttrs: (node) => {
|
|
1274
|
-
if (typeof node === "string") return false;
|
|
1275
|
-
const element = node;
|
|
1276
|
-
const attrs = {};
|
|
1277
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
1278
|
-
attrs[attr.name] = attr.value;
|
|
1279
|
-
});
|
|
1280
|
-
return attrs;
|
|
1281
|
-
}
|
|
1282
|
-
}];
|
|
1283
|
-
},
|
|
1284
|
-
renderHTML({ HTMLAttributes }) {
|
|
1285
|
-
return [
|
|
1286
|
-
"tr",
|
|
1287
|
-
HTMLAttributes,
|
|
1288
|
-
0
|
|
1289
|
-
];
|
|
1290
|
-
},
|
|
1291
|
-
renderToReactEmail({ children, node, styles }) {
|
|
1292
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1293
|
-
return /* @__PURE__ */ jsx("tr", {
|
|
1294
|
-
className: node.attrs?.class || void 0,
|
|
1295
|
-
style: {
|
|
1296
|
-
...styles.reset,
|
|
1297
|
-
...inlineStyles
|
|
1298
|
-
},
|
|
1299
|
-
children
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
const TableCell = EmailNode.create({
|
|
1304
|
-
name: "tableCell",
|
|
1305
|
-
group: "tableCell",
|
|
1306
|
-
content: "block+",
|
|
1307
|
-
isolating: true,
|
|
1308
|
-
addAttributes() {
|
|
1309
|
-
return { ...createStandardAttributes([
|
|
1310
|
-
...TABLE_CELL_ATTRIBUTES,
|
|
1311
|
-
...LAYOUT_ATTRIBUTES,
|
|
1312
|
-
...COMMON_HTML_ATTRIBUTES
|
|
1313
|
-
]) };
|
|
1314
|
-
},
|
|
1315
|
-
parseHTML() {
|
|
1316
|
-
return [{
|
|
1317
|
-
tag: "td",
|
|
1318
|
-
getAttrs: (node) => {
|
|
1319
|
-
if (typeof node === "string") return false;
|
|
1320
|
-
const element = node;
|
|
1321
|
-
const attrs = {};
|
|
1322
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
1323
|
-
attrs[attr.name] = attr.value;
|
|
1324
|
-
});
|
|
1325
|
-
return attrs;
|
|
1326
|
-
}
|
|
1327
|
-
}];
|
|
1328
|
-
},
|
|
1329
|
-
renderHTML({ HTMLAttributes }) {
|
|
1330
|
-
return [
|
|
1331
|
-
"td",
|
|
1332
|
-
HTMLAttributes,
|
|
1333
|
-
0
|
|
1334
|
-
];
|
|
1335
|
-
},
|
|
1336
|
-
renderToReactEmail({ children, node, styles }) {
|
|
1337
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1338
|
-
return /* @__PURE__ */ jsx(Column, {
|
|
1339
|
-
className: node.attrs?.class || void 0,
|
|
1340
|
-
align: node.attrs?.align || node.attrs?.alignment,
|
|
1341
|
-
style: {
|
|
1342
|
-
...styles.reset,
|
|
1343
|
-
...inlineStyles
|
|
1344
|
-
},
|
|
1345
|
-
children
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
|
-
});
|
|
1349
|
-
const TableHeader = Node.create({
|
|
1350
|
-
name: "tableHeader",
|
|
1351
|
-
group: "tableCell",
|
|
1352
|
-
content: "block+",
|
|
1353
|
-
isolating: true,
|
|
1354
|
-
addAttributes() {
|
|
1355
|
-
return { ...createStandardAttributes([
|
|
1356
|
-
...TABLE_HEADER_ATTRIBUTES,
|
|
1357
|
-
...TABLE_CELL_ATTRIBUTES,
|
|
1358
|
-
...LAYOUT_ATTRIBUTES,
|
|
1359
|
-
...COMMON_HTML_ATTRIBUTES
|
|
1360
|
-
]) };
|
|
1361
|
-
},
|
|
1362
|
-
parseHTML() {
|
|
1363
|
-
return [{
|
|
1364
|
-
tag: "th",
|
|
1365
|
-
getAttrs: (node) => {
|
|
1366
|
-
if (typeof node === "string") return false;
|
|
1367
|
-
const element = node;
|
|
1368
|
-
const attrs = {};
|
|
1369
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
1370
|
-
attrs[attr.name] = attr.value;
|
|
1371
|
-
});
|
|
1372
|
-
return attrs;
|
|
1373
|
-
}
|
|
1374
|
-
}];
|
|
1375
|
-
},
|
|
1376
|
-
renderHTML({ HTMLAttributes }) {
|
|
1377
|
-
return [
|
|
1378
|
-
"th",
|
|
1379
|
-
HTMLAttributes,
|
|
1380
|
-
0
|
|
1381
|
-
];
|
|
1382
|
-
}
|
|
52
|
+
handleDrop: createDropHandler({ onUploadImage })
|
|
53
|
+
}), [onUploadImage, extensions]),
|
|
54
|
+
editorContainerProps: { className },
|
|
55
|
+
onCreate: ({ editor }) => onReady?.(editor),
|
|
56
|
+
onUpdate: ({ editor }) => onChange?.(editor),
|
|
57
|
+
children: [
|
|
58
|
+
/* @__PURE__ */ jsx(RefBridge, { editorRef: ref }),
|
|
59
|
+
/* @__PURE__ */ jsx(BubbleMenuDefault, {
|
|
60
|
+
hideWhenActiveNodes: bubbleMenu?.hideWhenActiveNodes ?? ["button"],
|
|
61
|
+
hideWhenActiveMarks: bubbleMenu?.hideWhenActiveMarks ?? ["link"]
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ jsx(BubbleMenuLinkDefault, {}),
|
|
64
|
+
/* @__PURE__ */ jsx(BubbleMenuButtonDefault, {}),
|
|
65
|
+
/* @__PURE__ */ jsx(BubbleMenuImageDefault, {}),
|
|
66
|
+
/* @__PURE__ */ jsx(SlashCommandRoot, {})
|
|
67
|
+
]
|
|
68
|
+
}, theme);
|
|
1383
69
|
});
|
|
70
|
+
EmailEditor.displayName = "EmailEditor";
|
|
1384
71
|
|
|
1385
72
|
//#endregion
|
|
1386
|
-
export {
|
|
73
|
+
export { EmailEditor };
|
|
1387
74
|
//# sourceMappingURL=index.mjs.map
|