import { EmailNode } from '@react-email/editor/core';
import { StarterKit } from '@react-email/editor/extensions';
import { BubbleMenu } from '@react-email/editor/ui';
import { mergeAttributes } from '@tiptap/core';
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
import { Info } from 'lucide-react';
const Callout = EmailNode.create({
name: 'callout',
group: 'block',
content: 'inline*',
parseHTML() {
return [{ tag: 'div[data-callout]' }];
},
renderHTML({ HTMLAttributes }) {
return [
'div',
mergeAttributes(HTMLAttributes, {
'data-callout': '',
style:
'padding: 12px 16px; background: #f4f4f5; border-left: 3px solid #1c1c1c; border-radius: 4px; margin: 8px 0;',
}),
0,
];
},
renderToReactEmail({ children, style }) {
return (
<div
style={{
...style,
padding: '12px 16px',
backgroundColor: '#f4f4f5',
borderLeft: '3px solid #1c1c1c',
borderRadius: '4px',
margin: '8px 0',
}}
>
{children}
</div>
);
},
});
const extensions = [StarterKit, Callout];
const content = {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'This editor includes a custom Callout node. Use the toolbar to insert one.',
},
],
},
{
type: 'callout',
content: [
{ type: 'text', text: 'This is a callout block — a custom extension!' },
],
},
],
};
function Toolbar() {
const { editor } = useCurrentEditor();
if (!editor) return null;
return (
<button
onClick={() =>
editor
.chain()
.focus()
.insertContent({
type: 'callout',
content: [{ type: 'text', text: 'New callout' }],
})
.run()
}
>
<Info size={16} />
Insert Callout
</button>
);
}
export function MyEditor() {
return (
<EditorProvider
extensions={extensions}
content={content}
slotBefore={<Toolbar />}
>
<BubbleMenu.Default />
</EditorProvider>
);
}