lexical-beautiful-mentions
Version:
A mentions plugin for the lexical text editor.
73 lines (72 loc) • 3.66 kB
JavaScript
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { mergeRegister } from "@lexical/utils";
import { $getRoot, $getSelection, $isDecoratorNode, $isRangeSelection, $nodesOfType, COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND, LineBreakNode, SELECTION_CHANGE_COMMAND, } from "lexical";
import { useEffect } from "react";
import { $createZeroWidthNode, $isZeroWidthNode, ZeroWidthNode, } from "./ZeroWidthNode";
export const ZERO_WIDTH_CHARACTER = ""; // 🚨 contains a zero-width space (U+200B)
/**
* This plugin serves as a patch to fix an incorrect cursor position in Safari.
* It also ensures that the cursor is correctly aligned with the line height in
* all browsers.
* {@link https://github.com/facebook/lexical/issues/4487}.
*
* @deprecated Use `PlaceholderPlugin` instead. This Plugin will be removed in a future version.
*/
export function ZeroWidthPlugin({ textContent }) {
const [editor] = useLexicalComposerContext();
useEffect(() => {
return mergeRegister(editor.registerUpdateListener(() => {
// add a zero-width space node at the end if the last node is a decorator node
editor.update(() => {
const root = $getRoot();
const last = root.getLastDescendant();
// add ZeroWidthNode at the end of the editor
if ($isDecoratorNode(last)) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
$nodesOfType(ZeroWidthNode).forEach((node) => {
node.remove();
}); // cleanup
last.insertAfter($createZeroWidthNode(textContent));
}
// add ZeroWidthNode before each line break
// eslint-disable-next-line @typescript-eslint/no-deprecated
$nodesOfType(LineBreakNode).forEach((node) => {
const prev = node.getPreviousSibling();
if ($isDecoratorNode(prev)) {
node.insertBefore($createZeroWidthNode(textContent));
}
});
},
// merge with previous history entry to allow undoing
{ tag: "history-merge" });
}), editor.registerCommand(KEY_DOWN_COMMAND, (event) => {
// prevent the unnecessary removal of the zero-width space, since this
// would lead to the insertion of another zero-width space and thus break
// undo with Ctrl+z
if (event.ctrlKey || event.metaKey || event.altKey) {
return false;
}
// remove the zero-width space if the user starts typing
const selection = $getSelection();
if ($isRangeSelection(selection)) {
const node = selection.anchor.getNode();
if ($isZeroWidthNode(node)) {
node.remove();
}
}
return false;
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
// select the previous node to avoid an error that occurs when the
// user tries to insert a node directly after the zero-width space
const selection = $getSelection();
if ($isRangeSelection(selection) && selection.isCollapsed()) {
const node = selection.anchor.getNode();
if ($isZeroWidthNode(node)) {
node.selectPrevious();
}
}
return false;
}, COMMAND_PRIORITY_HIGH));
}, [editor, textContent]);
return null;
}