Skip to content
Snippets Groups Projects
Commit 4b3d3446 authored by Alfredo Sumaran's avatar Alfredo Sumaran
Browse files

Working version of autocomplete with categorized results

parent bc590ce6
No related branches found
No related tags found
No related merge requests found
Loading
@@ -151,9 +151,4 @@ class Dispatcher
Loading
@@ -151,9 +151,4 @@ class Dispatcher
new Shortcuts() new Shortcuts()
   
initSearch: -> initSearch: ->
opts = $('.search-autocomplete-opts') new SearchAutocomplete()
path = opts.data('autocomplete-path')
project_id = opts.data('autocomplete-project-id')
project_ref = opts.data('autocomplete-project-ref')
new SearchAutocomplete(path, project_id, project_ref)
$.widget( "custom.catcomplete", $.ui.autocomplete,
_create: ->
@_super();
@widget().menu("option", "items", "> :not(.ui-autocomplete-category)")
_renderMenu: (ul, items) ->
currentCategory = ''
$.each items, (index, item) =>
if item.category isnt currentCategory
ul.append("<li class='ui-autocomplete-category'>#{item.category}</li>")
currentCategory = item.category
li = @_renderItemData(ul, item)
if item.category?
li.attr('aria-label', item.category + " : " + item.label)
)
class @SearchAutocomplete class @SearchAutocomplete
constructor: (search_autocomplete_path, project_id, project_ref) -> constructor: (opts = {}) ->
project_id = '' unless project_id {
project_ref = '' unless project_ref @wrap = $('.search')
query = "?project_id=" + project_id + "&project_ref=" + project_ref @optsEl = @wrap.find('.search-autocomplete-opts')
@autocompletePath = @optsEl.data('autocomplete-path')
@projectId = @optsEl.data('autocomplete-project-id') || ''
@projectRef = @optsEl.data('autocomplete-project-ref') || ''
} = opts
   
$("#search").autocomplete @keyCode =
source: search_autocomplete_path + query ESCAPE: 27
BACKSPACE: 8
TAB: 9
ENTER: 13
@locationBadgeEl = @$('.search-location-badge')
@locationText = @$('.location-text')
@searchInput = @$('.search-input')
@projectInputEl = @$('#project_id')
@groupInputEl = @$('#group_id')
@searchCodeInputEl = @$('#search_code')
@repositoryInputEl = @$('#repository_ref')
@scopeInputEl = @$('#scope')
@saveOriginalState()
@createAutocomplete()
@bindEvents()
$: (selector) ->
@wrap.find(selector)
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
project_id: @projectInputEl.val()
group_id: @groupInputEl.val()
search_code: @searchCodeInputEl.val()
repository_ref: @repositoryInputEl.val()
# Location badge
_location: $.trim(@locationText.text())
}
createAutocomplete: ->
@query = "?project_id=" + @projectId + "&project_ref=" + @projectRef
@catComplete = @searchInput.catcomplete
appendTo: 'form.navbar-form'
source: @autocompletePath + @query
minLength: 1 minLength: 1
select: (event, ui) -> close: (e) ->
location.href = ui.item.url e.preventDefault()
select: (event, ui) =>
# Pressing enter choses an alternative
if event.keyCode is @keyCode.ENTER
@goToResult(ui.item)
else
# Pressing tab sets the scope
if event.keyCode is @keyCode.TAB and ui.item.scope?
@setLocationBadge(ui.item)
@searchInput
.val('') # remove selected value from input
.focus()
else
# If option is not a scope go to page
@goToResult(ui.item)
# Return false to avoid focus on the next element
return false
bindEvents: ->
@searchInput.on 'keydown', @onSearchKeyDown
@wrap.on 'click', '.remove-badge', @onRemoveLocationBadgeClick
onRemoveLocationBadgeClick: (e) =>
e.preventDefault()
@removeLocationBadge()
@searchInput.focus()
onSearchKeyDown: (e) =>
# Remove tag when pressing backspace and input search is empty
if e.keyCode is @keyCode.BACKSPACE and e.currentTarget.value is ''
@removeLocationBadge()
@destroyAutocomplete()
@searchInput.focus()
else if e.keyCode is @keyCode.ESCAPE
@restoreOriginalState()
else
# Create new autocomplete instance if it's not created
@createAutocomplete() unless @catcomplete?
addLocationBadge: (item) ->
category = if item.category? then "#{item.category}: " else ''
value = if item.value? then item.value else ''
html = "<span class='label label-primary'>
<i class='location-text'>#{category}#{value}</i>
<a class='remove-badge' href='#'>x</a>
</span>"
@locationBadgeEl.html(html)
setLocationBadge: (item) ->
@addLocationBadge(item)
# Reset input states
@resetSearchState()
switch item.scope
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)
removeLocationBadge: ->
@locationBadgeEl.empty()
# Reset state
@resetSearchState()
resetSearchState: ->
# Remove scope
@scopeInputEl.val('')
# Remove group
@groupInputEl.val('')
# Remove project id
@projectInputEl.val('')
# Remove code search
@searchCodeInputEl.val('')
# Remove repository ref
@repositoryInputEl.val('')
goToResult: (result) ->
location.href = result.url
destroyAutocomplete: ->
@catComplete.destroy() if @catcomplete?
@catComplete = null
Loading
@@ -23,45 +23,45 @@ module SearchHelper
Loading
@@ -23,45 +23,45 @@ module SearchHelper
# Autocomplete results for various settings pages # Autocomplete results for various settings pages
def default_autocomplete def default_autocomplete
[ [
{ label: "Profile settings", url: profile_path }, { category: "Settings", label: "Profile settings", url: profile_path },
{ label: "SSH Keys", url: profile_keys_path }, { category: "Settings", label: "SSH Keys", url: profile_keys_path },
{ label: "Dashboard", url: root_path }, { category: "Settings", label: "Dashboard", url: root_path },
{ label: "Admin Section", url: admin_root_path }, { category: "Settings", label: "Admin Section", url: admin_root_path },
] ]
end end
   
# Autocomplete results for internal help pages # Autocomplete results for internal help pages
def help_autocomplete def help_autocomplete
[ [
{ label: "help: API Help", url: help_page_path("api", "README") }, { category: "Help", label: "API Help", url: help_page_path("api", "README") },
{ label: "help: Markdown Help", url: help_page_path("markdown", "markdown") }, { category: "Help", label: "Markdown Help", url: help_page_path("markdown", "markdown") },
{ label: "help: Permissions Help", url: help_page_path("permissions", "permissions") }, { category: "Help", label: "Permissions Help", url: help_page_path("permissions", "permissions") },
{ label: "help: Public Access Help", url: help_page_path("public_access", "public_access") }, { category: "Help", label: "Public Access Help", url: help_page_path("public_access", "public_access") },
{ label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") }, { category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks", "README") },
{ label: "help: SSH Keys Help", url: help_page_path("ssh", "README") }, { category: "Help", label: "SSH Keys Help", url: help_page_path("ssh", "README") },
{ label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") }, { category: "Help", label: "System Hooks Help", url: help_page_path("system_hooks", "system_hooks") },
{ label: "help: Webhooks Help", url: help_page_path("web_hooks", "web_hooks") }, { category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks", "web_hooks") },
{ label: "help: Workflow Help", url: help_page_path("workflow", "README") }, { category: "Help", label: "Workflow Help", url: help_page_path("workflow", "README") },
] ]
end end
   
# Autocomplete results for the current project, if it's defined # Autocomplete results for the current project, if it's defined
def project_autocomplete def project_autocomplete
if @project && @project.repository.exists? && @project.repository.root_ref if @project && @project.repository.exists? && @project.repository.root_ref
prefix = search_result_sanitize(@project.name_with_namespace) prefix = "Project - " + search_result_sanitize(@project.name_with_namespace)
ref = @ref || @project.repository.root_ref ref = @ref || @project.repository.root_ref
   
[ [
{ label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, { category: prefix, label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
{ label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, { category: prefix, label: "Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
{ label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, { category: prefix, label: "Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
{ label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, { category: prefix, label: "Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
{ label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, { category: prefix, label: "Issues", url: namespace_project_issues_path(@project.namespace, @project) },
{ label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, { category: prefix, label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
{ label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, { category: prefix, label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
{ label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, { category: prefix, label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
{ label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, { category: prefix, label: "Members", url: namespace_project_project_members_path(@project.namespace, @project) },
{ label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, { category: prefix, label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
] ]
else else
[] []
Loading
@@ -72,7 +72,10 @@ module SearchHelper
Loading
@@ -72,7 +72,10 @@ module SearchHelper
def groups_autocomplete(term, limit = 5) def groups_autocomplete(term, limit = 5)
current_user.authorized_groups.search(term).limit(limit).map do |group| current_user.authorized_groups.search(term).limit(limit).map do |group|
{ {
label: "group: #{search_result_sanitize(group.name)}", category: "Groups",
scope: "groups",
id: group.id,
label: "#{search_result_sanitize(group.name)}",
url: group_path(group) url: group_path(group)
} }
end end
Loading
@@ -83,7 +86,11 @@ module SearchHelper
Loading
@@ -83,7 +86,11 @@ module SearchHelper
current_user.authorized_projects.search_by_title(term). current_user.authorized_projects.search_by_title(term).
sorted_by_stars.non_archived.limit(limit).map do |p| sorted_by_stars.non_archived.limit(limit).map do |p|
{ {
label: "project: #{search_result_sanitize(p.name_with_namespace)}", category: "Projects",
scope: "projects",
id: p.id,
value: "#{search_result_sanitize(p.name)}",
label: "#{search_result_sanitize(p.name_with_namespace)}",
url: namespace_project_path(p.namespace, p) url: namespace_project_path(p.namespace, p)
} }
end end
Loading
Loading
.search .search
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f| = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
= render 'shared/location_badge'
= search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false, tabindex: "1" = search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false, tabindex: "1"
= hidden_field_tag :group_id, @group.try(:id) = hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
   
= hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : ''
- if @project && @project.persisted?
- if current_controller?(:issues) - if current_controller?(:issues)
= hidden_field_tag :scope, 'issues' = hidden_field_tag :scope, 'issues'
- elsif current_controller?(:merge_requests) - elsif current_controller?(:merge_requests)
Loading
@@ -21,10 +23,3 @@
Loading
@@ -21,10 +23,3 @@
= hidden_field_tag :repository_ref, @ref = hidden_field_tag :repository_ref, @ref
= button_tag 'Go' if ENV['RAILS_ENV'] == 'test' = button_tag 'Go' if ENV['RAILS_ENV'] == 'test'
.search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
:javascript
$('.search-input').on('keyup', function(e) {
if (e.keyCode == 27) {
$('.search-input').blur();
}
});
- if controller.controller_path =~ /^groups/
- label = 'This group'
- if controller.controller_path =~ /^projects/
- label = 'This project'
.search-location-badge
- if label.present?
%span.label.label-primary
%i.location-text
= label
%a.remove-badge{href: '#'}
x
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment