summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfredo Sumaran <alfredo@gitlab.com>2016-03-14 16:14:29 -0500
committerAlfredo Sumaran <alfredo@gitlab.com>2016-03-15 13:34:33 -0500
commitd38ef7b5b07890d02256bf05cf6b126fceee5770 (patch)
tree1d3f7f9981ee639c9f1796f9d5a53df5ede65cbb
parentdce5e9ce4824b62ef939aa635357a813a858322e (diff)
downloadgitlab-ce-d38ef7b5b07890d02256bf05cf6b126fceee5770.tar.gz
Use new dropdown class for search suggestions
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee5
-rw-r--r--app/assets/javascripts/search_autocomplete.js.coffee196
-rw-r--r--app/assets/stylesheets/framework/jquery.scss6
-rw-r--r--app/assets/stylesheets/pages/search.scss13
-rw-r--r--app/views/layouts/_search.html.haml6
5 files changed, 119 insertions, 107 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 79696cc679d..0684e7852fa 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -4,6 +4,7 @@ class GitLabDropdownFilter
constructor: (@dropdown, @options) ->
{
@input
+ @filterInputBlur = true
} = @options
# Key events
@@ -19,7 +20,7 @@ class GitLabDropdownFilter
blur_field = @shouldBlur e.keyCode
search_text = @input.val()
- if blur_field
+ if blur_field && @filterInputBlur
@input.blur()
if @options.remote
@@ -89,6 +90,7 @@ class GitLabDropdown
# If no input is passed create a default one
@filterInput = @$(FILTER_INPUT)
@highlight = false
+ @filterInputBlur = true
} = @options
self = @
@@ -119,6 +121,7 @@ class GitLabDropdown
# Init filiterable
if @options.filterable
@filter = new GitLabDropdownFilter @dropdown,
+ filterInputBlur: @filterInputBlur
input: @filterInput
remote: @options.filterRemote
query: @options.data
diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee
index b8671900862..e21a140b2a6 100644
--- a/app/assets/javascripts/search_autocomplete.js.coffee
+++ b/app/assets/javascripts/search_autocomplete.js.coffee
@@ -1,21 +1,28 @@
class @SearchAutocomplete
+
+ KEYCODE =
+ ESCAPE: 27
+ BACKSPACE: 8
+ TAB: 9
+ ENTER: 13
+
constructor: (opts = {}) ->
{
@wrap = $('.search')
+
@optsEl = @wrap.find('.search-autocomplete-opts')
@autocompletePath = @optsEl.data('autocomplete-path')
@projectId = @optsEl.data('autocomplete-project-id') || ''
@projectRef = @optsEl.data('autocomplete-project-ref') || ''
+
} = opts
- @keyCode =
- ESCAPE: 27
- BACKSPACE: 8
- TAB: 9
- ENTER: 13
+ # Dropdown Element
+ @dropdown = @wrap.find('.dropdown')
@locationBadgeEl = @$('.search-location-badge')
@locationText = @$('.location-text')
+ @scopeInputEl = @$('#scope')
@searchInput = @$('.search-input')
@projectInputEl = @$('#search_project_id')
@groupInputEl = @$('#group_id')
@@ -25,9 +32,7 @@ class @SearchAutocomplete
@saveOriginalState()
- # If there's no location badge
- if !@locationBadgeEl.children().length
- @createAutocomplete()
+ @searchInput.addClass('disabled')
@bindEvents()
@@ -37,20 +42,6 @@ class @SearchAutocomplete
saveOriginalState: ->
@originalState = @serializeState()
- restoreOriginalState: ->
- inputs = Object.keys @originalState
-
- for input in inputs
- @$("##{input}").val(@originalState[input])
-
-
- if @originalState._location is ''
- @locationBadgeEl.html('')
- else
- @addLocationBadge(
- value: @originalState._location
- )
-
serializeState: ->
{
# Search Criteria
@@ -63,70 +54,85 @@ class @SearchAutocomplete
_location: $.trim(@locationText.text())
}
- createAutocomplete: ->
- @query = "?project_id=" + @projectId + "&project_ref=" + @projectRef
-
- @searchInput.catcomplete
- appendTo: 'form.navbar-form'
- source: @autocompletePath + @query
- minLength: 1
- maxShowItems: 15
- position:
- # { my: "left top", at: "left bottom", collision: "none" }
- my: "left-10 top+9"
- at: "left bottom"
- collision: "none"
- close: (e) ->
- e.preventDefault()
-
- select: (event, ui) =>
- # Pressing enter choses an alternative
- if event.keyCode is @keyCode.ENTER
- @goToResult(ui.item)
- else
- # Pressing tab sets the location
- if event.keyCode is @keyCode.TAB and ui.item.location?
- @setLocationBadge(ui.item)
- @searchInput
- .val('') # remove selected value from input
- .focus()
- else
- # If option is not a location go to page
- @goToResult(ui.item)
-
- # Return false to avoid focus on the next element
- return false
-
- @autocomplete = @searchInput.data 'customCatcomplete'
-
bindEvents: ->
@searchInput.on 'keydown', @onSearchInputKeyDown
@searchInput.on 'focus', @onSearchInputFocus
@searchInput.on 'blur', @onSearchInputBlur
- @wrap.on 'click', '.remove-badge', @onRemoveLocationBadgeClick
- onRemoveLocationBadgeClick: (e) =>
- e.preventDefault()
- @removeLocationBadge()
- @searchInput.focus()
+ enableAutocomplete: ->
+ self = @
+ @query = "?project_id=" + @projectId + "&project_ref=" + @projectRef
+ dropdownMenu = self.dropdown.find('.dropdown-menu')
+
+ @searchInput.glDropdown(
+ filterInputBlur: false
+ filterable: true
+ filterRemote: true
+ highlight: true
+ filterInput: 'input#search'
+ search:
+ fields: ['text']
+ data: (term, callback) ->
+ $.ajax
+ url: self.autocompletePath + self.query
+ data:
+ term: term
+ beforeSend: ->
+ # dropdownMenu.addClass 'is-loading'
+ success: (response) ->
+ data = []
+
+ # Save groups ordering according to server response
+ groupNames = _.unique(_.pluck(response, 'category'))
+
+ # Group results by category name
+ groups = _.groupBy response, (item) ->
+ item.category
+
+ # List results
+ for groupName in groupNames
+
+ # Add group header before list each group
+ data.push
+ header: groupName
+
+ # List group
+ for item in groups[groupName]
+ data.push
+ text: item.label
+ url: item.url
+
+ callback(data)
+ complete: ->
+ # dropdownMenu.removeClass 'is-loading'
+
+ )
+
+ @dropdown.addClass('open')
+ @searchInput.removeClass('disabled')
+ @autocomplete = true;
+
+ onDropdownOpen: (e) =>
+ @dropdown.dropdown('toggle')
onSearchInputKeyDown: (e) =>
# Remove tag when pressing backspace and input search is empty
- if e.keyCode is @keyCode.BACKSPACE and e.currentTarget.value is ''
+ if e.keyCode is KEYCODE.BACKSPACE and e.currentTarget.value is ''
@removeLocationBadge()
- # @destroyAutocomplete()
@searchInput.focus()
- else if e.keyCode is @keyCode.ESCAPE
+
+ else if e.keyCode is KEYCODE.ESCAPE
+ @searchInput.val('')
@restoreOriginalState()
else
- # Create new autocomplete if hasn't been created yet and there's no badge
+ # Create new autocomplete if it hasn't been created yet and there's no badge
if @autocomplete is undefined
- if !@locationBadgeEl.children().length
- @createAutocomplete()
+ if !@badgePresent()
+ @enableAutocomplete()
else
# There's a badge
- if @locationBadgeEl.children().length
- @destroyAutocomplete()
+ if @badgePresent()
+ @disableAutocomplete()
onSearchInputFocus: =>
@wrap.addClass('search-active')
@@ -135,7 +141,8 @@ class @SearchAutocomplete
@wrap.removeClass('search-active')
# If input is blank then restore state
- @restoreOriginalState() if @searchInput.val() is ''
+ if @searchInput.val() is ''
+ @restoreOriginalState()
addLocationBadge: (item) ->
category = if item.category? then "#{item.category}: " else ''
@@ -147,26 +154,28 @@ class @SearchAutocomplete
</span>"
@locationBadgeEl.html(html)
- setLocationBadge: (item) ->
- @addLocationBadge(item)
+ restoreOriginalState: ->
+ inputs = Object.keys @originalState
- # Reset input states
- @resetSearchState()
+ for input in inputs
+ @$("##{input}").val(@originalState[input])
- switch item.location
- when 'projects'
- @projectInputEl.val(item.id)
- # @searchCodeInputEl.val('true') # TODO: always true for projects?
- # @repositoryInputEl.val('master') # TODO: always master?
- when 'groups'
- @groupInputEl.val(item.id)
+ if @originalState._location is ''
+ @locationBadgeEl.html('')
+ else
+ @addLocationBadge(
+ value: @originalState._location
+ )
- removeLocationBadge: ->
- @locationBadgeEl.empty()
+ @dropdown.removeClass 'open'
- # Reset state
- @resetSearchState()
+ # Only add class if there's a badge
+ if @badgePresent()
+ @searchInput.addClass 'disabled'
+
+ badgePresent: ->
+ @locationBadgeEl.children().length
resetSearchState: ->
# Remove scope
@@ -184,10 +193,13 @@ class @SearchAutocomplete
# Remove repository ref
@repositoryInputEl.val('')
- goToResult: (result) ->
- location.href = result.url
+ removeLocationBadge: ->
+ @locationBadgeEl.empty()
+
+ # Reset state
+ @resetSearchState()
- destroyAutocomplete: ->
- @autocomplete.destroy() if @autocomplete isnt undefined
- @searchInput.attr('autocomplete', 'off')
+ disableAutocomplete: ->
+ if @autocomplete isnt undefined
+ @searchInput.addClass('disabled')
@autocomplete = undefined
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 76b4cea4778..85a6f4b8b55 100644
--- a/app/assets/stylesheets/framework/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
@@ -49,12 +49,6 @@
margin: 0;
}
}
-
- .ui-autocomplete-category {
- text-transform: uppercase;
- font-size: 11px;
- color: #7f8fa4;
- }
}
.ui-state-default {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index bc660985ecb..ff32bca98dc 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -21,7 +21,6 @@
}
}
-
.search {
margin-right: 10px;
margin-left: 10px;
@@ -51,7 +50,6 @@
padding: 4px;
width: 350px;
line-height: 24px;
- overflow: hidden;
}
.location-text {
@@ -69,7 +67,7 @@
padding: 0;
margin-left: 5px;
line-height: 25px;
- width: 100%;
+ width: 98%;
}
.location-badge {
@@ -89,7 +87,7 @@
}
.search-location-badge, .search-input-wrap {
- // Fallback if flex is not supported
+ // Fallback if flexbox is not supported
display: inline-block;
}
@@ -103,6 +101,7 @@
position: absolute;
right: 5px;
color: #E7E9ED;
+ top: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
@@ -114,9 +113,9 @@
}
}
- .ui-autocomplete-loading + .search-icon {
- @extend .fa-spinner;
- @extend .fa-spin;
+ .dropdown-header {
+ text-transform: uppercase;
+ font-size: 11px;
}
}
}
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index a004908fb6f..f051e7a1867 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -4,7 +4,11 @@
.search-location-badge
= render 'shared/location_badge'
.search-input-wrap
- = search_field_tag "search", nil, placeholder: 'Search', class: "search-input", spellcheck: false, tabindex: "1", autocomplete: 'off'
+ .dropdown{ data: {url: search_autocomplete_path } }
+ = search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off', data: { toggle: 'dropdown' }
+ .dropdown-menu.dropdown-select
+ = dropdown_content
+ = dropdown_loading
%i.search-icon
= hidden_field_tag :group_id, @group.try(:id)