gitlab-org--gitlab-foss/app/assets/javascripts/labels_select.js.coffee

387 lines
13 KiB
CoffeeScript
Raw Normal View History

2016-03-07 17:17:11 +00:00
class @LabelsSelect
constructor: ->
2016-04-26 18:05:21 +00:00
_this = @
2016-03-07 17:17:11 +00:00
$('.js-label-select').each (i, dropdown) ->
$dropdown = $(dropdown)
projectId = $dropdown.data('project-id')
labelUrl = $dropdown.data('labels')
2016-03-12 20:37:02 +00:00
issueUpdateURL = $dropdown.data('issueUpdate')
selectedLabel = $dropdown.data('selected')
2016-04-07 18:57:21 +00:00
if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
selectedLabel = selectedLabel.split(',')
2016-04-18 17:49:23 +00:00
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')
2016-03-12 20:37:02 +00:00
$selectbox = $dropdown.closest('.selectbox')
$block = $selectbox.closest('.block')
$form = $dropdown.closest('form')
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
2016-03-12 20:37:02 +00:00
$value = $block.find('.value')
2016-04-18 17:49:23 +00:00
$newLabelError = $('.js-label-error')
$colorPreview = $('.js-dropdown-label-color-preview')
$newLabelCreateButton = $('.js-new-label-btn')
2016-04-15 18:01:46 +00:00
$newLabelError.hide()
2016-03-12 20:37:02 +00:00
$loading = $block.find('.block-loading').fadeOut()
2016-03-08 11:23:54 +00:00
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) %>">
2016-04-21 07:50:15 +00:00
<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>'
2016-04-15 18:01:46 +00:00
if newLabelField.length
2016-04-12 04:05:49 +00:00
# Suggested colors in the dropdown to chose from pre-chosen colors
$('.suggest-colors-dropdown a').on "click", (e) ->
2016-03-08 11:23:54 +00:00
e.preventDefault()
e.stopPropagation()
newColorField
.val($(this).data('color'))
.trigger('change')
$colorPreview
.css 'background-color', $(this).data('color')
.parent()
2016-03-08 11:23:54 +00:00
.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()
2016-04-15 18:01:46 +00:00
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]}"
2016-04-15 18:01:46 +00:00
$newLabelError
.html errors.join("<br/>")
2016-04-15 18:01:46 +00:00
.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()
2016-04-15 18:01:46 +00:00
saveLabel()
2016-03-07 17:17:11 +00:00
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()
2016-03-27 15:04:51 +00:00
$dropdown.trigger('loading.gl.dropdown')
$.ajax(
type: 'PUT'
url: issueUpdateURL
2016-03-18 18:35:39 +00:00
dataType: 'JSON'
data: data
).done (data) ->
$loading.fadeOut()
2016-03-27 15:04:51 +00:00
$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)
2016-04-11 22:42:51 +00:00
$('.has-tooltip', $value).tooltip(container: 'body')
$value
.find('a')
.each((i) ->
setTimeout(=>
gl.animate.animate($(@), 'pulse')
,200 * i
)
)
$dropdown.glDropdown(
2016-03-08 09:09:39 +00:00
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
2016-04-26 18:05:21 +00:00
renderRow: (label, instance) ->
2016-05-31 21:11:46 +00:00
$li = $('<li>')
$a = $('<a href="#">')
2016-04-26 18:05:21 +00:00
selectedClass = []
removesAll = label.id is 0 or not label.id?
2016-04-26 18:05:21 +00:00
if $dropdown.hasClass('js-filter-bulk-update')
2016-04-26 19:34:19 +00:00
indeterminate = instance.indeterminateIds
active = instance.activeIds
2016-04-26 19:34:19 +00:00
if indeterminate.indexOf(label.id) isnt -1
selectedClass.push 'is-indeterminate'
2016-04-26 18:05:21 +00:00
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'
2016-03-08 08:38:16 +00:00
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 = ''
2016-03-18 17:11:51 +00:00
2016-05-05 22:10:12 +00:00
# We need to identify which items are actually labels
if label.id
2016-05-31 21:11:46 +00:00
selectedClass.push('label-item')
$a.attr('data-label-id', label.id)
2016-05-05 22:10:12 +00:00
2016-05-31 21:11:46 +00:00
$a.addClass(selectedClass.join(' '))
.html("#{colorEl} #{_.escape(label.title)}")
2016-05-05 22:10:12 +00:00
2016-05-31 21:11:46 +00:00
# Return generated html
$li.html($a).prop('outerHTML')
2016-05-05 22:10:12 +00:00
persistWhenHide: $dropdown.data('persistWhenHide')
2016-03-07 17:17:11 +00:00
search:
fields: ['title']
2016-03-07 17:17:11 +00:00
selectable: true
filterable: true
toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
if selected and selected.title?
2016-04-19 16:22:55 +00:00
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')
2016-03-07 17:17:11 +00:00
id: (label) ->
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
_.escape label.title
2016-03-12 20:37:02 +00:00
else
label.id
hidden: ->
2016-03-29 14:52:25 +00:00
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
2016-04-19 16:22:55 +00:00
isMRIndex = page is 'projects:merge_requests:index'
2016-03-12 20:37:02 +00:00
$selectbox.hide()
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
if $dropdown.hasClass 'js-multiselect'
2016-03-29 14:52:25 +00:00
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown
.closest('form')
2016-04-19 16:22:55 +00:00
.find("input:hidden[name='#{$dropdown.data('fieldName')}']")
Issuable.filterResults $dropdown.closest('form')
2016-03-29 14:52:25 +00:00
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')
2016-05-05 22:10:12 +00:00
# If we are persisting state we need the classes
if not @options.persistWhenHide
$dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
2016-03-12 20:37:02 +00:00
2016-03-14 19:37:16 +00:00
multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) ->
_this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update')
return
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
2016-04-19 16:22:55 +00:00
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()
2016-03-12 20:37:02 +00:00
else
if $dropdown.hasClass 'js-multiselect'
return
else
saveLabelData()
2016-04-26 18:05:21 +00:00
2016-04-26 19:34:19 +00:00
setIndeterminateIds: ->
2016-04-26 18:05:21 +00:00
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
2016-04-26 19:34:19 +00:00
@indeterminateIds = _this.getIndeterminateIds()
setActiveIds: ->
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
@activeIds = _this.getActiveIds()
2016-03-07 17:17:11 +00:00
)
2016-04-26 18:05:21 +00:00
@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')
2016-04-26 19:34:19 +00:00
getIndeterminateIds: ->
2016-04-26 18:05:21 +00:00
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
enableBulkLabelDropdown: ->
if $('.selected_issue:checked').length
issuableBulkActions = $('.bulk-update').data('bulkActions')
issuableBulkActions.willUpdateLabels = true