@blocknote/xl-email-exporter 0.35.0 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -373
- package/dist/blocknote-xl-email-exporter.js +443 -208
- package/dist/blocknote-xl-email-exporter.js.map +1 -1
- package/dist/blocknote-xl-email-exporter.umd.cjs +1 -1
- package/dist/blocknote-xl-email-exporter.umd.cjs.map +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +4 -4
- package/src/react-email/__snapshots__/reactEmailExporter.test.tsx.snap +17 -17
- package/src/react-email/defaultSchema/blocks.tsx +230 -28
- package/src/react-email/defaultSchema/index.ts +71 -3
- package/src/react-email/defaultSchema/inlinecontent.tsx +21 -5
- package/src/react-email/defaultSchema/styles.tsx +28 -10
- package/src/react-email/reactEmailExporter.tsx +26 -7
- package/types/src/react-email/defaultSchema/blocks.d.ts +632 -4
- package/types/src/react-email/defaultSchema/index.d.ts +16 -0
- package/types/src/react-email/defaultSchema/inlinecontent.d.ts +66 -3
- package/types/src/react-email/defaultSchema/styles.d.ts +54 -1
- package/types/src/react-email/reactEmailExporter.d.ts +6 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BlockMapping,
|
|
2
3
|
DefaultBlockSchema,
|
|
3
4
|
mapTableCell,
|
|
4
5
|
pageBreakSchema,
|
|
5
6
|
StyledText,
|
|
6
7
|
} from "@blocknote/core";
|
|
7
|
-
import { BlockMapping } from "@blocknote/core/src/exporter/mapping.js";
|
|
8
8
|
import {
|
|
9
9
|
CodeBlock,
|
|
10
10
|
dracula,
|
|
@@ -15,27 +15,169 @@ import {
|
|
|
15
15
|
Text,
|
|
16
16
|
} from "@react-email/components";
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
// Define TextProps type based on React Email Text component
|
|
19
|
+
type TextProps = React.ComponentPropsWithoutRef<typeof Text>;
|
|
20
|
+
|
|
21
|
+
// Define the styles interface for configurable Text components
|
|
22
|
+
export interface ReactEmailTextStyles {
|
|
23
|
+
paragraph?: Partial<TextProps>;
|
|
24
|
+
bulletListItem?: Partial<TextProps>;
|
|
25
|
+
toggleListItem?: Partial<TextProps>;
|
|
26
|
+
numberedListItem?: Partial<TextProps>;
|
|
27
|
+
checkListItem?: Partial<TextProps>;
|
|
28
|
+
quote?: Partial<TextProps>;
|
|
29
|
+
tableError?: Partial<TextProps>;
|
|
30
|
+
tableCell?: Partial<TextProps>;
|
|
31
|
+
caption?: Partial<TextProps>;
|
|
32
|
+
heading1?: Partial<TextProps>;
|
|
33
|
+
heading2?: Partial<TextProps>;
|
|
34
|
+
heading3?: Partial<TextProps>;
|
|
35
|
+
heading4?: Partial<TextProps>;
|
|
36
|
+
heading5?: Partial<TextProps>;
|
|
37
|
+
heading6?: Partial<TextProps>;
|
|
38
|
+
codeBlock?: Partial<React.ComponentProps<typeof CodeBlock>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const defaultTextStyle: TextProps["style"] = {
|
|
42
|
+
fontSize: 16,
|
|
43
|
+
lineHeight: 1.5,
|
|
44
|
+
margin: 3,
|
|
45
|
+
minHeight: 24,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Default styles for Text components
|
|
49
|
+
export const defaultReactEmailTextStyles = {
|
|
50
|
+
paragraph: {
|
|
51
|
+
style: defaultTextStyle,
|
|
52
|
+
},
|
|
53
|
+
bulletListItem: {
|
|
54
|
+
style: defaultTextStyle,
|
|
55
|
+
},
|
|
56
|
+
toggleListItem: {
|
|
57
|
+
style: defaultTextStyle,
|
|
58
|
+
},
|
|
59
|
+
numberedListItem: {
|
|
60
|
+
style: defaultTextStyle,
|
|
61
|
+
},
|
|
62
|
+
checkListItem: {
|
|
63
|
+
style: defaultTextStyle,
|
|
64
|
+
},
|
|
65
|
+
quote: {
|
|
66
|
+
style: defaultTextStyle,
|
|
67
|
+
},
|
|
68
|
+
tableError: {
|
|
69
|
+
style: defaultTextStyle,
|
|
70
|
+
},
|
|
71
|
+
tableCell: {
|
|
72
|
+
style: defaultTextStyle,
|
|
73
|
+
},
|
|
74
|
+
caption: {
|
|
75
|
+
style: defaultTextStyle,
|
|
76
|
+
},
|
|
77
|
+
heading1: {
|
|
78
|
+
style: {
|
|
79
|
+
fontSize: 48,
|
|
80
|
+
margin: 3,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
heading2: {
|
|
84
|
+
style: {
|
|
85
|
+
fontSize: 36,
|
|
86
|
+
margin: 3,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
heading3: {
|
|
90
|
+
style: {
|
|
91
|
+
fontSize: 24,
|
|
92
|
+
margin: 3,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
heading4: {
|
|
96
|
+
style: {
|
|
97
|
+
fontSize: 20,
|
|
98
|
+
margin: 3,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
heading5: {
|
|
102
|
+
style: {
|
|
103
|
+
fontSize: 18,
|
|
104
|
+
margin: 3,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
heading6: {
|
|
108
|
+
style: {
|
|
109
|
+
fontSize: 16,
|
|
110
|
+
margin: 3,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
codeBlock: {
|
|
114
|
+
style: defaultTextStyle,
|
|
115
|
+
},
|
|
116
|
+
} satisfies ReactEmailTextStyles;
|
|
117
|
+
|
|
118
|
+
export const createReactEmailBlockMappingForDefaultSchema = (
|
|
119
|
+
textStyles: ReactEmailTextStyles = defaultReactEmailTextStyles,
|
|
120
|
+
): BlockMapping<
|
|
19
121
|
DefaultBlockSchema & typeof pageBreakSchema.blockSchema,
|
|
20
122
|
any,
|
|
21
123
|
any,
|
|
22
124
|
React.ReactElement<any>,
|
|
23
125
|
React.ReactElement<typeof Link> | React.ReactElement<HTMLSpanElement>
|
|
24
|
-
>
|
|
126
|
+
> => ({
|
|
25
127
|
paragraph: (block, t) => {
|
|
26
|
-
return
|
|
128
|
+
return (
|
|
129
|
+
<Text
|
|
130
|
+
{...textStyles.paragraph}
|
|
131
|
+
style={{
|
|
132
|
+
...defaultReactEmailTextStyles.paragraph.style,
|
|
133
|
+
...textStyles.paragraph?.style,
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{t.transformInlineContent(block.content)}
|
|
137
|
+
</Text>
|
|
138
|
+
);
|
|
27
139
|
},
|
|
28
140
|
bulletListItem: (block, t) => {
|
|
29
141
|
// Return only the <li> for grouping in the exporter
|
|
30
|
-
return
|
|
142
|
+
return (
|
|
143
|
+
<Text
|
|
144
|
+
{...textStyles.bulletListItem}
|
|
145
|
+
style={{
|
|
146
|
+
...defaultReactEmailTextStyles.bulletListItem.style,
|
|
147
|
+
...textStyles.bulletListItem?.style,
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{t.transformInlineContent(block.content)}
|
|
151
|
+
</Text>
|
|
152
|
+
);
|
|
31
153
|
},
|
|
32
154
|
toggleListItem: (block, t) => {
|
|
33
155
|
// Return only the <li> for grouping in the exporter
|
|
34
|
-
return
|
|
156
|
+
return (
|
|
157
|
+
<Text
|
|
158
|
+
{...textStyles.toggleListItem}
|
|
159
|
+
style={{
|
|
160
|
+
...defaultReactEmailTextStyles.toggleListItem.style,
|
|
161
|
+
...textStyles.toggleListItem?.style,
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
{t.transformInlineContent(block.content)}
|
|
165
|
+
</Text>
|
|
166
|
+
);
|
|
35
167
|
},
|
|
36
168
|
numberedListItem: (block, t, _nestingLevel) => {
|
|
37
169
|
// Return only the <li> for grouping in the exporter
|
|
38
|
-
return
|
|
170
|
+
return (
|
|
171
|
+
<Text
|
|
172
|
+
{...textStyles.numberedListItem}
|
|
173
|
+
style={{
|
|
174
|
+
...defaultReactEmailTextStyles.numberedListItem.style,
|
|
175
|
+
...textStyles.numberedListItem?.style,
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
{t.transformInlineContent(block.content)}
|
|
179
|
+
</Text>
|
|
180
|
+
);
|
|
39
181
|
},
|
|
40
182
|
checkListItem: (block, t) => {
|
|
41
183
|
// Render a checkbox using inline SVG for better appearance in email
|
|
@@ -85,7 +227,13 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
85
227
|
</svg>
|
|
86
228
|
);
|
|
87
229
|
return (
|
|
88
|
-
<Text
|
|
230
|
+
<Text
|
|
231
|
+
{...textStyles.checkListItem}
|
|
232
|
+
style={{
|
|
233
|
+
...defaultReactEmailTextStyles.checkListItem.style,
|
|
234
|
+
...textStyles.checkListItem?.style,
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
89
237
|
{checkboxSvg}
|
|
90
238
|
<span>{t.transformInlineContent(block.content)}</span>
|
|
91
239
|
</Text>
|
|
@@ -93,7 +241,14 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
93
241
|
},
|
|
94
242
|
heading: (block, t) => {
|
|
95
243
|
return (
|
|
96
|
-
<Heading
|
|
244
|
+
<Heading
|
|
245
|
+
as={`h${block.props.level}`}
|
|
246
|
+
{...textStyles[`heading${block.props.level}`]}
|
|
247
|
+
style={{
|
|
248
|
+
...defaultReactEmailTextStyles[`heading${block.props.level}`].style,
|
|
249
|
+
...textStyles[`heading${block.props.level}`]?.style,
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
97
252
|
{t.transformInlineContent(block.content)}
|
|
98
253
|
</Heading>
|
|
99
254
|
);
|
|
@@ -108,6 +263,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
108
263
|
fontFamily="'CommitMono', monospace"
|
|
109
264
|
language={block.props.language as PrismLanguage}
|
|
110
265
|
theme={dracula}
|
|
266
|
+
{...textStyles.codeBlock}
|
|
267
|
+
style={{
|
|
268
|
+
...defaultReactEmailTextStyles.codeBlock.style,
|
|
269
|
+
...textStyles.codeBlock?.style,
|
|
270
|
+
}}
|
|
111
271
|
/>
|
|
112
272
|
);
|
|
113
273
|
},
|
|
@@ -136,7 +296,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
136
296
|
defaultText="Open audio file"
|
|
137
297
|
icon={icon}
|
|
138
298
|
/>
|
|
139
|
-
<Caption
|
|
299
|
+
<Caption
|
|
300
|
+
caption={block.props.caption}
|
|
301
|
+
width={previewWidth}
|
|
302
|
+
textStyles={textStyles}
|
|
303
|
+
/>
|
|
140
304
|
</div>
|
|
141
305
|
);
|
|
142
306
|
},
|
|
@@ -165,7 +329,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
165
329
|
defaultText="Open video file"
|
|
166
330
|
icon={icon}
|
|
167
331
|
/>
|
|
168
|
-
<Caption
|
|
332
|
+
<Caption
|
|
333
|
+
caption={block.props.caption}
|
|
334
|
+
width={previewWidth}
|
|
335
|
+
textStyles={textStyles}
|
|
336
|
+
/>
|
|
169
337
|
</div>
|
|
170
338
|
);
|
|
171
339
|
},
|
|
@@ -194,7 +362,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
194
362
|
defaultText="Open file"
|
|
195
363
|
icon={icon}
|
|
196
364
|
/>
|
|
197
|
-
<Caption
|
|
365
|
+
<Caption
|
|
366
|
+
caption={block.props.caption}
|
|
367
|
+
width={previewWidth}
|
|
368
|
+
textStyles={textStyles}
|
|
369
|
+
/>
|
|
198
370
|
</div>
|
|
199
371
|
);
|
|
200
372
|
},
|
|
@@ -211,7 +383,7 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
211
383
|
// Render table using standard HTML table elements for email compatibility
|
|
212
384
|
const table = block.content;
|
|
213
385
|
if (!table || typeof table !== "object" || !Array.isArray(table.rows)) {
|
|
214
|
-
return <Text>Table data not available</Text>;
|
|
386
|
+
return <Text {...textStyles.tableError}>Table data not available</Text>;
|
|
215
387
|
}
|
|
216
388
|
const headerRowsCount = (table.headerRows as number) ?? 0;
|
|
217
389
|
const headerColsCount = (table.headerCols as number) ?? 0;
|
|
@@ -247,17 +419,24 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
247
419
|
style={{
|
|
248
420
|
border: "1px solid #ddd",
|
|
249
421
|
padding: "8px 12px",
|
|
250
|
-
background:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
422
|
+
background:
|
|
423
|
+
normalizedCell.props.backgroundColor !== "default"
|
|
424
|
+
? t.options.colors[
|
|
425
|
+
normalizedCell.props
|
|
426
|
+
.backgroundColor as keyof typeof t.options.colors
|
|
427
|
+
].background
|
|
428
|
+
: "inherit",
|
|
429
|
+
fontWeight: isHeader ? "bold" : undefined,
|
|
256
430
|
textAlign: normalizedCell.props.textAlignment || "left",
|
|
257
431
|
color:
|
|
258
432
|
normalizedCell.props.textColor !== "default"
|
|
259
|
-
?
|
|
433
|
+
? t.options.colors[
|
|
434
|
+
normalizedCell.props
|
|
435
|
+
.textColor as keyof typeof t.options.colors
|
|
436
|
+
].text
|
|
260
437
|
: "inherit",
|
|
438
|
+
...defaultReactEmailTextStyles.tableCell.style,
|
|
439
|
+
...textStyles.tableCell?.style,
|
|
261
440
|
}}
|
|
262
441
|
{...((normalizedCell.props.colspan || 1) > 1 && {
|
|
263
442
|
colSpan: normalizedCell.props.colspan || 1,
|
|
@@ -280,14 +459,15 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
280
459
|
// Render block quote with a left border and subtle background for email compatibility
|
|
281
460
|
return (
|
|
282
461
|
<Text
|
|
462
|
+
{...textStyles.quote}
|
|
283
463
|
style={{
|
|
284
|
-
borderLeft: "
|
|
285
|
-
|
|
286
|
-
padding: "12px 16px",
|
|
287
|
-
margin: "16px 0",
|
|
464
|
+
borderLeft: "2px solid #bdbdbd",
|
|
465
|
+
padding: "0px 12px",
|
|
288
466
|
fontStyle: "italic",
|
|
289
|
-
color:
|
|
467
|
+
color: t.options.colors.gray.text,
|
|
290
468
|
display: "block",
|
|
469
|
+
...defaultReactEmailTextStyles.quote.style,
|
|
470
|
+
...textStyles.quote?.style,
|
|
291
471
|
}}
|
|
292
472
|
>
|
|
293
473
|
{t.transformInlineContent(block.content)}
|
|
@@ -306,7 +486,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping<
|
|
|
306
486
|
/>
|
|
307
487
|
);
|
|
308
488
|
},
|
|
309
|
-
};
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Export the original mapping for backward compatibility
|
|
492
|
+
export const reactEmailBlockMappingForDefaultSchema =
|
|
493
|
+
createReactEmailBlockMappingForDefaultSchema();
|
|
310
494
|
|
|
311
495
|
// Helper for file-like blocks (audio, video, file)
|
|
312
496
|
function FileLink({
|
|
@@ -338,12 +522,30 @@ function FileLink({
|
|
|
338
522
|
);
|
|
339
523
|
}
|
|
340
524
|
|
|
341
|
-
function Caption({
|
|
525
|
+
function Caption({
|
|
526
|
+
caption,
|
|
527
|
+
width,
|
|
528
|
+
textStyles,
|
|
529
|
+
}: {
|
|
530
|
+
caption?: string;
|
|
531
|
+
width?: number;
|
|
532
|
+
textStyles: ReactEmailTextStyles;
|
|
533
|
+
}) {
|
|
342
534
|
if (!caption) {
|
|
343
535
|
return null;
|
|
344
536
|
}
|
|
345
537
|
return (
|
|
346
|
-
<Text
|
|
538
|
+
<Text
|
|
539
|
+
{...textStyles.caption}
|
|
540
|
+
style={{
|
|
541
|
+
width,
|
|
542
|
+
fontSize: 13,
|
|
543
|
+
color: "#888",
|
|
544
|
+
margin: "4px 0 0 0",
|
|
545
|
+
...defaultReactEmailTextStyles.caption.style,
|
|
546
|
+
...textStyles.caption?.style,
|
|
547
|
+
}}
|
|
548
|
+
>
|
|
347
549
|
{caption}
|
|
348
550
|
</Text>
|
|
349
551
|
);
|
|
@@ -1,9 +1,77 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
reactEmailBlockMappingForDefaultSchema,
|
|
3
|
+
createReactEmailBlockMappingForDefaultSchema,
|
|
4
|
+
type ReactEmailTextStyles,
|
|
5
|
+
defaultReactEmailTextStyles,
|
|
6
|
+
} from "./blocks.js";
|
|
7
|
+
import {
|
|
8
|
+
reactEmailInlineContentMappingForDefaultSchema,
|
|
9
|
+
createReactEmailInlineContentMappingForDefaultSchema,
|
|
10
|
+
type ReactEmailLinkStyles,
|
|
11
|
+
defaultReactEmailLinkStyles,
|
|
12
|
+
} from "./inlinecontent.js";
|
|
13
|
+
import {
|
|
14
|
+
reactEmailStyleMappingForDefaultSchema,
|
|
15
|
+
createReactEmailStyleMappingForDefaultSchema,
|
|
16
|
+
type ReactEmailStyleTransformStyles,
|
|
17
|
+
defaultReactEmailStyleTransformStyles,
|
|
18
|
+
} from "./styles.js";
|
|
19
|
+
|
|
20
|
+
// Re-export for backward compatibility
|
|
21
|
+
export { reactEmailBlockMappingForDefaultSchema } from "./blocks.js";
|
|
22
|
+
export { reactEmailInlineContentMappingForDefaultSchema } from "./inlinecontent.js";
|
|
23
|
+
export { reactEmailStyleMappingForDefaultSchema } from "./styles.js";
|
|
24
|
+
|
|
25
|
+
// Export the new configurable functions
|
|
26
|
+
export {
|
|
27
|
+
createReactEmailBlockMappingForDefaultSchema,
|
|
28
|
+
type ReactEmailTextStyles,
|
|
29
|
+
defaultReactEmailTextStyles,
|
|
30
|
+
} from "./blocks.js";
|
|
31
|
+
export {
|
|
32
|
+
createReactEmailInlineContentMappingForDefaultSchema,
|
|
33
|
+
type ReactEmailLinkStyles,
|
|
34
|
+
defaultReactEmailLinkStyles,
|
|
35
|
+
} from "./inlinecontent.js";
|
|
36
|
+
export {
|
|
37
|
+
createReactEmailStyleMappingForDefaultSchema,
|
|
38
|
+
type ReactEmailStyleTransformStyles,
|
|
39
|
+
defaultReactEmailStyleTransformStyles,
|
|
40
|
+
} from "./styles.js";
|
|
41
|
+
|
|
42
|
+
// Export the combined styles interface
|
|
43
|
+
export interface ReactEmailDefaultSchemaStyles {
|
|
44
|
+
textStyles?: ReactEmailTextStyles;
|
|
45
|
+
linkStyles?: ReactEmailLinkStyles;
|
|
46
|
+
styleTransformStyles?: ReactEmailStyleTransformStyles;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Export the default combined styles
|
|
50
|
+
export const defaultReactEmailDefaultSchemaStyles: ReactEmailDefaultSchemaStyles =
|
|
51
|
+
{
|
|
52
|
+
textStyles: defaultReactEmailTextStyles,
|
|
53
|
+
linkStyles: defaultReactEmailLinkStyles,
|
|
54
|
+
styleTransformStyles: defaultReactEmailStyleTransformStyles,
|
|
55
|
+
};
|
|
4
56
|
|
|
5
57
|
export const reactEmailDefaultSchemaMappings = {
|
|
6
58
|
blockMapping: reactEmailBlockMappingForDefaultSchema,
|
|
7
59
|
inlineContentMapping: reactEmailInlineContentMappingForDefaultSchema,
|
|
8
60
|
styleMapping: reactEmailStyleMappingForDefaultSchema,
|
|
9
61
|
};
|
|
62
|
+
|
|
63
|
+
export const reactEmailDefaultSchemaMappingsWithStyles = (
|
|
64
|
+
styles: ReactEmailDefaultSchemaStyles = defaultReactEmailDefaultSchemaStyles,
|
|
65
|
+
): typeof reactEmailDefaultSchemaMappings => {
|
|
66
|
+
return {
|
|
67
|
+
blockMapping: createReactEmailBlockMappingForDefaultSchema(
|
|
68
|
+
styles.textStyles,
|
|
69
|
+
),
|
|
70
|
+
inlineContentMapping: createReactEmailInlineContentMappingForDefaultSchema(
|
|
71
|
+
styles.linkStyles,
|
|
72
|
+
),
|
|
73
|
+
styleMapping: createReactEmailStyleMappingForDefaultSchema(
|
|
74
|
+
styles.styleTransformStyles,
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DefaultInlineContentSchema,
|
|
3
3
|
DefaultStyleSchema,
|
|
4
|
+
InlineContentMapping,
|
|
4
5
|
} from "@blocknote/core";
|
|
5
|
-
import { InlineContentMapping } from "@blocknote/core/src/exporter/mapping.js";
|
|
6
6
|
import { Link } from "@react-email/components";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// Define the styles interface for configurable Link components
|
|
9
|
+
export interface ReactEmailLinkStyles {
|
|
10
|
+
link?: Partial<React.ComponentPropsWithoutRef<typeof Link>>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Default styles for Link components
|
|
14
|
+
export const defaultReactEmailLinkStyles: ReactEmailLinkStyles = {
|
|
15
|
+
link: {},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const createReactEmailInlineContentMappingForDefaultSchema = (
|
|
19
|
+
linkStyles: ReactEmailLinkStyles = defaultReactEmailLinkStyles,
|
|
20
|
+
): InlineContentMapping<
|
|
9
21
|
DefaultInlineContentSchema,
|
|
10
22
|
DefaultStyleSchema,
|
|
11
23
|
React.ReactElement<typeof Link> | React.ReactElement<HTMLSpanElement>,
|
|
12
24
|
React.ReactElement<HTMLSpanElement>
|
|
13
|
-
>
|
|
25
|
+
> => ({
|
|
14
26
|
link: (ic, t) => {
|
|
15
27
|
return (
|
|
16
|
-
<Link href={ic.href}>
|
|
28
|
+
<Link href={ic.href} {...linkStyles.link}>
|
|
17
29
|
{...ic.content.map((content) => {
|
|
18
30
|
return t.transformStyledText(content);
|
|
19
31
|
})}
|
|
@@ -23,4 +35,8 @@ export const reactEmailInlineContentMappingForDefaultSchema: InlineContentMappin
|
|
|
23
35
|
text: (ic, t) => {
|
|
24
36
|
return t.transformStyledText(ic);
|
|
25
37
|
},
|
|
26
|
-
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Export the original mapping for backward compatibility
|
|
41
|
+
export const reactEmailInlineContentMappingForDefaultSchema =
|
|
42
|
+
createReactEmailInlineContentMappingForDefaultSchema();
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { DefaultStyleSchema, StyleMapping } from "@blocknote/core";
|
|
2
2
|
import { CSSProperties } from "react";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
// Define the styles interface for configurable style transformations
|
|
5
|
+
// This can be extended in the future to allow customizing style transformations
|
|
6
|
+
export type ReactEmailStyleTransformStyles = Record<string, never>;
|
|
7
|
+
|
|
8
|
+
// Default styles for style transformations
|
|
9
|
+
export const defaultReactEmailStyleTransformStyles: ReactEmailStyleTransformStyles =
|
|
10
|
+
{};
|
|
11
|
+
|
|
12
|
+
export const createReactEmailStyleMappingForDefaultSchema = (
|
|
13
|
+
_styleTransformStyles: ReactEmailStyleTransformStyles = defaultReactEmailStyleTransformStyles,
|
|
14
|
+
): StyleMapping<DefaultStyleSchema, CSSProperties> => ({
|
|
8
15
|
bold: (val) => {
|
|
9
16
|
if (!val) {
|
|
10
17
|
return {};
|
|
@@ -37,17 +44,24 @@ export const reactEmailStyleMappingForDefaultSchema: StyleMapping<
|
|
|
37
44
|
textDecoration: "line-through",
|
|
38
45
|
};
|
|
39
46
|
},
|
|
40
|
-
backgroundColor: (val) => {
|
|
47
|
+
backgroundColor: (val, exporter) => {
|
|
48
|
+
if (!val) {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
41
51
|
return {
|
|
42
|
-
backgroundColor:
|
|
52
|
+
backgroundColor:
|
|
53
|
+
exporter.options.colors[val as keyof typeof exporter.options.colors]
|
|
54
|
+
.background,
|
|
43
55
|
};
|
|
44
56
|
},
|
|
45
|
-
textColor: (val) => {
|
|
57
|
+
textColor: (val, exporter) => {
|
|
46
58
|
if (!val) {
|
|
47
59
|
return {};
|
|
48
60
|
}
|
|
49
61
|
return {
|
|
50
|
-
color:
|
|
62
|
+
color:
|
|
63
|
+
exporter.options.colors[val as keyof typeof exporter.options.colors]
|
|
64
|
+
.text,
|
|
51
65
|
};
|
|
52
66
|
},
|
|
53
67
|
code: (val) => {
|
|
@@ -55,7 +69,11 @@ export const reactEmailStyleMappingForDefaultSchema: StyleMapping<
|
|
|
55
69
|
return {};
|
|
56
70
|
}
|
|
57
71
|
return {
|
|
58
|
-
fontFamily: "
|
|
72
|
+
fontFamily: "GeistMono",
|
|
59
73
|
};
|
|
60
74
|
},
|
|
61
|
-
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Export the original mapping for backward compatibility
|
|
78
|
+
export const reactEmailStyleMappingForDefaultSchema =
|
|
79
|
+
createReactEmailStyleMappingForDefaultSchema();
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "@blocknote/core";
|
|
13
13
|
import {
|
|
14
14
|
Body,
|
|
15
|
-
Container,
|
|
16
15
|
Head,
|
|
17
16
|
Html,
|
|
18
17
|
Link,
|
|
@@ -63,7 +62,14 @@ export class ReactEmailExporter<
|
|
|
63
62
|
public transformStyledText(styledText: StyledText<S>) {
|
|
64
63
|
const stylesArray = this.mapStyles(styledText.styles);
|
|
65
64
|
const styles = Object.assign({}, ...stylesArray);
|
|
66
|
-
return
|
|
65
|
+
return (
|
|
66
|
+
<span
|
|
67
|
+
style={styles}
|
|
68
|
+
dangerouslySetInnerHTML={{
|
|
69
|
+
__html: styledText.text.replace(/\n/g, "<br />"),
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
private async renderGroupedListBlocks(
|
|
@@ -95,10 +101,10 @@ export class ReactEmailExporter<
|
|
|
95
101
|
);
|
|
96
102
|
}
|
|
97
103
|
listItems.push(
|
|
98
|
-
<
|
|
104
|
+
<li key={block.id}>
|
|
99
105
|
{liContent}
|
|
100
106
|
{nestedList.length > 0 && nestedList}
|
|
101
|
-
</
|
|
107
|
+
</li>,
|
|
102
108
|
);
|
|
103
109
|
}
|
|
104
110
|
let element: React.ReactElement;
|
|
@@ -226,7 +232,11 @@ export class ReactEmailExporter<
|
|
|
226
232
|
let i = 0;
|
|
227
233
|
while (i < blocks.length) {
|
|
228
234
|
const b = blocks[i];
|
|
229
|
-
if (
|
|
235
|
+
if (
|
|
236
|
+
b.type === "bulletListItem" ||
|
|
237
|
+
b.type === "numberedListItem" ||
|
|
238
|
+
b.type === "toggleListItem"
|
|
239
|
+
) {
|
|
230
240
|
const { element, nextIndex } = await this.renderGroupedListBlocks(
|
|
231
241
|
blocks,
|
|
232
242
|
i,
|
|
@@ -278,9 +288,18 @@ export class ReactEmailExporter<
|
|
|
278
288
|
* @see https://react.email/components
|
|
279
289
|
*/
|
|
280
290
|
footer?: React.ReactElement;
|
|
291
|
+
/**
|
|
292
|
+
* Customize the container element
|
|
293
|
+
*/
|
|
294
|
+
container?: React.FC<{ children: React.ReactNode }>;
|
|
281
295
|
},
|
|
282
296
|
) {
|
|
283
297
|
const transformedBlocks = await this.transformBlocks(blocks);
|
|
298
|
+
const DefaultContainer =
|
|
299
|
+
options?.container ||
|
|
300
|
+
(({ children }: { children: React.ReactNode }) => (
|
|
301
|
+
<React.Fragment>{children}</React.Fragment>
|
|
302
|
+
));
|
|
284
303
|
return renderEmail(
|
|
285
304
|
<Html>
|
|
286
305
|
<Head>{options?.head}</Head>
|
|
@@ -295,11 +314,11 @@ export class ReactEmailExporter<
|
|
|
295
314
|
>
|
|
296
315
|
{options?.preview && <Preview>{options.preview}</Preview>}
|
|
297
316
|
<Tailwind>
|
|
298
|
-
<
|
|
317
|
+
<DefaultContainer>
|
|
299
318
|
{options?.header}
|
|
300
319
|
{transformedBlocks}
|
|
301
320
|
{options?.footer}
|
|
302
|
-
</
|
|
321
|
+
</DefaultContainer>
|
|
303
322
|
</Tailwind>
|
|
304
323
|
</Body>
|
|
305
324
|
</Html>,
|