in packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts [300:392]
private onKeyUpDomEvent(event: PluginKeyboardEvent) {
if (this.isSuggesting) {
// Word before cursor represents the text prior to the cursor, up to and including the trigger symbol.
const wordBeforeCursor = this.getWord(event);
const wordBeforeCursorWithoutTriggerChar = wordBeforeCursor.substring(1);
const trimmedWordBeforeCursor = wordBeforeCursorWithoutTriggerChar.trim();
// If we hit a case where wordBeforeCursor is just the trigger character,
// that means we've gotten a onKeyUp event right after it's been typed.
// Otherwise, update the query string when:
// 1. There's an actual value
// 2. That actual value isn't just pure whitespace
// 3. That actual value isn't more than 4 words long (at which point we assume the person kept typing)
// Otherwise, we want to dismiss the picker plugin's UX.
if (
wordBeforeCursor == this.pickerOptions.triggerCharacter ||
(trimmedWordBeforeCursor &&
trimmedWordBeforeCursor.length > 0 &&
trimmedWordBeforeCursor.split(' ').length <= 4)
) {
this.dataProvider.queryStringUpdated(
trimmedWordBeforeCursor,
wordBeforeCursorWithoutTriggerChar == trimmedWordBeforeCursor
);
this.setLastKnownRange(this.editor.getSelectionRange());
} else {
this.setIsSuggesting(false);
}
} else {
let wordBeforeCursor = this.getWordBeforeCursor(event);
if (!this.blockSuggestions) {
if (
wordBeforeCursor != null &&
wordBeforeCursor.split(' ').length <= 4 &&
wordBeforeCursor[0] == this.pickerOptions.triggerCharacter
) {
this.setIsSuggesting(true);
const wordBeforeCursorWithoutTriggerChar = wordBeforeCursor.substring(1);
let trimmedWordBeforeCursor = wordBeforeCursorWithoutTriggerChar.trim();
this.dataProvider.queryStringUpdated(
trimmedWordBeforeCursor,
wordBeforeCursorWithoutTriggerChar == trimmedWordBeforeCursor
);
this.setLastKnownRange(this.editor.getSelectionRange());
if (this.dataProvider.setCursorPoint) {
// Determine the bounding rectangle for the @mention
let searcher = this.editor.getContentSearcherOfCursor(event);
let rangeNode = this.editor.getDocument().createRange();
let nodeBeforeCursor = searcher.getInlineElementBefore().getContainerNode();
let rangeStartSuccessfullySet = this.setRangeStart(
rangeNode,
nodeBeforeCursor,
wordBeforeCursor
);
if (!rangeStartSuccessfullySet) {
// VSO 24891: Out of range error is occurring because nodeBeforeCursor
// is not including the trigger character. In this case, the node before
// the node before cursor is the trigger character, and this is where the range should start.
let nodeBeforeNodeBeforeCursor = nodeBeforeCursor.previousSibling;
this.setRangeStart(
rangeNode,
nodeBeforeNodeBeforeCursor,
this.pickerOptions.triggerCharacter
);
}
let rect = rangeNode.getBoundingClientRect();
// Safari's support for range.getBoundingClientRect is incomplete.
// We perform this check to fall back to getClientRects in case it's at the page origin.
if (rect.left == 0 && rect.bottom == 0 && rect.top == 0) {
rect = rangeNode.getClientRects()[0];
}
if (rect) {
rangeNode.detach();
// Display the @mention popup in the correct place
let targetPoint = { x: rect.left, y: (rect.bottom + rect.top) / 2 };
let bufferZone = (rect.bottom - rect.top) / 2;
this.dataProvider.setCursorPoint(targetPoint, bufferZone);
}
}
}
} else {
if (
wordBeforeCursor != null &&
wordBeforeCursor[0] != this.pickerOptions.triggerCharacter
) {
this.blockSuggestions = false;
}
}
}
}