import { useEffect, useRef } from 'react' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import TextAlign from '@tiptap/extension-text-align' import Underline from '@tiptap/extension-underline' export function RichTextEditor({ onChange, value }) { const lastSyncedHtmlRef = useRef(value || '') const applyingExternalContentRef = useRef(false) const tiptapEditor = useEditor({ extensions: [ StarterKit, Underline, TextAlign.configure({ types: ['heading', 'paragraph'], }), ], content: value || '', editorProps: { attributes: { class: 'report-rich-surface min-h-[560px] px-4 py-3 text-sm leading-6 text-[#e5e5e5] outline-none', }, }, shouldRerenderOnTransaction: false, onUpdate: ({ editor: currentEditor }) => { if (applyingExternalContentRef.current) return const nextHtml = currentEditor.getHTML() lastSyncedHtmlRef.current = nextHtml onChange(nextHtml) }, }) useEffect(() => { if (!tiptapEditor) return const nextValue = value || '' if (lastSyncedHtmlRef.current === nextValue) return if (tiptapEditor.getHTML() === nextValue) { lastSyncedHtmlRef.current = nextValue return } applyingExternalContentRef.current = true try { tiptapEditor.commands.setContent(nextValue, { emitUpdate: false }) } finally { applyingExternalContentRef.current = false } lastSyncedHtmlRef.current = nextValue }, [tiptapEditor, value]) const blockFormat = tiptapEditor?.isActive('heading', { level: 2 }) ? 'h2' : tiptapEditor?.isActive('heading', { level: 3 }) ? 'h3' : 'p' return (
tiptapEditor?.chain().focus().undo().run()} /> tiptapEditor?.chain().focus().redo().run()} /> tiptapEditor?.chain().focus().toggleBold().run()} /> tiptapEditor?.chain().focus().toggleItalic().run()} /> tiptapEditor?.chain().focus().toggleUnderline().run()} /> tiptapEditor?.chain().focus().toggleStrike().run()} /> tiptapEditor?.chain().focus().setTextAlign('left').run()} /> tiptapEditor?.chain().focus().setTextAlign('center').run()} /> tiptapEditor?.chain().focus().setTextAlign('right').run()} /> tiptapEditor?.chain().focus().toggleBulletList().run()} />
) } function ToolbarButton({ active = false, disabled = false, label, name, onClick }) { return ( ) } function RichTextIcon({ className = 'size-4', name }) { const common = { className, fill: 'none', stroke: 'currentColor', strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 1.8, viewBox: '0 0 24 24', } const icons = { undo: , redo: , bold: , italic: , underline: , strike: , 'align-left': , 'align-center': , 'align-right': , list: , } return {icons[name] || icons.list} }