Commit 9612e575 authored by Jacob Schatz's avatar Jacob Schatz Committed by Rémy Coutable

Merge branch 'dropdown-alignment-fixes' into 'master'

Dropdown pixel perfect 🎉

Closes #14333, #14331, #14472, #14515

See merge request !3337
parent 329fd857
......@@ -5,6 +5,7 @@ v 8.6.3 (unreleased)
v 8.6.2
- Fix dropdown alignment. !3298
- Fix issuable sidebar overlaps on tablet. !3299
- Make dropdowns pixel perfect. !3337
v 8.6.1
- Add option to reload the schema before restoring a database backup. !2807
......
class GitLabDropdownFilter
BLUR_KEYCODES = [27, 40]
HAS_VALUE_CLASS = "has-value"
constructor: (@dropdown, @options) ->
@input = @dropdown.find(".dropdown-input .dropdown-input-field")
constructor: (@input, @options) ->
$inputContainer = @input.parent()
$clearButton = $inputContainer.find('.js-dropdown-input-clear')
# Clear click
$clearButton.on 'click', (e) =>
e.preventDefault()
e.stopPropagation()
@input
.val('')
.trigger('keyup')
.focus()
# Key events
timeout = ""
@input.on "keyup", (e) =>
if e.keyCode is 13 && @input.val() isnt ""
if @input.val() isnt "" and !$inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.addClass HAS_VALUE_CLASS
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.removeClass HAS_VALUE_CLASS
if e.keyCode is 13 and @input.val() isnt ""
if @options.enterCallback
@options.enterCallback()
return
......@@ -95,7 +111,9 @@ class GitLabDropdown
# Init filiterable
if @options.filterable
@filter = new GitLabDropdownFilter @dropdown,
@input = @dropdown.find('.dropdown-input .dropdown-input-field')
@filter = new GitLabDropdownFilter @input,
remote: @options.filterRemote
query: @options.data
keys: @options.search.fields
......@@ -103,6 +121,7 @@ class GitLabDropdown
return @fullData
callback: (data) =>
@parseData data
@highlightRow 1
enterCallback: =>
@selectFirstRow()
......@@ -224,11 +243,19 @@ class GitLabDropdown
noResults: ->
html = "<li>"
html += "<a href='#' class='is-focused'>"
html += "<a href='#' class='dropdown-menu-empty-link is-focused'>"
html += "No matching results."
html += "</a>"
html += "</li>"
highlightRow: (index) ->
if @input.val() isnt ""
selector = '.dropdown-content li:first-child a'
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content li:first-child a"
$(selector).addClass 'is-focused'
rowClicked: (el) ->
fieldName = @options.fieldName
field = @dropdown.parent().find("input[name='#{fieldName}']")
......@@ -272,7 +299,7 @@ class GitLabDropdown
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content li:first-child a"
# similute a click on the first link
# simulate a click on the first link
$(selector).trigger "click"
$.fn.glDropdown = (opts) ->
......
......@@ -6,7 +6,7 @@ class @LabelsSelect
labelUrl = $dropdown.data('labels')
selectedLabel = $dropdown.data('selected')
if selectedLabel
selectedLabel = selectedLabel.split(',')
selectedLabel = selectedLabel.toString().split(',')
newLabelField = $('#new_label_name')
newColorField = $('#new_label_color')
showNo = $dropdown.data('show-no')
......@@ -14,38 +14,81 @@ class @LabelsSelect
defaultLabel = $dropdown.data('default-label')
if newLabelField.length
$newLabelCreateButton = $('.js-new-label-btn')
$colorPreview = $('.js-dropdown-label-color-preview')
$newLabelError = $dropdown.parent().find('.js-label-error')
$newLabelError.hide()
# Suggested colors in the dropdown to chose from pre-chosen colors
$('.suggest-colors-dropdown a').on 'click', (e) ->
e.preventDefault()
e.stopPropagation()
newColorField.val $(this).data('color')
$('.js-dropdown-label-color-preview')
newColorField
.val($(this).data('color'))
.trigger('change')
$colorPreview
.css 'background-color', $(this).data('color')
.parent()
.addClass 'is-active'
$('.js-new-label-btn').on 'click', (e) ->
# Cancel button takes back to first page
resetForm = ->
newLabelField
.val ''
.trigger 'change'
newColorField
.val ''
.trigger 'change'
$colorPreview
.css 'background-color', ''
.parent()
.removeClass 'is-active'
$('.dropdown-menu-back').on 'click', ->
resetForm()
$('.js-cancel-label-btn').on 'click', (e) ->
e.preventDefault()
e.stopPropagation()
resetForm()
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
# Listen for change and keyup events on label and color field
# This allows us to enable the button when ready
enableLabelCreateButton = ->
if newLabelField.val() isnt '' and newColorField.val() isnt ''
$newLabelError.hide()
$('.js-new-label-btn').disable()
# Create new label with API
Api.newLabel projectId, {
name: newLabelField.val()
color: newColorField.val()
}, (label) ->
$('.js-new-label-btn').enable()
if label.message?
$newLabelError
.text label.message
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
$newLabelCreateButton.enable()
else
$newLabelCreateButton.disable()
newLabelField.on 'keyup change', enableLabelCreateButton
newColorField.on 'keyup change', enableLabelCreateButton
# Send the API call to create the label
$newLabelCreateButton
.disable()
.on 'click', (e) ->
e.preventDefault()
e.stopPropagation()
if newLabelField.val() isnt '' and newColorField.val() isnt ''
$newLabelError.hide()
$('.js-new-label-btn').disable()
# Create new label with API
Api.newLabel projectId, {
name: newLabelField.val()
color: newColorField.val()
}, (label) ->
$('.js-new-label-btn').enable()
if label.message?
$newLabelError
.text label.message
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
$dropdown.glDropdown(
data: (term, callback) ->
......@@ -78,8 +121,11 @@ class @LabelsSelect
else
selected = if label.title is selectedLabel then 'is-active' else ''
color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else ""
"<li>
<a href='#' class='#{selected}'>
#{color}
#{label.title}
</a>
</li>"
......
......@@ -30,6 +30,7 @@ class @UsersSelect
if showNullUser
showDivider += 1
users.unshift(
beforeDivider: true
name: 'Unassigned',
id: 0
)
......@@ -39,6 +40,7 @@ class @UsersSelect
name = showAnyUser
name = 'Any User' if name == true
anyUser = {
beforeDivider: true
name: name,
id: null
}
......@@ -75,20 +77,27 @@ class @UsersSelect
selected = if user.id is selectedId then "is-active" else ""
img = ""
if avatar
img = "<img src='#{avatar}' class='avatar avatar-inline' width='30' />"
"<li>
<a href='#' class='dropdown-menu-user-link #{selected}'>
#{img}
<strong class='dropdown-menu-user-full-name'>
if user.beforeDivider?
"<li>
<a href='#' class='#{selected}'>
#{user.name}
</strong>
<span class='dropdown-menu-user-username'>
#{username}
</span>
</a>
</li>"
</a>
</li>"
else
if avatar
img = "<img src='#{avatar}' class='avatar avatar-inline' width='30' />"
"<li>
<a href='#' class='dropdown-menu-user-link #{selected}'>
#{img}
<strong class='dropdown-menu-user-full-name'>
#{user.name}
</strong>
<span class='dropdown-menu-user-username'>
#{username}
</span>
</a>
</li>"
)
$('.ajax-users-select').each (i, select) =>
......
......@@ -130,6 +130,12 @@
text-decoration: none;
outline: 0;
}
&.dropdown-menu-empty-link {
&.is-focused {
background-color: $dropdown-empty-row-bg;
}
}
}
}
......@@ -183,7 +189,7 @@
}
.dropdown-select {
width: 280px;
width: 300px;
}
.dropdown-menu-align-right {
......@@ -237,7 +243,7 @@
.dropdown-title-button {
position: absolute;
top: -1px;
top: 0;
padding: 0;
color: $dropdown-title-btn-color;
font-size: 14px;
......@@ -270,6 +276,22 @@
font-size: 12px;
pointer-events: none;
}
.dropdown-input-clear {
display: none;
cursor: pointer;
pointer-events: all;
}
&.has-value {
.dropdown-input-clear {
display: block;
}
.dropdown-input-search {
display: none;
}
}
}
.dropdown-input-field {
......@@ -286,13 +308,13 @@
border-color: $dropdown-input-focus-border;
box-shadow: 0 0 4px $dropdown-input-focus-shadow;
+ .fa {
~ .fa {
color: $dropdown-link-color;
}
}
&:hover {
+ .fa {
~ .fa {
color: $dropdown-link-color;
}
}
......@@ -338,11 +360,12 @@
}
}
.dropdown-menu-labels {
.label {
position: relative;
width: 30px;
margin-right: 5px;
text-indent: -99999px;
}
.dropdown-label-box {
position: relative;
top: 3px;
margin-right: 5px;
display: inline-block;
width: 15px;
height: 15px;
border-radius: $border-radius-base;
}
......@@ -138,13 +138,14 @@ $regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif
*/
$dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: rgba(#000, .04);
$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04);
$dropdown-border-color: rgba(#000, .1);
$dropdown-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494;
$dropdown-title-btn-color: #bfbfbf;
$dropdown-input-color: #c7c7c7;
$dropdown-input-color: #555;
$dropdown-input-focus-border: rgb(58, 171, 240);
$dropdown-input-focus-shadow: rgba(#000, .2);
$dropdown-loading-bg: rgba(#fff, .6);
......
......@@ -9,28 +9,45 @@
}
&.suggest-colors-dropdown {
margin-bottom: 5px;
margin-top: 10px;
margin-bottom: 10px;
border-radius: $border-radius-base;
overflow: hidden;
a {
@include border-radius(0);
width: 36.7px;
width: (100% / 7);
margin-right: 0;
margin-bottom: -5px;
}
}
}
.dropdown-label-color-preview {
display: none;
margin-top: 5px;
width: 100%;
height: 25px;
.dropdown-new-label {
.dropdown-content {
max-height: 260px;
}
}
.dropdown-label-color-input {
position: relative;
margin-bottom: 10px;
&.is-active {
display: block;
padding-left: 32px;
}
}
.dropdown-label-color-preview {
position: absolute;
left: 0;
top: 0;
width: 32px;
height: 32px;
border-top-left-radius: $border-radius-base;
border-bottom-left-radius: $border-radius-base;
}
.label-row {
.label {
padding: 9px;
......
......@@ -70,7 +70,8 @@ module DropdownsHelper
def dropdown_filter(placeholder)
content_tag :div, class: "dropdown-input" do
filter_output = search_field_tag nil, nil, class: "dropdown-input-field", placeholder: placeholder
filter_output << icon('search')
filter_output << icon('search', class: "dropdown-input-search")
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
filter_output.html_safe
end
......
......@@ -24,17 +24,21 @@
- else
View labels
- if can? current_user, :admin_label, @project and @project
.dropdown-page-two
.dropdown-page-two.dropdown-new-label
= dropdown_title("Create new label", back: true)
= dropdown_content do
.dropdown-labels-error.js-label-error
%input#new_label_color{type: "hidden"}
%input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"}
.dropdown-label-color-preview.js-dropdown-label-color-preview
.suggest-colors.suggest-colors-dropdown
- suggested_colors.each do |color|
= link_to '#', style: "background-color: #{color}", data: { color: color } do
&nbsp
%button.btn.btn-primary.js-new-label-btn{type: "button"}
Create
.dropdown-label-color-input
.dropdown-label-color-preview.js-dropdown-label-color-preview
%input#new_label_color.dropdown-input-field{ type: "text" }
.clearfix
%button.btn.btn-primary.pull-left.js-new-label-btn{type: "button"}
Create
%button.btn.btn-default.pull-right.js-cancel-label-btn{type: "button"}
Cancel
= dropdown_loading
......@@ -43,10 +43,10 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
step 'I click "All" link' do
find('.js-author-search').click
find('.dropdown-menu-user-full-name', match: :first).click
find('.dropdown-content a', match: :first).click
find('.js-assignee-search').click
find('.dropdown-menu-user-full-name', match: :first).click
find('.dropdown-content a', match: :first).click
end
def should_see(issue)
......
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