Apply styling and tweaks to autocomplete dropdown
This commit is contained in:
parent
c9e202c1d7
commit
6a7f4a0767
9 changed files with 186 additions and 86 deletions
|
@ -14,4 +14,36 @@ $.widget( "custom.catcomplete", $.ui.autocomplete,
|
|||
|
||||
if item.category?
|
||||
li.attr('aria-label', item.category + " : " + item.label)
|
||||
|
||||
_renderItem: (ul, item) ->
|
||||
# Highlight occurrences
|
||||
item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
|
||||
|
||||
return $( "<li></li>" )
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<a>#{item.label}</a>" )
|
||||
.appendTo( ul );
|
||||
|
||||
_resizeMenu: ->
|
||||
if (isNaN(this.options.maxShowItems))
|
||||
return
|
||||
|
||||
ul = this.menu.element.css(overflowX: '', overflowY: '', width: '', maxHeight: '')
|
||||
|
||||
lis = ul.children('li').css('whiteSpace', 'nowrap');
|
||||
|
||||
if (lis.length > this.options.maxShowItems)
|
||||
ulW = ul.prop('clientWidth')
|
||||
|
||||
ul.css(
|
||||
overflowX: 'hidden'
|
||||
overflowY: 'auto'
|
||||
maxHeight: lis.eq(0).outerHeight() * this.options.maxShowItems + 1
|
||||
)
|
||||
|
||||
barW = ulW - ul.prop('clientWidth');
|
||||
ul.width('+=' + barW);
|
||||
|
||||
# Original code from jquery.ui.autocomplete.js _resizeMenu()
|
||||
ul.outerWidth(Math.max(ul.outerWidth() + 1, this.element.outerWidth()));
|
||||
)
|
||||
|
|
|
@ -24,7 +24,10 @@ class @SearchAutocomplete
|
|||
@scopeInputEl = @$('#scope')
|
||||
|
||||
@saveOriginalState()
|
||||
@createAutocomplete()
|
||||
|
||||
if @locationBadgeEl.is(':empty')
|
||||
@createAutocomplete()
|
||||
|
||||
@bindEvents()
|
||||
|
||||
$: (selector) ->
|
||||
|
@ -66,6 +69,12 @@ class @SearchAutocomplete
|
|||
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()
|
||||
|
||||
|
@ -89,7 +98,9 @@ class @SearchAutocomplete
|
|||
|
||||
|
||||
bindEvents: ->
|
||||
@searchInput.on 'keydown', @onSearchKeyDown
|
||||
@searchInput.on 'keydown', @onSearchInputKeyDown
|
||||
@searchInput.on 'focus', @onSearchInputFocus
|
||||
@searchInput.on 'blur', @onSearchInputBlur
|
||||
@wrap.on 'click', '.remove-badge', @onRemoveLocationBadgeClick
|
||||
|
||||
onRemoveLocationBadgeClick: (e) =>
|
||||
|
@ -97,7 +108,7 @@ class @SearchAutocomplete
|
|||
@removeLocationBadge()
|
||||
@searchInput.focus()
|
||||
|
||||
onSearchKeyDown: (e) =>
|
||||
onSearchInputKeyDown: (e) =>
|
||||
# Remove tag when pressing backspace and input search is empty
|
||||
if e.keyCode is @keyCode.BACKSPACE and e.currentTarget.value is ''
|
||||
@removeLocationBadge()
|
||||
|
@ -106,14 +117,24 @@ class @SearchAutocomplete
|
|||
else if e.keyCode is @keyCode.ESCAPE
|
||||
@restoreOriginalState()
|
||||
else
|
||||
# Create new autocomplete instance if it's not created
|
||||
@createAutocomplete() unless @catcomplete?
|
||||
# Create new autocomplete if hasn't been created yet and there's no badge
|
||||
if !@catComplete? and @locationBadgeEl.is(':empty')
|
||||
@createAutocomplete()
|
||||
|
||||
onSearchInputFocus: =>
|
||||
@wrap.addClass('search-active')
|
||||
|
||||
onSearchInputBlur: =>
|
||||
@wrap.removeClass('search-active')
|
||||
|
||||
# If input is blank then restore state
|
||||
@restoreOriginalState() if @searchInput.val() is ''
|
||||
|
||||
addLocationBadge: (item) ->
|
||||
category = if item.category? then "#{item.category}: " else ''
|
||||
value = if item.value? then item.value else ''
|
||||
|
||||
html = "<span class='label label-primary'>
|
||||
html = "<span class='location-badge'>
|
||||
<i class='location-text'>#{category}#{value}</i>
|
||||
<a class='remove-badge' href='#'>x</a>
|
||||
</span>"
|
||||
|
@ -160,5 +181,5 @@ class @SearchAutocomplete
|
|||
location.href = result.url
|
||||
|
||||
destroyAutocomplete: ->
|
||||
@catComplete.destroy() if @catcomplete?
|
||||
@catComplete.destroy() if @catComplete?
|
||||
@catComplete = null
|
||||
|
|
|
@ -6,40 +6,6 @@ input {
|
|||
border-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
input[type='search'] {
|
||||
background-color: white;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
input[type='search'].search-input {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 10px;
|
||||
background-size: 16px;
|
||||
background-position-x: 30%;
|
||||
padding-left: 10px;
|
||||
background-color: $gray-light;
|
||||
|
||||
&.search-input[value=""] {
|
||||
background-image: url('data:image/png;base64,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');
|
||||
}
|
||||
|
||||
&.search-input::-webkit-input-placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.search-input:-moz-placeholder { /* Firefox 18- */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.search-input::-moz-placeholder { /* Firefox 19+ */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.search-input:-ms-input-placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
input[type='text'].danger {
|
||||
background: #f2dede!important;
|
||||
border-color: #d66;
|
||||
|
|
|
@ -112,26 +112,6 @@ header {
|
|||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
margin-top: ($header-height - 36) / 2;
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 220px;
|
||||
|
||||
&:focus {
|
||||
@include box-shadow(none);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.impersonation i {
|
||||
color: $red-normal;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,39 @@
|
|||
padding: 0;
|
||||
margin-top: 2px;
|
||||
z-index: 1001;
|
||||
width: 240px;
|
||||
margin-bottom: 0;
|
||||
padding: 10px 10px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
background-color: $dropdown-bg;
|
||||
border: 1px solid $dropdown-border-color;
|
||||
border-radius: $border-radius-base;
|
||||
box-shadow: 0 2px 4px $dropdown-shadow-color;
|
||||
|
||||
.ui-menu-item a {
|
||||
padding: 4px 10px;
|
||||
.ui-menu-item {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
color: $dropdown-link-color;
|
||||
line-height: 34px;
|
||||
text-overflow: ellipsis;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
|
||||
&.ui-state-focus {
|
||||
background-color: $dropdown-link-hover-bg;
|
||||
text-decoration: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete-category {
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
color: #7f8fa4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,3 +21,73 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.search {
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
margin-top: ($header-height - 35) / 2;
|
||||
|
||||
&.search-active {
|
||||
form {
|
||||
@extend .form-control:focus;
|
||||
}
|
||||
|
||||
.location-badge {
|
||||
@include transition(all .15s);
|
||||
background-color: $input-border-focus;
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
@extend .form-control;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
width: 350px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.location-text {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.remove-badge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
margin-left: 2px;
|
||||
line-height: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.location-badge {
|
||||
line-height: 25px;
|
||||
padding: 0 5px;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
color: #AAAAAA;
|
||||
display: inline-block;
|
||||
background-color: #F5F5F5;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.search-input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.search-location-badge, .search-input-wrap {
|
||||
// Fallback if flex is not supported
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,20 +48,19 @@ module SearchHelper
|
|||
# Autocomplete results for the current project, if it's defined
|
||||
def project_autocomplete
|
||||
if @project && @project.repository.exists? && @project.repository.root_ref
|
||||
prefix = "Project - " + search_result_sanitize(@project.name_with_namespace)
|
||||
ref = @ref || @project.repository.root_ref
|
||||
|
||||
[
|
||||
{ category: prefix, label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
|
||||
{ category: prefix, label: "Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
|
||||
{ category: prefix, label: "Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
|
||||
{ category: prefix, label: "Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
|
||||
{ category: prefix, label: "Issues", url: namespace_project_issues_path(@project.namespace, @project) },
|
||||
{ category: prefix, label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
|
||||
{ category: prefix, label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
|
||||
{ category: prefix, label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
|
||||
{ category: prefix, label: "Members", url: namespace_project_project_members_path(@project.namespace, @project) },
|
||||
{ category: prefix, label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
|
||||
{ category: "Current Project", label: "Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
|
||||
{ category: "Current Project", label: "Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
|
||||
{ category: "Current Project", label: "Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
|
||||
{ category: "Current Project", label: "Issues", url: namespace_project_issues_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Members", url: namespace_project_project_members_path(@project.namespace, @project) },
|
||||
{ category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
|
||||
]
|
||||
else
|
||||
[]
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
.search
|
||||
= 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"
|
||||
= hidden_field_tag :group_id, @group.try(:id)
|
||||
.search.search-form
|
||||
= form_tag search_path, method: :get, class: 'navbar-form' do |f|
|
||||
.search-input-container
|
||||
.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'
|
||||
|
||||
= hidden_field_tag :group_id, @group.try(:id)
|
||||
= hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : ''
|
||||
|
||||
- if @project && @project.persisted?
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
- if controller.controller_path =~ /^projects/
|
||||
- label = 'This project'
|
||||
|
||||
.search-location-badge
|
||||
- if label.present?
|
||||
%span.label.label-primary
|
||||
%i.location-text
|
||||
= label
|
||||
- if label.present?
|
||||
%span.location-badge
|
||||
%i.location-text
|
||||
= label
|
||||
|
||||
%a.remove-badge{href: '#'}
|
||||
x
|
||||
%a.remove-badge{href: '#'}
|
||||
x
|
||||
|
|
Loading…
Reference in a new issue