PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/app/assets/javascripts/labels_select.js.coffee

https://gitlab.com/OdNairy/gitlab-ee
CoffeeScript | 386 lines | 309 code | 62 blank | 15 comment | 46 complexity | 20074d47dfa1bb49546e9624ac52635f MD5 | raw file
  1. class @LabelsSelect
  2. constructor: ->
  3. _this = @
  4. $('.js-label-select').each (i, dropdown) ->
  5. $dropdown = $(dropdown)
  6. projectId = $dropdown.data('project-id')
  7. labelUrl = $dropdown.data('labels')
  8. issueUpdateURL = $dropdown.data('issueUpdate')
  9. selectedLabel = $dropdown.data('selected')
  10. if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
  11. selectedLabel = selectedLabel.split(',')
  12. newLabelField = $('#new_label_name')
  13. newColorField = $('#new_label_color')
  14. showNo = $dropdown.data('show-no')
  15. showAny = $dropdown.data('show-any')
  16. defaultLabel = $dropdown.data('default-label')
  17. abilityName = $dropdown.data('ability-name')
  18. $selectbox = $dropdown.closest('.selectbox')
  19. $block = $selectbox.closest('.block')
  20. $form = $dropdown.closest('form')
  21. $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
  22. $value = $block.find('.value')
  23. $newLabelError = $('.js-label-error')
  24. $colorPreview = $('.js-dropdown-label-color-preview')
  25. $newLabelCreateButton = $('.js-new-label-btn')
  26. $newLabelError.hide()
  27. $loading = $block.find('.block-loading').fadeOut()
  28. issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
  29. if issueUpdateURL
  30. labelHTMLTemplate = _.template(
  31. '<% _.each(labels, function(label){ %>
  32. <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
  33. <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
  34. <%- label.title %>
  35. </span>
  36. </a>
  37. <% }); %>'
  38. )
  39. labelNoneHTMLTemplate = '<span class="no-value">None</span>'
  40. if newLabelField.length
  41. # Suggested colors in the dropdown to chose from pre-chosen colors
  42. $('.suggest-colors-dropdown a').on "click", (e) ->
  43. e.preventDefault()
  44. e.stopPropagation()
  45. newColorField
  46. .val($(this).data('color'))
  47. .trigger('change')
  48. $colorPreview
  49. .css 'background-color', $(this).data('color')
  50. .parent()
  51. .addClass 'is-active'
  52. # Cancel button takes back to first page
  53. resetForm = ->
  54. newLabelField
  55. .val ''
  56. .trigger 'change'
  57. newColorField
  58. .val ''
  59. .trigger 'change'
  60. $colorPreview
  61. .css 'background-color', ''
  62. .parent()
  63. .removeClass 'is-active'
  64. $('.dropdown-menu-back').on 'click', ->
  65. resetForm()
  66. $('.js-cancel-label-btn').on 'click', (e) ->
  67. e.preventDefault()
  68. e.stopPropagation()
  69. resetForm()
  70. $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
  71. # Listen for change and keyup events on label and color field
  72. # This allows us to enable the button when ready
  73. enableLabelCreateButton = ->
  74. if newLabelField.val() isnt '' and newColorField.val() isnt ''
  75. $newLabelError.hide()
  76. $newLabelCreateButton.enable()
  77. else
  78. $newLabelCreateButton.disable()
  79. saveLabel = ->
  80. # Create new label with API
  81. Api.newLabel projectId, {
  82. name: newLabelField.val()
  83. color: newColorField.val()
  84. }, (label) ->
  85. $newLabelCreateButton.enable()
  86. if label.message?
  87. errors = _.map label.message, (value, key) ->
  88. "#{key} #{value[0]}"
  89. $newLabelError
  90. .html errors.join("<br/>")
  91. .show()
  92. else
  93. $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
  94. newLabelField.on 'keyup change', enableLabelCreateButton
  95. newColorField.on 'keyup change', enableLabelCreateButton
  96. # Send the API call to create the label
  97. $newLabelCreateButton
  98. .disable()
  99. .on 'click', (e) ->
  100. e.preventDefault()
  101. e.stopPropagation()
  102. saveLabel()
  103. saveLabelData = ->
  104. selected = $dropdown
  105. .closest('.selectbox')
  106. .find("input[name='#{$dropdown.data('field-name')}']")
  107. .map(->
  108. @value
  109. ).get()
  110. data = {}
  111. data[abilityName] = {}
  112. data[abilityName].label_ids = selected
  113. if not selected.length
  114. data[abilityName].label_ids = ['']
  115. $loading.fadeIn()
  116. $dropdown.trigger('loading.gl.dropdown')
  117. $.ajax(
  118. type: 'PUT'
  119. url: issueUpdateURL
  120. dataType: 'JSON'
  121. data: data
  122. ).done (data) ->
  123. $loading.fadeOut()
  124. $dropdown.trigger('loaded.gl.dropdown')
  125. $selectbox.hide()
  126. data.issueURLSplit = issueURLSplit
  127. labelCount = 0
  128. if data.labels.length
  129. template = labelHTMLTemplate(data)
  130. labelCount = data.labels.length
  131. else
  132. template = labelNoneHTMLTemplate
  133. $value
  134. .removeAttr('style')
  135. .html(template)
  136. $sidebarCollapsedValue.text(labelCount)
  137. $('.has-tooltip', $value).tooltip(container: 'body')
  138. $value
  139. .find('a')
  140. .each((i) ->
  141. setTimeout(=>
  142. gl.animate.animate($(@), 'pulse')
  143. ,200 * i
  144. )
  145. )
  146. $dropdown.glDropdown(
  147. data: (term, callback) ->
  148. $.ajax(
  149. url: labelUrl
  150. ).done (data) ->
  151. data = _.chain data
  152. .groupBy (label) ->
  153. label.title
  154. .map (label) ->
  155. color = _.map label, (dup) ->
  156. dup.color
  157. return {
  158. id: label[0].id
  159. title: label[0].title
  160. color: color
  161. duplicate: color.length > 1
  162. }
  163. .value()
  164. if $dropdown.hasClass 'js-extra-options'
  165. if showNo
  166. data.unshift(
  167. id: 0
  168. title: 'No Label'
  169. )
  170. if showAny
  171. data.unshift(
  172. isAny: true
  173. title: 'Any Label'
  174. )
  175. if data.length > 2
  176. data.splice 2, 0, 'divider'
  177. callback data
  178. renderRow: (label, instance) ->
  179. $li = $('<li>')
  180. $a = $('<a href="#">')
  181. selectedClass = []
  182. removesAll = label.id is 0 or not label.id?
  183. if $dropdown.hasClass('js-filter-bulk-update')
  184. indeterminate = instance.indeterminateIds
  185. active = instance.activeIds
  186. if indeterminate.indexOf(label.id) isnt -1
  187. selectedClass.push 'is-indeterminate'
  188. if active.indexOf(label.id) isnt -1
  189. # Remove is-indeterminate class if the item will be marked as active
  190. i = selectedClass.indexOf 'is-indeterminate'
  191. selectedClass.splice i, 1 unless i is -1
  192. selectedClass.push 'is-active'
  193. # Add input manually
  194. instance.addInput @fieldName, label.id
  195. if $form.find("input[type='hidden']\
  196. [name='#{$dropdown.data('fieldName')}']\
  197. [value='#{this.id(label)}']").length
  198. selectedClass.push 'is-active'
  199. if $dropdown.hasClass('js-multiselect') and removesAll
  200. selectedClass.push 'dropdown-clear-active'
  201. if label.duplicate
  202. spacing = 100 / label.color.length
  203. # Reduce the colors to 4
  204. label.color = label.color.filter (color, i) ->
  205. i < 4
  206. color = _.map(label.color, (color, i) ->
  207. percentFirst = Math.floor(spacing * i)
  208. percentSecond = Math.floor(spacing * (i + 1))
  209. "#{color} #{percentFirst}%,#{color} #{percentSecond}% "
  210. ).join(',')
  211. color = "linear-gradient(#{color})"
  212. else
  213. if label.color?
  214. color = label.color[0]
  215. if color
  216. colorEl = "<span class='dropdown-label-box' style='background: #{color}'></span>"
  217. else
  218. colorEl = ''
  219. # We need to identify which items are actually labels
  220. if label.id
  221. selectedClass.push('label-item')
  222. $a.attr('data-label-id', label.id)
  223. $a.addClass(selectedClass.join(' '))
  224. .html("#{colorEl} #{label.title}")
  225. # Return generated html
  226. $li.html($a).prop('outerHTML')
  227. persistWhenHide: $dropdown.data('persistWhenHide')
  228. search:
  229. fields: ['title']
  230. selectable: true
  231. filterable: true
  232. toggleLabel: (selected, el) ->
  233. selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
  234. if selected and selected.title?
  235. if selected_labels.length > 1
  236. "#{selected.title} +#{selected_labels.length - 1} more"
  237. else
  238. selected.title
  239. else if not selected and selected_labels.length isnt 0
  240. if selected_labels.length > 1
  241. "#{$(selected_labels[0]).text()} +#{selected_labels.length - 1} more"
  242. else if selected_labels.length is 1
  243. $(selected_labels).text()
  244. else
  245. defaultLabel
  246. fieldName: $dropdown.data('field-name')
  247. id: (label) ->
  248. if $dropdown.hasClass("js-filter-submit") and not label.isAny?
  249. label.title
  250. else
  251. label.id
  252. hidden: ->
  253. page = $('body').data 'page'
  254. isIssueIndex = page is 'projects:issues:index'
  255. isMRIndex = page is 'projects:merge_requests:index'
  256. $selectbox.hide()
  257. # display:block overrides the hide-collapse rule
  258. $value.removeAttr('style')
  259. if $dropdown.hasClass 'js-multiselect'
  260. if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
  261. selectedLabels = $dropdown
  262. .closest('form')
  263. .find("input:hidden[name='#{$dropdown.data('fieldName')}']")
  264. Issuable.filterResults $dropdown.closest('form')
  265. else if $dropdown.hasClass('js-filter-submit')
  266. $dropdown.closest('form').submit()
  267. else
  268. if not $dropdown.hasClass 'js-filter-bulk-update'
  269. saveLabelData()
  270. if $dropdown.hasClass('js-filter-bulk-update')
  271. # If we are persisting state we need the classes
  272. if not @options.persistWhenHide
  273. $dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
  274. multiSelect: $dropdown.hasClass 'js-multiselect'
  275. clicked: (label) ->
  276. _this.enableBulkLabelDropdown()
  277. if $dropdown.hasClass('js-filter-bulk-update')
  278. return
  279. page = $('body').data 'page'
  280. isIssueIndex = page is 'projects:issues:index'
  281. isMRIndex = page is 'projects:merge_requests:index'
  282. if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
  283. if not $dropdown.hasClass 'js-multiselect'
  284. selectedLabel = label.title
  285. Issuable.filterResults $dropdown.closest('form')
  286. else if $dropdown.hasClass 'js-filter-submit'
  287. $dropdown.closest('form').submit()
  288. else
  289. if $dropdown.hasClass 'js-multiselect'
  290. return
  291. else
  292. saveLabelData()
  293. setIndeterminateIds: ->
  294. if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
  295. @indeterminateIds = _this.getIndeterminateIds()
  296. setActiveIds: ->
  297. if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
  298. @activeIds = _this.getActiveIds()
  299. )
  300. @bindEvents()
  301. bindEvents: ->
  302. $('body').on 'change', '.selected_issue', @onSelectCheckboxIssue
  303. onSelectCheckboxIssue: ->
  304. return if $('.selected_issue:checked').length
  305. # Remove inputs
  306. $('.issues_bulk_update .labels-filter input[type="hidden"]').remove()
  307. # Also restore button text
  308. $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label')
  309. getIndeterminateIds: ->
  310. label_ids = []
  311. $('.selected_issue:checked').each (i, el) ->
  312. issue_id = $(el).data('id')
  313. label_ids.push $("#issue_#{issue_id}").data('labels')
  314. _.flatten(label_ids)
  315. getActiveIds: ->
  316. label_ids = []
  317. $('.selected_issue:checked').each (i, el) ->
  318. issue_id = $(el).data('id')
  319. label_ids.push $("#issue_#{issue_id}").data('labels')
  320. _.intersection.apply _, label_ids
  321. enableBulkLabelDropdown: ->
  322. if $('.selected_issue:checked').length
  323. issuableBulkActions = $('.bulk-update').data('bulkActions')
  324. issuableBulkActions.willUpdateLabels = true