diff options
author | Jacob Schatz <jschatz1@gmail.com> | 2016-03-23 22:13:31 +0000 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2016-03-29 12:12:40 +0200 |
commit | 9612e5759305985cefe4d99941398dea51f9ce02 (patch) | |
tree | f5f86998efd8985df50c546a7a60b537a2c74cc4 | |
parent | 329fd857b06bcc44cdfa6942db7a6f3a9fc6e2b6 (diff) | |
download | gitlab-ce-9612e5759305985cefe4d99941398dea51f9ce02.tar.gz |
Merge branch 'dropdown-alignment-fixes' into 'master'
Dropdown pixel perfect :tada:
Closes #14333, #14331, #14472, #14515
See merge request !3337
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | app/assets/javascripts/gl_dropdown.js.coffee | 39 | ||||
-rw-r--r-- | app/assets/javascripts/labels_select.js.coffee | 86 | ||||
-rw-r--r-- | app/assets/javascripts/users_select.js.coffee | 35 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/dropdowns.scss | 45 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/variables.scss | 5 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/labels.scss | 33 | ||||
-rw-r--r-- | app/helpers/dropdowns_helper.rb | 3 | ||||
-rw-r--r-- | app/views/shared/issuable/_label_dropdown.html.haml | 14 | ||||
-rw-r--r-- | features/steps/dashboard/issues.rb | 4 |
10 files changed, 197 insertions, 68 deletions
diff --git a/CHANGELOG b/CHANGELOG index 27cf08a398c..622e9c64944 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 960585245d7..4b78bcde774 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -1,13 +1,29 @@ 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) -> diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index f3cb1e3bc09..e08648d583b 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -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>" diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 3d6452d2f46..84193400890 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -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) => diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d92cf6e6c44..2d616fc660c 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -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; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 211ead7319d..12b25654c30 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -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); diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3c13573c8fe..4e02ec4e891 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -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; diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index ceff1fbb161..316a10b7da3 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -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 diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml index 186087e8f89..006a34a11e3 100644 --- a/app/views/shared/issuable/_label_dropdown.html.haml +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -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   - %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 diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb index f4a56865532..93aa77589be 100644 --- a/features/steps/dashboard/issues.rb +++ b/features/steps/dashboard/issues.rb @@ -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) |