@react-email/editor 0.0.0-experimental.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +90 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +607 -0
- package/dist/index.mjs +573 -0
- package/dist/index.mjs.map +1 -0
- package/license.md +7 -0
- package/package.json +67 -0
- package/readme.md +0 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let __tiptap_core = require("@tiptap/core");
|
|
25
|
+
__tiptap_core = __toESM(__tiptap_core);
|
|
26
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
27
|
+
react_jsx_runtime = __toESM(react_jsx_runtime);
|
|
28
|
+
let __react_email_components = require("@react-email/components");
|
|
29
|
+
__react_email_components = __toESM(__react_email_components);
|
|
30
|
+
let __tiptap_extension_code_block = require("@tiptap/extension-code-block");
|
|
31
|
+
__tiptap_extension_code_block = __toESM(__tiptap_extension_code_block);
|
|
32
|
+
let __tiptap_pm_state = require("@tiptap/pm/state");
|
|
33
|
+
__tiptap_pm_state = __toESM(__tiptap_pm_state);
|
|
34
|
+
let __tiptap_pm_view = require("@tiptap/pm/view");
|
|
35
|
+
__tiptap_pm_view = __toESM(__tiptap_pm_view);
|
|
36
|
+
let hast_util_from_html = require("hast-util-from-html");
|
|
37
|
+
hast_util_from_html = __toESM(hast_util_from_html);
|
|
38
|
+
let prismjs = require("prismjs");
|
|
39
|
+
prismjs = __toESM(prismjs);
|
|
40
|
+
|
|
41
|
+
//#region src/core/email-node.ts
|
|
42
|
+
var EmailNode = class EmailNode extends __tiptap_core.Node {
|
|
43
|
+
constructor(config) {
|
|
44
|
+
super(config);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a new Node instance
|
|
48
|
+
* @param config - Node configuration object or a function that returns a configuration object
|
|
49
|
+
*/
|
|
50
|
+
static create(config) {
|
|
51
|
+
return new EmailNode(typeof config === "function" ? config() : config);
|
|
52
|
+
}
|
|
53
|
+
static from(node, renderToReactEmail) {
|
|
54
|
+
const customNode = EmailNode.create({});
|
|
55
|
+
Object.assign(customNode, { ...node });
|
|
56
|
+
customNode.config = {
|
|
57
|
+
...node.config,
|
|
58
|
+
renderToReactEmail
|
|
59
|
+
};
|
|
60
|
+
return customNode;
|
|
61
|
+
}
|
|
62
|
+
configure(options) {
|
|
63
|
+
return super.configure(options);
|
|
64
|
+
}
|
|
65
|
+
extend(extendedConfig) {
|
|
66
|
+
const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
|
|
67
|
+
return super.extend(resolvedConfig);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/utils/attribute-helpers.ts
|
|
73
|
+
/**
|
|
74
|
+
* Creates TipTap attribute definitions for a list of HTML attributes.
|
|
75
|
+
* Each attribute will have the same pattern:
|
|
76
|
+
* - default: null
|
|
77
|
+
* - parseHTML: extracts the attribute from the element
|
|
78
|
+
* - renderHTML: conditionally renders the attribute if it has a value
|
|
79
|
+
*
|
|
80
|
+
* @param attributeNames - Array of HTML attribute names to create definitions for
|
|
81
|
+
* @returns Object with TipTap attribute definitions
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* const attrs = createStandardAttributes(['class', 'id', 'title']);
|
|
85
|
+
* // Returns:
|
|
86
|
+
* // {
|
|
87
|
+
* // class: {
|
|
88
|
+
* // default: null,
|
|
89
|
+
* // parseHTML: (element) => element.getAttribute('class'),
|
|
90
|
+
* // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
|
|
91
|
+
* // },
|
|
92
|
+
* // ...
|
|
93
|
+
* // }
|
|
94
|
+
*/
|
|
95
|
+
function createStandardAttributes(attributeNames) {
|
|
96
|
+
return Object.fromEntries(attributeNames.map((attr) => [attr, {
|
|
97
|
+
default: null,
|
|
98
|
+
parseHTML: (element) => element.getAttribute(attr),
|
|
99
|
+
renderHTML: (attributes) => {
|
|
100
|
+
if (!attributes[attr]) return {};
|
|
101
|
+
return { [attr]: attributes[attr] };
|
|
102
|
+
}
|
|
103
|
+
}]));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Common HTML attributes used across multiple extensions.
|
|
107
|
+
* These preserve attributes during HTML import and editing for better
|
|
108
|
+
* fidelity when importing existing email templates.
|
|
109
|
+
*/
|
|
110
|
+
const COMMON_HTML_ATTRIBUTES = [
|
|
111
|
+
"id",
|
|
112
|
+
"class",
|
|
113
|
+
"title",
|
|
114
|
+
"lang",
|
|
115
|
+
"dir",
|
|
116
|
+
"data-id"
|
|
117
|
+
];
|
|
118
|
+
/**
|
|
119
|
+
* Layout-specific HTML attributes used for positioning and sizing.
|
|
120
|
+
*/
|
|
121
|
+
const LAYOUT_ATTRIBUTES = [
|
|
122
|
+
"align",
|
|
123
|
+
"width",
|
|
124
|
+
"height"
|
|
125
|
+
];
|
|
126
|
+
/**
|
|
127
|
+
* Table cell-specific HTML attributes.
|
|
128
|
+
*/
|
|
129
|
+
const TABLE_CELL_ATTRIBUTES = [
|
|
130
|
+
"valign",
|
|
131
|
+
"bgcolor",
|
|
132
|
+
"colspan",
|
|
133
|
+
"rowspan"
|
|
134
|
+
];
|
|
135
|
+
/**
|
|
136
|
+
* Table header cell-specific HTML attributes.
|
|
137
|
+
* These are additional attributes that only apply to <th> elements.
|
|
138
|
+
*/
|
|
139
|
+
const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/utils/styles.ts
|
|
143
|
+
const inlineCssToJs = (inlineStyle, options = {}) => {
|
|
144
|
+
const styleObject = {};
|
|
145
|
+
if (!inlineStyle || inlineStyle === "" || typeof inlineStyle === "object") return styleObject;
|
|
146
|
+
inlineStyle.split(";").forEach((style) => {
|
|
147
|
+
if (style.trim()) {
|
|
148
|
+
const [key, value] = style.split(":");
|
|
149
|
+
const valueTrimmed = value?.trim();
|
|
150
|
+
if (!valueTrimmed) return;
|
|
151
|
+
const formattedKey = key.trim().replace(/-\w/g, (match) => match[1].toUpperCase());
|
|
152
|
+
styleObject[formattedKey] = options?.removeUnit ? valueTrimmed.replace(/px|%/g, "") : valueTrimmed;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return styleObject;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/extensions/body.tsx
|
|
160
|
+
const Body = EmailNode.create({
|
|
161
|
+
name: "body",
|
|
162
|
+
group: "block",
|
|
163
|
+
content: "block+",
|
|
164
|
+
defining: true,
|
|
165
|
+
isolating: true,
|
|
166
|
+
addAttributes() {
|
|
167
|
+
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
168
|
+
},
|
|
169
|
+
parseHTML() {
|
|
170
|
+
return [{
|
|
171
|
+
tag: "body",
|
|
172
|
+
getAttrs: (node) => {
|
|
173
|
+
if (typeof node === "string") return false;
|
|
174
|
+
const element = node;
|
|
175
|
+
const attrs = {};
|
|
176
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
177
|
+
attrs[attr.name] = attr.value;
|
|
178
|
+
});
|
|
179
|
+
return attrs;
|
|
180
|
+
}
|
|
181
|
+
}];
|
|
182
|
+
},
|
|
183
|
+
renderHTML({ HTMLAttributes }) {
|
|
184
|
+
return [
|
|
185
|
+
"div",
|
|
186
|
+
(0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
|
|
187
|
+
0
|
|
188
|
+
];
|
|
189
|
+
},
|
|
190
|
+
renderToReactEmail({ children, node, styles }) {
|
|
191
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
192
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
193
|
+
className: node.attrs?.class || void 0,
|
|
194
|
+
style: {
|
|
195
|
+
...styles.reset,
|
|
196
|
+
...inlineStyles
|
|
197
|
+
},
|
|
198
|
+
children
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/extensions/button.tsx
|
|
205
|
+
const Button = EmailNode.create({
|
|
206
|
+
name: "button",
|
|
207
|
+
group: "block",
|
|
208
|
+
content: "inline*",
|
|
209
|
+
defining: true,
|
|
210
|
+
draggable: true,
|
|
211
|
+
marks: "bold",
|
|
212
|
+
addAttributes() {
|
|
213
|
+
return {
|
|
214
|
+
class: { default: "button" },
|
|
215
|
+
href: { default: "#" },
|
|
216
|
+
alignment: { default: "left" }
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
parseHTML() {
|
|
220
|
+
return [{
|
|
221
|
+
tag: "a[data-id=\"react-email-button\"]",
|
|
222
|
+
getAttrs: (node) => {
|
|
223
|
+
if (typeof node === "string") return false;
|
|
224
|
+
const element = node;
|
|
225
|
+
const attrs = {};
|
|
226
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
227
|
+
attrs[attr.name] = attr.value;
|
|
228
|
+
});
|
|
229
|
+
return attrs;
|
|
230
|
+
}
|
|
231
|
+
}];
|
|
232
|
+
},
|
|
233
|
+
renderHTML({ HTMLAttributes }) {
|
|
234
|
+
return [
|
|
235
|
+
"div",
|
|
236
|
+
(0, __tiptap_core.mergeAttributes)({ class: `align-${HTMLAttributes?.alignment}` }),
|
|
237
|
+
[
|
|
238
|
+
"a",
|
|
239
|
+
(0, __tiptap_core.mergeAttributes)({
|
|
240
|
+
class: `node-button ${HTMLAttributes?.class}`,
|
|
241
|
+
style: HTMLAttributes?.style,
|
|
242
|
+
"data-id": "react-email-button",
|
|
243
|
+
"data-href": HTMLAttributes?.href
|
|
244
|
+
}),
|
|
245
|
+
0
|
|
246
|
+
]
|
|
247
|
+
];
|
|
248
|
+
},
|
|
249
|
+
addCommands() {
|
|
250
|
+
return {
|
|
251
|
+
updateButton: (attributes) => ({ commands }) => {
|
|
252
|
+
return commands.updateAttributes("button", attributes);
|
|
253
|
+
},
|
|
254
|
+
setButton: () => ({ commands }) => {
|
|
255
|
+
return commands.insertContent({
|
|
256
|
+
type: "button",
|
|
257
|
+
content: [{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: "Button"
|
|
260
|
+
}]
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
renderToReactEmail({ children, node, styles }) {
|
|
266
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
267
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
|
|
268
|
+
align: node.attrs?.align || node.attrs?.alignment,
|
|
269
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Button, {
|
|
270
|
+
className: node.attrs?.class || void 0,
|
|
271
|
+
href: node.attrs?.href,
|
|
272
|
+
style: {
|
|
273
|
+
...styles.reset,
|
|
274
|
+
...styles.button,
|
|
275
|
+
...inlineStyles
|
|
276
|
+
},
|
|
277
|
+
children
|
|
278
|
+
})
|
|
279
|
+
}) });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/utils/prism-utils.ts
|
|
285
|
+
const publicURL = "/styles/prism";
|
|
286
|
+
function loadPrismTheme(theme) {
|
|
287
|
+
const link = document.createElement("link");
|
|
288
|
+
link.rel = "stylesheet";
|
|
289
|
+
link.href = `${publicURL}/prism-${theme}.css`;
|
|
290
|
+
link.setAttribute("data-prism-theme", "");
|
|
291
|
+
document.head.appendChild(link);
|
|
292
|
+
}
|
|
293
|
+
function removePrismTheme() {
|
|
294
|
+
const existingTheme = document.querySelectorAll("link[rel=\"stylesheet\"][data-prism-theme]");
|
|
295
|
+
if (existingTheme.length > 0) existingTheme.forEach((cssLinkTag) => {
|
|
296
|
+
cssLinkTag.remove();
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function hasPrismThemeLoaded(theme) {
|
|
300
|
+
return !!document.querySelector(`link[rel="stylesheet"][data-prism-theme][href="${publicURL}/prism-${theme}.css"]`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region src/extensions/prism-plugin.ts
|
|
305
|
+
const PRISM_LANGUAGE_LOADED_META = "prismLanguageLoaded";
|
|
306
|
+
function parseNodes(nodes, className = []) {
|
|
307
|
+
return nodes.flatMap((node) => {
|
|
308
|
+
const classes = [...className, ...node.properties ? node.properties.className : []];
|
|
309
|
+
if (node.children) return parseNodes(node.children, classes);
|
|
310
|
+
return {
|
|
311
|
+
text: node.value ?? "",
|
|
312
|
+
classes
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function getHighlightNodes(html) {
|
|
317
|
+
return (0, hast_util_from_html.fromHtml)(html, { fragment: true }).children;
|
|
318
|
+
}
|
|
319
|
+
function registeredLang(aliasOrLanguage) {
|
|
320
|
+
const allSupportLang = Object.keys(prismjs.default.languages).filter((id) => typeof prismjs.default.languages[id] === "object");
|
|
321
|
+
return Boolean(allSupportLang.find((x) => x === aliasOrLanguage));
|
|
322
|
+
}
|
|
323
|
+
function getDecorations({ doc, name, defaultLanguage, defaultTheme, loadingLanguages, onLanguageLoaded }) {
|
|
324
|
+
const decorations = [];
|
|
325
|
+
(0, __tiptap_core.findChildren)(doc, (node) => node.type.name === name).forEach((block) => {
|
|
326
|
+
let from = block.pos + 1;
|
|
327
|
+
const language = block.node.attrs.language || defaultLanguage;
|
|
328
|
+
const theme = block.node.attrs.theme || defaultTheme;
|
|
329
|
+
let html = "";
|
|
330
|
+
try {
|
|
331
|
+
if (!registeredLang(language) && !loadingLanguages.has(language)) {
|
|
332
|
+
loadingLanguages.add(language);
|
|
333
|
+
import(`prismjs/components/prism-${language}`).then(() => {
|
|
334
|
+
loadingLanguages.delete(language);
|
|
335
|
+
onLanguageLoaded(language);
|
|
336
|
+
}).catch(() => {
|
|
337
|
+
loadingLanguages.delete(language);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (!hasPrismThemeLoaded(theme)) loadPrismTheme(theme);
|
|
341
|
+
html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages[language], language);
|
|
342
|
+
} catch {
|
|
343
|
+
html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages.javascript, "js");
|
|
344
|
+
}
|
|
345
|
+
parseNodes(getHighlightNodes(html)).forEach((node) => {
|
|
346
|
+
const to = from + node.text.length;
|
|
347
|
+
if (node.classes.length) {
|
|
348
|
+
const decoration = __tiptap_pm_view.Decoration.inline(from, to, { class: node.classes.join(" ") });
|
|
349
|
+
decorations.push(decoration);
|
|
350
|
+
}
|
|
351
|
+
from = to;
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
return __tiptap_pm_view.DecorationSet.create(doc, decorations);
|
|
355
|
+
}
|
|
356
|
+
function PrismPlugin({ name, defaultLanguage, defaultTheme }) {
|
|
357
|
+
if (!defaultLanguage) throw Error("You must specify the defaultLanguage parameter");
|
|
358
|
+
const loadingLanguages = /* @__PURE__ */ new Set();
|
|
359
|
+
let pluginView = null;
|
|
360
|
+
const onLanguageLoaded = (language) => {
|
|
361
|
+
if (pluginView) pluginView.dispatch(pluginView.state.tr.setMeta(PRISM_LANGUAGE_LOADED_META, language));
|
|
362
|
+
};
|
|
363
|
+
const prismjsPlugin = new __tiptap_pm_state.Plugin({
|
|
364
|
+
key: new __tiptap_pm_state.PluginKey("prism"),
|
|
365
|
+
view(view) {
|
|
366
|
+
pluginView = view;
|
|
367
|
+
return { destroy() {
|
|
368
|
+
pluginView = null;
|
|
369
|
+
} };
|
|
370
|
+
},
|
|
371
|
+
state: {
|
|
372
|
+
init: (_, { doc }) => {
|
|
373
|
+
return getDecorations({
|
|
374
|
+
doc,
|
|
375
|
+
name,
|
|
376
|
+
defaultLanguage,
|
|
377
|
+
defaultTheme,
|
|
378
|
+
loadingLanguages,
|
|
379
|
+
onLanguageLoaded
|
|
380
|
+
});
|
|
381
|
+
},
|
|
382
|
+
apply: (transaction, decorationSet, oldState, newState) => {
|
|
383
|
+
const oldNodeName = oldState.selection.$head.parent.type.name;
|
|
384
|
+
const newNodeName = newState.selection.$head.parent.type.name;
|
|
385
|
+
const oldNodes = (0, __tiptap_core.findChildren)(oldState.doc, (node) => node.type.name === name);
|
|
386
|
+
const newNodes = (0, __tiptap_core.findChildren)(newState.doc, (node) => node.type.name === name);
|
|
387
|
+
if (transaction.getMeta(PRISM_LANGUAGE_LOADED_META) || transaction.docChanged && ([oldNodeName, newNodeName].includes(name) || newNodes.length !== oldNodes.length || transaction.steps.some((step) => {
|
|
388
|
+
const rangeStep = step;
|
|
389
|
+
return rangeStep.from !== void 0 && rangeStep.to !== void 0 && oldNodes.some((node) => {
|
|
390
|
+
return node.pos >= rangeStep.from && node.pos + node.node.nodeSize <= rangeStep.to;
|
|
391
|
+
});
|
|
392
|
+
}))) return getDecorations({
|
|
393
|
+
doc: transaction.doc,
|
|
394
|
+
name,
|
|
395
|
+
defaultLanguage,
|
|
396
|
+
defaultTheme,
|
|
397
|
+
loadingLanguages,
|
|
398
|
+
onLanguageLoaded
|
|
399
|
+
});
|
|
400
|
+
return decorationSet.map(transaction.mapping, transaction.doc);
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
props: { decorations(state) {
|
|
404
|
+
return prismjsPlugin.getState(state);
|
|
405
|
+
} },
|
|
406
|
+
destroy() {
|
|
407
|
+
pluginView = null;
|
|
408
|
+
removePrismTheme();
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return prismjsPlugin;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/extensions/code-block.tsx
|
|
416
|
+
const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.extend({
|
|
417
|
+
addOptions() {
|
|
418
|
+
return {
|
|
419
|
+
languageClassPrefix: "language-",
|
|
420
|
+
exitOnTripleEnter: false,
|
|
421
|
+
exitOnArrowDown: false,
|
|
422
|
+
enableTabIndentation: true,
|
|
423
|
+
tabSize: 2,
|
|
424
|
+
defaultLanguage: "javascript",
|
|
425
|
+
defaultTheme: "default",
|
|
426
|
+
HTMLAttributes: {}
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
addAttributes() {
|
|
430
|
+
return {
|
|
431
|
+
...this.parent?.(),
|
|
432
|
+
language: {
|
|
433
|
+
default: this.options.defaultLanguage,
|
|
434
|
+
parseHTML: (element) => {
|
|
435
|
+
if (!element) return null;
|
|
436
|
+
const { languageClassPrefix } = this.options;
|
|
437
|
+
if (!languageClassPrefix) return null;
|
|
438
|
+
const language = [...element.firstElementChild?.classList || []].filter((className) => className.startsWith(languageClassPrefix || "")).map((className) => className.replace(languageClassPrefix, ""))[0];
|
|
439
|
+
if (!language) return null;
|
|
440
|
+
return language;
|
|
441
|
+
},
|
|
442
|
+
rendered: false
|
|
443
|
+
},
|
|
444
|
+
theme: {
|
|
445
|
+
default: this.options.defaultTheme,
|
|
446
|
+
rendered: false
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
},
|
|
450
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
451
|
+
return [
|
|
452
|
+
"pre",
|
|
453
|
+
(0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language}` : null }, { "data-theme": node.attrs.theme }),
|
|
454
|
+
[
|
|
455
|
+
"code",
|
|
456
|
+
{ class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language} node-codeTag` : "node-codeTag" },
|
|
457
|
+
0
|
|
458
|
+
]
|
|
459
|
+
];
|
|
460
|
+
},
|
|
461
|
+
addProseMirrorPlugins() {
|
|
462
|
+
return [...this.parent?.() || [], PrismPlugin({
|
|
463
|
+
name: this.name,
|
|
464
|
+
defaultLanguage: this.options.defaultLanguage,
|
|
465
|
+
defaultTheme: this.options.defaultTheme
|
|
466
|
+
})];
|
|
467
|
+
}
|
|
468
|
+
}), ({ node, styles }) => {
|
|
469
|
+
const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
|
|
470
|
+
const userTheme = __react_email_components[node.attrs?.theme];
|
|
471
|
+
const theme = userTheme ? {
|
|
472
|
+
...userTheme,
|
|
473
|
+
base: {
|
|
474
|
+
...userTheme.base,
|
|
475
|
+
borderRadius: "0.125rem",
|
|
476
|
+
padding: "0.75rem 1rem"
|
|
477
|
+
}
|
|
478
|
+
} : { base: {
|
|
479
|
+
color: "#1e293b",
|
|
480
|
+
background: "#f1f5f9",
|
|
481
|
+
lineHeight: "1.5",
|
|
482
|
+
fontFamily: "\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace",
|
|
483
|
+
padding: "0.75rem 1rem",
|
|
484
|
+
borderRadius: "0.125rem"
|
|
485
|
+
} };
|
|
486
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.CodeBlock, {
|
|
487
|
+
code: node.content?.[0]?.text ?? "",
|
|
488
|
+
language,
|
|
489
|
+
theme,
|
|
490
|
+
style: {
|
|
491
|
+
width: "auto",
|
|
492
|
+
...styles.codeBlock
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
//#endregion
|
|
498
|
+
//#region src/extensions/div.tsx
|
|
499
|
+
const Div = EmailNode.create({
|
|
500
|
+
name: "div",
|
|
501
|
+
group: "block",
|
|
502
|
+
content: "block+",
|
|
503
|
+
defining: true,
|
|
504
|
+
isolating: true,
|
|
505
|
+
parseHTML() {
|
|
506
|
+
return [{
|
|
507
|
+
tag: "div:not([data-type])",
|
|
508
|
+
getAttrs: (node) => {
|
|
509
|
+
if (typeof node === "string") return false;
|
|
510
|
+
const element = node;
|
|
511
|
+
const attrs = {};
|
|
512
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
513
|
+
attrs[attr.name] = attr.value;
|
|
514
|
+
});
|
|
515
|
+
return attrs;
|
|
516
|
+
}
|
|
517
|
+
}];
|
|
518
|
+
},
|
|
519
|
+
renderHTML({ HTMLAttributes }) {
|
|
520
|
+
return [
|
|
521
|
+
"div",
|
|
522
|
+
(0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
|
|
523
|
+
0
|
|
524
|
+
];
|
|
525
|
+
},
|
|
526
|
+
addAttributes() {
|
|
527
|
+
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
528
|
+
},
|
|
529
|
+
renderToReactEmail({ children, node, styles }) {
|
|
530
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
531
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
532
|
+
className: node.attrs?.class || void 0,
|
|
533
|
+
style: {
|
|
534
|
+
...styles.reset,
|
|
535
|
+
...inlineStyles
|
|
536
|
+
},
|
|
537
|
+
children
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/utils/get-text-alignment.ts
|
|
544
|
+
function getTextAlignment(alignment) {
|
|
545
|
+
switch (alignment) {
|
|
546
|
+
case "left": return { textAlign: "left" };
|
|
547
|
+
case "center": return { textAlign: "center" };
|
|
548
|
+
case "right": return { textAlign: "right" };
|
|
549
|
+
default: return {};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/extensions/section.tsx
|
|
555
|
+
const Section = EmailNode.create({
|
|
556
|
+
name: "section",
|
|
557
|
+
group: "block",
|
|
558
|
+
content: "block+",
|
|
559
|
+
isolating: true,
|
|
560
|
+
defining: true,
|
|
561
|
+
parseHTML() {
|
|
562
|
+
return [{ tag: "section[data-type=\"section\"]" }];
|
|
563
|
+
},
|
|
564
|
+
renderHTML({ HTMLAttributes }) {
|
|
565
|
+
return [
|
|
566
|
+
"section",
|
|
567
|
+
(0, __tiptap_core.mergeAttributes)({
|
|
568
|
+
"data-type": "section",
|
|
569
|
+
class: "node-section"
|
|
570
|
+
}, HTMLAttributes),
|
|
571
|
+
0
|
|
572
|
+
];
|
|
573
|
+
},
|
|
574
|
+
addCommands() {
|
|
575
|
+
return { insertSection: () => ({ commands }) => {
|
|
576
|
+
return commands.insertContent({
|
|
577
|
+
type: this.name,
|
|
578
|
+
content: [{
|
|
579
|
+
type: "paragraph",
|
|
580
|
+
content: []
|
|
581
|
+
}]
|
|
582
|
+
});
|
|
583
|
+
} };
|
|
584
|
+
},
|
|
585
|
+
renderToReactEmail({ children, node, styles }) {
|
|
586
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
587
|
+
const textAlign = node.attrs?.align || node.attrs?.alignment;
|
|
588
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
|
|
589
|
+
className: node.attrs?.class || void 0,
|
|
590
|
+
align: textAlign,
|
|
591
|
+
style: {
|
|
592
|
+
...styles.section,
|
|
593
|
+
...inlineStyles,
|
|
594
|
+
...getTextAlignment(textAlign)
|
|
595
|
+
},
|
|
596
|
+
children
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
//#endregion
|
|
602
|
+
exports.Body = Body;
|
|
603
|
+
exports.Button = Button;
|
|
604
|
+
exports.CodeBlockPrism = CodeBlockPrism;
|
|
605
|
+
exports.Div = Div;
|
|
606
|
+
exports.EmailNode = EmailNode;
|
|
607
|
+
exports.Section = Section;
|