summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJacob Schatz <jschatz@gitlab.com>2017-01-21 21:34:32 +0000
committerJacob Schatz <jschatz@gitlab.com>2017-01-21 21:34:32 +0000
commitbddd09c46534809876339e3c998440185c0f0036 (patch)
tree6183bd0b18096ce11b1bbdaf770decd17c249543 /app
parente8552366b8997b7471745c25967a6c0f5cb1871c (diff)
parente75c20fcff87f8f0de8daa556e7286659f111ab6 (diff)
downloadgitlab-ce-bddd09c46534809876339e3c998440185c0f0036.tar.gz
Merge branch 'issue-search-token-position' into 'master'
Filtered search input click back at token See merge request !8617
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js.es62
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js.es62
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js.es62
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js.es653
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es644
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js.es613
6 files changed, 84 insertions, 32 deletions
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
index 63c20f57520..f4ec3b206cc 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
@@ -9,7 +9,7 @@
this.config = {
droplabFilter: {
template: 'hint',
- filterFunction: gl.DropdownUtils.filterHint,
+ filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
},
};
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6 b/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6
index f06c3fc9c6f..13cbec1be4a 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6
@@ -15,7 +15,7 @@
loadingTemplate: this.loadingTemplate,
},
droplabFilter: {
- filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol),
+ filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
},
};
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js.es6 b/app/assets/javascripts/filtered_search/dropdown_user.js.es6
index e80d266ae89..7bf199d9274 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js.es6
@@ -37,7 +37,7 @@
}
getSearchInput() {
- const query = this.input.value.trim();
+ const query = gl.DropdownUtils.getSearchInput(this.input);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
return lastToken.value || '';
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
index c27ef3042d1..443ac222f70 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
@@ -20,17 +20,15 @@
return escapedText;
}
- static filterWithSymbol(filterSymbol, item, query) {
+ static filterWithSymbol(filterSymbol, input, item) {
const updatedItem = item;
+ const query = gl.DropdownUtils.getSearchInput(input);
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(query);
if (lastToken !== searchToken) {
const title = updatedItem.title.toLowerCase();
let value = lastToken.value.toLowerCase();
-
- if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
- value = value.slice(1);
- }
+ value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1));
// Eg. filterSymbol = ~ for labels
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
@@ -44,8 +42,9 @@
return updatedItem;
}
- static filterHint(item, query) {
+ static filterHint(input, item) {
const updatedItem = item;
+ const query = gl.DropdownUtils.getSearchInput(input);
let { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
lastToken = lastToken.key || lastToken || '';
@@ -72,6 +71,48 @@
// Return boolean based on whether it was set
return dataValue !== null;
}
+
+ static getSearchInput(filteredSearchInput) {
+ const inputValue = filteredSearchInput.value;
+ const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
+
+ return inputValue.slice(0, right);
+ }
+
+ static getInputSelectionPosition(input) {
+ const selectionStart = input.selectionStart;
+ let inputValue = input.value;
+ // Replace all spaces inside quote marks with underscores
+ // This helps with matching the beginning & end of a token:key
+ inputValue = inputValue.replace(/"(.*?)"/g, str => str.replace(/\s/g, '_'));
+
+ // Get the right position for the word selected
+ // Regex matches first space
+ let right = inputValue.slice(selectionStart).search(/\s/);
+
+ if (right >= 0) {
+ right += selectionStart;
+ } else if (right < 0) {
+ right = inputValue.length;
+ }
+
+ // Get the left position for the word selected
+ // Regex matches last non-whitespace character
+ let left = inputValue.slice(0, right).search(/\S+$/);
+
+ if (selectionStart === 0) {
+ left = 0;
+ } else if (selectionStart === inputValue.length && left < 0) {
+ left = inputValue.length;
+ } else if (left < 0) {
+ left = selectionStart;
+ }
+
+ return {
+ left,
+ right,
+ };
+ }
}
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
index 1cd0483877a..04873115580 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
@@ -57,28 +57,25 @@
static addWordToInput(tokenName, tokenValue = '') {
const input = document.querySelector('.filtered-search');
+ const inputValue = input.value;
const word = `${tokenName}:${tokenValue}`;
- const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(input.value);
- const lastSearchToken = searchToken.split(' ').last();
- const lastInputCharacter = input.value[input.value.length - 1];
- const lastInputTrimmedCharacter = input.value.trim()[input.value.trim().length - 1];
-
- // Remove the typed tokenName
- if (word.indexOf(lastSearchToken) === 0 && searchToken !== '') {
- // Remove spaces after the colon
- if (lastInputCharacter === ' ' && lastInputTrimmedCharacter === ':') {
- input.value = input.value.trim();
- }
-
- input.value = input.value.slice(0, -1 * lastSearchToken.length);
- } else if (lastInputCharacter !== ' ' || (lastToken && lastToken.value[lastToken.value.length - 1] === ' ')) {
- // Remove the existing tokenValue
- const lastTokenString = `${lastToken.key}:${lastToken.symbol}${lastToken.value}`;
- input.value = input.value.slice(0, -1 * lastTokenString.length);
- }
+ // Get the string to replace
+ const selectionStart = input.selectionStart;
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition(input);
+
+ input.value = `${inputValue.substr(0, left)}${word}${inputValue.substr(right)}`;
+ gl.FilteredSearchDropdownManager.updateInputCaretPosition(selectionStart, input);
+ }
+
+ static updateInputCaretPosition(selectionStart, input) {
+ // Reset the position
+ // Sometimes can end up at end of input
+ input.setSelectionRange(selectionStart, selectionStart);
+
+ const { right } = gl.DropdownUtils.getInputSelectionPosition(input);
- input.value += word;
+ input.setSelectionRange(right, right);
}
updateCurrentDropdownOffset() {
@@ -90,9 +87,10 @@
this.font = window.getComputedStyle(this.filteredSearchInput).font;
}
+ const input = this.filteredSearchInput;
+ const inputText = input.value.slice(0, input.selectionStart);
const filterIconPadding = 27;
- const offset = gl.text
- .getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding;
+ const offset = gl.text.getTextWidth(inputText, this.font) + filterIconPadding;
this.mapping[key].reference.setOffset(offset);
}
@@ -148,9 +146,9 @@
setDropdown() {
const { lastToken, searchToken } = this.tokenizer
- .processTokens(this.filteredSearchInput.value);
+ .processTokens(gl.DropdownUtils.getSearchInput(this.filteredSearchInput));
- if (this.filteredSearchInput.value.split('').last() === ' ') {
+ if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
index 4e8a7cfc940..c7b72b36561 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
@@ -30,11 +30,14 @@
this.checkForEnterWrapper = this.checkForEnter.bind(this);
this.clearSearchWrapper = this.clearSearch.bind(this);
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
+ this.tokenChange = this.tokenChange.bind(this);
this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
+ this.filteredSearchInput.addEventListener('click', this.tokenChange);
+ this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
}
@@ -43,6 +46,8 @@
this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
+ this.filteredSearchInput.removeEventListener('click', this.tokenChange);
+ this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
}
@@ -188,6 +193,14 @@
}
return usernamesById;
}
+
+ tokenChange() {
+ const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
+ const currentDropdownRef = dropdown.reference;
+
+ this.setDropdownWrapper();
+ currentDropdownRef.dispatchInputEvent();
+ }
}
window.gl = window.gl || {};