kabulmark
Version:
A React-based rich text editor built as a wrapper over Meta's Lexical library.
104 lines (103 loc) • 4.25 kB
JavaScript
import { $createLinkNode } from "@lexical/link";
import { $createListItemNode, $createListNode } from "@lexical/list";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $createHeadingNode, $createQuoteNode } from "@lexical/rich-text";
import { $createParagraphNode, $createTextNode, $getRoot, $isElementNode } from "lexical";
import { useEffect, useRef } from "react";
function generateNodesFromDOM(nodes, editor, format = 0) {
const lexicalNodes = [];
for (const domNode of nodes) {
if (domNode.nodeType === Node.TEXT_NODE) {
const textNode = $createTextNode(domNode.data || "");
textNode.setFormat(format);
lexicalNodes.push(textNode);
}
else if (domNode.nodeType === Node.ELEMENT_NODE) {
const element = domNode;
const tag = element.tagName.toLowerCase();
let newFormat = format;
if (["strong", "b"].indexOf(tag) !== -1)
newFormat |= 1; // bold
if (["em", "i"].indexOf(tag) !== -1)
newFormat |= 2; // italic
if (["u"].indexOf(tag) !== -1)
newFormat |= 8; // underline
if (["strong", "b", "em", "i", "u", "span"].indexOf(tag) !== -1) {
lexicalNodes.push(...generateNodesFromDOM(element.childNodes, editor, newFormat));
continue;
}
if (tag === "a") {
const linkNode = $createLinkNode(element.getAttribute("href") || "", {
target: element.getAttribute("target") || null
});
linkNode.append(...generateNodesFromDOM(element.childNodes, editor, newFormat));
lexicalNodes.push(linkNode);
continue;
}
let blockNode = null;
if (["p", "div"].indexOf(tag) !== -1) {
blockNode = $createParagraphNode();
}
else if (/^h[1-6]$/.test(tag)) {
blockNode = $createHeadingNode(tag);
}
else if (tag === "blockquote") {
blockNode = $createQuoteNode();
}
else if (tag === "ul") {
blockNode = $createListNode("bullet");
}
else if (tag === "ol") {
blockNode = $createListNode("number");
}
else if (tag === "li") {
blockNode = $createListItemNode();
}
else {
// Unknown tag, recurse
lexicalNodes.push(...generateNodesFromDOM(element.childNodes, editor, newFormat));
continue;
}
const childNodes = generateNodesFromDOM(element.childNodes, editor, 0); // reset format for blocks
blockNode.append(...childNodes);
lexicalNodes.push(blockNode);
}
}
return lexicalNodes;
}
export default function InitialContentPlugin({ initialHtml }) {
const [editor] = useLexicalComposerContext();
const isSet = useRef(false);
useEffect(() => {
if (!initialHtml || isSet.current)
return;
isSet.current = true;
editor.update(() => {
const root = $getRoot();
root.clear();
const parser = new DOMParser();
const dom = parser.parseFromString(initialHtml, "text/html");
const nodes = generateNodesFromDOM(dom.body.childNodes, editor);
let currentParagraph = null;
for (const node of nodes) {
if ($isElementNode(node) && !node.isInline()) {
if (currentParagraph) {
root.append(currentParagraph);
currentParagraph = null;
}
root.append(node);
}
else {
if (!currentParagraph) {
currentParagraph = $createParagraphNode();
}
currentParagraph.append(node);
}
}
if (currentParagraph) {
root.append(currentParagraph);
}
});
}, [editor, initialHtml]);
return null;
}