@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
JavaScript
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