You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by ro...@apache.org on 2025/12/25 03:51:56 UTC
(answer) 03/12: refactor(editor): streamline editor component structure and enhance command methods
This is an automated email from the ASF dual-hosted git repository.
robin0716 pushed a commit to branch feat/1.7.2/ui
in repository https://gitbox.apache.org/repos/asf/answer.git
commit 3d43700556306965f7816e6cdb031327c16193a6
Author: robin <ro...@rnode.me>
AuthorDate: Tue Dec 16 14:50:29 2025 +0800
refactor(editor): streamline editor component structure and enhance command methods
- Removed unnecessary conditional rendering in the MDEditor component for the PluginRender.
- Simplified the MarkdownEditor component's useEffect hooks for better clarity.
- Refactored command methods to utilize self-referencing for improved maintainability.
---
ui/src/components/Editor/MarkdownEditor.tsx | 9 +---
ui/src/components/Editor/index.tsx | 50 +++++++++++-----------
.../components/Editor/utils/codemirror/commands.ts | 43 ++++++++++---------
3 files changed, 48 insertions(+), 54 deletions(-)
diff --git a/ui/src/components/Editor/MarkdownEditor.tsx b/ui/src/components/Editor/MarkdownEditor.tsx
index 10145a22..fe5d8bb4 100644
--- a/ui/src/components/Editor/MarkdownEditor.tsx
+++ b/ui/src/components/Editor/MarkdownEditor.tsx
@@ -55,30 +55,25 @@ const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
autoFocus,
});
- // 初始化内容(只在编辑器创建时执行)
useEffect(() => {
if (!editor) {
return;
}
- // 初始化编辑器内容
editor.setValue(value || '');
lastSyncedValueRef.current = value || '';
onEditorReady?.(editor);
- }, [editor]); // 只在编辑器创建时执行
+ }, [editor]);
- // 当外部 value 变化时更新(但不是用户输入导致的)
useEffect(() => {
if (!editor) {
return;
}
- // 如果 value 和 lastSyncedValueRef 相同,说明是用户输入导致的更新,跳过
if (value === lastSyncedValueRef.current) {
return;
}
- // 外部 value 真正变化,更新编辑器
const currentValue = editor.getValue();
if (currentValue !== value) {
editor.setValue(value || '');
@@ -86,11 +81,9 @@ const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
}
}, [editor, value]);
- // 清理:组件卸载时销毁编辑器
useEffect(() => {
return () => {
if (editor) {
- // CodeMirror EditorView 有 destroy 方法
const view = editor as unknown as EditorView;
if (view.destroy) {
view.destroy();
diff --git a/ui/src/components/Editor/index.tsx b/ui/src/components/Editor/index.tsx
index a914d912..94bd5836 100644
--- a/ui/src/components/Editor/index.tsx
+++ b/ui/src/components/Editor/index.tsx
@@ -123,32 +123,30 @@ const MDEditor: ForwardRefRenderFunction<EditorRef, Props> = (
<div className={classNames('md-editor-wrap rounded', className)}>
<div className="toolbar-wrap px-3 d-flex align-items-center flex-wrap">
<EditorContext.Provider value={currentEditor}>
- {currentEditor && (
- <PluginRender
- type={PluginType.Editor}
- className="d-flex align-items-center flex-wrap"
- editor={currentEditor}
- previewElement={previewRef.current?.element}>
- <Heading />
- <Bold />
- <Italice />
- <div className="toolbar-divider" />
- <Code />
- <LinkItem />
- <BlockQuote />
- <Image />
- <File />
- <Table />
- <div className="toolbar-divider" />
- <OL />
- <UL />
- <Indent />
- <Outdent />
- <Hr />
- <div className="toolbar-divider" />
- <Help />
- </PluginRender>
- )}
+ <PluginRender
+ type={PluginType.Editor}
+ className="d-flex align-items-center flex-wrap"
+ editor={currentEditor}
+ previewElement={previewRef.current?.element}>
+ <Heading />
+ <Bold />
+ <Italice />
+ <div className="toolbar-divider" />
+ <Code />
+ <LinkItem />
+ <BlockQuote />
+ <Image />
+ <File />
+ <Table />
+ <div className="toolbar-divider" />
+ <OL />
+ <UL />
+ <Indent />
+ <Outdent />
+ <Hr />
+ <div className="toolbar-divider" />
+ <Help />
+ </PluginRender>
</EditorContext.Provider>
<div className="btn-group ms-auto" role="group">
diff --git a/ui/src/components/Editor/utils/codemirror/commands.ts b/ui/src/components/Editor/utils/codemirror/commands.ts
index 59ab064e..152bc632 100644
--- a/ui/src/components/Editor/utils/codemirror/commands.ts
+++ b/ui/src/components/Editor/utils/codemirror/commands.ts
@@ -33,7 +33,8 @@ import { Editor, Level } from '../../types';
* @returns Object containing all command methods
*/
export function createCommandMethods(editor: Editor) {
- return {
+ // Create methods object that allows self-reference
+ const methods = {
wrapText: (before: string, after = before, defaultText) => {
const range = editor.state.selection.ranges[0];
const selectedText = editor.state.sliceDoc(range.from, range.to);
@@ -79,39 +80,39 @@ export function createCommandMethods(editor: Editor) {
},
insertBold: (text?: string) => {
- editor.wrapText('**', '**', text || 'bold text');
+ methods.wrapText('**', '**', text || 'bold text');
},
insertItalic: (text?: string) => {
- editor.wrapText('*', '*', text || 'italic text');
+ methods.wrapText('*', '*', text || 'italic text');
},
insertCode: (text?: string) => {
- editor.wrapText('`', '`', text || 'code');
+ methods.wrapText('`', '`', text || 'code');
},
insertStrikethrough: (text?: string) => {
- editor.wrapText('~~', '~~', text || 'strikethrough text');
+ methods.wrapText('~~', '~~', text || 'strikethrough text');
},
insertHeading: (level: Level, text?: string) => {
const headingText = '#'.repeat(level);
- editor.wrapText(`${headingText} `, '', text || 'heading');
+ methods.wrapText(`${headingText} `, '', text || 'heading');
},
insertBlockquote: (text?: string) => {
- editor.wrapText('> ', '', text || 'quote');
+ methods.wrapText('> ', '', text || 'quote');
},
insertCodeBlock: (language?: string, code?: string) => {
const lang = language || '';
const codeText = code || '';
const block = `\`\`\`${lang}\n${codeText}\n\`\`\``;
- editor.appendBlock(block);
+ methods.appendBlock(block);
},
insertHorizontalRule: () => {
- editor.appendBlock('---');
+ methods.appendBlock('---');
},
insertOrderedList: () => {
@@ -121,7 +122,7 @@ export function createCommandMethods(editor: Editor) {
if (/^\d+\.\s/.test(lineText)) {
return;
}
- editor.replaceLines((lineItem) => {
+ methods.replaceLines((lineItem) => {
if (lineItem.trim() === '') {
return lineItem;
}
@@ -136,7 +137,7 @@ export function createCommandMethods(editor: Editor) {
if (/^[-*+]\s/.test(lineText)) {
return;
}
- editor.replaceLines((lineItem) => {
+ methods.replaceLines((lineItem) => {
if (lineItem.trim() === '') {
return lineItem;
}
@@ -149,11 +150,11 @@ export function createCommandMethods(editor: Editor) {
const line = editor.state.doc.line(cursor.line);
const lineText = line.text.trim();
if (/^\d+\.\s/.test(lineText)) {
- editor.replaceLines((lineItem) => {
+ methods.replaceLines((lineItem) => {
return lineItem.replace(/^\d+\.\s/, '');
});
} else {
- editor.insertOrderedList();
+ methods.insertOrderedList();
}
},
@@ -162,22 +163,22 @@ export function createCommandMethods(editor: Editor) {
const line = editor.state.doc.line(cursor.line);
const lineText = line.text.trim();
if (/^[-*+]\s/.test(lineText)) {
- editor.replaceLines((lineItem) => {
+ methods.replaceLines((lineItem) => {
return lineItem.replace(/^[-*+]\s/, '');
});
} else {
- editor.insertUnorderedList();
+ methods.insertUnorderedList();
}
},
insertLink: (url: string, text?: string) => {
const linkText = text || url;
- editor.wrapText('[', `](${url})`, linkText);
+ methods.wrapText('[', `](${url})`, linkText);
},
insertImage: (url: string, alt?: string) => {
const altText = alt || '';
- editor.wrapText('`, altText);
+ methods.wrapText('`, altText);
},
insertTable: (rows = 3, cols = 3) => {
@@ -192,11 +193,11 @@ export function createCommandMethods(editor: Editor) {
table.push(`| ${'---'.repeat(cols).split('').join(' | ')} |`);
}
}
- editor.appendBlock(table.join('\n'));
+ methods.appendBlock(table.join('\n'));
},
indent: () => {
- editor.replaceLines((line) => {
+ methods.replaceLines((line) => {
if (line.trim() === '') {
return line;
}
@@ -205,7 +206,7 @@ export function createCommandMethods(editor: Editor) {
},
outdent: () => {
- editor.replaceLines((line) => {
+ methods.replaceLines((line) => {
if (line.trim() === '') {
return line;
}
@@ -261,4 +262,6 @@ export function createCommandMethods(editor: Editor) {
return /^[-*+]\s/.test(lineText);
},
};
+
+ return methods;
}