Commit 594c8b6b authored by iomodo's avatar iomodo
Browse files

Rework autocomplete UX

parent 07cede12
......@@ -760,7 +760,9 @@ class CreatePost extends React.PureComponent {
}
if (allowSending) {
e.persist();
if (e.persist) {
e.persist();
}
if (this.refs.textbox) {
this.refs.textbox.blur();
}
......
......@@ -10,9 +10,13 @@ import store from 'stores/redux_store.jsx';
import * as UserAgent from 'utils/user_agent';
import * as Utils from 'utils/utils.jsx';
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
export const EXECUTE_CURRENT_COMMAND_ITEM_ID = '_execute_current_command';
export class CommandSuggestion extends Suggestion {
render() {
const {item, isSelection} = this.props;
......@@ -21,8 +25,12 @@ export class CommandSuggestion extends Suggestion {
if (isSelection) {
className += ' suggestion--selected';
}
let icon = <div className='slash-command__icon'><span>{'/'}</span></div>;
if (item.iconData !== '') {
let symbolSpan = <span>{'/'}</span>;
if (item.iconData === EXECUTE_CURRENT_COMMAND_ITEM_ID) {
symbolSpan = <span className='block mt-1'>{''}</span>;
}
let icon = <div className='slash-command__icon'>{symbolSpan}</div>;
if (item.iconData !== '' && item.iconData !== EXECUTE_CURRENT_COMMAND_ITEM_ID) {
icon = (
<div
className='slash-command__icon'
......@@ -132,14 +140,29 @@ export default class CommandProvider extends Provider {
Client4.getCommandAutocompleteSuggestionsList(command, teamId, args).then(
(data) => {
const matches = [];
data.forEach((sug) => {
let cmd = 'Ctrl';
if (Utils.isMac()) {
cmd = '';
}
if (this.shouldAddExecuteItem(data, pretext)) {
matches.push({
complete: '/' + sug.Complete,
suggestion: '/' + sug.Suggestion,
hint: sug.Hint,
description: sug.Description,
iconData: sug.IconData,
complete: pretext + EXECUTE_CURRENT_COMMAND_ITEM_ID,
suggestion: '/Execute Current Command',
hint: '',
description: 'Select this option or use ' + cmd + '+Enter to execute the current command.',
iconData: EXECUTE_CURRENT_COMMAND_ITEM_ID,
});
}
data.forEach((sug) => {
if (!this.contains(matches, '/' + sug.Complete)) {
matches.push({
complete: '/' + sug.Complete,
suggestion: '/' + sug.Suggestion,
hint: sug.Hint,
description: sug.Description,
iconData: sug.IconData,
});
}
});
// pull out the suggested commands from the returned data
......@@ -158,4 +181,20 @@ export default class CommandProvider extends Provider {
return true;
}
shouldAddExecuteItem(data, pretext) {
if (data.length === 0) {
return false;
}
if (pretext[pretext.length - 1] === ' ') {
return true;
}
// If suggestion is empty it means that user can input any text so we allow them to execute.
return data.findIndex((item) => item.Suggestion === '') !== -1;
}
contains(matches, complete) {
return matches.findIndex((match) => match.complete === complete) !== -1;
}
}
......@@ -11,6 +11,7 @@ import Constants from 'utils/constants';
import * as UserAgent from 'utils/user_agent';
import * as Utils from 'utils/utils.jsx';
import {EXECUTE_CURRENT_COMMAND_ITEM_ID} from './command_provider';
const KeyCodes = Constants.KeyCodes;
export default class SuggestionBox extends React.PureComponent {
......@@ -80,6 +81,7 @@ export default class SuggestionBox extends React.PureComponent {
* Function called when a key is pressed and the input box is in focus
*/
onKeyDown: PropTypes.func,
onKeyPress: PropTypes.func,
onComposition: PropTypes.func,
/**
......@@ -441,18 +443,24 @@ export default class SuggestionBox extends React.PureComponent {
}
}
handleCompleteWord = (term, matchedPretext) => {
handleCompleteWord = (term, matchedPretext, e) => {
let fixedTerm = term;
let finish = false;
if (term.endsWith(EXECUTE_CURRENT_COMMAND_ITEM_ID)) {
fixedTerm = term.substring(0, term.length - EXECUTE_CURRENT_COMMAND_ITEM_ID.length);
finish = true;
}
if (this.props.replaceAllInputOnSelect) {
this.replaceText(term);
this.replaceText(fixedTerm);
} else {
this.addTextAtCaret(term, matchedPretext);
this.addTextAtCaret(fixedTerm, matchedPretext);
}
if (this.props.onItemSelected) {
const items = this.state.items;
const terms = this.state.terms;
for (let i = 0; i < terms.length; i++) {
if (terms[i] === term) {
if (terms[i] === fixedTerm) {
this.props.onItemSelected(items[i]);
break;
}
......@@ -463,11 +471,28 @@ export default class SuggestionBox extends React.PureComponent {
this.inputRef.current.focus();
for (const provider of this.props.providers) {
if (provider.handleCompleteWord) {
provider.handleCompleteWord(term, matchedPretext, this.handlePretextChanged);
if (finish && this.props.onKeyPress) {
let ke = e;
if (!e || Utils.isKeyPressed(e, Constants.KeyCodes.TAB)) {
ke = new KeyboardEvent('keydown', {
bubbles: true, cancelable: true, keyCode: 13,
});
if (e) {
e.preventDefault();
}
}
this.props.onKeyPress(ke);
return true;
}
if (!finish) {
for (const provider of this.props.providers) {
if (provider.handleCompleteWord) {
provider.handleCompleteWord(fixedTerm, matchedPretext, this.handlePretextChanged);
}
}
}
return false;
}
selectNext = () => {
......@@ -542,8 +567,10 @@ export default class SuggestionBox extends React.PureComponent {
// If these don't match, the user typed quickly and pressed enter before we could
// update the pretext, so update the pretext before completing
if (this.pretext.endsWith(matchedPretext)) {
this.handleCompleteWord(this.state.selection, matchedPretext);
if (this.pretext.toLowerCase().endsWith(matchedPretext.toLowerCase())) {
if (this.handleCompleteWord(this.state.selection, matchedPretext, e)) {
return;
}
} else {
clearTimeout(this.timeoutId);
this.nonDebouncedPretextChanged(this.pretext, true);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment