2025-09-01 21:25:33 +08:00

98 lines
2.6 KiB
TypeScript

import React, { useState, useCallback, useEffect } from 'react';
import { flushSync } from 'react-dom';
import { EditorContent, useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { HighlightTextExtension } from './HighlightText';
interface MainEditorProps {
content: any[];
onChangeContent?: (content: any[]) => void;
disabled?: boolean;
}
export default function MainEditor({ content, onChangeContent, disabled }: MainEditorProps) {
const [renderContent, setRenderContent] = useState<any[]>(content);
useEffect(() => {
onChangeContent?.(renderContent);
}, [renderContent]);
const editor = useEditor({
extensions: [
StarterKit.configure({
paragraph: {
HTMLAttributes: {
class: 'paragraph'
}
},
}),
HighlightTextExtension,
],
content: {
type: 'doc',
content: renderContent.length === 0 ? [{ type: 'paragraph', content: [] }] : renderContent
},
editorProps: {
attributes: {
class: `prose prose-invert max-w-none focus:outline-none ${disabled ? 'cursor-not-allowed' : ''}`
},
handleDOMEvents: {
keydown: (view, event) => {
// 如果内容为空且按下删除键或退格键,阻止默认行为
if (
(event.key === 'Backspace' || event.key === 'Delete') &&
editor?.isEmpty
) {
event.preventDefault();
return true;
}
return false;
}
}
},
onCreate: ({ editor }) => {
editor.setOptions({ editable: !disabled });
},
onUpdate: ({ editor }) => {
try {
const json = editor.getJSON();
// 确保至少有一个空段落
const safeContent = json.content.length === 0
? [{ type: 'paragraph', content: [] }]
: json.content;
flushSync(() => {
setRenderContent(safeContent);
});
} catch (error) {
console.error('Editor update error:', error);
}
},
immediatelyRender: false, // 解决 SSR 水合问题
});
// 监听编辑器内容变化,确保始终有一个段落
useEffect(() => {
const handleEmpty = () => {
if (editor?.isEmpty) {
editor.commands.setContent([{
type: 'paragraph',
content: []
}]);
}
};
editor?.on('update', handleEmpty);
return () => {
editor?.off('update', handleEmpty);
};
}, [editor]);
if (!editor) {
return null;
}
return (
<EditorContent editor={editor} />
);
}