UNPKG

@mdxeditor/editor

Version:

React component for rich text markdown editing

160 lines (159 loc) 5.58 kB
import { $getSelection, $getRoot, $isRangeSelection, $isElementNode, $isTextNode } from "lexical"; import { $isLinkNode } from "@lexical/link"; import { $isHeadingNode } from "@lexical/rich-text"; import { $isListItemNode, $isListNode } from "@lexical/list"; import { $isAtNodeEnd } from "@lexical/selection"; import { tap } from "./fp.js"; import { exportMarkdownFromLexical } from "../exportMarkdownFromLexical.js"; function fromWithinEditorRead(editor, fn) { let result = null; editor.getEditorState().read(() => { result = fn(); }); return result; } function getSelectedNode(selection) { try { const anchor = selection.anchor; const focus = selection.focus; const anchorNode = selection.anchor.getNode(); const focusNode = selection.focus.getNode(); if (anchorNode === focusNode) { return anchorNode; } const isBackward = selection.isBackward(); if (isBackward) { return $isAtNodeEnd(focus) ? anchorNode : focusNode; } else { return $isAtNodeEnd(anchor) ? anchorNode : focusNode; } } catch (e) { return null; } } function getSelectionRectangle(editor) { const selection = $getSelection(); const nativeSelection = window.getSelection(); const activeElement = document.activeElement; const rootElement = editor.getRootElement(); if (selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) { const domRange = nativeSelection.getRangeAt(0); let rect; if (nativeSelection.isCollapsed) { let node = nativeSelection.anchorNode; if ((node == null ? void 0 : node.nodeType) == 3) { node = node.parentNode; } rect = node.getBoundingClientRect(); rect.width = 0; } else { if (nativeSelection.anchorNode === rootElement) { let inner = rootElement; while (inner.firstElementChild != null) { inner = inner.firstElementChild; } rect = inner.getBoundingClientRect(); } else { rect = domRange.getBoundingClientRect(); } } return { top: Math.round(rect.top), left: Math.round(rect.left), width: Math.round(rect.width), height: Math.round(rect.height) }; } else if (!activeElement || activeElement.className !== "link-input") { return null; } return null; } function getStateAsMarkdown(editor, exportParams) { return tap({ markdown: "" }, (result) => { editor.getEditorState().read(() => { result.markdown = exportMarkdownFromLexical({ root: $getRoot(), ...exportParams }); }); }).markdown; } function getSelectionAsMarkdown(editor, _exportParams) { let markdown = ""; editor.getEditorState().read(() => { const selection = $getSelection(); if (!selection || !$isRangeSelection(selection) || selection.isCollapsed()) { return; } const nodes = selection.getNodes(); if (nodes.length === 0) { return; } const parentNodes = /* @__PURE__ */ new Set(); nodes.forEach((node) => { let current = node; while (current) { if ($isHeadingNode(current) || $isListItemNode(current) || current.getType() === "paragraph" || current.getType() === "quote") { if ($isElementNode(current)) { parentNodes.add(current); } break; } current = current.getParent(); } }); const nodesToProcess = parentNodes.size > 0 ? Array.from(parentNodes) : nodes; function nodeToMarkdown(node) { if ($isHeadingNode(node)) { const level = parseInt(node.getTag().replace("h", "")); const children = node.getChildren(); const headingText = children.map((child) => nodeToMarkdown(child)).join(""); return "#".repeat(level) + " " + headingText + "\n\n"; } else if ($isListItemNode(node)) { const parent = node.getParent(); const prefix = parent && $isListNode(parent) && parent.getListType() === "number" ? "1. " : "- "; const children = node.getChildren(); const itemText = children.map((child) => nodeToMarkdown(child)).join(""); return prefix + itemText + "\n"; } else if ($isListNode(node)) { const children = node.getChildren(); return children.map((child) => nodeToMarkdown(child)).join("") + "\n"; } else if ($isTextNode(node)) { let text = node.getTextContent(); const format = node.getFormat(); if (format & 16) { return `\`${text}\``; } if (format & 1) { text = `**${text}**`; } if (format & 2) { text = `*${text}*`; } if (format & 4) { text = `~~${text}~~`; } return text; } else if ($isLinkNode(node)) { const url = node.getURL(); const title = node.getTitle(); const children = node.getChildren(); const linkText = children.map((child) => nodeToMarkdown(child)).join(""); if (title) { return `[${linkText}](${url} "${title}")`; } return `[${linkText}](${url})`; } else if ($isElementNode(node)) { const children = node.getChildren(); return children.map((child) => nodeToMarkdown(child)).join(""); } return node.getTextContent(); } markdown = nodesToProcess.map((node) => nodeToMarkdown(node)).join(""); }); return markdown.trim(); } export { fromWithinEditorRead, getSelectedNode, getSelectionAsMarkdown, getSelectionRectangle, getStateAsMarkdown };