UNPKG

@churchapps/apphelper-markdown

Version:
148 lines (147 loc) 5.3 kB
import { createCommand, $isElementNode, $getSelection, $applyNodeReplacement, $isRangeSelection } from "lexical"; import { LinkNode } from "@lexical/link"; import { addClassNamesToElement } from "@lexical/utils"; export class CustomLinkNode extends LinkNode { constructor(url, target, classNames, key) { super(url, { target }, key); this.__url = url || "https://"; this.__target = target || "_self"; this.__classNames = classNames || []; } static importJSON(serializedNode) { return super.importJSON(serializedNode); } exportJSON() { return super.exportJSON(); } static getType() { return "customlinknode"; } static clone(node) { const newLinkNode = new CustomLinkNode(node.__url, node.__target, node.__classNames, node.__key); return $applyNodeReplacement(newLinkNode); } createDOM() { const link = document.createElement("a"); link.href = this.__url; link.setAttribute("target", this.__target || "_blank"); addClassNamesToElement(link, (this.__classNames || []).join(" ")); return link; } updateDOM() { return false; } setClassNames(classNames) { const writable = this.getWritable(); writable.__classNames = classNames; } } export const TOGGLE_CUSTOM_LINK_NODE_COMMAND = createCommand(); export function $createCustomLinkNode(url, target, classNames) { return $applyNodeReplacement(new CustomLinkNode(url, target, classNames)); } export function $isCustomLinkNode(node) { return node instanceof LinkNode; } export const toggleCustomLinkNode = ({ url, target = "_blank", classNames = [], getNodeByKey }) => { const addAttributesToLinkNode = (linkNode, { url, target, classNames }) => { const dom = getNodeByKey(linkNode.getKey()); if (!dom) return; const uniqueClassNames = classNames; linkNode.setURL(url); linkNode.setTarget(target); linkNode.setClassNames(uniqueClassNames); dom.setAttribute("href", url); dom.setAttribute("target", target); dom.setAttribute("class", uniqueClassNames.join(" ")); }; const selection = $getSelection(); if (!$isRangeSelection(selection)) { return; } const nodes = selection.extract(); if (url === null) { // Remove LinkNodes nodes.forEach((node) => { const parent = node.getParent(); if ($isCustomLinkNode(parent)) { const children = parent.getChildren(); for (let i = 0; i < children.length; i++) { parent.insertBefore(children[i]); } parent.remove(); } }); } else { // Add or merge LinkNodes if (nodes.length === 1) { const firstNode = nodes[0]; // if the first node is a LinkNode or if its // parent is a LinkNode, we update the URL, target and rel. const linkNode = $isCustomLinkNode(firstNode) ? firstNode : $getLinkAncestor(firstNode); if (linkNode !== null && $isCustomLinkNode(linkNode)) { addAttributesToLinkNode(linkNode, { url, target, classNames }); return; } } let prevParent = null; let linkNode = null; nodes.forEach((node) => { const parent = node.getParent(); if (parent === linkNode || parent === null || ($isElementNode(node) && !node.isInline())) { return; } if ($isCustomLinkNode(parent)) { linkNode = parent; addAttributesToLinkNode(parent, { url, target, classNames }); return; } if (!parent.is(prevParent)) { prevParent = parent; linkNode = $createCustomLinkNode(url, target, classNames); if ($isCustomLinkNode(parent)) { if (node.getPreviousSibling() === null) { parent.insertBefore(linkNode); } else { parent.insertAfter(linkNode); } } else { node.insertBefore(linkNode); } } if ($isCustomLinkNode(node)) { if (node.is(linkNode)) { return; } if (linkNode !== null) { const children = node.getChildren(); for (let i = 0; i < children.length; i++) { linkNode.append(children[i]); } } node.remove(); return; } if (linkNode !== null) { linkNode.append(node); } }); } }; const $getLinkAncestor = (node) => ($getAncestor(node, (ancestor) => $isCustomLinkNode(ancestor))); const $getAncestor = (node, predicate) => { let parent = node; while (parent !== null && (parent = parent.getParent()) !== null && !predicate(parent)) ; return parent; };