diff options
author | Dennis Tang <dtang@gitlab.com> | 2018-06-01 18:24:53 -0700 |
---|---|---|
committer | Dennis Tang <dtang@gitlab.com> | 2018-06-01 18:24:53 -0700 |
commit | a174a0dd22159f893606835ffa4328760d41936d (patch) | |
tree | f168ccabe57dd9db005307e6938306b7af08b130 | |
parent | f8bd8f4f062e518ab855a83f38965df51ec3b073 (diff) | |
download | gitlab-ce-3969-weight-issue-search-filter.tar.gz |
port 3969-weight-issue-search-filter to CE3969-weight-issue-search-filter
3 files changed, 118 insertions, 9 deletions
diff --git a/app/assets/javascripts/droplab/plugins/custom_number.js b/app/assets/javascripts/droplab/plugins/custom_number.js new file mode 100644 index 00000000000..bf22bae8e7f --- /dev/null +++ b/app/assets/javascripts/droplab/plugins/custom_number.js @@ -0,0 +1,62 @@ +const CustomNumber = { + keydown(e) { + if (this.destroyed) return; + + const { list } = e.detail.hook; + const value = parseInt(e.detail.hook.trigger.value, 0); + const config = e.detail.hook.config.CustomNumber; + const { defaultOptions } = config; + + const isOutOfBounds = defaultOptions.indexOf(value) === -1; + const isValidNumber = !Number.isNaN(value); + const customOption = [{ id: value, title: value }]; + const defaultDropdownOptions = defaultOptions.map(o => ({ id: o, title: o })); + + list.setData(isValidNumber && isOutOfBounds ? customOption : defaultDropdownOptions); + list.currentIndex = 0; + }, + + debounceKeydown: function debounceKeydown(e) { + if ( + [ + 13, // enter + 16, // shift + 17, // ctrl + 18, // alt + 20, // caps lock + 37, // left arrow + 38, // up arrow + 39, // right arrow + 40, // down arrow + 91, // left window + 92, // right window + 93, // select + ].indexOf(e.detail.which || e.detail.keyCode) > -1 + ) + return; + + if (this.timeout) clearTimeout(this.timeout); + this.timeout = setTimeout(this.keydown.bind(this, e), 200); + }, + + init: function init(hook) { + this.hook = hook; + this.destroyed = false; + + this.eventWrapper = {}; + this.eventWrapper.debounceKeydown = this.debounceKeydown.bind(this); + + this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown); + this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown); + }, + + destroy: function destroy() { + if (this.timeout) clearTimeout(this.timeout); + this.destroyed = true; + + this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceKeydown); + this.hook.trigger.removeEventListener('mousedown.dl', this.eventWrapper.debounceKeydown); + }, +}; + +export default CustomNumber; diff --git a/app/assets/javascripts/filtered_search/dropdown_weight.js b/app/assets/javascripts/filtered_search/dropdown_weight.js new file mode 100644 index 00000000000..ebafca2548e --- /dev/null +++ b/app/assets/javascripts/filtered_search/dropdown_weight.js @@ -0,0 +1,37 @@ +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; +import CustomNumber from '../droplab/plugins/custom_number'; + +export default class DropdownWeight extends FilteredSearchDropdown { + constructor(options = {}) { + super(options); + + this.defaultOptions = Array.from(Array(21).keys()); + + this.config = { + CustomNumber: { + defaultOptions: this.defaultOptions, + }, + }; + } + + itemClicked(e) { + super.itemClicked(e, selected => { + const title = selected.querySelector('.js-data-value').innerText.trim(); + return `${DropdownUtils.getEscapedText(title)}`; + }); + } + + renderContent(forceShowList = false) { + this.droplab.changeHookList(this.hookId, this.dropdown, [CustomNumber], this.config); + + const defaultDropdownOptions = this.defaultOptions.map(o => ({ id: o, title: o })); + this.droplab.setData(defaultDropdownOptions); + + super.renderContent(forceShowList); + } + + init() { + this.droplab.addHook(this.input, this.dropdown, [CustomNumber], this.config).init(); + } +} diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index d7e1de18d09..18b292cfc0d 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint'; import DropdownEmoji from './dropdown_emoji'; import DropdownNonUser from './dropdown_non_user'; import DropdownUser from './dropdown_user'; +import DropdownWeight from './dropdown_weight'; import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; export default class FilteredSearchDropdownManager { @@ -90,9 +91,14 @@ export default class FilteredSearchDropdownManager { gl: DropdownEmoji, element: this.container.querySelector('#js-dropdown-my-reaction'), }, + weight: { + reference: null, + gl: DropdownWeight, + element: this.container.querySelector('#js-dropdown-weight'), + }, }; - supportedTokens.forEach((type) => { + supportedTokens.forEach(type => { if (availableMappings[type]) { allowedMappings[type] = availableMappings[type]; } @@ -142,13 +148,16 @@ export default class FilteredSearchDropdownManager { updateDropdownOffset(key) { // Always align dropdown with the input field - let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left; + let offset = + this.filteredSearchInput.getBoundingClientRect().left - + this.container.querySelector('.scroll-container').getBoundingClientRect().left; const maxInputWidth = 240; const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth; // Make sure offset never exceeds the input container - const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth; + const offsetMaxWidth = + this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth; if (offsetMaxWidth < offset) { offset = offsetMaxWidth; } @@ -174,8 +183,7 @@ export default class FilteredSearchDropdownManager { const glArguments = Object.assign({}, defaultArguments, extraArguments); // Passing glArguments to `new glClass(<arguments>)` - mappingKey.reference = - new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); + mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); } if (firstLoad) { @@ -202,8 +210,8 @@ export default class FilteredSearchDropdownManager { } const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase()); - const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key - && this.mapping[match.key]; + const shouldOpenFilterDropdown = + match && this.currentDropdown !== match.key && this.mapping[match.key]; const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint'; if (shouldOpenFilterDropdown || shouldOpenHintDropdown) { @@ -214,8 +222,10 @@ export default class FilteredSearchDropdownManager { setDropdown() { const query = DropdownUtils.getSearchQuery(true); - const { lastToken, searchToken } = - this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys()); + const { lastToken, searchToken } = this.tokenizer.processTokens( + query, + this.filteredSearchTokenKeys.getKeys(), + ); if (this.currentDropdown) { this.updateCurrentDropdownOffset(); |