diff options
Diffstat (limited to 'app/assets/javascripts/gl_dropdown.js.coffee')
-rw-r--r-- | app/assets/javascripts/gl_dropdown.js.coffee | 183 |
1 files changed, 137 insertions, 46 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 1d1bfeb2e77..b49bd4565a7 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -11,6 +11,8 @@ class GitLabDropdownFilter $inputContainer = @input.parent() $clearButton = $inputContainer.find('.js-dropdown-input-clear') + @indeterminateIds = [] + # Clear click $clearButton.on 'click', (e) => e.preventDefault() @@ -35,20 +37,20 @@ class GitLabDropdownFilter if keyCode is 13 return false - clearTimeout timeout - timeout = setTimeout => - blur_field = @shouldBlur keyCode - search_text = @input.val() + # Only filter asynchronously only if option remote is set + if @options.remote + clearTimeout timeout + timeout = setTimeout => + blur_field = @shouldBlur keyCode - if blur_field and @filterInputBlur - @input.blur() + if blur_field and @filterInputBlur + @input.blur() - if @options.remote - @options.query search_text, (data) => + @options.query @input.val(), (data) => @options.callback(data) - else - @filter search_text - , 250 + , 250 + else + @filter @input.val() shouldBlur: (keyCode) -> return BLUR_KEYCODES.indexOf(keyCode) >= 0 @@ -60,9 +62,36 @@ class GitLabDropdownFilter results = data if search_text isnt '' - results = fuzzaldrinPlus.filter(data, search_text, - key: @options.keys - ) + # 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: @options.keys + ) + else + # If data is grouped therefore an [object Object]. e.g. + # { + # groupName1: [ + # { prop: 'foo' }, + # { prop: 'baz' } + # ], + # groupName2: [ + # { prop: 'abc' }, + # { prop: 'def' } + # ] + # } + if gl.utils.isObject data + results = {} + for key, group of data + tmp = fuzzaldrinPlus.filter(group, search_text, + key: @options.keys + ) + + if tmp.length + results[key] = tmp.map (item) -> item @options.callback results else @@ -115,6 +144,7 @@ class GitLabDropdown LOADING_CLASS = "is-loading" PAGE_TWO_CLASS = "is-page-two" ACTIVE_CLASS = "is-active" + INDETERMINATE_CLASS = "is-indeterminate" currentIndex = -1 FILTER_INPUT = '.dropdown-input .dropdown-input-field' @@ -141,8 +171,9 @@ class GitLabDropdown searchFields = if @options.search then @options.search.fields else []; if @options.data - # If data is an array - if _.isArray @options.data + # If we provided data + # data could be an array of objects or a group of arrays + if _.isObject(@options.data) and not _.isFunction(@options.data) @fullData = @options.data @parseData @options.data else @@ -154,9 +185,6 @@ class GitLabDropdown @fullData = data @parseData @fullData - - if @options.filterable - @filterInput.trigger 'keyup' } # Init filterable @@ -183,6 +211,7 @@ class GitLabDropdown @dropdown.on "shown.bs.dropdown", @opened @dropdown.on "hidden.bs.dropdown", @hidden + $(@el).on "update.label", @updateLabel @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on 'keyup', (e) => if e.which is 27 # Escape key @@ -230,19 +259,33 @@ class GitLabDropdown parseData: (data) -> @renderedData = data - # Render each row - html = $.map data, (obj) => - return @renderItem(obj) - if @options.filterable and data.length is 0 # render no matching results html = [@noResults()] + else + # Handle array groups + if gl.utils.isObject data + html = [] + for name, groupData of data + # Add header for each group + html.push(@renderItem(header: name, name)) + + @renderData(groupData, name) + .map (item) -> + html.push item + else + # Render each row + html = @renderData(data) # Render the full menu full_html = @renderMenu(html.join("")) @appendMenu(full_html) + renderData: (data, group = false) -> + data.map (obj, index) => + return @renderItem(obj, group, index) + shouldPropagate: (e) => if @options.multiSelect $target = $(e.target) @@ -256,6 +299,13 @@ class GitLabDropdown opened: => @addArrowKeyEvent() + if @options.setIndeterminateIds + @options.setIndeterminateIds.call(@) + + # Makes indeterminate items effective + if @fullData and @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update') + @parseData @fullData + contentHtml = $('.dropdown-content', @dropdown).html() if @remote && contentHtml is "" @remote.execute() @@ -267,12 +317,18 @@ class GitLabDropdown hidden: (e) => @removeArrayKeyEvent() + + $input = @dropdown.find(".dropdown-input-field") + if @options.filterable - @dropdown - .find(".dropdown-input-field") + $input .blur() .val("") - .trigger("keyup") + + # Triggering 'keyup' will re-render the dropdown which is not always required + # specially if we want to keep the state of the dropdown needed for bulk-assignment + if not @options.persistWhenHide + $input.trigger("keyup") if @dropdown.find(".dropdown-toggle-page").length $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS @@ -299,11 +355,10 @@ class GitLabDropdown selector = '.dropdown-content' if @dropdown.find(".dropdown-toggle-page").length selector = ".dropdown-page-one .dropdown-content" - $(selector, @dropdown).html html # Render the row - renderItem: (data) -> + renderItem: (data, group = false, index = false) -> html = "" # Divider @@ -317,7 +372,7 @@ class GitLabDropdown if @options.renderRow # Call the render function - html = @options.renderRow(data) + html = @options.renderRow.call(@options, data, @) else if not selected value = if @options.id then @options.id(data) else data.id @@ -346,8 +401,13 @@ class GitLabDropdown if @highlight text = @highlightTextMatches(text, @filterInput.val()) + if group + groupAttrs = "data-group='#{group}' data-index='#{index}'" + else + groupAttrs = '' + html = "<li> - <a href='#{url}' class='#{cssClass}'> + <a href='#{url}' #{groupAttrs} class='#{cssClass}'> #{text} </a> </li>" @@ -377,9 +437,15 @@ class GitLabDropdown rowClicked: (el) -> fieldName = @options.fieldName - selectedIndex = el.parent().index() if @renderedData - selectedObject = @renderedData[selectedIndex] + groupName = el.data('group') + if groupName + selectedIndex = el.data('index') + selectedObject = @renderedData[groupName][selectedIndex] + else + selectedIndex = el.closest('li').index() + selectedObject = @renderedData[selectedIndex] + value = if @options.id then @options.id(selectedObject, el) else selectedObject.id field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") if el.hasClass(ACTIVE_CLASS) @@ -388,9 +454,20 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - $(@el).find(".dropdown-toggle-text").text @options.toggleLabel + @updateLabel() else selectedObject + else if el.hasClass(INDETERMINATE_CLASS) + el.addClass ACTIVE_CLASS + el.removeClass INDETERMINATE_CLASS + + if not value? + field.remove() + + if not field.length and fieldName + @addInput(fieldName, value) + + return selectedObject else if not @options.multiSelect or el.hasClass('dropdown-clear-active') @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @@ -404,34 +481,45 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el) + @updateLabel(selectedObject, el) if value? if !field.length and fieldName - # Create hidden input for form - input = "<input type='hidden' name='#{fieldName}' value='#{value}' />" - if @options.inputId? - input = $(input) - .attr('id', @options.inputId) - @dropdown.before input + @addInput(fieldName, value) else field.val value return selectedObject - selectRowAtIndex: (index) -> - selector = ".dropdown-content li:not(.divider):eq(#{index}) a" + addInput: (fieldName, value)-> + # Create hidden input for form + $input = $('<input>').attr('type', 'hidden') + .attr('name', fieldName) + .val(value) + + if @options.inputId? + $input.attr('id', @options.inputId) + + @dropdown.before $input + + selectRowAtIndex: (e, index) -> + selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(#{index}) a" if @dropdown.find(".dropdown-toggle-page").length selector = ".dropdown-page-one #{selector}" # simulate a click on the first link - $(selector, @dropdown).trigger "click" + $el = $(selector, @dropdown) + + if $el.length + e.preventDefault() + e.stopImmediatePropagation() + $(selector, @dropdown)[0].click() addArrowKeyEvent: -> ARROW_KEY_CODES = [38, 40] $input = @dropdown.find(".dropdown-input-field") - selector = '.dropdown-content li:not(.divider)' + selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)' if @dropdown.find(".dropdown-toggle-page").length selector = ".dropdown-page-one #{selector}" @@ -459,8 +547,8 @@ class GitLabDropdown return false - if currentKeyCode is 13 - @selectRowAtIndex currentIndex + if currentKeyCode is 13 and currentIndex isnt -1 + @selectRowAtIndex e, currentIndex removeArrayKeyEvent: -> $('body').off 'keydown' @@ -492,6 +580,9 @@ class GitLabDropdown # Scroll the dropdown content up $dropdownContent.scrollTop(listItemTop - dropdownContentTop) + updateLabel: (selected = null, el = null) => + $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el) + $.fn.glDropdown = (opts) -> return @.each -> if (!$.data @, 'glDropdown') |