@mdxeditor/editor
Version:
React component for rich text markdown editing
160 lines (159 loc) • 5.58 kB
JavaScript
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
};