summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/gl_dropdown.js.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/gl_dropdown.js.coffee')
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee183
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')