Merge branch 'dropdown-arrow-support' into 'master'
Dropdown arrow support When the dropdown is open, you can scroll through the list of items with the up & down arrow keys. When an item is focused, the enter triggers the click event for that row. Closes #14455 See merge request !3385
This commit is contained in:
commit
a1c794c591
|
@ -1,5 +1,6 @@
|
||||||
class GitLabDropdownFilter
|
class GitLabDropdownFilter
|
||||||
BLUR_KEYCODES = [27, 40]
|
BLUR_KEYCODES = [27, 40]
|
||||||
|
ARROW_KEY_CODES = [38, 40]
|
||||||
HAS_VALUE_CLASS = "has-value"
|
HAS_VALUE_CLASS = "has-value"
|
||||||
|
|
||||||
constructor: (@input, @options) ->
|
constructor: (@input, @options) ->
|
||||||
|
@ -22,19 +23,23 @@ class GitLabDropdownFilter
|
||||||
# Key events
|
# Key events
|
||||||
timeout = ""
|
timeout = ""
|
||||||
@input.on "keyup", (e) =>
|
@input.on "keyup", (e) =>
|
||||||
|
keyCode = e.which
|
||||||
|
|
||||||
|
return if ARROW_KEY_CODES.indexOf(keyCode) >= 0
|
||||||
|
|
||||||
if @input.val() isnt "" and !$inputContainer.hasClass HAS_VALUE_CLASS
|
if @input.val() isnt "" and !$inputContainer.hasClass HAS_VALUE_CLASS
|
||||||
$inputContainer.addClass HAS_VALUE_CLASS
|
$inputContainer.addClass HAS_VALUE_CLASS
|
||||||
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
|
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
|
||||||
$inputContainer.removeClass HAS_VALUE_CLASS
|
$inputContainer.removeClass HAS_VALUE_CLASS
|
||||||
|
|
||||||
if e.keyCode is 13 and @input.val() isnt ""
|
if keyCode is 13 and @input.val() isnt ""
|
||||||
if @options.enterCallback
|
if @options.enterCallback
|
||||||
@options.enterCallback()
|
@options.enterCallback()
|
||||||
return
|
return
|
||||||
|
|
||||||
clearTimeout timeout
|
clearTimeout timeout
|
||||||
timeout = setTimeout =>
|
timeout = setTimeout =>
|
||||||
blur_field = @shouldBlur e.keyCode
|
blur_field = @shouldBlur keyCode
|
||||||
search_text = @input.val()
|
search_text = @input.val()
|
||||||
|
|
||||||
if blur_field and @filterInputBlur
|
if blur_field and @filterInputBlur
|
||||||
|
@ -96,6 +101,7 @@ class GitLabDropdown
|
||||||
LOADING_CLASS = "is-loading"
|
LOADING_CLASS = "is-loading"
|
||||||
PAGE_TWO_CLASS = "is-page-two"
|
PAGE_TWO_CLASS = "is-page-two"
|
||||||
ACTIVE_CLASS = "is-active"
|
ACTIVE_CLASS = "is-active"
|
||||||
|
currentIndex = -1
|
||||||
|
|
||||||
FILTER_INPUT = '.dropdown-input .dropdown-input-field'
|
FILTER_INPUT = '.dropdown-input .dropdown-input-field'
|
||||||
|
|
||||||
|
@ -145,11 +151,11 @@ class GitLabDropdown
|
||||||
data: =>
|
data: =>
|
||||||
return @fullData
|
return @fullData
|
||||||
callback: (data) =>
|
callback: (data) =>
|
||||||
|
currentIndex = -1
|
||||||
@parseData data
|
@parseData data
|
||||||
@highlightRow 1
|
|
||||||
enterCallback: =>
|
enterCallback: =>
|
||||||
if @enterCallback
|
if @enterCallback
|
||||||
@selectFirstRow()
|
@selectRowAtIndex 0
|
||||||
|
|
||||||
# Event listeners
|
# Event listeners
|
||||||
|
|
||||||
|
@ -218,6 +224,8 @@ class GitLabDropdown
|
||||||
return true
|
return true
|
||||||
|
|
||||||
opened: =>
|
opened: =>
|
||||||
|
@addArrowKeyEvent()
|
||||||
|
|
||||||
contentHtml = $('.dropdown-content', @dropdown).html()
|
contentHtml = $('.dropdown-content', @dropdown).html()
|
||||||
if @remote && contentHtml is ""
|
if @remote && contentHtml is ""
|
||||||
@remote.execute()
|
@remote.execute()
|
||||||
|
@ -228,6 +236,7 @@ class GitLabDropdown
|
||||||
@dropdown.trigger('shown.gl.dropdown')
|
@dropdown.trigger('shown.gl.dropdown')
|
||||||
|
|
||||||
hidden: (e) =>
|
hidden: (e) =>
|
||||||
|
@removeArrayKeyEvent()
|
||||||
if @options.filterable
|
if @options.filterable
|
||||||
@dropdown
|
@dropdown
|
||||||
.find(".dropdown-input-field")
|
.find(".dropdown-input-field")
|
||||||
|
@ -307,11 +316,11 @@ class GitLabDropdown
|
||||||
if @highlight
|
if @highlight
|
||||||
text = @highlightTextMatches(text, @filterInput.val())
|
text = @highlightTextMatches(text, @filterInput.val())
|
||||||
|
|
||||||
html = "<li>"
|
html = "<li>
|
||||||
html += "<a href='#{url}' class='#{cssClass}'>"
|
<a href='#{url}' class='#{cssClass}'>
|
||||||
html += text
|
#{text}
|
||||||
html += "</a>"
|
</a>
|
||||||
html += "</li>"
|
</li>"
|
||||||
|
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
@ -322,11 +331,11 @@ class GitLabDropdown
|
||||||
).join('')
|
).join('')
|
||||||
|
|
||||||
noResults: ->
|
noResults: ->
|
||||||
html = "<li>"
|
html = "<li class='dropdown-menu-empty-link'>
|
||||||
html += "<a class='dropdown-menu-empty-link is-focused'>"
|
<a href='#' class='is-focused'>
|
||||||
html += "No matching results."
|
No matching results.
|
||||||
html += "</a>"
|
</a>
|
||||||
html += "</li>"
|
</li>"
|
||||||
|
|
||||||
highlightRow: (index) ->
|
highlightRow: (index) ->
|
||||||
if @filterInput.val() isnt ""
|
if @filterInput.val() isnt ""
|
||||||
|
@ -378,16 +387,81 @@ class GitLabDropdown
|
||||||
|
|
||||||
return selectedObject
|
return selectedObject
|
||||||
|
|
||||||
selectFirstRow: ->
|
selectRowAtIndex: (index) ->
|
||||||
selector = '.dropdown-content li:first-child a'
|
selector = ".dropdown-content li:not(.divider):eq(#{index}) a"
|
||||||
|
|
||||||
if @dropdown.find(".dropdown-toggle-page").length
|
if @dropdown.find(".dropdown-toggle-page").length
|
||||||
selector = ".dropdown-page-one .dropdown-content li:first-child a"
|
selector = ".dropdown-page-one #{selector}"
|
||||||
|
|
||||||
# simulate a click on the first link
|
# simulate a click on the first link
|
||||||
$(selector).trigger "click"
|
$(selector).trigger "click"
|
||||||
|
|
||||||
|
addArrowKeyEvent: ->
|
||||||
|
ARROW_KEY_CODES = [38, 40]
|
||||||
|
$input = @dropdown.find(".dropdown-input-field")
|
||||||
|
|
||||||
|
selector = '.dropdown-content li:not(.divider)'
|
||||||
|
if @dropdown.find(".dropdown-toggle-page").length
|
||||||
|
selector = ".dropdown-page-one #{selector}"
|
||||||
|
|
||||||
|
$('body').on 'keydown', (e) =>
|
||||||
|
currentKeyCode = e.which
|
||||||
|
|
||||||
|
if ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
|
||||||
|
PREV_INDEX = currentIndex
|
||||||
|
$listItems = $(selector, @dropdown)
|
||||||
|
|
||||||
|
# if @options.filterable
|
||||||
|
# $input.blur()
|
||||||
|
|
||||||
|
if currentKeyCode is 40
|
||||||
|
# Move down
|
||||||
|
currentIndex += 1 if currentIndex < ($listItems.length - 1)
|
||||||
|
else if currentKeyCode is 38
|
||||||
|
# Move up
|
||||||
|
currentIndex -= 1 if currentIndex > 0
|
||||||
|
|
||||||
|
@highlightRowAtIndex($listItems, currentIndex) if currentIndex isnt PREV_INDEX
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
if currentKeyCode is 13
|
||||||
|
@selectRowAtIndex currentIndex
|
||||||
|
|
||||||
|
removeArrayKeyEvent: ->
|
||||||
|
$('body').off 'keydown'
|
||||||
|
|
||||||
|
highlightRowAtIndex: ($listItems, index) ->
|
||||||
|
# Remove the class for the previously focused row
|
||||||
|
$('.is-focused', @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 listItemBottom > dropdownContentBottom + dropdownScrollTop
|
||||||
|
# Scroll the dropdown content down
|
||||||
|
$dropdownContent.scrollTop(listItemBottom - dropdownContentBottom)
|
||||||
|
else if listItemTop < dropdownContentTop + dropdownScrollTop
|
||||||
|
# Scroll the dropdown content up
|
||||||
|
$dropdownContent.scrollTop(listItemTop - dropdownContentTop)
|
||||||
|
|
||||||
$.fn.glDropdown = (opts) ->
|
$.fn.glDropdown = (opts) ->
|
||||||
return @.each ->
|
return @.each ->
|
||||||
if (!$.data @, 'glDropdown')
|
if (!$.data @, 'glDropdown')
|
||||||
$.data(@, 'glDropdown', new GitLabDropdown @, opts)
|
$.data(@, 'glDropdown', new GitLabDropdown @, opts)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue