mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-22 02:56:06 +10:00
Fixes item icon state when for issues that have the same labels assigned ## What does this MR do? Previously if all issues had the same labels the dropdown was showing the labels with indeterminate stated which is wrong. This MR fixes that and shows the correct state. ## What are the relevant issue numbers? Fixes #18714 ## Screenshots (if relevant) **Before**  **After**  ## Does this MR meet the acceptance criteria? - [ ] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added - [ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md) - [ ] API support added - [ ] Tests - [ ] Added for this feature/bug - [ ] All builds are passing - [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) - [x] Branch has no merge conflicts with `master` (if you do - rebase it please) - [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) See merge request !4721
380 lines
13 KiB
CoffeeScript
380 lines
13 KiB
CoffeeScript
class @LabelsSelect
|
|
constructor: ->
|
|
_this = @
|
|
|
|
$('.js-label-select').each (i, dropdown) ->
|
|
$dropdown = $(dropdown)
|
|
projectId = $dropdown.data('project-id')
|
|
labelUrl = $dropdown.data('labels')
|
|
issueUpdateURL = $dropdown.data('issueUpdate')
|
|
selectedLabel = $dropdown.data('selected')
|
|
if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
|
|
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')
|
|
$form = $dropdown.closest('form')
|
|
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
|
|
$value = $block.find('.value')
|
|
$newLabelError = $('.js-label-error')
|
|
$colorPreview = $('.js-dropdown-label-color-preview')
|
|
$newLabelCreateButton = $('.js-new-label-btn')
|
|
|
|
$newLabelError.hide()
|
|
$loading = $block.find('.block-loading').fadeOut()
|
|
|
|
issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
|
|
if issueUpdateURL
|
|
labelHTMLTemplate = _.template(
|
|
'<% _.each(labels, function(label){ %>
|
|
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>">
|
|
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
|
|
<%= _.escape(label.title) %>
|
|
</span>
|
|
</a>
|
|
<% }); %>'
|
|
)
|
|
labelNoneHTMLTemplate = '<span class="no-value">None</span>'
|
|
|
|
if newLabelField.length
|
|
|
|
# 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'))
|
|
.trigger('change')
|
|
$colorPreview
|
|
.css 'background-color', $(this).data('color')
|
|
.parent()
|
|
.addClass 'is-active'
|
|
|
|
# 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()
|
|
$newLabelCreateButton.enable()
|
|
else
|
|
$newLabelCreateButton.disable()
|
|
|
|
saveLabel = ->
|
|
# Create new label with API
|
|
Api.newLabel projectId, {
|
|
name: newLabelField.val()
|
|
color: newColorField.val()
|
|
}, (label) ->
|
|
$newLabelCreateButton.enable()
|
|
|
|
if label.message?
|
|
errors = _.map label.message, (value, key) ->
|
|
"#{key} #{value[0]}"
|
|
|
|
$newLabelError
|
|
.html errors.join("<br/>")
|
|
.show()
|
|
else
|
|
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
|
|
|
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()
|
|
saveLabel()
|
|
|
|
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()
|
|
$dropdown.trigger('loading.gl.dropdown')
|
|
$.ajax(
|
|
type: 'PUT'
|
|
url: issueUpdateURL
|
|
dataType: 'JSON'
|
|
data: data
|
|
).done (data) ->
|
|
$loading.fadeOut()
|
|
$dropdown.trigger('loaded.gl.dropdown')
|
|
$selectbox.hide()
|
|
data.issueURLSplit = issueURLSplit
|
|
labelCount = 0
|
|
if data.labels.length
|
|
template = labelHTMLTemplate(data)
|
|
labelCount = data.labels.length
|
|
else
|
|
template = labelNoneHTMLTemplate
|
|
$value
|
|
.removeAttr('style')
|
|
.html(template)
|
|
$sidebarCollapsedValue.text(labelCount)
|
|
|
|
$('.has-tooltip', $value).tooltip(container: 'body')
|
|
|
|
$value
|
|
.find('a')
|
|
.each((i) ->
|
|
setTimeout(=>
|
|
gl.animate.animate($(@), 'pulse')
|
|
,200 * i
|
|
)
|
|
)
|
|
|
|
|
|
$dropdown.glDropdown(
|
|
data: (term, callback) ->
|
|
$.ajax(
|
|
url: labelUrl
|
|
).done (data) ->
|
|
data = _.chain data
|
|
.groupBy (label) ->
|
|
label.title
|
|
.map (label) ->
|
|
color = _.map label, (dup) ->
|
|
dup.color
|
|
|
|
return {
|
|
id: label[0].id
|
|
title: label[0].title
|
|
color: color
|
|
duplicate: color.length > 1
|
|
}
|
|
.value()
|
|
|
|
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, instance) ->
|
|
$li = $('<li>')
|
|
$a = $('<a href="#">')
|
|
|
|
selectedClass = []
|
|
removesAll = label.id is 0 or not label.id?
|
|
|
|
if $dropdown.hasClass('js-filter-bulk-update')
|
|
indeterminate = instance.indeterminateIds
|
|
active = instance.activeIds
|
|
|
|
if indeterminate.indexOf(label.id) isnt -1
|
|
selectedClass.push 'is-indeterminate'
|
|
|
|
if active.indexOf(label.id) isnt -1
|
|
# Remove is-indeterminate class if the item will be marked as active
|
|
i = selectedClass.indexOf 'is-indeterminate'
|
|
selectedClass.splice i, 1 unless i is -1
|
|
|
|
selectedClass.push 'is-active'
|
|
|
|
# Add input manually
|
|
instance.addInput @fieldName, label.id
|
|
|
|
if $form.find("input[type='hidden']\
|
|
[name='#{$dropdown.data('fieldName')}']\
|
|
[value='#{this.id(label)}']").length
|
|
selectedClass.push 'is-active'
|
|
|
|
if $dropdown.hasClass('js-multiselect') and removesAll
|
|
selectedClass.push 'dropdown-clear-active'
|
|
|
|
if label.duplicate
|
|
spacing = 100 / label.color.length
|
|
|
|
# Reduce the colors to 4
|
|
label.color = label.color.filter (color, i) ->
|
|
i < 4
|
|
|
|
color = _.map(label.color, (color, i) ->
|
|
percentFirst = Math.floor(spacing * i)
|
|
percentSecond = Math.floor(spacing * (i + 1))
|
|
"#{color} #{percentFirst}%,#{color} #{percentSecond}% "
|
|
).join(',')
|
|
color = "linear-gradient(#{color})"
|
|
else
|
|
if label.color?
|
|
color = label.color[0]
|
|
|
|
if color
|
|
colorEl = "<span class='dropdown-label-box' style='background: #{color}'></span>"
|
|
else
|
|
colorEl = ''
|
|
|
|
# We need to identify which items are actually labels
|
|
if label.id
|
|
selectedClass.push('label-item')
|
|
$a.attr('data-label-id', label.id)
|
|
|
|
$a.addClass(selectedClass.join(' '))
|
|
.html("#{colorEl} #{_.escape(label.title)}")
|
|
|
|
# Return generated html
|
|
$li.html($a).prop('outerHTML')
|
|
persistWhenHide: $dropdown.data('persistWhenHide')
|
|
search:
|
|
fields: ['title']
|
|
selectable: true
|
|
filterable: true
|
|
toggleLabel: (selected, el) ->
|
|
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
|
|
|
|
if selected and selected.title?
|
|
if selected_labels.length > 1
|
|
"#{selected.title} +#{selected_labels.length - 1} more"
|
|
else
|
|
selected.title
|
|
else if not selected and selected_labels.length isnt 0
|
|
if selected_labels.length > 1
|
|
"#{$(selected_labels[0]).text()} +#{selected_labels.length - 1} more"
|
|
else if selected_labels.length is 1
|
|
$(selected_labels).text()
|
|
else
|
|
defaultLabel
|
|
fieldName: $dropdown.data('field-name')
|
|
id: (label) ->
|
|
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
|
|
_.escape label.title
|
|
else
|
|
label.id
|
|
|
|
hidden: ->
|
|
page = $('body').data 'page'
|
|
isIssueIndex = page is 'projects:issues:index'
|
|
isMRIndex = page is 'projects:merge_requests:index'
|
|
|
|
$selectbox.hide()
|
|
# display:block overrides the hide-collapse rule
|
|
$value.removeAttr('style')
|
|
if $dropdown.hasClass 'js-multiselect'
|
|
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
|
|
selectedLabels = $dropdown
|
|
.closest('form')
|
|
.find("input:hidden[name='#{$dropdown.data('fieldName')}']")
|
|
Issuable.filterResults $dropdown.closest('form')
|
|
else if $dropdown.hasClass('js-filter-submit')
|
|
$dropdown.closest('form').submit()
|
|
else
|
|
if not $dropdown.hasClass 'js-filter-bulk-update'
|
|
saveLabelData()
|
|
|
|
if $dropdown.hasClass('js-filter-bulk-update')
|
|
# If we are persisting state we need the classes
|
|
if not @options.persistWhenHide
|
|
$dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
|
|
|
|
multiSelect: $dropdown.hasClass 'js-multiselect'
|
|
clicked: (label) ->
|
|
if $dropdown.hasClass('js-filter-bulk-update')
|
|
return
|
|
|
|
page = $('body').data 'page'
|
|
isIssueIndex = page is 'projects:issues:index'
|
|
isMRIndex = page is 'projects:merge_requests:index'
|
|
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
|
|
if not $dropdown.hasClass 'js-multiselect'
|
|
selectedLabel = label.title
|
|
Issuable.filterResults $dropdown.closest('form')
|
|
else if $dropdown.hasClass 'js-filter-submit'
|
|
$dropdown.closest('form').submit()
|
|
else
|
|
if $dropdown.hasClass 'js-multiselect'
|
|
return
|
|
else
|
|
saveLabelData()
|
|
|
|
setIndeterminateIds: ->
|
|
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
|
@indeterminateIds = _this.getIndeterminateIds()
|
|
|
|
setActiveIds: ->
|
|
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
|
@activeIds = _this.getActiveIds()
|
|
)
|
|
|
|
@bindEvents()
|
|
|
|
bindEvents: ->
|
|
$('body').on 'change', '.selected_issue', @onSelectCheckboxIssue
|
|
|
|
onSelectCheckboxIssue: ->
|
|
return if $('.selected_issue:checked').length
|
|
|
|
# Remove inputs
|
|
$('.issues_bulk_update .labels-filter input[type="hidden"]').remove()
|
|
|
|
# Also restore button text
|
|
$('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label')
|
|
|
|
getIndeterminateIds: ->
|
|
label_ids = []
|
|
|
|
$('.selected_issue:checked').each (i, el) ->
|
|
issue_id = $(el).data('id')
|
|
label_ids.push $("#issue_#{issue_id}").data('labels')
|
|
|
|
_.flatten(label_ids)
|
|
|
|
getActiveIds: ->
|
|
label_ids = []
|
|
|
|
$('.selected_issue:checked').each (i, el) ->
|
|
issue_id = $(el).data('id')
|
|
label_ids.push $("#issue_#{issue_id}").data('labels')
|
|
|
|
_.intersection.apply _, label_ids
|