Commit 53df7263 authored by Alfredo Sumaran's avatar Alfredo Sumaran Committed by Jacob Schatz

Use new dropdown class for search suggestions

parent 2d0f8f92
...@@ -4,6 +4,7 @@ class GitLabDropdownFilter ...@@ -4,6 +4,7 @@ class GitLabDropdownFilter
constructor: (@dropdown, @options) -> constructor: (@dropdown, @options) ->
{ {
@input @input
@filterInputBlur = true
} = @options } = @options
# Key events # Key events
...@@ -19,7 +20,7 @@ class GitLabDropdownFilter ...@@ -19,7 +20,7 @@ class GitLabDropdownFilter
blur_field = @shouldBlur e.keyCode blur_field = @shouldBlur e.keyCode
search_text = @input.val() search_text = @input.val()
if blur_field if blur_field && @filterInputBlur
@input.blur() @input.blur()
if @options.remote if @options.remote
...@@ -89,6 +90,7 @@ class GitLabDropdown ...@@ -89,6 +90,7 @@ class GitLabDropdown
# If no input is passed create a default one # If no input is passed create a default one
@filterInput = @$(FILTER_INPUT) @filterInput = @$(FILTER_INPUT)
@highlight = false @highlight = false
@filterInputBlur = true
} = @options } = @options
self = @ self = @
...@@ -119,6 +121,7 @@ class GitLabDropdown ...@@ -119,6 +121,7 @@ class GitLabDropdown
# Init filiterable # Init filiterable
if @options.filterable if @options.filterable
@filter = new GitLabDropdownFilter @dropdown, @filter = new GitLabDropdownFilter @dropdown,
filterInputBlur: @filterInputBlur
input: @filterInput input: @filterInput
remote: @options.filterRemote remote: @options.filterRemote
query: @options.data query: @options.data
......
class @SearchAutocomplete class @SearchAutocomplete
KEYCODE =
ESCAPE: 27
BACKSPACE: 8
TAB: 9
ENTER: 13
constructor: (opts = {}) -> constructor: (opts = {}) ->
{ {
@wrap = $('.search') @wrap = $('.search')
@optsEl = @wrap.find('.search-autocomplete-opts') @optsEl = @wrap.find('.search-autocomplete-opts')
@autocompletePath = @optsEl.data('autocomplete-path') @autocompletePath = @optsEl.data('autocomplete-path')
@projectId = @optsEl.data('autocomplete-project-id') || '' @projectId = @optsEl.data('autocomplete-project-id') || ''
@projectRef = @optsEl.data('autocomplete-project-ref') || '' @projectRef = @optsEl.data('autocomplete-project-ref') || ''
} = opts } = opts
@keyCode = # Dropdown Element
ESCAPE: 27 @dropdown = @wrap.find('.dropdown')
BACKSPACE: 8
TAB: 9
ENTER: 13
@locationBadgeEl = @$('.search-location-badge') @locationBadgeEl = @$('.search-location-badge')
@locationText = @$('.location-text') @locationText = @$('.location-text')
@scopeInputEl = @$('#scope')
@searchInput = @$('.search-input') @searchInput = @$('.search-input')
@projectInputEl = @$('#search_project_id') @projectInputEl = @$('#search_project_id')
@groupInputEl = @$('#group_id') @groupInputEl = @$('#group_id')
...@@ -25,9 +32,7 @@ class @SearchAutocomplete ...@@ -25,9 +32,7 @@ class @SearchAutocomplete
@saveOriginalState() @saveOriginalState()
# If there's no location badge @searchInput.addClass('disabled')
if !@locationBadgeEl.children().length
@createAutocomplete()
@bindEvents() @bindEvents()
...@@ -37,20 +42,6 @@ class @SearchAutocomplete ...@@ -37,20 +42,6 @@ class @SearchAutocomplete
saveOriginalState: -> saveOriginalState: ->
@originalState = @serializeState() @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: -> serializeState: ->
{ {
# Search Criteria # Search Criteria
...@@ -63,70 +54,85 @@ class @SearchAutocomplete ...@@ -63,70 +54,85 @@ class @SearchAutocomplete
_location: $.trim(@locationText.text()) _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: -> bindEvents: ->
@searchInput.on 'keydown', @onSearchInputKeyDown @searchInput.on 'keydown', @onSearchInputKeyDown
@searchInput.on 'focus', @onSearchInputFocus @searchInput.on 'focus', @onSearchInputFocus
@searchInput.on 'blur', @onSearchInputBlur @searchInput.on 'blur', @onSearchInputBlur
@wrap.on 'click', '.remove-badge', @onRemoveLocationBadgeClick
onRemoveLocationBadgeClick: (e) => enableAutocomplete: ->
e.preventDefault() self = @
@removeLocationBadge() @query = "?project_id=" + @projectId + "&project_ref=" + @projectRef
@searchInput.focus() 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) => onSearchInputKeyDown: (e) =>
# Remove tag when pressing backspace and input search is empty # 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() @removeLocationBadge()
# @destroyAutocomplete()
@searchInput.focus() @searchInput.focus()
else if e.keyCode is @keyCode.ESCAPE
else if e.keyCode is KEYCODE.ESCAPE
@searchInput.val('')
@restoreOriginalState() @restoreOriginalState()
else 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 @autocomplete is undefined
if !@locationBadgeEl.children().length if !@badgePresent()
@createAutocomplete() @enableAutocomplete()
else else
# There's a badge # There's a badge
if @locationBadgeEl.children().length if @badgePresent()
@destroyAutocomplete() @disableAutocomplete()
onSearchInputFocus: => onSearchInputFocus: =>
@wrap.addClass('search-active') @wrap.addClass('search-active')
...@@ -135,7 +141,8 @@ class @SearchAutocomplete ...@@ -135,7 +141,8 @@ class @SearchAutocomplete
@wrap.removeClass('search-active') @wrap.removeClass('search-active')
# If input is blank then restore state # If input is blank then restore state
@restoreOriginalState() if @searchInput.val() is '' if @searchInput.val() is ''
@restoreOriginalState()
addLocationBadge: (item) -> addLocationBadge: (item) ->
category = if item.category? then "#{item.category}: " else '' category = if item.category? then "#{item.category}: " else ''
...@@ -147,26 +154,28 @@ class @SearchAutocomplete ...@@ -147,26 +154,28 @@ class @SearchAutocomplete
</span>" </span>"
@locationBadgeEl.html(html) @locationBadgeEl.html(html)
setLocationBadge: (item) -> restoreOriginalState: ->
@addLocationBadge(item) inputs = Object.keys @originalState
# Reset input states for input in inputs
@resetSearchState() @$("##{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' if @originalState._location is ''
@groupInputEl.val(item.id) @locationBadgeEl.html('')
else
@addLocationBadge(
value: @originalState._location
)
removeLocationBadge: -> @dropdown.removeClass 'open'
@locationBadgeEl.empty()
# Reset state # Only add class if there's a badge
@resetSearchState() if @badgePresent()
@searchInput.addClass 'disabled'
badgePresent: ->
@locationBadgeEl.children().length
resetSearchState: -> resetSearchState: ->
# Remove scope # Remove scope
...@@ -184,10 +193,13 @@ class @SearchAutocomplete ...@@ -184,10 +193,13 @@ class @SearchAutocomplete
# Remove repository ref # Remove repository ref
@repositoryInputEl.val('') @repositoryInputEl.val('')
goToResult: (result) -> removeLocationBadge: ->
location.href = result.url @locationBadgeEl.empty()
# Reset state
@resetSearchState()
destroyAutocomplete: -> disableAutocomplete: ->
@autocomplete.destroy() if @autocomplete isnt undefined if @autocomplete isnt undefined
@searchInput.attr('autocomplete', 'off') @searchInput.addClass('disabled')
@autocomplete = undefined @autocomplete = undefined
...@@ -51,12 +51,6 @@ ...@@ -51,12 +51,6 @@
margin: 0; margin: 0;
} }
} }
.ui-autocomplete-category {
text-transform: uppercase;
font-size: 11px;
color: #7f8fa4;
}
} }
.ui-state-default { .ui-state-default {
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
} }
} }
.search { .search {
margin-right: 10px; margin-right: 10px;
margin-left: 10px; margin-left: 10px;
...@@ -51,7 +50,6 @@ ...@@ -51,7 +50,6 @@
padding: 4px; padding: 4px;
width: 350px; width: 350px;
line-height: 24px; line-height: 24px;
overflow: hidden;
} }
.location-text { .location-text {
...@@ -69,7 +67,7 @@ ...@@ -69,7 +67,7 @@
padding: 0; padding: 0;
margin-left: 5px; margin-left: 5px;
line-height: 25px; line-height: 25px;
width: 100%; width: 98%;
} }
.location-badge { .location-badge {
...@@ -89,7 +87,7 @@ ...@@ -89,7 +87,7 @@
} }
.search-location-badge, .search-input-wrap { .search-location-badge, .search-input-wrap {
// Fallback if flex is not supported // Fallback if flexbox is not supported
display: inline-block; display: inline-block;
} }
...@@ -103,6 +101,7 @@ ...@@ -103,6 +101,7 @@
position: absolute; position: absolute;
right: 5px; right: 5px;
color: #E7E9ED; color: #E7E9ED;
top: 0;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
...@@ -114,9 +113,9 @@ ...@@ -114,9 +113,9 @@
} }
} }
.ui-autocomplete-loading + .search-icon { .dropdown-header {
@extend .fa-spinner; text-transform: uppercase;
@extend .fa-spin; font-size: 11px;
} }
} }
} }
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
.search-location-badge .search-location-badge
= render 'shared/location_badge' = render 'shared/location_badge'
.search-input-wrap .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 %i.search-icon
= hidden_field_tag :group_id, @group.try(:id) = hidden_field_tag :group_id, @group.try(:id)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment