@mdxeditor/editor
Version:
React component for rich text markdown editing
211 lines (210 loc) • 7.36 kB
JavaScript
import { $addUpdateTag, $getNodeByKey, createEditor, $getRoot, FOCUS_COMMAND, COMMAND_PRIORITY_LOW, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_HIGH, KEY_BACKSPACE_COMMAND, COMMAND_PRIORITY_CRITICAL } from "lexical";
import React__default from "react";
import { NESTED_EDITOR_UPDATED_COMMAND, rootEditor$, importVisitors$, exportVisitors$, usedLexicalNodes$, jsxComponentDescriptors$, directiveDescriptors$, codeBlockEditorDescriptors$, jsxIsAvailable$, nestedEditorChildren$, editorInFocus$ } from "./index.js";
import { ContentEditable } from "@lexical/react/LexicalContentEditable.js";
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary.js";
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer.js";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin.js";
import classNames from "classnames";
import { lexicalTheme } from "../../styles/lexicalTheme.js";
import { exportLexicalTreeToMdast } from "../../exportMarkdownFromLexical.js";
import { importMdastTreeToLexical } from "../../importMarkdownToLexical.js";
import styles from "../../styles/ui.module.css.js";
import { SharedHistoryPlugin } from "./SharedHistoryPlugin.js";
import { mergeRegister } from "@lexical/utils";
import { isPartOftheEditorUI } from "../../utils/isPartOftheEditorUI.js";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
const NestedEditorsContext = React__default.createContext(void 0);
function useNestedEditorContext() {
const context = React__default.useContext(NestedEditorsContext);
if (!context) {
throw new Error("useNestedEditor must be used within a NestedEditorsProvider");
}
return context;
}
function useMdastNodeUpdater() {
const { parentEditor, mdastNode, lexicalNode } = useNestedEditorContext();
return function updateMdastNode(node) {
parentEditor.update(
() => {
$addUpdateTag("history-push");
const currentNode = $getNodeByKey(lexicalNode.getKey());
if (currentNode) {
currentNode.setMdastNode({ ...mdastNode, ...node });
}
},
{ discrete: true }
);
parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
};
}
function useLexicalNodeRemove() {
const { parentEditor, lexicalNode } = useNestedEditorContext();
return () => {
parentEditor.update(() => {
const node = $getNodeByKey(lexicalNode.getKey());
node.selectNext();
node.remove();
});
};
}
const NestedLexicalEditor = function(props) {
const { getContent, getUpdatedMdastNode, contentEditableProps, block = false } = props;
const { mdastNode, lexicalNode, focusEmitter } = useNestedEditorContext();
const updateMdastNode = useMdastNodeUpdater();
const removeNode = useLexicalNodeRemove();
const content = getContent(mdastNode);
const [
rootEditor,
importVisitors,
exportVisitors,
usedLexicalNodes,
jsxComponentDescriptors,
directiveDescriptors,
codeBlockEditorDescriptors,
jsxIsAvailable,
nestedEditorChildren
] = useCellValues(
rootEditor$,
importVisitors$,
exportVisitors$,
usedLexicalNodes$,
jsxComponentDescriptors$,
directiveDescriptors$,
codeBlockEditorDescriptors$,
jsxIsAvailable$,
nestedEditorChildren$
);
const setEditorInFocus = usePublisher(editorInFocus$);
const [editor] = React__default.useState(() => {
const editor2 = createEditor({
nodes: usedLexicalNodes,
theme: lexicalTheme
});
return editor2;
});
React__default.useEffect(() => {
focusEmitter.subscribe(() => {
editor.focus();
});
}, [editor, focusEmitter]);
React__default.useEffect(() => {
editor.update(() => {
$getRoot().clear();
let theContent = content;
if (block) {
if (theContent.length === 0) {
theContent = [{ type: "paragraph", children: [] }];
}
} else {
theContent = [{ type: "paragraph", children: content }];
}
importMdastTreeToLexical({
root: $getRoot(),
mdastRoot: {
type: "root",
children: theContent
},
visitors: importVisitors,
directiveDescriptors,
codeBlockEditorDescriptors,
jsxComponentDescriptors
});
});
}, [editor, block, importVisitors]);
React__default.useEffect(() => {
function updateParentNode() {
editor.getEditorState().read(() => {
const mdast = exportLexicalTreeToMdast({
root: $getRoot(),
visitors: exportVisitors,
jsxComponentDescriptors,
jsxIsAvailable,
addImportStatements: false
});
const content2 = block ? mdast.children : mdast.children[0].children;
updateMdastNode(getUpdatedMdastNode(structuredClone(mdastNode), content2));
});
}
return mergeRegister(
editor.registerCommand(
FOCUS_COMMAND,
() => {
setEditorInFocus({ editorType: "lexical", rootNode: lexicalNode });
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
BLUR_COMMAND,
(payload) => {
const relatedTarget = payload.relatedTarget;
if (isPartOftheEditorUI(relatedTarget, rootEditor.getRootElement())) {
return false;
}
updateParentNode();
setEditorInFocus(null);
return true;
},
COMMAND_PRIORITY_EDITOR
),
// triggered by codemirror
editor.registerCommand(
NESTED_EDITOR_UPDATED_COMMAND,
() => {
updateParentNode();
return true;
},
COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
SELECTION_CHANGE_COMMAND,
() => {
setEditorInFocus({ editorType: "lexical", rootNode: lexicalNode });
return false;
},
COMMAND_PRIORITY_HIGH
),
editor.registerCommand(
KEY_BACKSPACE_COMMAND,
(_, editor2) => {
const editorElement = editor2.getRootElement();
if ((editorElement == null ? void 0 : editorElement.innerText) === "\n") {
removeNode();
return true;
}
return false;
},
COMMAND_PRIORITY_CRITICAL
)
);
}, [
block,
editor,
exportVisitors,
getUpdatedMdastNode,
jsxComponentDescriptors,
jsxIsAvailable,
lexicalNode,
mdastNode,
removeNode,
setEditorInFocus,
updateMdastNode,
rootEditor
]);
return /* @__PURE__ */ React__default.createElement(LexicalNestedComposer, { initialEditor: editor }, /* @__PURE__ */ React__default.createElement(
RichTextPlugin,
{
contentEditable: /* @__PURE__ */ React__default.createElement(ContentEditable, { ...contentEditableProps, className: classNames(styles.nestedEditor, contentEditableProps == null ? void 0 : contentEditableProps.className) }),
placeholder: null,
ErrorBoundary: LexicalErrorBoundary
}
), /* @__PURE__ */ React__default.createElement(SharedHistoryPlugin, null), nestedEditorChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })));
};
export {
NestedEditorsContext,
NestedLexicalEditor,
useLexicalNodeRemove,
useMdastNodeUpdater,
useNestedEditorContext
};