labels_select.js.coffee 15.2 KB
Newer Older
1 2
class @LabelsSelect
  constructor: ->
A
Alfredo Sumaran 已提交
3 4
    _this = @

5
    $('.js-label-select').each (i, dropdown) ->
P
Phil Hughes 已提交
6
      $dropdown = $(dropdown)
P
Phil Hughes 已提交
7
      $toggleText = $dropdown.find('.dropdown-toggle-text')
P
Phil Hughes 已提交
8 9
      projectId = $dropdown.data('project-id')
      labelUrl = $dropdown.data('labels')
10
      issueUpdateURL = $dropdown.data('issueUpdate')
P
Phil Hughes 已提交
11
      selectedLabel = $dropdown.data('selected')
J
Jacob Schatz 已提交
12
      if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
13
        selectedLabel = selectedLabel.split(',')
A
Alfredo Sumaran 已提交
14 15
      newLabelField = $('#new_label_name')
      newColorField = $('#new_label_color')
P
Phil Hughes 已提交
16 17
      showNo = $dropdown.data('show-no')
      showAny = $dropdown.data('show-any')
18
      defaultLabel = $dropdown.data('default-label')
19
      abilityName = $dropdown.data('ability-name')
20 21
      $selectbox = $dropdown.closest('.selectbox')
      $block = $selectbox.closest('.block')
22
      $form = $dropdown.closest('form')
23
      $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
24
      $value = $block.find('.value')
A
Alfredo Sumaran 已提交
25 26 27
      $newLabelError = $('.js-label-error')
      $colorPreview = $('.js-dropdown-label-color-preview')
      $newLabelCreateButton = $('.js-new-label-btn')
28
      selectedLabels = []
A
Alfredo Sumaran 已提交
29

30 31 32 33
      $("input[name='#{$dropdown.data('field-name')}']").each ->
        title = $(this).data('title')
        selectedLabels.push($(this).data('title')) if title

A
Alfredo Sumaran 已提交
34
      $newLabelError.hide()
35
      $loading = $block.find('.block-loading').fadeOut()
P
Phil Hughes 已提交
36

37 38
      issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
      if issueUpdateURL
39
        labelHTMLTemplate = _.template(
40
            '<% _.each(labels, function(label){ %>
41
            <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
42
            <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
43
            <%- label.title %>
44 45 46
            </span>
            </a>
            <% }); %>'
47
        )
48
        labelNoneHTMLTemplate = '<span class="no-value">None</span>'
49

A
Alfredo Sumaran 已提交
50
      if newLabelField.length
A
Alfredo Sumaran 已提交
51 52

        # Suggested colors in the dropdown to chose from pre-chosen colors
53
        $('.suggest-colors-dropdown a').on "click", (e) ->
P
Phil Hughes 已提交
54 55
          e.preventDefault()
          e.stopPropagation()
56 57 58 59
          newColorField
            .val($(this).data('color'))
            .trigger('change')
          $colorPreview
P
Phil Hughes 已提交
60
            .css 'background-color', $(this).data('color')
61
            .parent()
P
Phil Hughes 已提交
62 63
            .addClass 'is-active'

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
        # 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()

80 81 82
        $('.js-cancel-label-btn').on 'click', (e) ->
          e.preventDefault()
          e.stopPropagation()
83
          resetForm()
84 85
          $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'

86 87 88
        # Listen for change and keyup events on label and color field
        # This allows us to enable the button when ready
        enableLabelCreateButton = ->
P
Phil Hughes 已提交
89
          if newLabelField.val() isnt '' and newColorField.val() isnt ''
90
            $newLabelError.hide()
91 92 93 94
            $newLabelCreateButton.enable()
          else
            $newLabelCreateButton.disable()

A
Alfredo Sumaran 已提交
95 96 97 98 99 100 101 102 103
        saveLabel = ->
          # Create new label with API
          Api.newLabel projectId, {
            name: newLabelField.val()
            color: newColorField.val()
          }, (label) ->
            $newLabelCreateButton.enable()

            if label.message?
104 105 106
              errors = _.map label.message, (value, key) ->
                "#{key} #{value[0]}"

A
Alfredo Sumaran 已提交
107
              $newLabelError
108
                .html errors.join("<br/>")
A
Alfredo Sumaran 已提交
109 110 111 112
                .show()
            else
              $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'

113 114 115 116 117 118 119 120 121 122
        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()
A
Alfredo Sumaran 已提交
123
            saveLabel()
124

125 126 127 128 129 130 131 132 133 134 135 136 137
      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()
J
Jacob Schatz 已提交
138
        $dropdown.trigger('loading.gl.dropdown')
139 140
        $.ajax(
          type: 'PUT'
141
          url: issueUpdateURL
A
Alfredo Sumaran 已提交
142
          dataType: 'JSON'
143 144 145
          data: data
        ).done (data) ->
          $loading.fadeOut()
J
Jacob Schatz 已提交
146
          $dropdown.trigger('loaded.gl.dropdown')
147
          $selectbox.hide()
J
Jacob Schatz 已提交
148
          data.issueURLSplit = issueURLSplit
149 150
          labelCount = 0
          if data.labels.length
151
            template = labelHTMLTemplate(data)
152 153
            labelCount = data.labels.length
          else
154
            template = labelNoneHTMLTemplate
155 156 157 158 159
          $value
            .removeAttr('style')
            .html(template)
          $sidebarCollapsedValue.text(labelCount)

160 161
          $('.has-tooltip', $value).tooltip(container: 'body')

162 163 164 165
          $value
            .find('a')
            .each((i) ->
              setTimeout(=>
166
                gl.animate.animate($(@), 'pulse')
167 168 169 170
              ,200 * i
              )
            )

171

P
Phil Hughes 已提交
172
      $dropdown.glDropdown(
P
Phil Hughes 已提交
173
        data: (term, callback) ->
174 175 176
          $.ajax(
            url: labelUrl
          ).done (data) ->
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
            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()

192
            if $dropdown.hasClass 'js-extra-options'
193
              extraData = []
194
              if showAny
195
                extraData.push(
196 197 198 199
                  isAny: true
                  title: 'Any Label'
                )

200 201 202 203 204 205 206 207 208
              if showNo
                extraData.push(
                  id: 0
                  title: 'No Label'
                )

              if extraData.length
                extraData.push 'divider'
                data = extraData.concat(data)
209

210
            callback data
211

A
Alfredo Sumaran 已提交
212
        renderRow: (label, instance) ->
A
Alfredo Sumaran 已提交
213 214 215
          $li = $('<li>')
          $a  = $('<a href="#">')

A
Alfredo Sumaran 已提交
216
          selectedClass = []
217
          removesAll = label.id is 0 or not label.id?
218

A
Alfredo Sumaran 已提交
219
          if $dropdown.hasClass('js-filter-bulk-update')
A
typo  
Alfredo Sumaran 已提交
220
            indeterminate = instance.indeterminateIds
221 222
            active = instance.activeIds

A
typo  
Alfredo Sumaran 已提交
223
            if indeterminate.indexOf(label.id) isnt -1
224
              selectedClass.push 'is-indeterminate'
A
Alfredo Sumaran 已提交
225

226 227 228 229 230 231 232 233 234 235
            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

236
          if $form.find("input[type='hidden']\
237
            [name='#{$dropdown.data('fieldName')}']\
238
            [value='#{this.id(label)}']").length
239 240 241 242
            selectedClass.push 'is-active'

          if $dropdown.hasClass('js-multiselect') and removesAll
            selectedClass.push 'dropdown-clear-active'
P
Phil Hughes 已提交
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
          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 = ''
P
Phil Hughes 已提交
265

A
Alfredo Sumaran 已提交
266 267
          # We need to identify which items are actually labels
          if label.id
A
Alfredo Sumaran 已提交
268 269
            selectedClass.push('label-item')
            $a.attr('data-label-id', label.id)
A
Alfredo Sumaran 已提交
270

A
Alfredo Sumaran 已提交
271
          $a.addClass(selectedClass.join(' '))
272
            .html("#{colorEl} #{label.title}")
A
Alfredo Sumaran 已提交
273

A
Alfredo Sumaran 已提交
274 275
          # Return generated html
          $li.html($a).prop('outerHTML')
A
Alfredo Sumaran 已提交
276
        persistWhenHide: $dropdown.data('persistWhenHide')
277
        search:
278
          fields: ['title']
279
        selectable: true
280
        filterable: true
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
        toggleLabel: (selected, $el, glDropdownInstance) ->
          # When comes from a triggered event handle it VERY differently
          if selected instanceof jQuery.Event
            $dropdownParent = $dropdown.closest '.labels-filter'
            $labelInputs = $dropdownParent.find "input[name='#{@fieldName}']"
            numberSelectedLabels = $labelInputs.length
            firstLabel = _.pluck($labelInputs, 'value')[0]

            if numberSelectedLabels is 1
                firstLabel
            else if numberSelectedLabels > 1
              "#{firstLabel} +#{numberSelectedLabels - 1} more"
            else
              defaultLabel
          # when clicking on a dropdown option
296
          else
297 298 299
            # Return when clicking "No Label"
            return if selected.id is 0
            return 'Any Label' if selected.isAny is true
300

301 302 303 304
            if glDropdownInstance?
              $dropdownParent = glDropdownInstance.dropdown.closest '.issuable-form-select-holder, .labels-filter'
            else
              $dropdownParent = $()
P
Phil Hughes 已提交
305

306 307 308
            $labelInputs = $dropdownParent.find "input[name='#{@fieldName}']"

            # Find the label by its attribute according the dropdown settings
P
Phil Hughes 已提交
309
            if $dropdown.hasClass('js-issuable-form-dropdown') or $dropdown.hasClass('js-filter-bulk-update')
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
              # When settings labels to a issuable we find the label for its ID
              whereQuery = { id: parseInt $labelInputs.first().val() }
            else
              # When filtering issuables we find the label for its title
              whereQuery = { title: $labelInputs.first().val() }

            firstLabel = _.findWhere glDropdownInstance.fullData, whereQuery

            # Better rely on inputs since filtering may returns invalid number of active labels
            numberSelectedLabels = $labelInputs.length

            # If we are adding a label
            if $el.is '.is-active'
              if numberSelectedLabels is 1
                selected.title
              else
                "#{selected.title} +#{numberSelectedLabels - 1} more"

            # otherwise we are removing a label
            else
              if numberSelectedLabels is 1
                firstLabel.title
              else if numberSelectedLabels > 1
                "#{firstLabel.title} +#{numberSelectedLabels - 1} more"
              else
                defaultLabel
P
Phil Hughes 已提交
336
        defaultLabel: defaultLabel
P
Phil Hughes 已提交
337
        fieldName: $dropdown.data('field-name')
338
        id: (label) ->
339 340 341 342 343 344
          if $dropdown.hasClass('js-issuable-form-dropdown')
            if label.id is 0
              return
            else
              return label.id

345
          if $dropdown.hasClass("js-filter-submit") and not label.isAny?
346
            label.title
347 348 349 350
          else
            label.id

        hidden: ->
J
Jacob Schatz 已提交
351 352
          page = $('body').data 'page'
          isIssueIndex = page is 'projects:issues:index'
A
Arinde Eniola 已提交
353
          isMRIndex = page is 'projects:merge_requests:index'
354

355
          $selectbox.hide()
356 357
          # display:block overrides the hide-collapse rule
          $value.removeAttr('style')
358 359 360

          return if $dropdown.hasClass('js-issuable-form-dropdown')

361
          if $dropdown.hasClass 'js-multiselect'
J
Jacob Schatz 已提交
362
            if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
363 364
              selectedLabels = $dropdown
                .closest('form')
A
Arinde Eniola 已提交
365
                .find("input:hidden[name='#{$dropdown.data('fieldName')}']")
P
Phil Hughes 已提交
366
              Issuable.filterResults $dropdown.closest('form')
J
Jacob Schatz 已提交
367 368 369
            else if $dropdown.hasClass('js-filter-submit')
              $dropdown.closest('form').submit()
            else
370 371 372 373
              if not $dropdown.hasClass 'js-filter-bulk-update'
                saveLabelData()

          if $dropdown.hasClass('js-filter-bulk-update')
A
Alfredo Sumaran 已提交
374 375 376
            # If we are persisting state we need the classes
            if not @options.persistWhenHide
              $dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
377

J
Jacob Schatz 已提交
378
        multiSelect: $dropdown.hasClass 'js-multiselect'
P
Phil Hughes 已提交
379
        clicked: (label) ->
380 381
          _this.enableBulkLabelDropdown()

382
          if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
383 384
            return

P
Phil Hughes 已提交
385 386
          page = $('body').data 'page'
          isIssueIndex = page is 'projects:issues:index'
A
Arinde Eniola 已提交
387
          isMRIndex = page is 'projects:merge_requests:index'
P
Phil Hughes 已提交
388
          if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
389 390
            if not $dropdown.hasClass 'js-multiselect'
              selectedLabel = label.title
P
Phil Hughes 已提交
391
              Issuable.filterResults $dropdown.closest('form')
P
Phil Hughes 已提交
392 393
          else if $dropdown.hasClass 'js-filter-submit'
            $dropdown.closest('form').submit()
394
          else
395 396 397 398
            if $dropdown.hasClass 'js-multiselect'
              return
            else
              saveLabelData()
A
Alfredo Sumaran 已提交
399

A
typo  
Alfredo Sumaran 已提交
400
        setIndeterminateIds: ->
A
Alfredo Sumaran 已提交
401
          if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
A
typo  
Alfredo Sumaran 已提交
402
            @indeterminateIds = _this.getIndeterminateIds()
403 404 405 406

        setActiveIds: ->
          if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
            @activeIds = _this.getActiveIds()
407
      )
A
Alfredo Sumaran 已提交
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422
    @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')

A
typo  
Alfredo Sumaran 已提交
423
  getIndeterminateIds: ->
A
Alfredo Sumaran 已提交
424 425 426 427 428 429 430
    label_ids = []

    $('.selected_issue:checked').each (i, el) ->
      issue_id = $(el).data('id')
      label_ids.push $("#issue_#{issue_id}").data('labels')

    _.flatten(label_ids)
431 432 433 434 435 436 437 438 439

  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
440 441 442 443

  enableBulkLabelDropdown: ->
    if $('.selected_issue:checked').length
      issuableBulkActions = $('.bulk-update').data('bulkActions')
444
      issuableBulkActions.willUpdateLabels = true