diff options
Diffstat (limited to 'app/assets/javascripts/gl_dropdown.js')
-rw-r--r-- | app/assets/javascripts/gl_dropdown.js | 1661 |
1 files changed, 782 insertions, 879 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 045f77af7ea..65d05887453 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-shadow, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func */ +/* eslint-disable func-names, no-underscore-dangle, one-var, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func */ import $ from 'jquery'; import _ from 'underscore'; @@ -8,984 +8,887 @@ import { visitUrl } from './lib/utils/url_utility'; import { isObject } from './lib/utils/type_utility'; import renderItem from './gl_dropdown/render'; -var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput; - -GitLabDropdownInput = (function() { - function GitLabDropdownInput(input, options) { - var $inputContainer, $clearButton; - var _this = this; - this.input = input; - this.options = options; - this.fieldName = this.options.fieldName || 'field-name'; - $inputContainer = this.input.parent(); - $clearButton = $inputContainer.find('.js-dropdown-input-clear'); - $clearButton.on( - 'click', - (function(_this) { - // Clear click - return function(e) { - e.preventDefault(); - e.stopPropagation(); - return _this.input - .val('') - .trigger('input') - .focus(); - }; - })(this), - ); +const BLUR_KEYCODES = [27, 40]; - this.input - .on('keydown', e => { - var keyCode = e.which; - if (keyCode === 13 && !options.elIsInput) { - e.preventDefault(); - } - }) - .on('input', e => { - var val = e.currentTarget.value || _this.options.inputFieldName; - val = val - .split(' ') - .join('-') // replaces space with dash - .replace(/[^a-zA-Z0-9 -]/g, '') - .toLowerCase() // replace non alphanumeric - .replace(/(-)\1+/g, '-'); // replace repeated dashes - _this.cb(_this.options.fieldName, val, {}, true); - _this.input - .closest('.dropdown') - .find('.dropdown-toggle-text') - .text(val); - }); - } +const HAS_VALUE_CLASS = 'has-value'; - GitLabDropdownInput.prototype.onInput = function(cb) { - this.cb = cb; - }; - - return GitLabDropdownInput; -})(); - -GitLabDropdownFilter = (function() { - var BLUR_KEYCODES, HAS_VALUE_CLASS; - - BLUR_KEYCODES = [27, 40]; - - HAS_VALUE_CLASS = 'has-value'; - - function GitLabDropdownFilter(input, options) { - var $clearButton, $inputContainer, ref, timeout; - this.input = input; - this.options = options; - this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; - $inputContainer = this.input.parent(); - $clearButton = $inputContainer.find('.js-dropdown-input-clear'); - $clearButton.on( - 'click', - (function(_this) { - // Clear click - return function(e) { - e.preventDefault(); - e.stopPropagation(); - return _this.input - .val('') - .trigger('input') - .focus(); - }; - })(this), - ); - // Key events - timeout = ''; - this.input - .on('keydown', e => { - var keyCode = e.which; - if (keyCode === 13 && !options.elIsInput) { - e.preventDefault(); - } - }) - .on('input', () => { - if (this.input.val() !== '' && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { - $inputContainer.addClass(HAS_VALUE_CLASS); - } else if (this.input.val() === '' && $inputContainer.hasClass(HAS_VALUE_CLASS)) { - $inputContainer.removeClass(HAS_VALUE_CLASS); - } - // Only filter asynchronously only if option remote is set - if (this.options.remote) { - clearTimeout(timeout); - return (timeout = setTimeout(() => { - $inputContainer.parent().addClass('is-loading'); - - return this.options.query(this.input.val(), data => { - $inputContainer.parent().removeClass('is-loading'); - return this.options.callback(data); - }); - }, 250)); - } else { - return this.filter(this.input.val()); - } - }); - } +const LOADING_CLASS = 'is-loading'; - GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { - return BLUR_KEYCODES.indexOf(keyCode) !== -1; - }; +const PAGE_TWO_CLASS = 'is-page-two'; - GitLabDropdownFilter.prototype.filter = function(search_text) { - var data, elements, group, key, results, tmp; - if (this.options.onFilter) { - this.options.onFilter(search_text); - } - data = this.options.data(); - if (data != null && !this.options.filterByText) { - results = data; - if (search_text !== '') { - // When data is an array of objects therefore [object Array] e.g. - // [ - // { prop: 'foo' }, - // { prop: 'baz' } - // ] - if (_.isArray(data)) { - results = fuzzaldrinPlus.filter(data, search_text, { - key: this.options.keys, +const ACTIVE_CLASS = 'is-active'; + +const INDETERMINATE_CLASS = 'is-indeterminate'; + +let currentIndex = -1; + +const NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; + +const SELECTABLE_CLASSES = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES}, .option-hidden)`; + +const CURSOR_SELECT_SCROLL_PADDING = 5; + +const FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-filter)'; + +const NO_FILTER_INPUT = '.dropdown-input .dropdown-input-field.dropdown-no-filter'; + +function GitLabDropdownInput(input, options) { + const _this = this; + this.input = input; + this.options = options; + this.fieldName = this.options.fieldName || 'field-name'; + const $inputContainer = this.input.parent(); + const $clearButton = $inputContainer.find('.js-dropdown-input-clear'); + $clearButton.on('click', e => { + // Clear click + e.preventDefault(); + e.stopPropagation(); + return this.input + .val('') + .trigger('input') + .focus(); + }); + + this.input + .on('keydown', e => { + const keyCode = e.which; + if (keyCode === 13 && !options.elIsInput) { + e.preventDefault(); + } + }) + .on('input', e => { + let val = e.currentTarget.value || _this.options.inputFieldName; + val = val + .split(' ') + .join('-') // replaces space with dash + .replace(/[^a-zA-Z0-9 -]/g, '') + .toLowerCase() // replace non alphanumeric + .replace(/(-)\1+/g, '-'); // replace repeated dashes + _this.cb(_this.options.fieldName, val, {}, true); + _this.input + .closest('.dropdown') + .find('.dropdown-toggle-text') + .text(val); + }); +} + +GitLabDropdownInput.prototype.onInput = function(cb) { + this.cb = cb; +}; + +function GitLabDropdownFilter(input, options) { + let ref, timeout; + this.input = input; + this.options = options; + this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; + const $inputContainer = this.input.parent(); + const $clearButton = $inputContainer.find('.js-dropdown-input-clear'); + $clearButton.on('click', e => { + // Clear click + e.preventDefault(); + e.stopPropagation(); + return this.input + .val('') + .trigger('input') + .focus(); + }); + // Key events + timeout = ''; + this.input + .on('keydown', e => { + const keyCode = e.which; + if (keyCode === 13 && !options.elIsInput) { + e.preventDefault(); + } + }) + .on('input', () => { + if (this.input.val() !== '' && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.addClass(HAS_VALUE_CLASS); + } else if (this.input.val() === '' && $inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.removeClass(HAS_VALUE_CLASS); + } + // Only filter asynchronously only if option remote is set + if (this.options.remote) { + clearTimeout(timeout); + return (timeout = setTimeout(() => { + $inputContainer.parent().addClass('is-loading'); + + return this.options.query(this.input.val(), data => { + $inputContainer.parent().removeClass('is-loading'); + return this.options.callback(data); }); - } else { - // If data is grouped therefore an [object Object]. e.g. - // { - // groupName1: [ - // { prop: 'foo' }, - // { prop: 'baz' } - // ], - // groupName2: [ - // { prop: 'abc' }, - // { prop: 'def' } - // ] - // } - if (isObject(data)) { - results = {}; - for (key in data) { - group = data[key]; - tmp = fuzzaldrinPlus.filter(group, search_text, { - key: this.options.keys, - }); - if (tmp.length) { - results[key] = tmp.map(item => item); - } + }, 250)); + } else { + return this.filter(this.input.val()); + } + }); +} + +GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { + return BLUR_KEYCODES.indexOf(keyCode) !== -1; +}; + +GitLabDropdownFilter.prototype.filter = function(search_text) { + let elements, group, key, results, tmp; + if (this.options.onFilter) { + this.options.onFilter(search_text); + } + const data = this.options.data(); + if (data != null && !this.options.filterByText) { + results = data; + if (search_text !== '') { + // When data is an array of objects therefore [object Array] e.g. + // [ + // { prop: 'foo' }, + // { prop: 'baz' } + // ] + if (_.isArray(data)) { + results = fuzzaldrinPlus.filter(data, search_text, { + key: this.options.keys, + }); + } else { + // If data is grouped therefore an [object Object]. e.g. + // { + // groupName1: [ + // { prop: 'foo' }, + // { prop: 'baz' } + // ], + // groupName2: [ + // { prop: 'abc' }, + // { prop: 'def' } + // ] + // } + if (isObject(data)) { + results = {}; + for (key in data) { + group = data[key]; + tmp = fuzzaldrinPlus.filter(group, search_text, { + key: this.options.keys, + }); + if (tmp.length) { + results[key] = tmp.map(item => item); } } } } - return this.options.callback(results); - } else { - elements = this.options.elements(); - if (search_text) { - elements.each(function() { - var $el, matches; - $el = $(this); - matches = fuzzaldrinPlus.match($el.text().trim(), search_text); - if (!$el.is('.dropdown-header')) { - if (matches.length) { - return $el.show().removeClass('option-hidden'); - } else { - return $el.hide().addClass('option-hidden'); - } + } + return this.options.callback(results); + } else { + elements = this.options.elements(); + if (search_text) { + elements.each(function() { + const $el = $(this); + const matches = fuzzaldrinPlus.match($el.text().trim(), search_text); + if (!$el.is('.dropdown-header')) { + if (matches.length) { + return $el.show().removeClass('option-hidden'); + } else { + return $el.hide().addClass('option-hidden'); } - }); - } else { - elements.show().removeClass('option-hidden'); - } - - elements - .parent() - .find('.dropdown-menu-empty-item') - .toggleClass('hidden', elements.is(':visible')); + } + }); + } else { + elements.show().removeClass('option-hidden'); } - }; - - return GitLabDropdownFilter; -})(); -GitLabDropdownRemote = (function() { - function GitLabDropdownRemote(dataEndpoint, options) { - this.dataEndpoint = dataEndpoint; - this.options = options; + elements + .parent() + .find('.dropdown-menu-empty-item') + .toggleClass('hidden', elements.is(':visible')); } +}; - GitLabDropdownRemote.prototype.execute = function() { - if (typeof this.dataEndpoint === 'string') { - return this.fetchData(); - } else if (typeof this.dataEndpoint === 'function') { - if (this.options.beforeSend) { - this.options.beforeSend(); - } - return this.dataEndpoint( - '', - (function(_this) { - // Fetch the data by calling the data funcfion - return function(data) { - if (_this.options.success) { - _this.options.success(data); - } - if (_this.options.beforeSend) { - return _this.options.beforeSend(); - } - }; - })(this), - ); - } - }; +function GitLabDropdownRemote(dataEndpoint, options) { + this.dataEndpoint = dataEndpoint; + this.options = options; +} - GitLabDropdownRemote.prototype.fetchData = function() { +GitLabDropdownRemote.prototype.execute = function() { + if (typeof this.dataEndpoint === 'string') { + return this.fetchData(); + } else if (typeof this.dataEndpoint === 'function') { if (this.options.beforeSend) { this.options.beforeSend(); } - - // Fetch the data through ajax if the data is a string - return axios.get(this.dataEndpoint).then(({ data }) => { + return this.dataEndpoint('', data => { + // Fetch the data by calling the data function if (this.options.success) { - return this.options.success(data); + this.options.success(data); + } + if (this.options.beforeSend) { + return this.options.beforeSend(); } }); - }; - - return GitLabDropdownRemote; -})(); - -GitLabDropdown = (function() { - var ACTIVE_CLASS, - FILTER_INPUT, - NO_FILTER_INPUT, - INDETERMINATE_CLASS, - LOADING_CLASS, - PAGE_TWO_CLASS, - NON_SELECTABLE_CLASSES, - SELECTABLE_CLASSES, - CURSOR_SELECT_SCROLL_PADDING, - currentIndex; - - LOADING_CLASS = 'is-loading'; - - PAGE_TWO_CLASS = 'is-page-two'; - - ACTIVE_CLASS = 'is-active'; - - INDETERMINATE_CLASS = 'is-indeterminate'; + } +}; - currentIndex = -1; +GitLabDropdownRemote.prototype.fetchData = function() { + if (this.options.beforeSend) { + this.options.beforeSend(); + } - NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; - - SELECTABLE_CLASSES = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES}, .option-hidden)`; - - CURSOR_SELECT_SCROLL_PADDING = 5; - - FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-filter)'; - - NO_FILTER_INPUT = '.dropdown-input .dropdown-input-field.dropdown-no-filter'; - - function GitLabDropdown(el1, options) { - var searchFields, selector, self; - this.el = el1; - this.options = options; - this.updateLabel = this.updateLabel.bind(this); - this.hidden = this.hidden.bind(this); - this.opened = this.opened.bind(this); - this.shouldPropagate = this.shouldPropagate.bind(this); - self = this; - selector = $(this.el).data('target'); - this.dropdown = selector != null ? $(selector) : $(this.el).parent(); - // Set Defaults - this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); - this.noFilterInput = this.options.noFilterInput || this.getElement(NO_FILTER_INPUT); - this.highlight = Boolean(this.options.highlight); - this.icon = Boolean(this.options.icon); - this.filterInputBlur = - this.options.filterInputBlur != null ? this.options.filterInputBlur : true; - // If no input is passed create a default one - self = this; - // If selector was passed - if (_.isString(this.filterInput)) { - this.filterInput = this.getElement(this.filterInput); + // Fetch the data through ajax if the data is a string + return axios.get(this.dataEndpoint).then(({ data }) => { + if (this.options.success) { + return this.options.success(data); } - searchFields = this.options.search ? this.options.search.fields : []; - if (this.options.data) { - // If we provided data - // data could be an array of objects or a group of arrays - if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { - this.fullData = this.options.data; - currentIndex = -1; - this.parseData(this.options.data); - this.focusTextInput(); - } else { - this.remote = new GitLabDropdownRemote(this.options.data, { - dataType: this.options.dataType, - beforeSend: this.toggleLoading.bind(this), - success: (function(_this) { - return function(data) { - _this.fullData = data; - _this.parseData(_this.fullData); - _this.focusTextInput(); - - // Update dropdown position since remote data may have changed dropdown size - _this.dropdown.find('.dropdown-menu-toggle').dropdown('update'); - - if ( - _this.options.filterable && - _this.filter && - _this.filter.input && - _this.filter.input.val() && - _this.filter.input.val().trim() !== '' - ) { - return _this.filter.input.trigger('input'); - } - }; - // Remote data - })(this), - instance: this, - }); - } - } - if (this.noFilterInput.length) { - this.plainInput = new GitLabDropdownInput(this.noFilterInput, this.options); - this.plainInput.onInput(this.addInput.bind(this)); - } - // Init filterable - if (this.options.filterable) { - this.filter = new GitLabDropdownFilter(this.filterInput, { - elIsInput: $(this.el).is('input'), - filterInputBlur: this.filterInputBlur, - filterByText: this.options.filterByText, - onFilter: this.options.onFilter, - remote: this.options.filterRemote, - query: this.options.data, - keys: searchFields, + }); +}; + +function GitLabDropdown(el1, options) { + let selector, self; + this.el = el1; + this.options = options; + this.updateLabel = this.updateLabel.bind(this); + this.hidden = this.hidden.bind(this); + this.opened = this.opened.bind(this); + this.shouldPropagate = this.shouldPropagate.bind(this); + self = this; + selector = $(this.el).data('target'); + this.dropdown = selector != null ? $(selector) : $(this.el).parent(); + // Set Defaults + this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); + this.noFilterInput = this.options.noFilterInput || this.getElement(NO_FILTER_INPUT); + this.highlight = Boolean(this.options.highlight); + this.icon = Boolean(this.options.icon); + this.filterInputBlur = this.options.filterInputBlur != null ? this.options.filterInputBlur : true; + // If no input is passed create a default one + self = this; + // If selector was passed + if (_.isString(this.filterInput)) { + this.filterInput = this.getElement(this.filterInput); + } + const searchFields = this.options.search ? this.options.search.fields : []; + if (this.options.data) { + // If we provided data + // data could be an array of objects or a group of arrays + if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { + this.fullData = this.options.data; + currentIndex = -1; + this.parseData(this.options.data); + this.focusTextInput(); + } else { + this.remote = new GitLabDropdownRemote(this.options.data, { + dataType: this.options.dataType, + beforeSend: this.toggleLoading.bind(this), + success: data => { + this.fullData = data; + this.parseData(this.fullData); + this.focusTextInput(); + + // Update dropdown position since remote data may have changed dropdown size + this.dropdown.find('.dropdown-menu-toggle').dropdown('update'); + + if ( + this.options.filterable && + this.filter && + this.filter.input && + this.filter.input.val() && + this.filter.input.val().trim() !== '' + ) { + return this.filter.input.trigger('input'); + } + }, instance: this, - elements: (function(_this) { - return function() { - selector = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`; - if (_this.dropdown.find('.dropdown-toggle-page').length) { - selector = `.dropdown-page-one ${selector}`; - } - return $(selector, this.instance.dropdown); - }; - })(this), - data: (function(_this) { - return function() { - return _this.fullData; - }; - })(this), - callback: (function(_this) { - return function(data) { - _this.parseData(data); - if (_this.filterInput.val() !== '') { - selector = SELECTABLE_CLASSES; - if (_this.dropdown.find('.dropdown-toggle-page').length) { - selector = `.dropdown-page-one ${selector}`; - } - if ($(_this.el).is('input')) { - currentIndex = -1; - } else { - $(selector, _this.dropdown) - .first() - .find('a') - .addClass('is-focused'); - currentIndex = 0; - } - } - }; - })(this), }); } - // Event listeners - this.dropdown.on('shown.bs.dropdown', this.opened); - this.dropdown.on('hidden.bs.dropdown', this.hidden); - $(this.el).on('update.label', this.updateLabel); - this.dropdown.on('click', '.dropdown-menu, .dropdown-menu-close', this.shouldPropagate); - this.dropdown.on( - 'keyup', - (function(_this) { - return function(e) { - // Escape key - if (e.which === 27) { - return $('.dropdown-menu-close', _this.dropdown).trigger('click'); + } + if (this.noFilterInput.length) { + this.plainInput = new GitLabDropdownInput(this.noFilterInput, this.options); + this.plainInput.onInput(this.addInput.bind(this)); + } + // Init filterable + if (this.options.filterable) { + this.filter = new GitLabDropdownFilter(this.filterInput, { + elIsInput: $(this.el).is('input'), + filterInputBlur: this.filterInputBlur, + filterByText: this.options.filterByText, + onFilter: this.options.onFilter, + remote: this.options.filterRemote, + query: this.options.data, + keys: searchFields, + instance: this, + elements: () => { + selector = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; + } + return $(selector, this.dropdown); + }, + data: () => this.fullData, + callback: data => { + this.parseData(data); + if (this.filterInput.val() !== '') { + selector = SELECTABLE_CLASSES; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; } - }; - })(this), - ); - this.dropdown.on( - 'blur', - 'a', - (function(_this) { - return function(e) { - var $dropdownMenu, $relatedTarget; - if (e.relatedTarget != null) { - $relatedTarget = $(e.relatedTarget); - $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); - if ($dropdownMenu.length === 0) { - return _this.dropdown.removeClass('show'); - } + if ($(this.el).is('input')) { + currentIndex = -1; + } else { + $(selector, this.dropdown) + .first() + .find('a') + .addClass('is-focused'); + currentIndex = 0; } - }; - })(this), - ); - if (this.dropdown.find('.dropdown-toggle-page').length) { - this.dropdown.find('.dropdown-toggle-page, .dropdown-menu-back').on( - 'click', - (function(_this) { - return function(e) { - e.preventDefault(); - e.stopPropagation(); - return _this.togglePage(); - }; - })(this), - ); + } + }, + }); + } + // Event listeners + this.dropdown.on('shown.bs.dropdown', this.opened); + this.dropdown.on('hidden.bs.dropdown', this.hidden); + $(this.el).on('update.label', this.updateLabel); + this.dropdown.on('click', '.dropdown-menu, .dropdown-menu-close', this.shouldPropagate); + this.dropdown.on('keyup', e => { + // Escape key + if (e.which === 27) { + return $('.dropdown-menu-close', this.dropdown).trigger('click'); } - if (this.options.selectable) { - selector = '.dropdown-content a'; - if (this.dropdown.find('.dropdown-toggle-page').length) { - selector = '.dropdown-page-one .dropdown-content a'; + }); + this.dropdown.on('blur', 'a', e => { + let $dropdownMenu, $relatedTarget; + if (e.relatedTarget != null) { + $relatedTarget = $(e.relatedTarget); + $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); + if ($dropdownMenu.length === 0) { + return this.dropdown.removeClass('show'); + } + } + }); + if (this.dropdown.find('.dropdown-toggle-page').length) { + this.dropdown.find('.dropdown-toggle-page, .dropdown-menu-back').on('click', e => { + e.preventDefault(); + e.stopPropagation(); + return this.togglePage(); + }); + } + if (this.options.selectable) { + selector = '.dropdown-content a'; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = '.dropdown-page-one .dropdown-content a'; + } + this.dropdown.on('click', selector, e => { + const $el = $(e.currentTarget); + const selected = self.rowClicked($el); + const selectedObj = selected ? selected[0] : null; + const isMarking = selected ? selected[1] : null; + if (this.options.clicked) { + this.options.clicked.call(this, { + selectedObj, + $el, + e, + isMarking, + }); } - this.dropdown.on('click', selector, e => { - var $el, selected, selectedObj, isMarking; - $el = $(e.currentTarget); - selected = self.rowClicked($el); - selectedObj = selected ? selected[0] : null; - isMarking = selected ? selected[1] : null; - if (this.options.clicked) { - this.options.clicked.call(this, { - selectedObj, - $el, - e, - isMarking, - }); - } - // Update label right after all modifications in dropdown has been done - if (this.options.toggleLabel) { - this.updateLabel(selectedObj, $el, this); - } + // Update label right after all modifications in dropdown has been done + if (this.options.toggleLabel) { + this.updateLabel(selectedObj, $el, this); + } - $el.trigger('blur'); - }); - } + $el.trigger('blur'); + }); } +} - // Finds an element inside wrapper element - GitLabDropdown.prototype.getElement = function(selector) { - return this.dropdown.find(selector); - }; +// Finds an element inside wrapper element +GitLabDropdown.prototype.getElement = function(selector) { + return this.dropdown.find(selector); +}; - GitLabDropdown.prototype.toggleLoading = function() { - return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); - }; +GitLabDropdown.prototype.toggleLoading = function() { + return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); +}; - GitLabDropdown.prototype.togglePage = function() { - var menu; - menu = $('.dropdown-menu', this.dropdown); - if (menu.hasClass(PAGE_TWO_CLASS)) { - if (this.remote) { - this.remote.execute(); - } +GitLabDropdown.prototype.togglePage = function() { + const menu = $('.dropdown-menu', this.dropdown); + if (menu.hasClass(PAGE_TWO_CLASS)) { + if (this.remote) { + this.remote.execute(); } - menu.toggleClass(PAGE_TWO_CLASS); - // Focus first visible input on active page - return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); - }; - - GitLabDropdown.prototype.parseData = function(data) { - var full_html, groupData, html, name; - this.renderedData = data; - if (this.options.filterable && data.length === 0) { - // render no matching results - html = [this.noResults()]; - } else { - // Handle array groups - if (isObject(data)) { - html = []; - for (name in data) { - groupData = data[name]; - html.push( - this.renderItem( - { - content: name, - type: 'header', - }, - name, - ), - ); - this.renderData(groupData, name).map(item => html.push(item)); - } - } else { - // Render each row - html = this.renderData(data); + } + menu.toggleClass(PAGE_TWO_CLASS); + // Focus first visible input on active page + return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); +}; + +GitLabDropdown.prototype.parseData = function(data) { + let groupData, html, name; + this.renderedData = data; + if (this.options.filterable && data.length === 0) { + // render no matching results + html = [this.noResults()]; + } else { + // Handle array groups + if (isObject(data)) { + html = []; + for (name in data) { + groupData = data[name]; + html.push( + this.renderItem( + { + content: name, + type: 'header', + }, + name, + ), + ); + this.renderData(groupData, name).map(item => html.push(item)); } + } else { + // Render each row + html = this.renderData(data); } - // Render the full menu - full_html = this.renderMenu(html); - return this.appendMenu(full_html); - }; - - GitLabDropdown.prototype.renderData = function(data, group) { - return data.map((obj, index) => this.renderItem(obj, group || false, index)); - }; - - GitLabDropdown.prototype.shouldPropagate = function(e) { - var $target; - if (this.options.multiSelect || this.options.shouldPropagate === false) { - $target = $(e.target); - if ( - $target && - !$target.hasClass('dropdown-menu-close') && - !$target.hasClass('dropdown-menu-close-icon') && - !$target.data('isLink') - ) { - e.stopPropagation(); - - // This prevents automatic scrolling to the top - if ($target.closest('a').length) { - return false; - } - } + } + // Render the full menu + const full_html = this.renderMenu(html); + return this.appendMenu(full_html); +}; + +GitLabDropdown.prototype.renderData = function(data, group) { + return data.map((obj, index) => this.renderItem(obj, group || false, index)); +}; - return true; +GitLabDropdown.prototype.shouldPropagate = function(e) { + let $target; + if (this.options.multiSelect || this.options.shouldPropagate === false) { + $target = $(e.target); + if ( + $target && + !$target.hasClass('dropdown-menu-close') && + !$target.hasClass('dropdown-menu-close-icon') && + !$target.data('isLink') + ) { + e.stopPropagation(); + + // This prevents automatic scrolling to the top + if ($target.closest('a').length) { + return false; + } } - }; - - GitLabDropdown.prototype.filteredFullData = function() { - return this.fullData.filter( - r => - typeof r === 'object' && - !Object.prototype.hasOwnProperty.call(r, 'beforeDivider') && - !Object.prototype.hasOwnProperty.call(r, 'header'), - ); - }; - GitLabDropdown.prototype.opened = function(e) { - var contentHtml; - this.resetRows(); - this.addArrowKeyEvent(); + return true; + } +}; - const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle'); - const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update'); - const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open'); - const hasMultiSelect = dropdownToggle.hasClass('js-multiselect'); +GitLabDropdown.prototype.filteredFullData = function() { + return this.fullData.filter( + r => + typeof r === 'object' && + !Object.prototype.hasOwnProperty.call(r, 'beforeDivider') && + !Object.prototype.hasOwnProperty.call(r, 'header'), + ); +}; - // Makes indeterminate items effective - if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) { - this.parseData(this.fullData); - } +GitLabDropdown.prototype.opened = function(e) { + this.resetRows(); + this.addArrowKeyEvent(); - // Process the data to make sure rendered data - // matches the correct layout - const inputValue = this.filterInput.val(); - if (this.fullData && hasMultiSelect && this.options.processData && inputValue.length === 0) { - this.options.processData.call( - this.options, - inputValue, - this.filteredFullData(), - this.parseData.bind(this), - ); - } + const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle'); + const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update'); + const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open'); + const hasMultiSelect = dropdownToggle.hasClass('js-multiselect'); - contentHtml = $('.dropdown-content', this.dropdown).html(); - if (this.remote && contentHtml === '') { - this.remote.execute(); + // Makes indeterminate items effective + if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) { + this.parseData(this.fullData); + } + + // Process the data to make sure rendered data + // matches the correct layout + const inputValue = this.filterInput.val(); + if (this.fullData && hasMultiSelect && this.options.processData && inputValue.length === 0) { + this.options.processData.call( + this.options, + inputValue, + this.filteredFullData(), + this.parseData.bind(this), + ); + } + + const contentHtml = $('.dropdown-content', this.dropdown).html(); + if (this.remote && contentHtml === '') { + this.remote.execute(); + } else { + this.focusTextInput(); + } + + if (this.options.showMenuAbove) { + this.positionMenuAbove(); + } + + if (this.options.opened) { + if (this.options.preserveContext) { + this.options.opened(e); } else { - this.focusTextInput(); + this.options.opened.call(this, e); } + } - if (this.options.showMenuAbove) { - this.positionMenuAbove(); - } + return this.dropdown.trigger('shown.gl.dropdown'); +}; - if (this.options.opened) { - if (this.options.preserveContext) { - this.options.opened(e); - } else { - this.options.opened.call(this, e); - } - } +GitLabDropdown.prototype.positionMenuAbove = function() { + const $menu = this.dropdown.find('.dropdown-menu'); - return this.dropdown.trigger('shown.gl.dropdown'); - }; + $menu.addClass('dropdown-open-top'); + $menu.css('top', 'initial'); + $menu.css('bottom', '100%'); +}; - GitLabDropdown.prototype.positionMenuAbove = function() { - var $menu = this.dropdown.find('.dropdown-menu'); +GitLabDropdown.prototype.hidden = function(e) { + this.resetRows(); + this.removeArrowKeyEvent(); + const $input = this.dropdown.find('.dropdown-input-field'); + if (this.options.filterable) { + $input.blur(); + } + if (this.dropdown.find('.dropdown-toggle-page').length) { + $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); + } + if (this.options.hidden) { + this.options.hidden.call(this, e); + } + return this.dropdown.trigger('hidden.gl.dropdown'); +}; - $menu.addClass('dropdown-open-top'); - $menu.css('top', 'initial'); - $menu.css('bottom', '100%'); - }; +// Render the full menu +GitLabDropdown.prototype.renderMenu = function(html) { + if (this.options.renderMenu) { + return this.options.renderMenu(html); + } else { + return $('<ul>').append(html); + } +}; - GitLabDropdown.prototype.hidden = function(e) { - var $input; - this.resetRows(); - this.removeArrowKeyEvent(); - $input = this.dropdown.find('.dropdown-input-field'); - if (this.options.filterable) { - $input.blur(); - } - if (this.dropdown.find('.dropdown-toggle-page').length) { - $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); - } - if (this.options.hidden) { - this.options.hidden.call(this, e); - } - return this.dropdown.trigger('hidden.gl.dropdown'); - }; +// Append the menu into the dropdown +GitLabDropdown.prototype.appendMenu = function(html) { + return this.clearMenu().append(html); +}; - // Render the full menu - GitLabDropdown.prototype.renderMenu = function(html) { - if (this.options.renderMenu) { - return this.options.renderMenu(html); +GitLabDropdown.prototype.clearMenu = function() { + let selector; + selector = '.dropdown-content'; + if (this.dropdown.find('.dropdown-toggle-page').length) { + if (this.options.containerSelector) { + selector = this.options.containerSelector; } else { - return $('<ul>').append(html); + selector = '.dropdown-page-one .dropdown-content'; } - }; + } - // Append the menu into the dropdown - GitLabDropdown.prototype.appendMenu = function(html) { - return this.clearMenu().append(html); - }; + return $(selector, this.dropdown).empty(); +}; - GitLabDropdown.prototype.clearMenu = function() { - var selector; - selector = '.dropdown-content'; - if (this.dropdown.find('.dropdown-toggle-page').length) { - if (this.options.containerSelector) { - selector = this.options.containerSelector; - } else { - selector = '.dropdown-page-one .dropdown-content'; - } - } +GitLabDropdown.prototype.renderItem = function(data, group, index) { + let parent; - return $(selector, this.dropdown).empty(); - }; + if (this.dropdown && this.dropdown[0]) { + parent = this.dropdown[0].parentNode; + } - GitLabDropdown.prototype.renderItem = function(data, group, index) { - let parent; + return renderItem({ + instance: this, + options: Object.assign({}, this.options, { + icon: this.icon, + highlight: this.highlight, + highlightText: text => this.highlightTextMatches(text, this.filterInput.val()), + highlightTemplate: this.highlightTemplate.bind(this), + parent, + }), + data, + group, + index, + }); +}; - if (this.dropdown && this.dropdown[0]) { - parent = this.dropdown[0].parentNode; - } +GitLabDropdown.prototype.highlightTemplate = function(text, template) { + return `"<b>${_.escape(text)}</b>" ${template}`; +}; - return renderItem({ - instance: this, - options: Object.assign({}, this.options, { - icon: this.icon, - highlight: this.highlight, - highlightText: text => this.highlightTextMatches(text, this.filterInput.val()), - highlightTemplate: this.highlightTemplate.bind(this), - parent, - }), - data, - group, - index, - }); - }; - - GitLabDropdown.prototype.highlightTemplate = function(text, template) { - return `"<b>${_.escape(text)}</b>" ${template}`; - }; - - GitLabDropdown.prototype.highlightTextMatches = function(text, term) { - const occurrences = fuzzaldrinPlus.match(text, term); - const { indexOf } = []; - - return text - .split('') - .map((character, i) => { - if (indexOf.call(occurrences, i) !== -1) { - return `<b>${character}</b>`; - } else { - return character; - } - }) - .join(''); - }; - - GitLabDropdown.prototype.noResults = function() { - return '<li class="dropdown-menu-empty-item"><a>No matching results</a></li>'; - }; - - GitLabDropdown.prototype.rowClicked = function(el) { - var field, groupName, isInput, selectedIndex, selectedObject, value, isMarking; - - const { fieldName } = this.options; - isInput = $(this.el).is('input'); - if (this.renderedData) { - groupName = el.data('group'); - if (groupName) { - selectedIndex = el.data('index'); - selectedObject = this.renderedData[groupName][selectedIndex]; - } else { - selectedIndex = el.closest('li').index(); - this.selectedIndex = selectedIndex; - selectedObject = this.renderedData[selectedIndex]; - } - } +GitLabDropdown.prototype.highlightTextMatches = function(text, term) { + const occurrences = fuzzaldrinPlus.match(text, term); + const { indexOf } = []; - if (this.options.vue) { - if (el.hasClass(ACTIVE_CLASS)) { - el.removeClass(ACTIVE_CLASS); + return text + .split('') + .map((character, i) => { + if (indexOf.call(occurrences, i) !== -1) { + return `<b>${character}</b>`; } else { - el.addClass(ACTIVE_CLASS); + return character; } + }) + .join(''); +}; - return [selectedObject]; - } - - field = []; - value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id; - if (isInput) { - field = $(this.el); - } else if (value != null) { - field = this.dropdown - .parent() - .find(`input[name='${fieldName}'][value='${value.toString().replace(/'/g, "\\'")}']`); - } +GitLabDropdown.prototype.noResults = function() { + return '<li class="dropdown-menu-empty-item"><a>No matching results</a></li>'; +}; - if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { - return [selectedObject]; +GitLabDropdown.prototype.rowClicked = function(el) { + let field, groupName, selectedIndex, selectedObject, isMarking; + const { fieldName } = this.options; + const isInput = $(this.el).is('input'); + if (this.renderedData) { + groupName = el.data('group'); + if (groupName) { + selectedIndex = el.data('index'); + selectedObject = this.renderedData[groupName][selectedIndex]; + } else { + selectedIndex = el.closest('li').index(); + this.selectedIndex = selectedIndex; + selectedObject = this.renderedData[selectedIndex]; } + } - if (el.hasClass(ACTIVE_CLASS) && value !== 0) { - isMarking = false; + if (this.options.vue) { + if (el.hasClass(ACTIVE_CLASS)) { el.removeClass(ACTIVE_CLASS); - if (field && field.length) { - this.clearField(field, isInput); - } - } else if (el.hasClass(INDETERMINATE_CLASS)) { - isMarking = true; - el.addClass(ACTIVE_CLASS); - el.removeClass(INDETERMINATE_CLASS); - if (field && field.length && value == null) { - this.clearField(field, isInput); - } - if ((!field || !field.length) && fieldName) { - this.addInput(fieldName, value, selectedObject); - } } else { - isMarking = true; - if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { - this.dropdown.find(`.${ACTIVE_CLASS}`).removeClass(ACTIVE_CLASS); - if (!isInput) { - this.dropdown - .parent() - .find(`input[name='${fieldName}']`) - .remove(); - } - } - if (field && field.length && value == null) { - this.clearField(field, isInput); - } - // Toggle active class for the tick mark el.addClass(ACTIVE_CLASS); - if (value != null) { - if ((!field || !field.length) && fieldName) { - this.addInput(fieldName, value, selectedObject); - } else if (field && field.length) { - field.val(value).trigger('change'); - } - } } - return [selectedObject, isMarking]; - }; + return [selectedObject]; + } + + field = []; + const value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id; + if (isInput) { + field = $(this.el); + } else if (value != null) { + field = this.dropdown + .parent() + .find(`input[name='${fieldName}'][value='${value.toString().replace(/'/g, "\\'")}']`); + } - GitLabDropdown.prototype.focusTextInput = function() { - if (this.options.filterable) { - const initialScrollTop = $(window).scrollTop(); + if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { + return [selectedObject]; + } - if (this.dropdown.is('.show') && !this.filterInput.is(':focus')) { - this.filterInput.focus(); + if (el.hasClass(ACTIVE_CLASS) && value !== 0) { + isMarking = false; + el.removeClass(ACTIVE_CLASS); + if (field && field.length) { + this.clearField(field, isInput); + } + } else if (el.hasClass(INDETERMINATE_CLASS)) { + isMarking = true; + el.addClass(ACTIVE_CLASS); + el.removeClass(INDETERMINATE_CLASS); + if (field && field.length && value == null) { + this.clearField(field, isInput); + } + if ((!field || !field.length) && fieldName) { + this.addInput(fieldName, value, selectedObject); + } + } else { + isMarking = true; + if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { + this.dropdown.find(`.${ACTIVE_CLASS}`).removeClass(ACTIVE_CLASS); + if (!isInput) { + this.dropdown + .parent() + .find(`input[name='${fieldName}']`) + .remove(); } - - if ($(window).scrollTop() < initialScrollTop) { - $(window).scrollTop(initialScrollTop); + } + if (field && field.length && value == null) { + this.clearField(field, isInput); + } + // Toggle active class for the tick mark + el.addClass(ACTIVE_CLASS); + if (value != null) { + if ((!field || !field.length) && fieldName) { + this.addInput(fieldName, value, selectedObject); + } else if (field && field.length) { + field.val(value).trigger('change'); } } - }; + } - GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject, single) { - var $input; - // Create hidden input for form - if (single) { - $(`input[name="${fieldName}"]`).remove(); - } + return [selectedObject, isMarking]; +}; - $input = $('<input>') - .attr('type', 'hidden') - .attr('name', fieldName) - .val(value); - if (this.options.inputId != null) { - $input.attr('id', this.options.inputId); - } +GitLabDropdown.prototype.focusTextInput = function() { + if (this.options.filterable) { + const initialScrollTop = $(window).scrollTop(); - if (this.options.multiSelect) { - Object.keys(selectedObject).forEach(attribute => { - $input.attr(`data-${attribute}`, selectedObject[attribute]); - }); + if (this.dropdown.is('.show') && !this.filterInput.is(':focus')) { + this.filterInput.focus(); } - if (this.options.inputMeta) { - $input.attr('data-meta', selectedObject[this.options.inputMeta]); + if ($(window).scrollTop() < initialScrollTop) { + $(window).scrollTop(initialScrollTop); } + } +}; + +GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject, single) { + // Create hidden input for form + if (single) { + $(`input[name="${fieldName}"]`).remove(); + } + + const $input = $('<input>') + .attr('type', 'hidden') + .attr('name', fieldName) + .val(value); + if (this.options.inputId != null) { + $input.attr('id', this.options.inputId); + } - this.dropdown.before($input).trigger('change'); - }; + if (this.options.multiSelect) { + Object.keys(selectedObject).forEach(attribute => { + $input.attr(`data-${attribute}`, selectedObject[attribute]); + }); + } + + if (this.options.inputMeta) { + $input.attr('data-meta', selectedObject[this.options.inputMeta]); + } - GitLabDropdown.prototype.selectRowAtIndex = function(index) { - var $el, selector; - // If we pass an option index - if (typeof index !== 'undefined') { - selector = `${SELECTABLE_CLASSES}:eq(${index}) a`; + this.dropdown.before($input).trigger('change'); +}; + +GitLabDropdown.prototype.selectRowAtIndex = function(index) { + let selector; + // If we pass an option index + if (typeof index !== 'undefined') { + selector = `${SELECTABLE_CLASSES}:eq(${index}) a`; + } else { + selector = '.dropdown-content .is-focused'; + } + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; + } + // simulate a click on the first link + const $el = $(selector, this.dropdown); + if ($el.length) { + const href = $el.attr('href'); + if (href && href !== '#') { + visitUrl(href); } else { - selector = '.dropdown-content .is-focused'; + $el.trigger('click'); } - if (this.dropdown.find('.dropdown-toggle-page').length) { - selector = `.dropdown-page-one ${selector}`; - } - // simulate a click on the first link - $el = $(selector, this.dropdown); - if ($el.length) { - var href = $el.attr('href'); - if (href && href !== '#') { - visitUrl(href); - } else { - $el.trigger('click'); - } - } - }; + } +}; - GitLabDropdown.prototype.addArrowKeyEvent = function() { - var ARROW_KEY_CODES, selector; - ARROW_KEY_CODES = [38, 40]; - selector = SELECTABLE_CLASSES; - if (this.dropdown.find('.dropdown-toggle-page').length) { - selector = `.dropdown-page-one ${selector}`; +GitLabDropdown.prototype.addArrowKeyEvent = function() { + let selector; + const ARROW_KEY_CODES = [38, 40]; + selector = SELECTABLE_CLASSES; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; + } + return $('body').on('keydown', e => { + let $listItems, PREV_INDEX; + const currentKeyCode = e.which; + if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) { + e.preventDefault(); + e.stopImmediatePropagation(); + PREV_INDEX = currentIndex; + $listItems = $(selector, this.dropdown); + // if @options.filterable + // $input.blur() + if (currentKeyCode === 40) { + // Move down + if (currentIndex < $listItems.length - 1) { + currentIndex += 1; + } + } else if (currentKeyCode === 38) { + // Move up + if (currentIndex > 0) { + currentIndex -= 1; + } + } + if (currentIndex !== PREV_INDEX) { + this.highlightRowAtIndex($listItems, currentIndex); + } + return false; } - return $('body').on( - 'keydown', - (function(_this) { - return function(e) { - var $listItems, PREV_INDEX, currentKeyCode; - currentKeyCode = e.which; - if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) { - e.preventDefault(); - e.stopImmediatePropagation(); - PREV_INDEX = currentIndex; - $listItems = $(selector, _this.dropdown); - // if @options.filterable - // $input.blur() - if (currentKeyCode === 40) { - // Move down - if (currentIndex < $listItems.length - 1) { - currentIndex += 1; - } - } else if (currentKeyCode === 38) { - // Move up - if (currentIndex > 0) { - currentIndex -= 1; - } - } - if (currentIndex !== PREV_INDEX) { - _this.highlightRowAtIndex($listItems, currentIndex); - } - return false; - } - if (currentKeyCode === 13 && currentIndex !== -1) { - e.preventDefault(); - _this.selectRowAtIndex(); - } - }; - })(this), - ); - }; - - GitLabDropdown.prototype.removeArrowKeyEvent = function() { - return $('body').off('keydown'); - }; - - GitLabDropdown.prototype.resetRows = function resetRows() { - currentIndex = -1; - $('.is-focused', this.dropdown).removeClass('is-focused'); - }; - - GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) { - var $dropdownContent, - $listItem, - dropdownContentBottom, - dropdownContentHeight, - dropdownContentTop, - dropdownScrollTop, - listItemBottom, - listItemHeight, - listItemTop; - - if (!$listItems) { - $listItems = $(SELECTABLE_CLASSES, this.dropdown); + if (currentKeyCode === 13 && currentIndex !== -1) { + e.preventDefault(); + this.selectRowAtIndex(); } + }); +}; - // Remove the class for the previously focused row - $('.is-focused', this.dropdown).removeClass('is-focused'); - // Update the class for the row at the specific index - $listItem = $listItems.eq(index); - $listItem.find('a:first-child').addClass('is-focused'); - // Dropdown content scroll area - $dropdownContent = $listItem.closest('.dropdown-content'); - dropdownScrollTop = $dropdownContent.scrollTop(); - dropdownContentHeight = $dropdownContent.outerHeight(); - dropdownContentTop = $dropdownContent.prop('offsetTop'); - dropdownContentBottom = dropdownContentTop + dropdownContentHeight; - // Get the offset bottom of the list item - listItemHeight = $listItem.outerHeight(); - listItemTop = $listItem.prop('offsetTop'); - listItemBottom = listItemTop + listItemHeight; - if (!index) { - // Scroll the dropdown content to the top - $dropdownContent.scrollTop(0); - } else if (index === $listItems.length - 1) { - // Scroll the dropdown content to the bottom - $dropdownContent.scrollTop($dropdownContent.prop('scrollHeight')); - } else if (listItemBottom > dropdownContentBottom + dropdownScrollTop) { - // Scroll the dropdown content down - $dropdownContent.scrollTop( - listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING, - ); - } else if (listItemTop < dropdownContentTop + dropdownScrollTop) { - // Scroll the dropdown content up - return $dropdownContent.scrollTop( - listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING, - ); - } - }; +GitLabDropdown.prototype.removeArrowKeyEvent = function() { + return $('body').off('keydown'); +}; - GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { - if (selected == null) { - selected = null; - } - if (el == null) { - el = null; - } - if (instance == null) { - instance = null; - } +GitLabDropdown.prototype.resetRows = function resetRows() { + currentIndex = -1; + $('.is-focused', this.dropdown).removeClass('is-focused'); +}; - let toggleText = this.options.toggleLabel(selected, el, instance); - if (this.options.updateLabel) { - // Option to override the dropdown label text - toggleText = this.options.updateLabel; - } +GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) { + if (!$listItems) { + $listItems = $(SELECTABLE_CLASSES, this.dropdown); + } + + // Remove the class for the previously focused row + $('.is-focused', this.dropdown).removeClass('is-focused'); + // Update the class for the row at the specific index + const $listItem = $listItems.eq(index); + $listItem.find('a:first-child').addClass('is-focused'); + // Dropdown content scroll area + const $dropdownContent = $listItem.closest('.dropdown-content'); + const dropdownScrollTop = $dropdownContent.scrollTop(); + const dropdownContentHeight = $dropdownContent.outerHeight(); + const dropdownContentTop = $dropdownContent.prop('offsetTop'); + const dropdownContentBottom = dropdownContentTop + dropdownContentHeight; + // Get the offset bottom of the list item + const listItemHeight = $listItem.outerHeight(); + const listItemTop = $listItem.prop('offsetTop'); + const listItemBottom = listItemTop + listItemHeight; + if (!index) { + // Scroll the dropdown content to the top + $dropdownContent.scrollTop(0); + } else if (index === $listItems.length - 1) { + // Scroll the dropdown content to the bottom + $dropdownContent.scrollTop($dropdownContent.prop('scrollHeight')); + } else if (listItemBottom > dropdownContentBottom + dropdownScrollTop) { + // Scroll the dropdown content down + $dropdownContent.scrollTop( + listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING, + ); + } else if (listItemTop < dropdownContentTop + dropdownScrollTop) { + // Scroll the dropdown content up + return $dropdownContent.scrollTop( + listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING, + ); + } +}; + +GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { + if (selected == null) { + selected = null; + } + if (el == null) { + el = null; + } + if (instance == null) { + instance = null; + } - return $(this.el) - .find('.dropdown-toggle-text') - .text(toggleText); - }; + let toggleText = this.options.toggleLabel(selected, el, instance); + if (this.options.updateLabel) { + // Option to override the dropdown label text + toggleText = this.options.updateLabel; + } - GitLabDropdown.prototype.clearField = function(field, isInput) { - return isInput ? field.val('') : field.remove(); - }; + return $(this.el) + .find('.dropdown-toggle-text') + .text(toggleText); +}; - return GitLabDropdown; -})(); +GitLabDropdown.prototype.clearField = function(field, isInput) { + return isInput ? field.val('') : field.remove(); +}; $.fn.glDropdown = function(opts) { return this.each(function() { |