summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2016-03-28 23:22:15 +0200
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2016-03-28 23:22:15 +0200
commitd73e1288595ef673b2d9c3024d7fe3909dac7184 (patch)
tree9cf5efec920547bf2aff0c15a897557e85fd9f04 /app
parentbf7aac74a492c6de79001abbf3b41e23738509a7 (diff)
parent55df9a70bf3fb7fffa7491d53d3cf643d61de1c8 (diff)
downloadgitlab-ce-d73e1288595ef673b2d9c3024d7fe3909dac7184.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee50
-rw-r--r--app/assets/javascripts/issuable_context.js.coffee23
-rw-r--r--app/assets/javascripts/labels_select.js.coffee147
-rw-r--r--app/assets/javascripts/lib/animate.js.coffee13
-rw-r--r--app/assets/javascripts/milestone_select.js.coffee90
-rw-r--r--app/assets/javascripts/users_select.js.coffee111
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss6
-rw-r--r--app/assets/stylesheets/framework/files.scss4
-rw-r--r--app/assets/stylesheets/pages/commits.scss1
-rw-r--r--app/assets/stylesheets/pages/issuable.scss21
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/controllers/projects/issues_controller.rb13
-rw-r--r--app/controllers/projects/merge_requests_controller.rb5
-rw-r--r--app/controllers/projects/milestones_controller.rb1
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/issuables_helper.rb10
-rw-r--r--app/views/projects/find_file/show.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml4
-rw-r--r--app/views/shared/issuable/_filter.html.haml10
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml50
24 files changed, 447 insertions, 125 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 4b78bcde774..2b56ab2e6de 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -126,8 +126,10 @@ class GitLabDropdown
@selectFirstRow()
# Event listeners
+
@dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden
+ @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
if @dropdown.find(".dropdown-toggle-page").length
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
@@ -143,10 +145,11 @@ class GitLabDropdown
selector = ".dropdown-page-one .dropdown-content a"
@dropdown.on "click", selector, (e) ->
+ e.preventDefault()
self.rowClicked $(@)
if self.options.clicked
- self.options.clicked()
+ self.options.clicked.call(@,e)
toggleLoading: ->
$('.dropdown-menu', @dropdown).toggleClass LOADING_CLASS
@@ -176,6 +179,15 @@ class GitLabDropdown
@appendMenu(full_html)
+ shouldPropagate: (e) =>
+ if @options.multiSelect
+ $target = $(e.target)
+ if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon')
+ e.stopPropagation()
+ return false
+ else
+ return true
+
opened: =>
contentHtml = $('.dropdown-content', @dropdown).html()
if @remote && contentHtml is ""
@@ -184,7 +196,7 @@ class GitLabDropdown
if @options.filterable
@dropdown.find(".dropdown-input-field").focus()
- hidden: =>
+ hidden: (e) =>
if @options.filterable
@dropdown
.find(".dropdown-input-field")
@@ -195,6 +207,9 @@ class GitLabDropdown
if @dropdown.find(".dropdown-toggle-page").length
$('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
+ if @options.hidden
+ @options.hidden.call(@,e)
+
# Render the full menu
renderMenu: (html) ->
@@ -226,6 +241,13 @@ class GitLabDropdown
html = @options.renderRow(data)
else
selected = if @options.isSelected then @options.isSelected(data) else false
+ if not selected
+ value = if @options.id then @options.id(data) else data.id
+ fieldName = @options.fieldName
+ field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
+ if field.length
+ selected = true
+
url = if @options.url then @options.url(data) else "#"
text = if @options.text then @options.text(data) else ""
cssClass = "";
@@ -258,26 +280,28 @@ class GitLabDropdown
rowClicked: (el) ->
fieldName = @options.fieldName
- field = @dropdown.parent().find("input[name='#{fieldName}']")
-
+ selectedIndex = el.parent().index()
+ if @renderedData
+ selectedObject = @renderedData[selectedIndex]
+ value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
+ field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS)
+ el.removeClass(ACTIVE_CLASS)
field.remove()
else
fieldName = @options.fieldName
selectedIndex = el.parent().index()
if @renderedData
selectedObject = @renderedData[selectedIndex]
+ selectedObject.selected = true
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
if !value?
field.remove()
- if @options.multiSelect
- oldValue = field.val()
- if oldValue
- value = "#{oldValue},#{value}"
- else
+ if not @options.multiSelect
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
+ @dropdown.parent().find("input[name='#{fieldName}']").remove()
# Toggle active class for the tick mark
el.toggleClass "is-active"
@@ -285,15 +309,15 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject)
-
if value?
if !field.length
# Create hidden input for form
- input = "<input type='hidden' name='#{fieldName}' />"
+ input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
+ if @options.inputId?
+ input = $(input)
+ .attr('id', @options.inputId)
@dropdown.before input
- @dropdown.parent().find("input[name='#{fieldName}']").val value
-
selectFirstRow: ->
selector = '.dropdown-content li:first-child a'
if @dropdown.find(".dropdown-toggle-page").length
diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee
index d6d09b36d8d..6fc924d3d66 100644
--- a/app/assets/javascripts/issuable_context.js.coffee
+++ b/app/assets/javascripts/issuable_context.js.coffee
@@ -1,8 +1,7 @@
class @IssuableContext
- constructor: ->
+ constructor: (currentUser) ->
@initParticipants()
-
- new UsersSelect()
+ new UsersSelect(currentUser)
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$(".issuable-sidebar .inline-update").on "change", "select", ->
@@ -11,10 +10,20 @@ class @IssuableContext
$(this).submit()
$(document).on "click",".edit-link", (e) ->
- block = $(@).parents('.block')
- block.find('.selectbox').show()
- block.find('.value').hide()
- block.find('.js-select2').select2("open")
+ $block = $(@).parents('.block')
+ $selectbox = $block.find('.selectbox')
+ if $selectbox.is(':visible')
+ $selectbox.hide()
+ $block.find('.value').show()
+ else
+ $selectbox.show()
+ $block.find('.value').hide()
+
+ if $selectbox.is(':visible')
+ setTimeout (->
+ $block.find('.dropdown-menu-toggle').trigger 'click'
+ ), 0
+
$(".right-sidebar").niceScroll()
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index e08648d583b..b5c7af9a8ad 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -4,14 +4,20 @@ class @LabelsSelect
$dropdown = $(dropdown)
projectId = $dropdown.data('project-id')
labelUrl = $dropdown.data('labels')
+ issueUpdateURL = $dropdown.data('issueUpdate')
selectedLabel = $dropdown.data('selected')
- if selectedLabel
- selectedLabel = selectedLabel.toString().split(',')
+ if selectedLabel?
+ selectedLabel = selectedLabel.split(',')
newLabelField = $('#new_label_name')
newColorField = $('#new_label_color')
showNo = $dropdown.data('show-no')
showAny = $dropdown.data('show-any')
defaultLabel = $dropdown.data('default-label')
+ abilityName = $dropdown.data('ability-name')
+ $selectbox = $dropdown.closest('.selectbox')
+ $block = $selectbox.closest('.block')
+ $value = $block.find('.value')
+ $loading = $block.find('.block-loading').fadeOut()
if newLabelField.length
$newLabelCreateButton = $('.js-new-label-btn')
@@ -21,6 +27,22 @@ class @LabelsSelect
# Suggested colors in the dropdown to chose from pre-chosen colors
$('.suggest-colors-dropdown a').on 'click', (e) ->
+
+ issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
+ if issueUpdateURL
+ labelHTMLTemplate = _.template(
+ '<% _.each(labels, function(label){ %>
+ <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>">
+ <span class="label color-label" style="background-color: <%= label.color %>;">
+ <%= label.title %>
+ </span>
+ </a>
+ <% }); %>'
+ );
+ labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
+
+ if newLabelField.length and $dropdown.hasClass 'js-extra-options'
+ $('.suggest-colors-dropdown a').on "click", (e) ->
e.preventDefault()
e.stopPropagation()
newColorField
@@ -57,6 +79,23 @@ class @LabelsSelect
# 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()
@@ -90,41 +129,78 @@ class @LabelsSelect
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
+ saveLabelData = ->
+ selected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='#{$dropdown.data('field-name')}']")
+ .map(->
+ @value
+ ).get()
+ data = {}
+ data[abilityName] = {}
+ data[abilityName].label_ids = selected
+ if not selected.length
+ data[abilityName].label_ids = ['']
+ $loading.fadeIn()
+ $.ajax(
+ type: 'PUT'
+ url: issueUpdateURL
+ dataType: 'JSON'
+ data: data
+ ).done (data) ->
+ $loading.fadeOut()
+ $selectbox.hide()
+ data.issueURLSplit = issueURLSplit
+ if not data.labels.length
+ template = labelNoneHTMLTemplate()
+ else
+ template = labelHTMLTemplate(data)
+ href = $value
+ .show()
+ .html(template)
+ $value
+ .find('a')
+ .each((i) ->
+ setTimeout(=>
+ glAnimate($(@), 'pulse')
+ ,200 * i
+ )
+ )
+
+
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: labelUrl
).done (data) ->
- if showNo
- data.unshift(
- id: 0
- title: 'No Label'
- )
-
- if showAny
- data.unshift(
- isAny: true
- title: 'Any Label'
- )
-
- if data.length > 2
- data.splice 2, 0, 'divider'
-
+ if $dropdown.hasClass 'js-extra-options'
+ if showNo
+ data.unshift(
+ id: 0
+ title: 'No Label'
+ )
+
+ if showAny
+ data.unshift(
+ isAny: true
+ title: 'Any Label'
+ )
+
+ if data.length > 2
+ data.splice 2, 0, 'divider'
callback data
+
renderRow: (label) ->
- if $.isArray(selectedLabel)
- selected = ''
- $.each selectedLabel, (i, selectedLbl) ->
- selectedLbl = selectedLbl.trim()
- if selected is '' and label.title is selectedLbl
- selected = 'is-active'
- else
- selected = if label.title is selectedLabel then 'is-active' else ''
+ selectedClass = ''
+ if $selectbox.find("input[type='hidden']\
+ [name='#{$dropdown.data('field-name')}']\
+ [value='#{label.id}']").length
+ selectedClass = 'is-active'
color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else ""
"<li>
- <a href='#' class='#{selected}'>
+ <a href='#' class='#{selectedClass}'>
#{color}
#{label.title}
</a>
@@ -133,6 +209,7 @@ class @LabelsSelect
search:
fields: ['title']
selectable: true
+
toggleLabel: (selected) ->
if selected and selected.title isnt 'Any Label'
selected.title
@@ -142,8 +219,19 @@ class @LabelsSelect
id: (label) ->
if label.isAny?
''
- else
+ else if $dropdown.hasClass "js-filter-submit"
label.title
+ else
+ label.id
+
+ hidden: ->
+ $selectbox.hide()
+ $value.show()
+ if $dropdown.hasClass 'js-multiselect'
+ saveLabelData()
+
+ multiSelect: $dropdown.hasClass 'js-multiselect'
+
clicked: ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
@@ -153,4 +241,9 @@ class @LabelsSelect
Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit()
+ else
+ if $dropdown.hasClass 'js-multiselect'
+ return
+ else
+ saveLabelData()
)
diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/animate.js.coffee
new file mode 100644
index 00000000000..8f892b5a2b9
--- /dev/null
+++ b/app/assets/javascripts/lib/animate.js.coffee
@@ -0,0 +1,13 @@
+((w) ->
+
+ w.glAnimate = ($el, animation, done) ->
+ $el
+ .removeClass()
+ .addClass(animation + ' animated')
+ .one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', ->
+ $(this).removeClass()
+ return
+ return
+ return
+
+) window \ No newline at end of file
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index e17a1adb648..d1746c38e74 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -1,35 +1,52 @@
class @MilestoneSelect
- constructor: ->
+ constructor: (currentProject) ->
+ if currentProject?
+ _this = @
+ @currentProject = JSON.parse(currentProject)
$('.js-milestone-select').each (i, dropdown) ->
$dropdown = $(dropdown)
projectId = $dropdown.data('project-id')
milestonesUrl = $dropdown.data('milestones')
+ issueUpdateURL = $dropdown.data('issueUpdate')
selectedMilestone = $dropdown.data('selected')
showNo = $dropdown.data('show-no')
showAny = $dropdown.data('show-any')
useId = $dropdown.data('use-id')
defaultLabel = $dropdown.data('default-label')
+ issuableId = $dropdown.data('issuable-id')
+ abilityName = $dropdown.data('ability-name')
+ $selectbox = $dropdown.closest('.selectbox')
+ $block = $selectbox.closest('.block')
+ $value = $block.find('.value')
+ $loading = $block.find('.block-loading').fadeOut()
+
+ if issueUpdateURL
+ milestoneLinkTemplate = _.template(
+ '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>'
+ )
+
+ milestoneLinkNoneTemplate = '<div class="light">None</div>'
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: milestonesUrl
).done (data) ->
- if showNo
- data.unshift(
- id: '0'
- title: 'No Milestone'
- )
-
- if showAny
- data.unshift(
- isAny: true
- title: 'Any Milestone'
- )
+ if $dropdown.hasClass "js-extra-options"
+ if showNo
+ data.unshift(
+ id: '0'
+ title: 'No Milestone'
+ )
- if data.length > 2
- data.splice 2, 0, 'divider'
+ if showAny
+ data.unshift(
+ isAny: true
+ title: 'Any Milestone'
+ )
+ if data.length > 2
+ data.splice 2, 0, 'divider'
callback(data)
filterable: true
search:
@@ -53,13 +70,38 @@ class @MilestoneSelect
milestone.id
isSelected: (milestone) ->
milestone.title is selectedMilestone
- clicked: ->
- page = $('body').data 'page'
- isIssueIndex = page is 'projects:issues:index'
- isMRIndex = page is page is 'projects:merge_requests:index'
-
- if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
- Issues.filterResults $dropdown.closest('form')
- else if $dropdown.hasClass 'js-filter-submit'
- $dropdown.closest('form').submit()
- )
+ hidden: ->
+ $selectbox.hide()
+ $value.show()
+ clicked: (e) ->
+ if $dropdown.hasClass 'js-filter-bulk-update'
+ return
+
+ if $dropdown.hasClass 'js-filter-submit'
+ $dropdown.parents('form').submit()
+ else
+ selected = $selectbox
+ .find('input[type="hidden"]')
+ .val()
+ data = {}
+ data[abilityName] = {}
+ data[abilityName].milestone_id = selected
+ $loading
+ .fadeIn()
+ $.ajax(
+ type: 'PUT'
+ url: issueUpdateURL
+ data: data
+ ).done (data) ->
+ $loading.fadeOut()
+ $selectbox.hide()
+ $milestoneLink = $value
+ .show()
+ .find('a')
+ if data.milestone?
+ data.milestone.namespace = _this.currentProject.namespace
+ data.milestone.path = _this.currentProject.path
+ $value.html(milestoneLinkTemplate(data.milestone))
+ else
+ $value.html(milestoneLinkNoneTemplate)
+ ) \ No newline at end of file
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 84193400890..3262d8b8c90 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -1,7 +1,9 @@
class @UsersSelect
- constructor: ->
+ constructor: (currentUser) ->
@usersPath = "/autocomplete/users.json"
@userPath = "/autocomplete/users/:id.json"
+ if currentUser?
+ @currentUser = JSON.parse(currentUser)
$('.js-user-search').each (i, dropdown) =>
$dropdown = $(dropdown)
@@ -12,6 +14,67 @@ class @UsersSelect
firstUser = $dropdown.data('first-user')
selectedId = $dropdown.data('selected')
defaultLabel = $dropdown.data('default-label')
+ issueURL = $dropdown.data('issueUpdate')
+ $selectbox = $dropdown.closest('.selectbox')
+ $block = $selectbox.closest('.block')
+ abilityName = $dropdown.data('ability-name')
+ $value = $block.find('.value')
+ $loading = $block.find('.block-loading').fadeOut()
+
+ $block.on('click', '.js-assign-yourself', (e) =>
+ e.preventDefault()
+ assignTo(@currentUser.id)
+ )
+
+ assignTo = (selected) ->
+ data = {}
+ data[abilityName] = {}
+ data[abilityName].assignee_id = selected
+ $loading
+ .fadeIn()
+ $.ajax(
+ type: 'PUT'
+ dataType: 'json'
+ url: issueURL
+ data: data
+ ).done (data) ->
+ $loading.fadeOut()
+ $selectbox.hide()
+
+ if data.assignee
+ user =
+ name: data.assignee.name
+ username: data.assignee.username
+ avatar: data.assignee.avatar_url
+ else
+ user =
+ name: 'Unassigned'
+ username: ''
+ avatar: ''
+
+ $value.html(noAssigneeTemplate(user))
+ $value.find('a').attr('href')
+
+ noAssigneeTemplate = _.template(
+ '<% if (username) { %>
+ <a class="author_link " href="/u/<%= username %>">
+ <% if( avatar ) { %>
+ <img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
+ <% } %>
+ <span class="author"><%= name %></span>
+ <span class="username">
+ @<%= username %>
+ </span>
+ </a>
+ <% } else { %>
+ <span class="assign-yourself">
+ No assignee -
+ <a href="#" class="js-assign-yourself">
+ assign yourself
+ </a>
+ </span>
+ <% } %>'
+ )
$dropdown.glDropdown(
data: (term, callback) =>
@@ -57,20 +120,36 @@ class @UsersSelect
fields: ['name', 'username']
selectable: true
fieldName: $dropdown.data('field-name')
+
toggleLabel: (selected) ->
if selected && 'id' of selected
selected.name
else
defaultLabel
+
+ inputId: 'issue_assignee_id'
+
+ hidden: (e) ->
+ $selectbox.hide()
+ $value.show()
+
clicked: ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
+ if $dropdown.hasClass('js-filter-bulk-update')
+ return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit()
+ else
+ selected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='#{$dropdown.data('field-name')}']").val()
+ assignTo(selected)
+
renderRow: (user) ->
username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false
@@ -87,17 +166,25 @@ class @UsersSelect
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>"
+ # split into three parts so we can remove the username section if nessesary
+ listWithName = "<li>
+ <a href='#' class='dropdown-menu-user-link #{selected}'>
+ #{img}
+ <strong class='dropdown-menu-user-full-name'>
+ #{user.name}
+ </strong>"
+
+ listWithUserName = "<span class='dropdown-menu-user-username'>
+ #{username}
+ </span>"
+ listClosingTags = "</a>
+ </li>"
+
+
+ if username is ''
+ listWithUserName = ''
+
+ listWithName + listWithUserName + listClosingTags
)
$('.ajax-users-select').each (i, select) =>
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index e2d590f4df4..69b3b6586de 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -10,6 +10,7 @@
*= require dropzone/basic
*= require cal-heatmap
*= require cropper.css
+ *= require animate
*/
/*
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 2d616fc660c..11e1e4275ac 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -117,7 +117,7 @@
padding-left: 10px;
padding-right: 10px;
color: $dropdown-link-color;
- line-height: 34px;
+ line-height: 16px;
text-overflow: ellipsis;
border-radius: 2px;
white-space: nowrap;
@@ -167,13 +167,13 @@
}
.dropdown-menu-user-link {
- padding-top: 7px;
+ padding-top: 10px;
padding-bottom: 7px;
}
.dropdown-menu-user-full-name {
display: block;
- font-weight: 600;
+ font-weight: 500;
line-height: 16px;
text-overflow: ellipsis;
overflow: hidden;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index ad0e88cda86..a26ace5cc19 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -3,12 +3,10 @@
*
*/
.file-holder {
- border: none;
border: 1px solid $border-color;
&.readme-holder {
- margin-top: 10px;
- border-bottom: 0;
+ margin: $gl-padding-top 0;
}
table {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 5e91496679a..b6011fe7679 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -93,7 +93,6 @@ li.commit {
.commit-row-info {
color: $gl-gray;
line-height: 24px;
- font-size: 13px;
a {
color: $gl-gray;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 5300bb52a1b..88c1b614c74 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -30,6 +30,10 @@
}
.issuable-sidebar {
+ a {
+ color: inherit;
+ }
+
.block {
@include clearfix;
padding: $gl-padding 0;
@@ -89,7 +93,7 @@
}
.cross-project-reference {
- color: $gl-link-color;
+ color: inherit;
span {
white-space: nowrap;
@@ -133,6 +137,12 @@
.value {
line-height: 1;
+
+ .assign-yourself {
+ margin-top: 10px;
+ font-weight: normal;
+ display: block;
+ }
}
.bold {
@@ -252,6 +262,15 @@
text-decoration: none;
}
}
+
+ .dropdown-menu-toggle {
+ width: 100%;
+ padding-top: 6px;
+ }
+
+ .open .dropdown-menu {
+ width: 100%;
+ }
}
.btn-default.gutter-toggle {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index daf2651425f..655f88b0c2c 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -71,8 +71,6 @@
}
.note-form-actions {
- background: #fff;
-
.note-form-option {
margin-top: 8px;
margin-left: 30px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 71bde1174ee..ee7c6a3d55f 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -222,7 +222,7 @@
padding: 0;
background: transparent;
border: none;
- line-height: 42px;
+ line-height: 36px;
margin: 0;
> li + li:before {
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 877b39c9b1b..6d649e72f84 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -68,7 +68,13 @@ class Projects::IssuesController < Projects::ApplicationController
@merge_requests = @issue.referenced_merge_requests(current_user)
@related_branches = @issue.related_branches - @merge_requests.map(&:source_branch)
- respond_with(@issue)
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: @issue.to_json(include: [:milestone, :labels])
+ end
+ end
+
end
def create
@@ -107,10 +113,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
end
format.json do
- render json: {
- saved: @issue.valid?,
- assignee_avatar_url: @issue.assignee.try(:avatar_url)
- }
+ render json: @issue.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
end
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index b830d777752..6189de09f27 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -154,10 +154,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request])
end
format.json do
- render json: {
- saved: @merge_request.valid?,
- assignee_avatar_url: @merge_request.assignee.try(:avatar_url)
- }
+ render json: @merge_request.to_json(include: [:milestone, :labels, :assignee])
end
end
else
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index b2e974eff17..5b0a63a933c 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -19,7 +19,6 @@ class Projects::MilestonesController < Projects::ApplicationController
end
@milestones = @milestones.includes(:project)
-
respond_to do |format|
format.html do
@milestones = @milestones.page(params[:page])
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 316a10b7da3..14697f774cc 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -60,7 +60,7 @@ module DropdownsHelper
title_output << content_tag(:span, title)
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close", aria: { label: "Close" }, type: "button") do
- icon('times')
+ icon('times', class: 'dropdown-menu-close-icon')
end
title_output.html_safe
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 81df2094392..62050691a39 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -16,6 +16,16 @@ module IssuablesHelper
base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
end
+ def issuable_json_path(issuable)
+ project = issuable.project
+
+ if issuable.kind_of?(MergeRequest)
+ namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json)
+ else
+ namespace_project_issue_path(project.namespace, project, issuable.iid, :json)
+ end
+ end
+
def prev_issuable_for(issuable)
base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
end
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index 905f6bbbd48..1fe1d98bf13 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -2,7 +2,7 @@
- header_title project_title(@project, "Files", project_files_path(@project))
.file-finder-holder.tree-holder.clearfix
- .gray-content-block.top-block
+ .nav-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'find_file', path: @path
%ul.breadcrumb.repo-breadcrumb
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index ee5b9fd95a8..1dd8f721f7e 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -10,7 +10,7 @@
.merge-request{'data-url' => merge_request_path(@merge_request)}
= render "projects/merge_requests/show/mr_title"
- .merge-request-details.issuable-details
+ .merge-request-details.issuable-details{data: {id: @merge_request.project.id}}
= render "projects/merge_requests/show/mr_box"
.append-bottom-default.mr-source-target.prepend-top-default
- if @merge_request.open?
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index ba69569b1e7..1c5f8b3928b 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -15,11 +15,11 @@
- if current_user
%li
- if !on_top_of_branch?
- %span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
+ %span.btn.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
= icon('plus')
- else
%span.dropdown
- %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
= icon('plus')
%ul.dropdown-menu
- if can_edit_tree?
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index f91ff0e3694..c99da92be9f 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -9,13 +9,13 @@
.filter-item.inline
- if params[:author_id]
= hidden_field_tag(:author_id, params[:author_id])
- = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author",
+ = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
.filter-item.inline
- if params[:assignee_id]
= hidden_field_tag(:assignee_id, params[:assignee_id])
- = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee",
+ = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter
@@ -23,7 +23,6 @@
.filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown"
-
.pull-right
= render 'shared/sort_dropdown'
@@ -38,11 +37,10 @@
%li
%a{href: "#", data: {id: "close"}} Closed
.filter-item.inline
- = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable",
+ = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee js-filter-submit js-filter-bulk-update", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable",
placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } })
.filter-item.inline
- = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
- placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } })
+ = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } })
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :state_event, params[:state_event]
.filter-item.inline
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 0434506c8d7..1c79494f816 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,6 +1,6 @@
- if params[:milestone_title]
= hidden_field_tag(:milestone_title, params[:milestone_title])
-= dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
+= dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit js-extra-options', filter: true, dropdown_class: "dropdown-menu-selectable",
placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project
%ul.dropdown-footer-list
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0e20e86356d..451c64da2c4 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -28,6 +28,7 @@
= icon('user')
.title.hide-collapsed
Assignee
+ = icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.hide-collapsed
@@ -39,10 +40,14 @@
%span.username
= issuable.assignee.to_reference
- else
- .light None
+ %span.assign-yourself
+ No assignee -
+ %a.js-assign-yourself{ href: '#' }
+ assign yourself
.selectbox.hide-collapsed
- = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
+ = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id'
+ = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } })
.block.milestone
.sidebar-collapsed-icon
@@ -54,6 +59,7 @@
No
.title.hide-collapsed
Milestone
+ = icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.hide-collapsed
@@ -62,10 +68,10 @@
= issuable.milestone.title
- else
.light None
+
.selectbox.hide-collapsed
- = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
- = hidden_field_tag :issuable_context
- = f.submit class: 'btn hide'
+ = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil
+ = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }})
- if issuable.project.labels.any?
.block.labels
@@ -75,6 +81,7 @@
= issuable.labels.count
.title.hide-collapsed
Labels
+ = icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels.any?) }
@@ -84,8 +91,31 @@
- else
.light None
.selectbox.hide-collapsed
- = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
- { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
+ - issuable.labels.each do |label|
+ = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
+ .dropdown
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect{type: "button", data: {toggle: "dropdown", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", project_id: (@project.id if @project), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}}
+ %span.dropdown-toggle-text
+ Label
+ = icon('chevron-down')
+ .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
+ .dropdown-page-one
+ = dropdown_title("Assign labels")
+ = dropdown_filter("Search labels")
+ = dropdown_content
+ - if @project
+ = dropdown_footer do
+ %ul.dropdown-footer-list
+ - if can? current_user, :admin_label, @project
+ %li
+ %a.dropdown-toggle-page{href: "#"}
+ Create new
+ %li
+ = link_to namespace_project_labels_path(@project.namespace, @project) do
+ - if can? current_user, :admin_label, @project
+ Manage labels
+ - else
+ View labels
= render "shared/issuable/participants", participants: issuable.participants(current_user)
- if current_user
@@ -116,5 +146,7 @@
= clipboard_button(clipboard_text: project_ref)
:javascript
- new Subscription('.subscription');
- new IssuableContext();
+ new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}');
+ new LabelsSelect();
+ new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}');
+ new Subscription('.subscription')