UX: Autocomplete suggestions should be unselected by default (#11637)
When you type # or @ in the search box, a popup appears with autocomplete suggestions. Currently, when the popup is rendered it has the first item selected and upon pressing Enter, the first item is inserted into the search box. The problem with this behavior is that the first suggestion may not be what you want, and if you are typing quickly and hit enter, the first suggestion (which is not what you want) is inserted in the search box. This PR amends the popup so that it has no suggestions selected by default which means the enter key will not insert anything unless you select a suggestion via the up or down arrow keys.
This commit is contained in:
parent
690f1a257a
commit
e02cc98092
|
@ -94,6 +94,19 @@ export default function (options) {
|
||||||
let div = null;
|
let div = null;
|
||||||
let prevTerm = null;
|
let prevTerm = null;
|
||||||
|
|
||||||
|
// By default, when the autcomplete popup is rendered it has the
|
||||||
|
// first suggestion 'selected', and pressing enter key inserts
|
||||||
|
// the first suggestion into the input box.
|
||||||
|
// If you want to stop that behavior, i.e. have the popup renders
|
||||||
|
// with no suggestions selected, set the `autoSelectFirstSuggestion`
|
||||||
|
// option to false.
|
||||||
|
// With this option set to false, users will have to select
|
||||||
|
// a suggestion via the up/down arrow keys and then press enter
|
||||||
|
// to insert it.
|
||||||
|
if (!("autoSelectFirstSuggestion" in options)) {
|
||||||
|
options.autoSelectFirstSuggestion = true;
|
||||||
|
}
|
||||||
|
|
||||||
// input is handled differently
|
// input is handled differently
|
||||||
const isInput = me[0].tagName === "INPUT" && !options.treatAsTextarea;
|
const isInput = me[0].tagName === "INPUT" && !options.treatAsTextarea;
|
||||||
let inputSelectedItems = [];
|
let inputSelectedItems = [];
|
||||||
|
@ -187,7 +200,11 @@ export default function (options) {
|
||||||
" " +
|
" " +
|
||||||
text.substring(completeEnd + 1, text.length);
|
text.substring(completeEnd + 1, text.length);
|
||||||
me.val(text);
|
me.val(text);
|
||||||
setCaretPosition(me[0], completeStart + 1 + term.length);
|
let newCaretPos = completeStart + 1 + term.length;
|
||||||
|
if (options.key) {
|
||||||
|
newCaretPos++;
|
||||||
|
}
|
||||||
|
setCaretPosition(me[0], newCaretPos);
|
||||||
|
|
||||||
if (options && options.afterComplete) {
|
if (options && options.afterComplete) {
|
||||||
options.afterComplete(text);
|
options.afterComplete(text);
|
||||||
|
@ -276,8 +293,12 @@ export default function (options) {
|
||||||
div = $(options.template({ options: autocompleteOptions }));
|
div = $(options.template({ options: autocompleteOptions }));
|
||||||
|
|
||||||
let ul = div.find("ul");
|
let ul = div.find("ul");
|
||||||
|
if (options.autoSelectFirstSuggestion) {
|
||||||
selectedOption = 0;
|
selectedOption = 0;
|
||||||
markSelected();
|
markSelected();
|
||||||
|
} else {
|
||||||
|
selectedOption = -1;
|
||||||
|
}
|
||||||
ul.find("li").click(function () {
|
ul.find("li").click(function () {
|
||||||
selectedOption = ul.find("li").index(this);
|
selectedOption = ul.find("li").index(this);
|
||||||
completeTerm(autocompleteOptions[selectedOption]);
|
completeTerm(autocompleteOptions[selectedOption]);
|
||||||
|
@ -549,8 +570,8 @@ export default function (options) {
|
||||||
// Keyboard codes! So 80's.
|
// Keyboard codes! So 80's.
|
||||||
switch (e.which) {
|
switch (e.which) {
|
||||||
case keys.enter:
|
case keys.enter:
|
||||||
case keys.tab:
|
|
||||||
if (!autocompleteOptions) {
|
if (!autocompleteOptions) {
|
||||||
|
closeAutocomplete();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@ -583,6 +604,7 @@ export default function (options) {
|
||||||
markSelected();
|
markSelected();
|
||||||
return false;
|
return false;
|
||||||
case keys.backSpace:
|
case keys.backSpace:
|
||||||
|
autocompleteOptions = null;
|
||||||
completeEnd = cp;
|
completeEnd = cp;
|
||||||
cp--;
|
cp--;
|
||||||
|
|
||||||
|
@ -606,6 +628,7 @@ export default function (options) {
|
||||||
updateAutoComplete(dataSource(term, options));
|
updateAutoComplete(dataSource(term, options));
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
autocompleteOptions = null;
|
||||||
completeEnd = cp;
|
completeEnd = cp;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,6 +220,7 @@ export function applySearchAutocomplete(
|
||||||
key: "#",
|
key: "#",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
treatAsTextarea: true,
|
treatAsTextarea: true,
|
||||||
|
autoSelectFirstSuggestion: false,
|
||||||
transformComplete(obj) {
|
transformComplete(obj) {
|
||||||
return obj.text;
|
return obj.text;
|
||||||
},
|
},
|
||||||
|
@ -240,6 +241,7 @@ export function applySearchAutocomplete(
|
||||||
key: "@",
|
key: "@",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
treatAsTextarea: true,
|
treatAsTextarea: true,
|
||||||
|
autoSelectFirstSuggestion: false,
|
||||||
transformComplete: (v) => v.username || v.name,
|
transformComplete: (v) => v.username || v.name,
|
||||||
dataSource: (term) => userSearch({ term, includeGroups: true }),
|
dataSource: (term) => userSearch({ term, includeGroups: true }),
|
||||||
afterComplete,
|
afterComplete,
|
||||||
|
|
Loading…
Reference in New Issue