diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 9aef5433c3..243cfe2f75 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -171,26 +171,13 @@ export default class Autocomplete extends React.Component { } // called from MessageComposerInput - onUpArrow(): ?Completion { + moveSelection(delta): ?Completion { const completionCount = this.countCompletions(); - // completionCount + 1, since 0 means composer is selected - const selectionOffset = (completionCount + 1 + this.state.selectionOffset - 1) - % (completionCount + 1); - if (!completionCount) { - return null; - } - this.setSelection(selectionOffset); - } + if (completionCount === 0) return; // there are no items to move the selection through - // called from MessageComposerInput - onDownArrow(): ?Completion { - const completionCount = this.countCompletions(); - // completionCount + 1, since 0 means composer is selected - const selectionOffset = (this.state.selectionOffset + 1) % (completionCount + 1); - if (!completionCount) { - return null; - } - this.setSelection(selectionOffset); + // Note: selectionOffset 0 represents the unsubstituted text, while 1 means first pill selected + const index = (this.state.selectionOffset + delta + completionCount + 1) % (completionCount + 1); + this.setSelection(index); } onEscape(e): boolean { diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 939ec4d9f5..e99e87ae1d 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -673,6 +673,31 @@ export default class MessageComposerInput extends React.Component { onKeyDown = (ev: KeyboardEvent, change: Change, editor: Editor) => { this.suppressAutoComplete = false; + this.direction = ''; + + // Navigate autocomplete list with arrow keys + if (this.autocomplete.countCompletions() > 0) { + if (!(ev.ctrlKey || ev.shiftKey || ev.altKey || ev.metaKey)) { + switch (ev.keyCode) { + case KeyCode.LEFT: + this.autocomplete.moveSelection(-1); + ev.preventDefault(); + return true; + case KeyCode.RIGHT: + this.autocomplete.moveSelection(+1); + ev.preventDefault(); + return true; + case KeyCode.UP: + this.autocomplete.moveSelection(-1); + ev.preventDefault(); + return true; + case KeyCode.DOWN: + this.autocomplete.moveSelection(+1); + ev.preventDefault(); + return true; + } + } + } // skip void nodes - see // https://github.com/ianstormtaylor/slate/issues/762#issuecomment-304855095 @@ -680,8 +705,6 @@ export default class MessageComposerInput extends React.Component { this.direction = 'Previous'; } else if (ev.keyCode === KeyCode.RIGHT) { this.direction = 'Next'; - } else { - this.direction = ''; } switch (ev.keyCode) { @@ -1175,35 +1198,28 @@ export default class MessageComposerInput extends React.Component { }; onVerticalArrow = (e, up) => { - if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) { - return; - } + if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) return; - // Select history only if we are not currently auto-completing - if (this.autocomplete.state.completionList.length === 0) { - const selection = this.state.editorState.selection; + // Select history + const selection = this.state.editorState.selection; - // selection must be collapsed - if (!selection.isCollapsed) return; - const document = this.state.editorState.document; + // selection must be collapsed + if (!selection.isCollapsed) return; + const document = this.state.editorState.document; - // and we must be at the edge of the document (up=start, down=end) - if (up) { - if (!selection.anchor.isAtStartOfNode(document)) return; + // and we must be at the edge of the document (up=start, down=end) + if (up) { + if (!selection.anchor.isAtStartOfNode(document)) return; - const editEvent = findEditableEvent(this.props.room, false); - if (editEvent) { - // We're selecting history, so prevent the key event from doing anything else - e.preventDefault(); - dis.dispatch({ - action: 'edit_event', - event: editEvent, - }); - } + const editEvent = findEditableEvent(this.props.room, false); + if (editEvent) { + // We're selecting history, so prevent the key event from doing anything else + e.preventDefault(); + dis.dispatch({ + action: 'edit_event', + event: editEvent, + }); } - } else { - this.moveAutocompleteSelection(up); - e.preventDefault(); } }; @@ -1212,23 +1228,19 @@ export default class MessageComposerInput extends React.Component { someCompletions: null, }); e.preventDefault(); - if (this.autocomplete.state.completionList.length === 0) { + if (this.autocomplete.countCompletions() === 0) { // Force completions to show for the text currently entered const completionCount = await this.autocomplete.forceComplete(); this.setState({ someCompletions: completionCount > 0, }); // Select the first item by moving "down" - await this.moveAutocompleteSelection(false); + await this.autocomplete.moveSelection(+1); } else { - await this.moveAutocompleteSelection(e.shiftKey); + await this.autocomplete.moveSelection(e.shiftKey ? -1 : +1); } }; - moveAutocompleteSelection = (up) => { - up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow(); - }; - onEscape = async (e) => { e.preventDefault(); if (this.autocomplete) { diff --git a/src/editor/autocomplete.js b/src/editor/autocomplete.js index 92c4db415e..6cb5974729 100644 --- a/src/editor/autocomplete.js +++ b/src/editor/autocomplete.js @@ -43,17 +43,13 @@ export default class AutocompleteWrapperModel { async onTab(e) { const acComponent = this._getAutocompleterComponent(); - if (acComponent.state.completionList.length === 0) { + if (acComponent.countCompletions() === 0) { // Force completions to show for the text currently entered await acComponent.forceComplete(); // Select the first item by moving "down" - await acComponent.onDownArrow(); + await acComponent.moveSelection(+1); } else { - if (e.shiftKey) { - await acComponent.onUpArrow(); - } else { - await acComponent.onDownArrow(); - } + await acComponent.moveSelection(e.shiftKey ? -1 : +1); } this._updateCallback({ close: true, @@ -61,11 +57,11 @@ export default class AutocompleteWrapperModel { } onUpArrow() { - this._getAutocompleterComponent().onUpArrow(); + this._getAutocompleterComponent().moveSelection(-1); } onDownArrow() { - this._getAutocompleterComponent().onDownArrow(); + this._getAutocompleterComponent().moveSelection(+1); } onPartUpdate(part, offset) {