diff options
Diffstat (limited to 'app/assets/javascripts/boards/components')
4 files changed, 142 insertions, 28 deletions
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js index 0fa85b6fe14..1ce95b62138 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js +++ b/app/assets/javascripts/boards/components/board_new_issue.js @@ -26,6 +26,7 @@ export default { title: this.title, labels, subscribed: true, + assignees: [], }); this.list.newIssue(issue) diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index f0066d4ec5d..317cef9f227 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -3,8 +3,13 @@ /* global MilestoneSelect */ /* global LabelsSelect */ /* global Sidebar */ +/* global Flash */ import Vue from 'vue'; +import eventHub from '../../sidebar/event_hub'; + +import AssigneeTitle from '../../sidebar/components/assignees/assignee_title'; +import Assignees from '../../sidebar/components/assignees/assignees'; require('./sidebar/remove_issue'); @@ -22,6 +27,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({ detail: Store.detail, issue: {}, list: {}, + loadingAssignees: false, }; }, computed: { @@ -43,6 +49,10 @@ gl.issueBoards.BoardSidebar = Vue.extend({ this.issue = this.detail.issue; this.list = this.detail.list; + + this.$nextTick(() => { + this.endpoint = this.$refs.assigneeDropdown.dataset.issueUpdate; + }); }, deep: true }, @@ -53,12 +63,57 @@ gl.issueBoards.BoardSidebar = Vue.extend({ $('.right-sidebar').getNiceScroll().resize(); }); } - } + + this.issue = this.detail.issue; + this.list = this.detail.list; + }, + deep: true }, methods: { closeSidebar () { this.detail.issue = {}; - } + }, + assignSelf () { + // Notify gl dropdown that we are now assigning to current user + this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself')); + + this.addAssignee(this.currentUser); + this.saveAssignees(); + }, + removeAssignee (a) { + gl.issueBoards.BoardsStore.detail.issue.removeAssignee(a); + }, + addAssignee (a) { + gl.issueBoards.BoardsStore.detail.issue.addAssignee(a); + }, + removeAllAssignees () { + gl.issueBoards.BoardsStore.detail.issue.removeAllAssignees(); + }, + saveAssignees () { + this.loadingAssignees = true; + + gl.issueBoards.BoardsStore.detail.issue.update(this.endpoint) + .then(() => { + this.loadingAssignees = false; + }) + .catch(() => { + this.loadingAssignees = false; + return new Flash('An error occurred while saving assignees'); + }); + }, + }, + created () { + // Get events from glDropdown + eventHub.$on('sidebar.removeAssignee', this.removeAssignee); + eventHub.$on('sidebar.addAssignee', this.addAssignee); + eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees); + eventHub.$on('sidebar.saveAssignees', this.saveAssignees); + }, + beforeDestroy() { + eventHub.$off('sidebar.removeAssignee', this.removeAssignee); + eventHub.$off('sidebar.addAssignee', this.addAssignee); + eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees); + eventHub.$off('sidebar.saveAssignees', this.saveAssignees); }, mounted () { new IssuableContext(this.currentUser); @@ -70,5 +125,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({ }, components: { removeBtn: gl.issueBoards.RemoveIssueBtn, + 'assignee-title': AssigneeTitle, + assignees: Assignees, }, }); diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js index fc154ee7b8b..2f06d186c50 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.js +++ b/app/assets/javascripts/boards/components/issue_card_inner.js @@ -31,18 +31,36 @@ gl.issueBoards.IssueCardInner = Vue.extend({ default: false, }, }, + data() { + return { + limitBeforeCounter: 3, + maxRender: 4, + maxCounter: 99, + }; + }, computed: { - cardUrl() { - return `${this.issueLinkBase}/${this.issue.id}`; + numberOverLimit() { + return this.issue.assignees.length - this.limitBeforeCounter; }, - assigneeUrl() { - return `${this.rootPath}${this.issue.assignee.username}`; + assigneeCounterTooltip() { + return `${this.assigneeCounterLabel} more`; + }, + assigneeCounterLabel() { + if (this.numberOverLimit > this.maxCounter) { + return `${this.maxCounter}+`; + } + + return `+${this.numberOverLimit}`; }, - assigneeUrlTitle() { - return `Assigned to ${this.issue.assignee.name}`; + shouldRenderCounter() { + if (this.issue.assignees.length <= this.maxRender) { + return false; + } + + return this.issue.assignees.length > this.numberOverLimit; }, - avatarUrlTitle() { - return `Avatar for ${this.issue.assignee.name}`; + cardUrl() { + return `${this.issueLinkBase}/${this.issue.id}`; }, issueId() { return `#${this.issue.id}`; @@ -52,6 +70,28 @@ gl.issueBoards.IssueCardInner = Vue.extend({ }, }, methods: { + isIndexLessThanlimit(index) { + return index < this.limitBeforeCounter; + }, + shouldRenderAssignee(index) { + // Eg. maxRender is 4, + // Render up to all 4 assignees if there are only 4 assigness + // Otherwise render up to the limitBeforeCounter + if (this.issue.assignees.length <= this.maxRender) { + return index < this.maxRender; + } + + return index < this.limitBeforeCounter; + }, + assigneeUrl(assignee) { + return `${this.rootPath}${assignee.username}`; + }, + assigneeUrlTitle(assignee) { + return `Assigned to ${assignee.name}`; + }, + avatarUrlTitle(assignee) { + return `Avatar for ${assignee.name}`; + }, showLabel(label) { if (!this.list) return true; @@ -105,25 +145,39 @@ gl.issueBoards.IssueCardInner = Vue.extend({ {{ issueId }} </span> </h4> - <a - class="card-assignee has-tooltip js-no-trigger" - :href="assigneeUrl" - :title="assigneeUrlTitle" - v-if="issue.assignee" - data-container="body" - > - <img - class="avatar avatar-inline s20 js-no-trigger" - :src="issue.assignee.avatar" - width="20" - height="20" - :alt="avatarUrlTitle" - /> - </a> + <div class="card-assignee"> + <a + class="has-tooltip js-no-trigger" + :href="assigneeUrl(assignee)" + :title="assigneeUrlTitle(assignee)" + v-for="(assignee, index) in issue.assignees" + v-if="shouldRenderAssignee(index)" + data-container="body" + data-placement="bottom" + > + <img + class="avatar avatar-inline s20" + :src="assignee.avatarUrl" + width="20" + height="20" + :alt="avatarUrlTitle(assignee)" + /> + </a> + <span + class="avatar-counter has-tooltip" + :title="assigneeCounterTooltip" + v-if="shouldRenderCounter" + > + {{ assigneeCounterLabel }} + </span> + </div> </div> - <div class="card-footer" v-if="showLabelFooter"> + <div + class="card-footer" + v-if="showLabelFooter" + > <button - class="label color-label has-tooltip js-no-trigger" + class="label color-label has-tooltip" v-for="label in issue.labels" type="button" v-if="showLabel(label)" diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js index 7e3bb79af1d..f29b6caa1ac 100644 --- a/app/assets/javascripts/boards/components/new_list_dropdown.js +++ b/app/assets/javascripts/boards/components/new_list_dropdown.js @@ -52,7 +52,9 @@ gl.issueBoards.newListDropdownInit = () => { filterable: true, selectable: true, multiSelect: true, - clicked (label, $el, e) { + clicked (options) { + const { e } = options; + const label = options.selectedObj; e.preventDefault(); if (!Store.findList('title', label.title)) { |