132 lines
3.6 KiB
TypeScript
132 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
import 'quill/dist/quill.snow.css';
|
|
import { sanitizeHtml } from '@/lib/utils/email-formatter';
|
|
|
|
interface RichEmailEditorProps {
|
|
initialContent: string;
|
|
onChange: (content: string) => void;
|
|
placeholder?: string;
|
|
minHeight?: string;
|
|
maxHeight?: string;
|
|
}
|
|
|
|
const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|
initialContent,
|
|
onChange,
|
|
placeholder = 'Write your message here...',
|
|
minHeight = '200px',
|
|
maxHeight = 'calc(100vh - 400px)',
|
|
}) => {
|
|
const editorRef = useRef<HTMLDivElement>(null);
|
|
const quillRef = useRef<any>(null);
|
|
const [isReady, setIsReady] = useState(false);
|
|
|
|
// Initialize Quill editor when component mounts
|
|
useEffect(() => {
|
|
// Import Quill dynamically (client-side only)
|
|
const initializeQuill = async () => {
|
|
if (!editorRef.current) return;
|
|
|
|
const Quill = (await import('quill')).default;
|
|
|
|
// Define custom formats/modules as needed for email
|
|
const emailToolbarOptions = [
|
|
['bold', 'italic', 'underline', 'strike'],
|
|
[{ 'color': [] }, { 'background': [] }],
|
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
|
[{ 'indent': '-1'}, { 'indent': '+1' }],
|
|
[{ 'align': [] }],
|
|
['link'],
|
|
['clean'],
|
|
];
|
|
|
|
// Create new Quill instance with the DOM element
|
|
const editorElement = editorRef.current;
|
|
quillRef.current = new Quill(editorElement, {
|
|
modules: {
|
|
toolbar: emailToolbarOptions
|
|
},
|
|
placeholder: placeholder,
|
|
theme: 'snow',
|
|
});
|
|
|
|
// Set initial content (sanitized)
|
|
if (initialContent) {
|
|
quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
|
|
}
|
|
|
|
// Add change listener
|
|
quillRef.current.on('text-change', () => {
|
|
const html = quillRef.current.root.innerHTML;
|
|
onChange(html);
|
|
});
|
|
|
|
setIsReady(true);
|
|
};
|
|
|
|
initializeQuill();
|
|
|
|
// Clean up on unmount
|
|
return () => {
|
|
if (quillRef.current) {
|
|
// Clean up any event listeners or resources
|
|
quillRef.current.off('text-change');
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// Update content from props if changed externally
|
|
useEffect(() => {
|
|
if (quillRef.current && isReady) {
|
|
const currentContent = quillRef.current.root.innerHTML;
|
|
if (initialContent !== currentContent) {
|
|
quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
|
|
}
|
|
}
|
|
}, [initialContent, isReady]);
|
|
|
|
return (
|
|
<div className="rich-email-editor-container">
|
|
{/* Quill container */}
|
|
<div
|
|
ref={editorRef}
|
|
className="quill-editor"
|
|
style={{
|
|
height: 'auto',
|
|
minHeight: minHeight,
|
|
maxHeight: maxHeight
|
|
}}
|
|
/>
|
|
|
|
{/* Loading indicator */}
|
|
{!isReady && (
|
|
<div className="flex items-center justify-center py-8">
|
|
<div className="h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent"></div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Custom styles for email context */}
|
|
<style jsx>{`
|
|
.rich-email-editor-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
flex: 1;
|
|
}
|
|
.quill-editor {
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
|
|
/* Hide the editor until it's ready */
|
|
.quill-editor ${!isReady ? '{ display: none; }' : ''}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default RichEmailEditor;
|