summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/users_select.js
diff options
context:
space:
mode:
authorValery Sizov <valery@gitlab.com>2017-05-04 15:11:15 +0300
committerValery Sizov <valery@gitlab.com>2017-05-04 17:11:53 +0300
commit387c4b2c21a44360386a9b8ce6849e7f1b8a3de9 (patch)
tree446b8338efe8ad22ca03b00b2dc72b22c4174e02 /app/assets/javascripts/users_select.js
parent68c12e15cc236548918f91393ebef3c06c124814 (diff)
downloadgitlab-ce-387c4b2c21a44360386a9b8ce6849e7f1b8a3de9.tar.gz
Backport of multiple_assignees_feature [ci skip]
Diffstat (limited to 'app/assets/javascripts/users_select.js')
-rw-r--r--app/assets/javascripts/users_select.js377
1 files changed, 294 insertions, 83 deletions
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 68cf9ced3ef..7828bf86f60 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
/* global Issuable */
-/* global ListUser */
+
+import eventHub from './sidebar/event_hub';
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
@@ -54,42 +55,92 @@
selectedIdDefault = (defaultNullUser && showNullUser) ? 0 : null;
selectedId = $dropdown.data('selected') || selectedIdDefault;
- var updateIssueBoardsIssue = function () {
- $loading.removeClass('hidden').fadeIn();
- gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
- .then(function () {
- $loading.fadeOut();
- })
- .catch(function () {
- $loading.fadeOut();
- });
+ const assignYourself = function () {
+ const unassignedSelected = $dropdown.closest('.selectbox')
+ .find(`input[name='${$dropdown.data('field-name')}'][value=0]`);
+
+ if (unassignedSelected) {
+ unassignedSelected.remove();
+ }
+
+ // Save current selected user to the DOM
+ const input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = $dropdown.data('field-name');
+
+ const currentUserInfo = $dropdown.data('currentUserInfo');
+
+ if (currentUserInfo) {
+ input.value = currentUserInfo.id;
+ input.dataset.meta = currentUserInfo.name;
+ } else if (_this.currentUser) {
+ input.value = _this.currentUser.id;
+ }
+
+ $dropdown.before(input);
+ };
+
+ if ($block[0]) {
+ $block[0].addEventListener('assignYourself', assignYourself);
+ }
+
+ const getSelectedUserInputs = function() {
+ return $selectbox
+ .find(`input[name="${$dropdown.data('field-name')}"]`);
+ };
+
+ const getSelected = function() {
+ return getSelectedUserInputs()
+ .map((index, input) => parseInt(input.value, 10))
+ .get();
+ };
+
+ const getMultiSelectDropdownTitle = function(selectedUser, isSelected) {
+ const selectedUsers = getSelected()
+ .filter(u => u !== 0);
+
+ const firstUser = getSelectedUserInputs()
+ .map((index, input) => ({
+ name: input.dataset.meta,
+ value: parseInt(input.value, 10),
+ }))
+ .filter(u => u.id !== 0)
+ .get(0);
+
+ if (selectedUsers.length === 0) {
+ return 'Unassigned';
+ } else if (selectedUsers.length === 1) {
+ return firstUser.name;
+ } else if (isSelected) {
+ const otherSelected = selectedUsers.filter(s => s !== selectedUser.id);
+ return `${selectedUser.name} + ${otherSelected.length} more`;
+ } else {
+ return `${firstUser.name} + ${selectedUsers.length - 1} more`;
+ }
};
$('.assign-to-me-link').on('click', (e) => {
e.preventDefault();
$(e.currentTarget).hide();
- const $input = $(`input[name="${$dropdown.data('field-name')}"]`);
- $input.val(gon.current_user_id);
- selectedId = $input.val();
- $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default');
- });
-
- $block.on('click', '.js-assign-yourself', function(e) {
- e.preventDefault();
- if ($dropdown.hasClass('js-issue-board-sidebar')) {
- gl.issueBoards.boardStoreIssueSet('assignee', new ListUser({
- id: _this.currentUser.id,
- username: _this.currentUser.username,
- name: _this.currentUser.name,
- avatar_url: _this.currentUser.avatar_url
- }));
+ if ($dropdown.data('multiSelect')) {
+ assignYourself();
- updateIssueBoardsIssue();
+ const currentUserInfo = $dropdown.data('currentUserInfo');
+ $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default');
} else {
- return assignTo(_this.currentUser.id);
+ const $input = $(`input[name="${$dropdown.data('field-name')}"]`);
+ $input.val(gon.current_user_id);
+ selectedId = $input.val();
+ $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default');
}
});
+
+ $block.on('click', '.js-assign-yourself', (e) => {
+ e.preventDefault();
+ return assignTo(_this.currentUser.id);
+ });
+
assignTo = function(selected) {
var data;
data = {};
@@ -97,6 +148,7 @@
data[abilityName].assignee_id = selected != null ? selected : null;
$loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown');
+
return $.ajax({
type: 'PUT',
dataType: 'json',
@@ -106,7 +158,6 @@
var user;
$dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut();
- $selectbox.hide();
if (data.assignee) {
user = {
name: data.assignee.name,
@@ -133,51 +184,90 @@
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
return _this.users(term, options, function(users) {
- var anyUser, index, j, len, name, obj, showDivider;
- if (term.length === 0) {
- showDivider = 0;
- if (firstUser) {
- // Move current user to the front of the list
- for (index = j = 0, len = users.length; j < len; index = (j += 1)) {
- obj = users[index];
- if (obj.username === firstUser) {
- users.splice(index, 1);
- users.unshift(obj);
- break;
- }
+ // GitLabDropdownFilter returns this.instance
+ // GitLabDropdownRemote returns this.options.instance
+ const glDropdown = this.instance || this.options.instance;
+ glDropdown.options.processData(term, users, callback);
+ }.bind(this));
+ },
+ processData: function(term, users, callback) {
+ let anyUser;
+ let index;
+ let j;
+ let len;
+ let name;
+ let obj;
+ let showDivider;
+ if (term.length === 0) {
+ showDivider = 0;
+ if (firstUser) {
+ // Move current user to the front of the list
+ for (index = j = 0, len = users.length; j < len; index = (j += 1)) {
+ obj = users[index];
+ if (obj.username === firstUser) {
+ users.splice(index, 1);
+ users.unshift(obj);
+ break;
}
}
- if (showNullUser) {
- showDivider += 1;
- users.unshift({
- beforeDivider: true,
- name: 'Unassigned',
- id: 0
- });
- }
- if (showAnyUser) {
- showDivider += 1;
- name = showAnyUser;
- if (name === true) {
- name = 'Any User';
- }
- anyUser = {
- beforeDivider: true,
- name: name,
- id: null
- };
- users.unshift(anyUser);
+ }
+ if (showNullUser) {
+ showDivider += 1;
+ users.unshift({
+ beforeDivider: true,
+ name: 'Unassigned',
+ id: 0
+ });
+ }
+ if (showAnyUser) {
+ showDivider += 1;
+ name = showAnyUser;
+ if (name === true) {
+ name = 'Any User';
}
+ anyUser = {
+ beforeDivider: true,
+ name: name,
+ id: null
+ };
+ users.unshift(anyUser);
}
+
if (showDivider) {
- users.splice(showDivider, 0, "divider");
+ users.splice(showDivider, 0, 'divider');
}
- callback(users);
- if (showMenuAbove) {
- $dropdown.data('glDropdown').positionMenuAbove();
+ if ($dropdown.hasClass('js-multiselect')) {
+ const selected = getSelected().filter(i => i !== 0);
+
+ if (selected.length > 0) {
+ if ($dropdown.data('dropdown-header')) {
+ showDivider += 1;
+ users.splice(showDivider, 0, {
+ header: $dropdown.data('dropdown-header'),
+ });
+ }
+
+ const selectedUsers = users
+ .filter(u => selected.indexOf(u.id) !== -1)
+ .sort((a, b) => a.name > b.name);
+
+ users = users.filter(u => selected.indexOf(u.id) === -1);
+
+ selectedUsers.forEach((selectedUser) => {
+ showDivider += 1;
+ users.splice(showDivider, 0, selectedUser);
+ });
+
+ users.splice(showDivider + 1, 0, 'divider');
+ }
}
- });
+ }
+
+ callback(users);
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
},
filterable: true,
filterRemote: true,
@@ -186,7 +276,22 @@
},
selectable: true,
fieldName: $dropdown.data('field-name'),
- toggleLabel: function(selected, el) {
+ toggleLabel: function(selected, el, glDropdown) {
+ const inputValue = glDropdown.filterInput.val();
+
+ if (this.multiSelect && inputValue === '') {
+ // Remove non-users from the fullData array
+ const users = glDropdown.filteredFullData();
+ const callback = glDropdown.parseData.bind(glDropdown);
+
+ // Update the data model
+ this.processData(inputValue, users, callback);
+ }
+
+ if (this.multiSelect) {
+ return getMultiSelectDropdownTitle(selected, $(el).hasClass('is-active'));
+ }
+
if (selected && 'id' in selected && $(el).hasClass('is-active')) {
$dropdown.find('.dropdown-toggle-text').removeClass('is-default');
if (selected.text) {
@@ -200,15 +305,92 @@
}
},
defaultLabel: defaultLabel,
- inputId: 'issue_assignee_id',
hidden: function(e) {
- $selectbox.hide();
- // display:block overrides the hide-collapse rule
- return $value.css('display', '');
+ if ($dropdown.hasClass('js-multiselect')) {
+ eventHub.$emit('sidebar.saveAssignees');
+ }
+
+ if (!$dropdown.data('always-show-selectbox')) {
+ $selectbox.hide();
+
+ // Recalculate where .value is because vue might have changed it
+ $block = $selectbox.closest('.block');
+ $value = $block.find('.value');
+ // display:block overrides the hide-collapse rule
+ $value.css('display', '');
+ }
},
+<<<<<<< HEAD
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(user, $el, e) {
var isIssueIndex, isMRIndex, page, selected, isSelecting;
+=======
+ multiSelect: $dropdown.hasClass('js-multiselect'),
+ inputMeta: $dropdown.data('input-meta'),
+ clicked: function(options) {
+ const { $el, e, isMarking } = options;
+ const user = options.selectedObj;
+
+ if ($dropdown.hasClass('js-multiselect')) {
+ const isActive = $el.hasClass('is-active');
+ const previouslySelected = $dropdown.closest('.selectbox')
+ .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]");
+
+ // Enables support for limiting the number of users selected
+ // Automatically removes the first on the list if more users are selected
+ const maxSelect = $dropdown.data('max-select');
+ if (maxSelect) {
+ const selected = getSelected();
+
+ if (selected.length > maxSelect) {
+ const firstSelectedId = selected[0];
+ const firstSelected = $dropdown.closest('.selectbox')
+ .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`);
+
+ firstSelected.remove();
+ eventHub.$emit('sidebar.removeAssignee', {
+ id: firstSelectedId,
+ });
+ }
+ }
+
+ if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') {
+ // Unassigned selected
+ previouslySelected.each((index, element) => {
+ const id = parseInt(element.value, 10);
+ element.remove();
+ });
+ eventHub.$emit('sidebar.removeAllAssignees');
+ } else if (isActive) {
+ // user selected
+ eventHub.$emit('sidebar.addAssignee', user);
+
+ // Remove unassigned selection (if it was previously selected)
+ const unassignedSelected = $dropdown.closest('.selectbox')
+ .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]");
+
+ if (unassignedSelected) {
+ unassignedSelected.remove();
+ }
+ } else {
+ if (previouslySelected.length === 0) {
+ // Select unassigned because there is no more selected users
+ this.addInput($dropdown.data('field-name'), 0, {});
+ }
+
+ // User unselected
+ eventHub.$emit('sidebar.removeAssignee', user);
+ }
+
+ if (getSelected().find(u => u === gon.current_user_id)) {
+ $('.assign-to-me-link').hide();
+ } else {
+ $('.assign-to-me-link').show();
+ }
+ }
+
+ var isIssueIndex, isMRIndex, page, selected;
+>>>>>>> b0a2435... Merge branch 'multiple_assignees_review_upstream' into ee_master
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index');
@@ -229,6 +411,7 @@
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
+<<<<<<< HEAD
} else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if (user.id && isSelecting) {
gl.issueBoards.boardStoreIssueSet('assignee', new ListUser({
@@ -243,6 +426,9 @@
updateIssueBoardsIssue();
} else {
+=======
+ } else if (!$dropdown.hasClass('js-multiselect')) {
+>>>>>>> b0a2435... Merge branch 'multiple_assignees_review_upstream' into ee_master
selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val();
return assignTo(selected);
}
@@ -256,29 +442,54 @@
selectedId = parseInt($dropdown[0].dataset.selected, 10) || selectedIdDefault;
}
$el.find('.is-active').removeClass('is-active');
- $el.find(`li[data-user-id="${selectedId}"] .dropdown-menu-user-link`).addClass('is-active');
+
+ function highlightSelected(id) {
+ $el.find(`li[data-user-id="${id}"] .dropdown-menu-user-link`).addClass('is-active');
+ }
+
+ if ($selectbox[0]) {
+ getSelected().forEach(selectedId => highlightSelected(selectedId));
+ } else {
+ highlightSelected(selectedId);
+ }
},
+ updateLabel: $dropdown.data('dropdown-title'),
renderRow: function(user) {
- var avatar, img, listClosingTags, listWithName, listWithUserName, selected, username;
+ var avatar, img, listClosingTags, listWithName, listWithUserName, username;
username = user.username ? "@" + user.username : "";
avatar = user.avatar_url ? user.avatar_url : false;
- selected = user.id === parseInt(selectedId, 10) ? "is-active" : "";
+
+ let selected = user.id === parseInt(selectedId, 10);
+
+ if (this.multiSelect) {
+ const fieldName = this.fieldName;
+ const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']");
+
+ if (field.length) {
+ selected = true;
+ }
+ }
+
img = "";
if (user.beforeDivider != null) {
- "<li> <a href='#' class='" + selected + "'> " + user.name + " </a> </li>";
+ `<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${user.name}</a></li>`;
} else {
if (avatar) {
- img = "<img src='" + avatar + "' class='avatar avatar-inline' width='30' />";
+ img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
}
}
- // split into three parts so we can remove the username section if nessesary
- listWithName = "<li data-user-id=" + user.id + "> <a href='#' class='dropdown-menu-user-link " + selected + "'> " + img + " <strong class='dropdown-menu-user-full-name'> " + user.name + " </strong>";
- listWithUserName = "<span class='dropdown-menu-user-username'> " + username + " </span>";
- listClosingTags = "</a> </li>";
- if (username === '') {
- listWithUserName = '';
- }
- return listWithName + listWithUserName + listClosingTags;
+
+ return `
+ <li data-user-id=${user.id}>
+ <a href='#' class='dropdown-menu-user-link ${selected === true ? 'is-active' : ''}'>
+ ${img}
+ <strong class='dropdown-menu-user-full-name'>
+ ${user.name}
+ </strong>
+ ${username ? `<span class='dropdown-menu-user-username'>${username}</span>` : ''}
+ </a>
+ </li>
+ `;
}
});
};