labels_select.js 18.9 KB
Newer Older
1
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */
2 3 4
/* global Issuable */
/* global ListLabel */

Fatih Acet's avatar
Fatih Acet committed
5 6 7 8 9 10
(function() {
  this.LabelsSelect = (function() {
    function LabelsSelect() {
      var _this;
      _this = this;
      $('.js-label-select').each(function(i, dropdown) {
Alfredo Sumaran's avatar
Alfredo Sumaran committed
11
        var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
Fatih Acet's avatar
Fatih Acet committed
12
        $dropdown = $(dropdown);
Alfredo Sumaran's avatar
Alfredo Sumaran committed
13
        $dropdownContainer = $dropdown.closest('.labels-filter');
14
        $toggleText = $dropdown.find('.dropdown-toggle-text');
15 16
        namespacePath = $dropdown.data('namespace-path');
        projectPath = $dropdown.data('project-path');
Fatih Acet's avatar
Fatih Acet committed
17 18 19 20 21 22 23 24
        labelUrl = $dropdown.data('labels');
        issueUpdateURL = $dropdown.data('issueUpdate');
        selectedLabel = $dropdown.data('selected');
        if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) {
          selectedLabel = selectedLabel.split(',');
        }
        showNo = $dropdown.data('show-no');
        showAny = $dropdown.data('show-any');
25
        showMenuAbove = $dropdown.data('showMenuAbove');
Fatih Acet's avatar
Fatih Acet committed
26 27 28 29
        defaultLabel = $dropdown.data('default-label');
        abilityName = $dropdown.data('ability-name');
        $selectbox = $dropdown.closest('.selectbox');
        $block = $selectbox.closest('.block');
Phil Hughes's avatar
Phil Hughes committed
30
        $form = $dropdown.closest('form, .js-issuable-update');
Fatih Acet's avatar
Fatih Acet committed
31
        $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
32
        $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
Fatih Acet's avatar
Fatih Acet committed
33 34
        $value = $block.find('.value');
        $loading = $block.find('.block-loading').fadeOut();
35 36 37
        fieldName = $dropdown.data('field-name');
        useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown');
        propertyName = useId ? 'id' : 'title';
38 39 40 41 42
        initialSelected = $selectbox
          .find('input[name="' + $dropdown.data('field-name') + '"]')
          .map(function () {
            return this.value;
          }).get();
Fatih Acet's avatar
Fatih Acet committed
43 44 45 46 47 48 49
        if (issueUpdateURL != null) {
          issueURLSplit = issueUpdateURL.split('/');
        }
        if (issueUpdateURL) {
          labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>');
          labelNoneHTMLTemplate = '<span class="no-value">None</span>';
        }
50

Phil Hughes's avatar
Phil Hughes committed
51 52
        $sidebarLabelTooltip.tooltip();

53
        if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
54
          new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath);
55
        }
56

Fatih Acet's avatar
Fatih Acet committed
57 58
        saveLabelData = function() {
          var data, selected;
59
          selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() {
Fatih Acet's avatar
Fatih Acet committed
60 61
            return this.value;
          }).get();
62 63 64 65

          if (_.isEqual(initialSelected, selected)) return;
          initialSelected = selected;

Fatih Acet's avatar
Fatih Acet committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79
          data = {};
          data[abilityName] = {};
          data[abilityName].label_ids = selected;
          if (!selected.length) {
            data[abilityName].label_ids = [''];
          }
          $loading.fadeIn();
          $dropdown.trigger('loading.gl.dropdown');
          return $.ajax({
            type: 'PUT',
            url: issueUpdateURL,
            dataType: 'JSON',
            data: data
          }).done(function(data) {
80
            var labelCount, template, labelTooltipTitle, labelTitles;
Fatih Acet's avatar
Fatih Acet committed
81 82 83 84 85 86 87 88
            $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;
89 90
            }
            else {
Fatih Acet's avatar
Fatih Acet committed
91 92 93 94
              template = labelNoneHTMLTemplate;
            }
            $value.removeAttr('style').html(template);
            $sidebarCollapsedValue.text(labelCount);
95 96

            if (data.labels.length) {
97 98 99
              labelTitles = data.labels.map(function(label) {
                return label.title;
              });
100

101 102 103
              if (labelTitles.length > 5) {
                labelTitles = labelTitles.slice(0, 5);
                labelTitles.push('and ' + (data.labels.length - 5) + ' more');
104 105
              }

106
              labelTooltipTitle = labelTitles.join(', ');
107 108
            }
            else {
109 110 111 112 113 114 115 116
              labelTooltipTitle = '';
              $sidebarLabelTooltip.tooltip('destroy');
            }

            $sidebarLabelTooltip
              .attr('title', labelTooltipTitle)
              .tooltip('fixTitle');

Fatih Acet's avatar
Fatih Acet committed
117 118 119 120 121 122 123 124 125 126 127 128
            $('.has-tooltip', $value).tooltip({
              container: 'body'
            });
            return $value.find('a').each(function(i) {
              return setTimeout((function(_this) {
                return function() {
                  return gl.animate.animate($(_this), 'pulse');
                };
              })(this), 200 * i);
            });
          });
        };
129
        $dropdown.glDropdown({
130
          showMenuAbove: showMenuAbove,
Fatih Acet's avatar
Fatih Acet committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
          data: function(term, callback) {
            return $.ajax({
              url: labelUrl
            }).done(function(data) {
              data = _.chain(data).groupBy(function(label) {
                return label.title;
              }).map(function(label) {
                var color;
                color = _.map(label, function(dup) {
                  return dup.color;
                });
                return {
                  id: label[0].id,
                  title: label[0].title,
                  color: color,
                  duplicate: color.length > 1
                };
              }).value();
              if ($dropdown.hasClass('js-extra-options')) {
150
                var extraData = [];
Fatih Acet's avatar
Fatih Acet committed
151
                if (showNo) {
152
                  extraData.unshift({
Fatih Acet's avatar
Fatih Acet committed
153 154 155 156 157
                    id: 0,
                    title: 'No Label'
                  });
                }
                if (showAny) {
158
                  extraData.unshift({
Fatih Acet's avatar
Fatih Acet committed
159 160 161 162
                    isAny: true,
                    title: 'Any Label'
                  });
                }
163 164 165
                if (extraData.length) {
                  extraData.push('divider');
                  data = extraData.concat(data);
Fatih Acet's avatar
Fatih Acet committed
166 167
                }
              }
168 169 170 171 172

              callback(data);
              if (showMenuAbove) {
                $dropdown.data('glDropdown').positionMenuAbove();
              }
Fatih Acet's avatar
Fatih Acet committed
173 174 175
            });
          },
          renderRow: function(label, instance) {
Alfredo Sumaran's avatar
Alfredo Sumaran committed
176
            var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue;
Fatih Acet's avatar
Fatih Acet committed
177 178 179
            $li = $('<li>');
            $a = $('<a href="#">');
            selectedClass = [];
180
            removesAll = label.id <= 0 || (label.id == null);
Fatih Acet's avatar
Fatih Acet committed
181
            if ($dropdown.hasClass('js-filter-bulk-update')) {
182 183 184
              indeterminate = $dropdown.data('indeterminate') || [];
              marked = $dropdown.data('marked') || [];

Fatih Acet's avatar
Fatih Acet committed
185 186 187
              if (indeterminate.indexOf(label.id) !== -1) {
                selectedClass.push('is-indeterminate');
              }
188 189

              if (marked.indexOf(label.id) !== -1) {
190
                // Remove is-indeterminate class if the item will be marked as active
Fatih Acet's avatar
Fatih Acet committed
191 192 193 194 195 196
                i = selectedClass.indexOf('is-indeterminate');
                if (i !== -1) {
                  selectedClass.splice(i, 1);
                }
                selectedClass.push('is-active');
              }
197
            } else {
Alfredo Sumaran's avatar
Alfredo Sumaran committed
198 199 200 201 202 203 204
              if (this.id(label)) {
                dropdownName = $dropdown.data('fieldName');
                dropdownValue = this.id(label).toString().replace(/'/g, '\\\'');

                if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) {
                  selectedClass.push('is-active');
                }
205 206 207 208 209
              }

              if ($dropdown.hasClass('js-multiselect') && removesAll) {
                selectedClass.push('dropdown-clear-active');
              }
Fatih Acet's avatar
Fatih Acet committed
210 211 212
            }
            if (label.duplicate) {
              spacing = 100 / label.color.length;
213
              // Reduce the colors to 4
Fatih Acet's avatar
Fatih Acet committed
214 215 216 217 218 219 220 221 222 223
              label.color = label.color.filter(function(color, i) {
                return i < 4;
              });
              color = _.map(label.color, function(color, i) {
                var percentFirst, percentSecond;
                percentFirst = Math.floor(spacing * i);
                percentSecond = Math.floor(spacing * (i + 1));
                return color + " " + percentFirst + "%," + color + " " + percentSecond + "% ";
              }).join(',');
              color = "linear-gradient(" + color + ")";
224 225
            }
            else {
Fatih Acet's avatar
Fatih Acet committed
226 227 228 229 230 231
              if (label.color != null) {
                color = label.color[0];
              }
            }
            if (color) {
              colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>";
232 233
            }
            else {
Fatih Acet's avatar
Fatih Acet committed
234 235
              colorEl = '';
            }
236
            // We need to identify which items are actually labels
Fatih Acet's avatar
Fatih Acet committed
237 238 239 240 241
            if (label.id) {
              selectedClass.push('label-item');
              $a.attr('data-label-id', label.id);
            }
            $a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title);
242
            // Return generated html
Fatih Acet's avatar
Fatih Acet committed
243 244 245 246 247 248 249
            return $li.html($a).prop('outerHTML');
          },
          search: {
            fields: ['title']
          },
          selectable: true,
          filterable: true,
Phil Hughes's avatar
Phil Hughes committed
250
          selected: $dropdown.data('selected') || [],
Fatih Acet's avatar
Fatih Acet committed
251
          toggleLabel: function(selected, el) {
252 253 254
            var isSelected = el !== null ? el.hasClass('is-active') : false;
            var title = selected.title;
            var selectedLabels = this.selected;
Phil Hughes's avatar
Phil Hughes committed
255

256 257 258 259 260
            if (selected.id === 0) {
              this.selected = [];
              return 'No Label';
            }
            else if (isSelected) {
Phil Hughes's avatar
Phil Hughes committed
261
              this.selected.push(title);
262 263
            }
            else {
Phil Hughes's avatar
Phil Hughes committed
264 265 266 267 268 269
              var index = this.selected.indexOf(title);
              this.selected.splice(index, 1);
            }

            if (selectedLabels.length === 1) {
              return selectedLabels;
270 271
            }
            else if (selectedLabels.length) {
Phil Hughes's avatar
Phil Hughes committed
272
              return selectedLabels[0] + " +" + (selectedLabels.length - 1) + " more";
273 274
            }
            else {
Fatih Acet's avatar
Fatih Acet committed
275 276 277 278 279
              return defaultLabel;
            }
          },
          fieldName: $dropdown.data('field-name'),
          id: function(label) {
280
            if (label.id <= 0) return label.title;
281 282 283 284 285

            if ($dropdown.hasClass('js-issuable-form-dropdown')) {
              return label.id;
            }

Fatih Acet's avatar
Fatih Acet committed
286 287
            if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) {
              return label.title;
288 289
            }
            else {
Fatih Acet's avatar
Fatih Acet committed
290 291 292 293 294 295 296 297 298
              return label.id;
            }
          },
          hidden: function() {
            var isIssueIndex, isMRIndex, page, selectedLabels;
            page = $('body').data('page');
            isIssueIndex = page === 'projects:issues:index';
            isMRIndex = page === 'projects:merge_requests:index';
            $selectbox.hide();
299
            // display:block overrides the hide-collapse rule
Fatih Acet's avatar
Fatih Acet committed
300
            $value.removeAttr('style');
301 302 303 304 305

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

306
            if ($('html').hasClass('issue-boards-page')) {
307 308
              return;
            }
Fatih Acet's avatar
Fatih Acet committed
309 310 311 312
            if ($dropdown.hasClass('js-multiselect')) {
              if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
                selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']");
                Issuable.filterResults($dropdown.closest('form'));
313 314
              }
              else if ($dropdown.hasClass('js-filter-submit')) {
Fatih Acet's avatar
Fatih Acet committed
315
                $dropdown.closest('form').submit();
316 317
              }
              else {
Fatih Acet's avatar
Fatih Acet committed
318 319 320 321 322 323 324
                if (!$dropdown.hasClass('js-filter-bulk-update')) {
                  saveLabelData();
                }
              }
            }
          },
          multiSelect: $dropdown.hasClass('js-multiselect'),
325
          vue: $dropdown.hasClass('js-issue-board-sidebar'),
326
          clicked: function(label, $el, e, isMarking) {
Fatih Acet's avatar
Fatih Acet committed
327
            var isIssueIndex, isMRIndex, page;
328 329 330 331

            page = $('body').data('page');
            isIssueIndex = page === 'projects:issues:index';
            isMRIndex = page === 'projects:merge_requests:index';
332 333 334 335

            if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) {
              $dropdown.parent()
                .find('.dropdown-clear-active')
336
                .removeClass('is-active');
337 338
            }

339 340 341 342 343
            if ($dropdown.hasClass('js-issuable-form-dropdown')) {
              return;
            }

            if ($dropdown.hasClass('js-filter-bulk-update')) {
344 345
              _this.enableBulkLabelDropdown();
              _this.setDropdownData($dropdown, isMarking, this.id(label));
Fatih Acet's avatar
Fatih Acet committed
346 347
              return;
            }
348

349
            if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
Phil Hughes's avatar
Phil Hughes committed
350
              if (label.isAny) {
351
                gl.issueBoards.BoardsStore.state.filters['label_name'] = [];
352 353
              }
              else if ($el.hasClass('is-active')) {
354
                gl.issueBoards.BoardsStore.state.filters['label_name'].push(label.title);
355 356
              }
              else {
357
                var filters = gl.issueBoards.BoardsStore.state.filters['label_name'];
358 359
                filters = filters.filter(function (filteredLabel) {
                  return filteredLabel !== label.title;
360
                });
361
                gl.issueBoards.BoardsStore.state.filters['label_name'] = filters;
Phil Hughes's avatar
Phil Hughes committed
362 363
              }

364
              gl.issueBoards.BoardsStore.updateFiltersUrl();
365
              e.preventDefault();
366
              return;
367 368
            }
            else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
Fatih Acet's avatar
Fatih Acet committed
369 370 371 372
              if (!$dropdown.hasClass('js-multiselect')) {
                selectedLabel = label.title;
                return Issuable.filterResults($dropdown.closest('form'));
              }
373 374
            }
            else if ($dropdown.hasClass('js-filter-submit')) {
Fatih Acet's avatar
Fatih Acet committed
375
              return $dropdown.closest('form').submit();
376
            }
377
            else if ($dropdown.hasClass('js-issue-board-sidebar')) {
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
              if ($el.hasClass('is-active')) {
                gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({
                  id: label.id,
                  title: label.title,
                  color: label.color[0],
                  textColor: '#fff'
                }));
              }
              else {
                var labels = gl.issueBoards.BoardsStore.detail.issue.labels;
                labels = labels.filter(function (selectedLabel) {
                  return selectedLabel.id !== label.id;
                });
                gl.issueBoards.BoardsStore.detail.issue.labels = labels;
              }

              $loading.fadeIn();

396
              gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
397 398 399 400
                .then(function () {
                  $loading.fadeOut();
                });
            }
401
            else {
Fatih Acet's avatar
Fatih Acet committed
402 403
              if ($dropdown.hasClass('js-multiselect')) {

404 405
              }
              else {
Fatih Acet's avatar
Fatih Acet committed
406 407 408 409 410
                return saveLabelData();
              }
            }
          },
        });
411 412 413

        // Set dropdown data
        _this.setOriginalDropdownData($dropdownContainer, $dropdown);
Fatih Acet's avatar
Fatih Acet committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
      });
      this.bindEvents();
    }

    LabelsSelect.prototype.bindEvents = function() {
      return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue);
    };

    LabelsSelect.prototype.onSelectCheckboxIssue = function() {
      if ($('.selected_issue:checked').length) {
        return;
      }
      return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label');
    };

    LabelsSelect.prototype.enableBulkLabelDropdown = function() {
      var issuableBulkActions;
      if ($('.selected_issue:checked').length) {
        issuableBulkActions = $('.bulk-update').data('bulkActions');
        return issuableBulkActions.willUpdateLabels = true;
      }
    };

437
    LabelsSelect.prototype.setDropdownData = function($dropdown, isMarking, value) {
Alfredo Sumaran's avatar
Alfredo Sumaran committed
438
      var i, markedIds, unmarkedIds, indeterminateIds;
439
      var issuableBulkActions = $('.bulk-update').data('bulkActions');
Fatih Acet's avatar
Fatih Acet committed
440

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
      markedIds = $dropdown.data('marked') || [];
      unmarkedIds = $dropdown.data('unmarked') || [];
      indeterminateIds = $dropdown.data('indeterminate') || [];

      if (isMarking) {
        markedIds.push(value);

        i = indeterminateIds.indexOf(value);
        if (i > -1) {
          indeterminateIds.splice(i, 1);
        }

        i = unmarkedIds.indexOf(value);
        if (i > -1) {
          unmarkedIds.splice(i, 1);
        }
      } else {
        // If marked item (not common) is unmarked
        i = markedIds.indexOf(value);
        if (i > -1) {
          markedIds.splice(i, 1);
        }

        // If an indeterminate item is being unmarked
        if (issuableBulkActions.getOriginalIndeterminateIds().indexOf(value) > -1) {
          unmarkedIds.push(value);
        }

        // If a marked item is being unmarked
        // (a marked item could also be a label that is present in all selection)
        if (issuableBulkActions.getOriginalCommonIds().indexOf(value) > -1) {
          unmarkedIds.push(value);
        }
      }
Fatih Acet's avatar
Fatih Acet committed
475

476 477 478 479 480 481 482
      $dropdown.data('marked', markedIds);
      $dropdown.data('unmarked', unmarkedIds);
      $dropdown.data('indeterminate', indeterminateIds);
    };

    LabelsSelect.prototype.setOriginalDropdownData = function($container, $dropdown) {
      var labels = [];
Alfredo Sumaran's avatar
Alfredo Sumaran committed
483 484 485
      $container.find('[name="label_name[]"]').map(function() {
        return labels.push(this.value);
      });
486 487 488 489
      $dropdown.data('marked', labels);
    };

    return LabelsSelect;
Fatih Acet's avatar
Fatih Acet committed
490 491
  })();
}).call(this);