diff options
author | Alfredo Sumaran <alfredo@gitlab.com> | 2016-03-14 16:14:29 -0500 |
---|---|---|
committer | Alfredo Sumaran <alfredo@gitlab.com> | 2016-03-15 13:34:33 -0500 |
commit | d38ef7b5b07890d02256bf05cf6b126fceee5770 (patch) | |
tree | 1d3f7f9981ee639c9f1796f9d5a53df5ede65cbb | |
parent | dce5e9ce4824b62ef939aa635357a813a858322e (diff) | |
download | gitlab-ce-d38ef7b5b07890d02256bf05cf6b126fceee5770.tar.gz |
Use new dropdown class for search suggestions
-rw-r--r-- | app/assets/javascripts/gl_dropdown.js.coffee | 5 | ||||
-rw-r--r-- | app/assets/javascripts/search_autocomplete.js.coffee | 196 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/jquery.scss | 6 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/search.scss | 13 | ||||
-rw-r--r-- | app/views/layouts/_search.html.haml | 6 |
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) |