diff --git a/src/components/Popups/ReplyField/ReplyField.tsx b/src/components/Popups/ReplyField/ReplyField.tsx
index 48a4f33dc..72c6a1c79 100644
--- a/src/components/Popups/ReplyField/ReplyField.tsx
+++ b/src/components/Popups/ReplyField/ReplyField.tsx
@@ -5,11 +5,11 @@ import {
getActiveMentionForEditorState,
} from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector';
import fuzzySearch from 'box-ui-elements/es/utils/fuzzySearch';
-import { ContentState, Editor, EditorState, SelectionState } from 'draft-js';
+import { ContentState, DraftHandleValue, Editor, EditorState, SelectionState } from 'draft-js';
import PopupList from './PopupList';
import withMentionDecorator from './withMentionDecorator';
import { Collaborator } from '../../../@types';
-import { VirtualElement } from '../Popper';
+import { State as PopperState, VirtualElement } from '../Popper';
import './ReplyField.scss';
export type Mention = {
@@ -36,7 +36,6 @@ export type Props = {
export type State = {
activeItemIndex: number;
editorState: EditorState;
- popupItems: Collaborator[];
popupReference: VirtualElement | null;
};
@@ -51,7 +50,7 @@ export default class ReplyField extends React.Component
{
constructor(props: Props) {
super(props);
- const { value, collaborators, cursorPosition } = props;
+ const { value, cursorPosition } = props;
const contentState = ContentState.createFromText(value as string);
let prevEditorState = withMentionDecorator(EditorState.createWithContent(contentState));
let selectionState = prevEditorState.getSelection();
@@ -65,23 +64,28 @@ export default class ReplyField extends React.Component {
this.state = {
activeItemIndex: 0,
editorState: prevEditorState,
- popupItems: collaborators,
popupReference: null,
};
}
componentDidMount(): void {
this.focusEditor();
+ // restore PopupList if it was active
+ this.updatePopupReference();
}
componentWillUnmount(): void {
this.saveCursorPosition();
}
- getItemList = (mentionString: string): Collaborator[] => {
+ getCollaborators = (activeMention: Mention | null): Collaborator[] => {
const { collaborators } = this.props;
- const trimmedQuery = mentionString.trim();
+ if (!activeMention) {
+ return [];
+ }
+
+ const trimmedQuery = activeMention.mentionString.trim();
// fuzzySearch doesn't match anything if query length is less than 2
// Compared to empty list, full list has a better user experience
if (trimmedQuery.length < 2) {
@@ -128,19 +132,34 @@ export default class ReplyField extends React.Component {
};
};
+ updatePopupReference = (): void => {
+ const { editorState } = this.state;
+
+ const activeMention = getActiveMentionForEditorState(editorState);
+
+ this.setState({ popupReference: activeMention ? this.getVirtualElement(activeMention) : null });
+ };
+
+ handleFirstUpdate = (state: Partial): void => {
+ const { x, y } = state.rects?.reference || {};
+
+ // sometimes inner popper position is wrong due to parent popper position change, like zoom in/out
+ // the inner popper position should always be positive (bottom-right)
+ // so if position is negative after first positioning,
+ // we re-calculate virtualElement to re-position inner popper
+ if ((x && x < 0) || (y && y < 0)) {
+ this.updatePopupReference();
+ }
+ };
+
handleChange = (nextEditorState: EditorState): void => {
const { onChange } = this.props;
onChange(nextEditorState.getCurrentContent().getPlainText());
- const activeMention = getActiveMentionForEditorState(nextEditorState);
- const popupItems = this.getItemList(activeMention ? activeMention.mentionString : '');
-
- this.setState({ editorState: nextEditorState, popupItems }, () => {
- // In order to get correct selection, getVirtualElement has to be called after new texts are rendered
- const popupReference = activeMention ? this.getVirtualElement(activeMention) : null;
- this.setState({ popupReference });
- });
+ // In order to get correct selection, getVirtualElement in this.updatePopupReference
+ // has to be called after new texts are rendered in editor
+ this.setState({ editorState: nextEditorState }, this.updatePopupReference);
};
handleSelect = (index: number): void => {
@@ -167,63 +186,62 @@ export default class ReplyField extends React.Component {
setCursorPosition(editorState.getSelection().getFocusOffset());
};
- stopDefaultEvent = (e: React.KeyboardEvent): void => {
- e.preventDefault();
- e.stopPropagation();
+ stopDefaultEvent = (event: React.SyntheticEvent): void => {
+ event.preventDefault();
+ event.stopPropagation();
};
setPopupListActiveItem = (index: number): void => this.setState({ activeItemIndex: index });
- handleKeyDown = (event: React.KeyboardEvent): void => {
- const { activeItemIndex, popupItems, popupReference } = this.state;
+ handleKeyDown = (event: React.KeyboardEvent): DraftHandleValue => {
+ const { activeItemIndex, editorState, popupReference } = this.state;
+ const collaborators = this.getCollaborators(getActiveMentionForEditorState(editorState));
if (!popupReference) {
- return;
+ return 'not-handled';
}
- const itemsCount = popupItems.length;
-
switch (event.key) {
case 'ArrowDown':
- if (!itemsCount) {
+ if (!collaborators.length) {
break;
}
this.stopDefaultEvent(event);
- this.setPopupListActiveItem(activeItemIndex === itemsCount - 1 ? 0 : activeItemIndex + 1);
+ this.setPopupListActiveItem(activeItemIndex === collaborators.length - 1 ? 0 : activeItemIndex + 1);
break;
case 'ArrowUp':
- if (!itemsCount) {
+ if (!collaborators.length) {
break;
}
this.stopDefaultEvent(event);
- this.setPopupListActiveItem(activeItemIndex === 0 ? itemsCount - 1 : activeItemIndex - 1);
+ this.setPopupListActiveItem(activeItemIndex === 0 ? collaborators.length - 1 : activeItemIndex - 1);
break;
case 'Enter':
this.stopDefaultEvent(event);
this.handleSelect(activeItemIndex);
this.setPopupListActiveItem(0);
- break;
+ return 'handled';
default:
// don't stop default event if not above keys
}
+
+ return 'not-handled';
};
render(): JSX.Element {
const { className, isDisabled, itemRow, placeholder, ...rest } = this.props;
- const { activeItemIndex, editorState, popupItems, popupReference } = this.state;
+ const { activeItemIndex, editorState, popupReference } = this.state;
return (
-