import { memo, useMemo, useState } from "react"; import ToolbarPlugin from "@/components/ui/markdown/plugins/toolbar-plugin"; import { MarkdownEditorTheme } from "@/components/ui/markdown/theme"; import { CodeHighlightNode, CodeNode, registerCodeHighlighting, } from "@lexical/code"; import { LinkNode } from "@lexical/link"; import { ListItemNode, ListNode } from "@lexical/list"; import { $convertFromMarkdownString, $convertToMarkdownString, TRANSFORMERS, } from "@lexical/markdown"; import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin"; import { InitialConfigType, LexicalComposer, } from "@lexical/react/LexicalComposer"; import { ContentEditable } from "@lexical/react/LexicalContentEditable"; import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"; import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode"; import { ListPlugin } from "@lexical/react/LexicalListPlugin"; import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin"; import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"; import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin"; import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin"; import { HeadingNode, QuoteNode } from "@lexical/rich-text"; import { $getRoot, EditorState, LexicalEditor } from "lexical"; function onError(error: Error) { console.error(error); } const EDITOR_NODES = [ HeadingNode, ListNode, ListItemNode, QuoteNode, LinkNode, CodeNode, HorizontalRuleNode, CodeHighlightNode, ]; interface MarkdownEditorProps { children: string; onSave?: (markdown: string) => void; isSaving?: boolean; } const MarkdownEditor = memo( ({ children: initialMarkdown, onSave, isSaving }: MarkdownEditorProps) => { const [isRawMarkdownMode, setIsRawMarkdownMode] = useState(false); const [rawMarkdown, setRawMarkdown] = useState(initialMarkdown); const initialConfig: InitialConfigType = useMemo( () => ({ namespace: "editor", onError, theme: MarkdownEditorTheme, nodes: EDITOR_NODES, editorState: (editor: LexicalEditor) => { registerCodeHighlighting(editor); $convertFromMarkdownString(initialMarkdown, TRANSFORMERS); }, }), [initialMarkdown], ); const handleOnChange = (editorState: EditorState) => { editorState.read(() => { let markdownString; if (isRawMarkdownMode) { markdownString = $getRoot()?.getFirstChild()?.getTextContent() ?? ""; } else { markdownString = $convertToMarkdownString(TRANSFORMERS); } setRawMarkdown(markdownString); }); }; return (
onSave(rawMarkdown))} isSaving={!!isSaving} /> {isRawMarkdownMode ? ( } ErrorBoundary={LexicalErrorBoundary} /> ) : ( } ErrorBoundary={LexicalErrorBoundary} /> )}
); }, ); // needed for linter because of memo MarkdownEditor.displayName = "MarkdownEditor"; export default MarkdownEditor;