import { useEffect, useState, useRef, useCallback } from "react"; import { createPortal } from "react-dom"; import { CAN_REDO_COMMAND, CAN_UNDO_COMMAND, REDO_COMMAND, UNDO_COMMAND, SELECTION_CHANGE_COMMAND, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, $getSelection, $isRangeSelection, $createParagraphNode, $getNodeByKey, RangeSelection, NodeSelection, GridSelection, } from "lexical"; import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST_COMMAND, $isListNode, ListNode, } from "@lexical/list"; import { $isLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link"; import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; import { $isParentElementRTL, $wrapNodes, $isAtNodeEnd, } from "@lexical/selection"; import { $getNearestNodeOfType, mergeRegister } from "@lexical/utils"; import { $createHeadingNode, $createQuoteNode, $isHeadingNode, } from "@lexical/rich-text"; // custom elements import { FloatingLinkEditor } from "./floating-link-editor"; import { BlockTypeSelect } from "./block-type-select"; const LowPriority = 1; function getSelectedNode(selection: any) { 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) ? focusNode : anchorNode; } } export const LexicalToolbar = () => { // editor const [editor] = useLexicalComposerContext(); // ref const toolbarRef = useRef(null); // states const [canUndo, setCanUndo] = useState(false); const [canRedo, setCanRedo] = useState(false); const [blockType, setBlockType] = useState("paragraph"); const [selectedElementKey, setSelectedElementKey] = useState( null ); const [isRTL, setIsRTL] = useState(false); const [isLink, setIsLink] = useState(false); const [isBold, setIsBold] = useState(false); const [isItalic, setIsItalic] = useState(false); const [isUnderline, setIsUnderline] = useState(false); const [isStrikethrough, setIsStrikethrough] = useState(false); const [isCode, setIsCode] = useState(false); const updateToolbar = useCallback(() => { const selection = $getSelection(); if ($isRangeSelection(selection)) { const anchorNode = selection.anchor.getNode(); const element = anchorNode.getKey() === "root" ? anchorNode : anchorNode.getTopLevelElementOrThrow(); const elementKey = element.getKey(); const elementDOM = editor.getElementByKey(elementKey); if (elementDOM !== null) { setSelectedElementKey(elementKey); if ($isListNode(element)) { const parentList = $getNearestNodeOfType(anchorNode, ListNode); const type = parentList ? parentList.getTag() : element.getTag(); setBlockType(type); } else { const type = $isHeadingNode(element) ? element.getTag() : element.getType(); setBlockType(type); } } // Update text format setIsBold(selection.hasFormat("bold")); setIsItalic(selection.hasFormat("italic")); setIsUnderline(selection.hasFormat("underline")); setIsStrikethrough(selection.hasFormat("strikethrough")); setIsRTL($isParentElementRTL(selection)); // Update links const node = getSelectedNode(selection); const parent = node.getParent(); if ($isLinkNode(parent) || $isLinkNode(node)) { setIsLink(true); } else { setIsLink(false); } } }, [editor]); useEffect(() => { return mergeRegister( editor.registerUpdateListener(({ editorState }) => { editorState.read(() => { updateToolbar(); }); }), editor.registerCommand( SELECTION_CHANGE_COMMAND, (_payload, newEditor) => { updateToolbar(); return false; }, LowPriority ), editor.registerCommand( CAN_UNDO_COMMAND, (payload) => { setCanUndo(payload); return false; }, LowPriority ), editor.registerCommand( CAN_REDO_COMMAND, (payload) => { setCanRedo(payload); return false; }, LowPriority ) ); }, [editor, updateToolbar]); const insertLink = useCallback( (e: any) => { e.preventDefault(); if (!isLink) { editor.dispatchCommand(TOGGLE_LINK_COMMAND, "https://"); } else { editor.dispatchCommand(TOGGLE_LINK_COMMAND, null); } }, [editor, isLink] ); return (
{isLink && createPortal(, document.body)} {" "}
); };