Made the emoji autocomplete aligned with the :word

This commit is contained in:
Alvaro 2022-08-18 09:02:13 -06:00 committed by GitHub
parent 3436283165
commit bb9a7113f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 5 deletions

View file

@ -6800,7 +6800,7 @@ button.module-image__border-overlay:focus {
padding: 0; padding: 0;
margin-bottom: 6px; margin-bottom: 6px;
border-radius: 8px; border-radius: 8px;
z-index: $z-index-popup; z-index: $z-index-above-popup;
overflow: hidden; overflow: hidden;
&--scroller { &--scroller {

View file

@ -4,11 +4,12 @@
import Quill from 'quill'; import Quill from 'quill';
import Delta from 'quill-delta'; import Delta from 'quill-delta';
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _, { isNumber } from 'lodash';
import { Popper } from 'react-popper'; import { Popper } from 'react-popper';
import classNames from 'classnames'; import classNames from 'classnames';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import type { VirtualElement } from '@popperjs/core';
import type { EmojiData } from '../../components/emoji/lib'; import type { EmojiData } from '../../components/emoji/lib';
import { import {
search, search,
@ -19,7 +20,7 @@ import {
import { Emoji } from '../../components/emoji/Emoji'; import { Emoji } from '../../components/emoji/Emoji';
import type { EmojiPickDataType } from '../../components/emoji/EmojiPicker'; import type { EmojiPickDataType } from '../../components/emoji/EmojiPicker';
import { getBlotTextPartitions, matchBlotTextPartitions } from '../util'; import { getBlotTextPartitions, matchBlotTextPartitions } from '../util';
import { sameWidthModifier } from '../../util/popperUtil'; import * as log from '../../logging/log';
const Keyboard = Quill.import('modules/keyboard'); const Keyboard = Quill.import('modules/keyboard');
@ -265,8 +266,47 @@ export class EmojiCompletion {
return; return;
} }
// a virtual reference to the text we are trying to auto-complete
const reference: VirtualElement = {
getBoundingClientRect() {
const selection = window.getSelection();
// there's a selection and at least one range
if (selection !== null && selection.rangeCount !== 0) {
// grab the first range, the one the user is actually on right now
// clone it so we don't actually modify the user's selection/caret position
const range = selection.getRangeAt(0).cloneRange();
// if for any reason the range is a selection (not just a caret)
// collapse it to just a caret, so we can walk it back to the :word
range.collapse(true);
// if we can, position the popper at the beginning of the emoji text (:word)
const endContainerTextContent = range.endContainer.textContent;
const startOfEmojiText = endContainerTextContent?.lastIndexOf(':');
if (
endContainerTextContent &&
isNumber(startOfEmojiText) &&
startOfEmojiText !== -1
) {
range.setStart(
range.endContainer,
range.endOffset -
(endContainerTextContent.length - startOfEmojiText)
);
} else {
log.warn(
`Could not find the beginning of the emoji word to be completed. startOfEmojiText=${startOfEmojiText}, endContainerTextContent.length=${endContainerTextContent?.length}, range.offsets=${range.startOffset}-${range.endOffset}`
);
}
return range.getClientRects()[0];
}
log.warn('No selection range when auto-completing emoji');
return new DOMRect(); // don't crash just because we couldn't get a rectangle
},
};
const element = createPortal( const element = createPortal(
<Popper placement="top-start" modifiers={[sameWidthModifier]}> <Popper placement="top-start" referenceElement={reference}>
{({ ref, style }) => ( {({ ref, style }) => (
<div <div
ref={ref} ref={ref}
@ -312,7 +352,7 @@ export class EmojiCompletion {
</div> </div>
)} )}
</Popper>, </Popper>,
this.root document.body
); );
this.options.setEmojiPickerElement(element); this.options.setEmojiPickerElement(element);