UNPKG

@liveblocks/react-ui

Version:

A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.

116 lines (113 loc) 3.96 kB
import { Range, Editor, Element, Transforms } from 'slate'; import { getCharacterBefore, getCharacterAfter } from '../utils/get-character.js'; import { getMatchRange } from '../utils/get-match-range.js'; import { isEmptyString } from '../utils/is-empty-string.js'; import { isWhitespaceCharacter } from '../utils/is-whitespace-character.js'; const MENTION_CHARACTER = "@"; function getMentionDraftAtSelection(editor) { const { selection } = editor; if (!selection || !Range.isCollapsed(selection)) { return; } const match = getMatchRange(editor, selection, ["@"], { include: true, allowConsecutiveWhitespace: false, ignoreTerminator: (_, point) => { const characterBefore = getCharacterBefore(editor, point); if (characterBefore && !isWhitespaceCharacter(characterBefore.text)) { return true; } return false; } }); if (!match) { return; } const matchText = Editor.string(editor, match); if (!matchText.startsWith(MENTION_CHARACTER) || matchText.length > 1 && isWhitespaceCharacter(matchText[1])) { return; } return { range: match, text: matchText.substring(1) }; } function isComposerBodyMention(node) { return Element.isElement(node) && node.type === "mention"; } function insertMention(editor, userId) { const mention = { type: "mention", id: userId, children: [{ text: "" }] }; Transforms.insertNodes(editor, mention); Transforms.move(editor); const afterCharacter = editor.selection ? getCharacterAfter(editor, editor.selection) : void 0; if (!afterCharacter || afterCharacter.void) { Transforms.insertText(editor, " "); } else if (isEmptyString(afterCharacter.text)) { Transforms.move(editor); } } function insertMentionCharacter(editor) { if (!editor.selection) { return; } const beforeCharacter = getCharacterBefore(editor, editor.selection, { filterVoids: true }); const afterCharacter = getCharacterAfter(editor, editor.selection, { filterVoids: true }); const shouldInsertSpaceBefore = beforeCharacter && !isEmptyString(beforeCharacter.text); const shouldInsertSpaceAfter = afterCharacter && !isEmptyString(afterCharacter.text); if (!Range.isCollapsed(editor.selection)) { const text = (shouldInsertSpaceBefore ? " " : "") + MENTION_CHARACTER + (shouldInsertSpaceAfter ? " " : ""); editor.insertText(text); if (shouldInsertSpaceAfter) { Transforms.move(editor, { distance: 1, unit: "character", reverse: true }); } } else { const beforeText = (shouldInsertSpaceBefore ? " " : "") + MENTION_CHARACTER; editor.insertText(beforeText, { at: Range.start(editor.selection) }); if (shouldInsertSpaceAfter) { editor.insertText(" ", { at: Range.end(editor.selection) }); } Transforms.collapse(editor, { edge: "end" }); } } function withMentions(editor) { const { isInline, isVoid, markableVoid, deleteBackward } = editor; editor.isInline = (element) => { return isComposerBodyMention(element) || isInline(element); }; editor.isVoid = (element) => { return isComposerBodyMention(element) || isVoid(element); }; editor.markableVoid = (element) => { return isComposerBodyMention(element) || markableVoid(element); }; editor.deleteBackward = (unit) => { const { selection } = editor; if (selection && Range.isCollapsed(selection)) { const [mention] = Editor.nodes(editor, { at: unit === "character" ? Editor.before(editor, selection, { unit: "character" }) : selection, match: isComposerBodyMention }); deleteBackward(unit); if (mention) { Transforms.insertText(editor, MENTION_CHARACTER); } } else { deleteBackward(unit); } }; return editor; } export { MENTION_CHARACTER, getMentionDraftAtSelection, insertMention, insertMentionCharacter, isComposerBodyMention, withMentions }; //# sourceMappingURL=mentions.js.map