UNPKG

@mdxeditor/editor

Version:

React component for rich text markdown editing

184 lines (183 loc) 9.15 kB
import { usePublisher, useCellValue, useCellValues, useRealm } from "@mdxeditor/gurx"; import React__default from "react"; import { corePlugin, editorRootElementRef$, editorWrapperElementRef$, rootEditor$, useTranslation, contentEditableRef$, contentEditableWrapperElement$, contentEditableClassName$, spellCheck$, composerChildren$, topAreaChildren$, editorWrappers$, placeholder$, bottomAreaChildren$, viewMode$, activeEditor$, exportVisitors$, toMarkdownExtensions$, toMarkdownOptions$, jsxComponentDescriptors$, jsxIsAvailable$, insertMarkdown$, setMarkdown$, markdownSourceEditorValue$, markdown$ } from "./plugins/core/index.js"; import { RealmWithPlugins } from "./RealmWithPlugins.js"; import { createLexicalComposerContext, LexicalComposerContext } from "@lexical/react/LexicalComposerContext"; import { ContentEditable } from "@lexical/react/LexicalContentEditable"; import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"; import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; import classNames from "classnames"; import { defaultSvgIcons } from "./defaultSvgIcons.js"; import { lexicalTheme } from "./styles/lexicalTheme.js"; import styles from "./styles/ui.module.css.js"; import { noop } from "./utils/fp.js"; import { getSelectionAsMarkdown } from "./utils/lexicalHelpers.js"; const LexicalProvider = ({ children }) => { const rootEditor = useCellValue(rootEditor$); const composerContextValue = React__default.useMemo(() => { return [rootEditor, createLexicalComposerContext(null, lexicalTheme)]; }, [rootEditor]); return /* @__PURE__ */ React__default.createElement(LexicalComposerContext.Provider, { value: composerContextValue }, children); }; const RichTextEditor = () => { const t = useTranslation(); const setContentEditableRef = usePublisher(contentEditableRef$); const setEditorRootWrapperElement = usePublisher(contentEditableWrapperElement$); const onRef = (el) => { setEditorRootWrapperElement(el); setContentEditableRef({ current: el }); }; const [contentEditableClassName, spellCheck, composerChildren, topAreaChildren, editorWrappers, placeholder, bottomAreaChildren] = useCellValues( contentEditableClassName$, spellCheck$, composerChildren$, topAreaChildren$, editorWrappers$, placeholder$, bottomAreaChildren$ ); return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, topAreaChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })), /* @__PURE__ */ React__default.createElement(RenderRecursiveWrappers, { wrappers: editorWrappers }, /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.rootContentEditableWrapper, "mdxeditor-root-contenteditable") }, /* @__PURE__ */ React__default.createElement( RichTextPlugin, { contentEditable: /* @__PURE__ */ React__default.createElement("div", { ref: onRef }, /* @__PURE__ */ React__default.createElement( ContentEditable, { className: classNames(styles.contentEditable, contentEditableClassName), ariaLabel: t("contentArea.editableMarkdown", "editable markdown"), spellCheck } )), placeholder: /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.contentEditable, styles.placeholder, contentEditableClassName) }, /* @__PURE__ */ React__default.createElement("p", null, placeholder)), ErrorBoundary: LexicalErrorBoundary } ))), composerChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })), bottomAreaChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index }))); }; const DEFAULT_MARKDOWN_OPTIONS = { listItemIndent: "one" }; const defaultIconComponentFor = (name) => { return defaultSvgIcons[name]; }; function defaultTranslation(key, defaultValue, interpolations = {}) { let value = defaultValue; for (const [k, v] of Object.entries(interpolations)) { value = value.replaceAll(`{{${k}}}`, String(v)); } return value; } const RenderRecursiveWrappers = ({ wrappers, children }) => { if (wrappers.length === 0) { return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children); } const Wrapper = wrappers[0]; return /* @__PURE__ */ React__default.createElement(Wrapper, null, /* @__PURE__ */ React__default.createElement(RenderRecursiveWrappers, { wrappers: wrappers.slice(1) }, children)); }; const EditorRootElement = ({ children, className, overlayContainer }) => { const editorRootElementRef = React__default.useRef(null); const wrapperElementRef = React__default.useRef(null); const setEditorRootElementRef = usePublisher(editorRootElementRef$); const setEditorWrapperElementRef = usePublisher(editorWrapperElementRef$); React__default.useEffect(() => { const popupContainer = document.createElement("div"); popupContainer.classList.add( "mdxeditor-popup-container", styles.editorRoot, styles.popupContainer, ...(className ?? "").trim().split(" ").filter(Boolean) ); const container = overlayContainer ?? document.body; container.appendChild(popupContainer); editorRootElementRef.current = popupContainer; setEditorRootElementRef(editorRootElementRef); setEditorWrapperElementRef(wrapperElementRef); return () => { popupContainer.remove(); }; }, [className, editorRootElementRef, overlayContainer, setEditorRootElementRef, setEditorWrapperElementRef]); return /* @__PURE__ */ React__default.createElement("div", { className: classNames("mdxeditor", styles.editorRoot, styles.editorWrapper, className), ref: wrapperElementRef }, children); }; const Methods = ({ mdxRef }) => { const realm = useRealm(); React__default.useImperativeHandle( mdxRef, () => { return { getMarkdown: () => { const viewMode = realm.getValue(viewMode$); if (viewMode === "source" || viewMode === "diff") { return realm.getValue(markdownSourceEditorValue$); } return realm.getValue(markdown$); }, setMarkdown: (markdown) => { realm.pub(setMarkdown$, markdown); }, insertMarkdown: (markdown) => { realm.pub(insertMarkdown$, markdown); }, focus: (callbackFn, opts) => { var _a; (_a = realm.getValue(rootEditor$)) == null ? void 0 : _a.focus(callbackFn, opts); }, getContentEditableHTML: () => { var _a, _b; return ((_b = (_a = realm.getValue(contentEditableRef$)) == null ? void 0 : _a.current) == null ? void 0 : _b.innerHTML) ?? ""; }, getSelectionMarkdown: () => { const viewMode = realm.getValue(viewMode$); if (viewMode === "source" || viewMode === "diff") { return ""; } const activeEditor = realm.getValue(activeEditor$); if (!activeEditor) { return ""; } realm.getValue(exportVisitors$); realm.getValue(toMarkdownExtensions$); realm.getValue(toMarkdownOptions$); realm.getValue(jsxComponentDescriptors$); realm.getValue(jsxIsAvailable$); return getSelectionAsMarkdown(activeEditor); } }; }, [realm] ); return null; }; const MDXEditor = React__default.forwardRef((props, ref) => { return /* @__PURE__ */ React__default.createElement( RealmWithPlugins, { plugins: [ corePlugin({ contentEditableClassName: props.contentEditableClassName ?? "", spellCheck: props.spellCheck ?? true, initialMarkdown: props.markdown, onChange: props.onChange ?? noop, onBlur: props.onBlur ?? noop, toMarkdownOptions: props.toMarkdownOptions ?? DEFAULT_MARKDOWN_OPTIONS, autoFocus: props.autoFocus ?? false, placeholder: props.placeholder ?? "", readOnly: Boolean(props.readOnly), iconComponentFor: props.iconComponentFor ?? defaultIconComponentFor, suppressHtmlProcessing: props.suppressHtmlProcessing ?? false, onError: props.onError ?? noop, translation: props.translation ?? defaultTranslation, trim: props.trim ?? true, lexicalTheme: props.lexicalTheme, ..."editorState" in props ? { editorState: props.editorState } : {}, suppressSharedHistory: props.suppressSharedHistory ?? false, additionalLexicalNodes: props.additionalLexicalNodes ?? [], lexicalEditorNamespace: props.lexicalEditorNamespace ?? "MDXEditor" }), ...props.plugins ?? [] ] }, /* @__PURE__ */ React__default.createElement(EditorRootElement, { className: props.className, overlayContainer: props.overlayContainer }, /* @__PURE__ */ React__default.createElement(LexicalProvider, null, /* @__PURE__ */ React__default.createElement(RichTextEditor, null))), /* @__PURE__ */ React__default.createElement(Methods, { mdxRef: ref }) ); }); export { MDXEditor };