Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
iv
gitlab-ce
Commits
4b3d3446
Commit
4b3d3446
authored
Mar 08, 2016
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Working version of autocomplete with categorized results
parent
bc590ce6
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
229 additions
and
49 deletions
+229
-49
app/assets/javascripts/dispatcher.js.coffee
app/assets/javascripts/dispatcher.js.coffee
+1
-6
app/assets/javascripts/lib/category_autocomplete.js.coffee
app/assets/javascripts/lib/category_autocomplete.js.coffee
+17
-0
app/assets/javascripts/search_autocomplete.js.coffee
app/assets/javascripts/search_autocomplete.js.coffee
+161
-8
app/helpers/search_helper.rb
app/helpers/search_helper.rb
+33
-26
app/views/layouts/_search.html.haml
app/views/layouts/_search.html.haml
+4
-9
app/views/shared/_location_badge.html.haml
app/views/shared/_location_badge.html.haml
+13
-0
No files found.
app/assets/javascripts/dispatcher.js.coffee
View file @
4b3d3446
...
@@ -151,9 +151,4 @@ class Dispatcher
...
@@ -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
)
app/assets/javascripts/lib/category_autocomplete.js.coffee
0 → 100644
View file @
4b3d3446
$
.
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
)
)
app/assets/javascripts/search_autocomplete.js.coffee
View file @
4b3d3446
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
app/helpers/search_helper.rb
View file @
4b3d3446
...
@@ -23,45 +23,45 @@ module SearchHelper
...
@@ -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
[]
[]
...
@@ -72,7 +72,10 @@ module SearchHelper
...
@@ -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
...
@@ -83,7 +86,11 @@ module SearchHelper
...
@@ -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
...
...
app/views/layouts/_search.html.haml
View file @
4b3d3446
.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
)
...
@@ -21,10 +23,3 @@
...
@@ -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
();
}
});
app/views/shared/_location_badge.html.haml
0 → 100644
View file @
4b3d3446
-
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment