2018-03-27 19:54:05 +05:30
|
|
|
<script>
|
2019-09-30 21:07:59 +05:30
|
|
|
import { __, sprintf } from '~/locale';
|
2018-10-15 14:42:47 +05:30
|
|
|
import tooltip from '~/vue_shared/directives/tooltip';
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
export default {
|
2019-10-12 21:52:04 +05:30
|
|
|
// name: 'Assignees' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives
|
|
|
|
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
|
2018-03-27 19:54:05 +05:30
|
|
|
name: 'Assignees',
|
2018-10-15 14:42:47 +05:30
|
|
|
directives: {
|
|
|
|
tooltip,
|
|
|
|
},
|
2018-03-27 19:54:05 +05:30
|
|
|
props: {
|
|
|
|
rootPath: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
users: {
|
|
|
|
type: Array,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
editable: {
|
|
|
|
type: Boolean,
|
|
|
|
required: true,
|
|
|
|
},
|
2018-10-15 14:42:47 +05:30
|
|
|
issuableType: {
|
|
|
|
type: String,
|
|
|
|
require: true,
|
|
|
|
default: 'issue',
|
|
|
|
},
|
2018-03-27 19:54:05 +05:30
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
defaultRenderCount: 5,
|
|
|
|
defaultMaxCounter: 99,
|
|
|
|
showLess: true,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
firstUser() {
|
|
|
|
return this.users[0];
|
|
|
|
},
|
|
|
|
hasMoreThanTwoAssignees() {
|
|
|
|
return this.users.length > 2;
|
|
|
|
},
|
|
|
|
hasMoreThanOneAssignee() {
|
|
|
|
return this.users.length > 1;
|
|
|
|
},
|
|
|
|
hasAssignees() {
|
|
|
|
return this.users.length > 0;
|
|
|
|
},
|
|
|
|
hasNoUsers() {
|
|
|
|
return !this.users.length;
|
|
|
|
},
|
|
|
|
hasOneUser() {
|
|
|
|
return this.users.length === 1;
|
|
|
|
},
|
|
|
|
renderShowMoreSection() {
|
|
|
|
return this.users.length > this.defaultRenderCount;
|
|
|
|
},
|
|
|
|
numberOfHiddenAssignees() {
|
|
|
|
return this.users.length - this.defaultRenderCount;
|
|
|
|
},
|
|
|
|
isHiddenAssignees() {
|
|
|
|
return this.numberOfHiddenAssignees > 0;
|
|
|
|
},
|
|
|
|
hiddenAssigneesLabel() {
|
2019-09-30 21:07:59 +05:30
|
|
|
const { numberOfHiddenAssignees } = this;
|
|
|
|
return sprintf(__('+ %{numberOfHiddenAssignees} more'), { numberOfHiddenAssignees });
|
2018-03-27 19:54:05 +05:30
|
|
|
},
|
|
|
|
collapsedTooltipTitle() {
|
|
|
|
const maxRender = Math.min(this.defaultRenderCount, this.users.length);
|
|
|
|
const renderUsers = this.users.slice(0, maxRender);
|
|
|
|
const names = renderUsers.map(u => u.name);
|
|
|
|
|
|
|
|
if (this.users.length > maxRender) {
|
|
|
|
names.push(`+ ${this.users.length - maxRender} more`);
|
|
|
|
}
|
|
|
|
|
2018-10-15 14:42:47 +05:30
|
|
|
if (!this.users.length) {
|
2019-07-31 22:56:46 +05:30
|
|
|
const emptyTooltipLabel = __('Assignee(s)');
|
2018-10-15 14:42:47 +05:30
|
|
|
names.push(emptyTooltipLabel);
|
|
|
|
}
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
return names.join(', ');
|
|
|
|
},
|
|
|
|
sidebarAvatarCounter() {
|
|
|
|
let counter = `+${this.users.length - 1}`;
|
|
|
|
|
|
|
|
if (this.users.length > this.defaultMaxCounter) {
|
|
|
|
counter = `${this.defaultMaxCounter}+`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return counter;
|
|
|
|
},
|
2019-07-31 22:56:46 +05:30
|
|
|
mergeNotAllowedTooltipMessage() {
|
|
|
|
const assigneesCount = this.users.length;
|
|
|
|
|
|
|
|
if (this.issuableType !== 'merge_request' || assigneesCount === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cannotMergeCount = this.users.filter(u => u.can_merge === false).length;
|
|
|
|
const canMergeCount = assigneesCount - cannotMergeCount;
|
|
|
|
|
|
|
|
if (canMergeCount === assigneesCount) {
|
|
|
|
// Everyone can merge
|
|
|
|
return null;
|
|
|
|
} else if (cannotMergeCount === assigneesCount && assigneesCount > 1) {
|
2019-09-30 21:07:59 +05:30
|
|
|
return __('No one can merge');
|
2019-07-31 22:56:46 +05:30
|
|
|
} else if (assigneesCount === 1) {
|
2019-09-30 21:07:59 +05:30
|
|
|
return __('Cannot merge');
|
2019-07-31 22:56:46 +05:30
|
|
|
}
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
return sprintf(__('%{canMergeCount}/%{assigneesCount} can merge'), {
|
|
|
|
canMergeCount,
|
|
|
|
assigneesCount,
|
|
|
|
});
|
2019-07-31 22:56:46 +05:30
|
|
|
},
|
2018-03-27 19:54:05 +05:30
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
assignSelf() {
|
|
|
|
this.$emit('assign-self');
|
|
|
|
},
|
|
|
|
toggleShowLess() {
|
|
|
|
this.showLess = !this.showLess;
|
|
|
|
},
|
|
|
|
renderAssignee(index) {
|
|
|
|
return !this.showLess || (index < this.defaultRenderCount && this.showLess);
|
|
|
|
},
|
|
|
|
avatarUrl(user) {
|
|
|
|
return user.avatar || user.avatar_url || gon.default_avatar_url;
|
|
|
|
},
|
|
|
|
assigneeUrl(user) {
|
|
|
|
return `${this.rootPath}${user.username}`;
|
|
|
|
},
|
|
|
|
assigneeAlt(user) {
|
2019-09-30 21:07:59 +05:30
|
|
|
return sprintf(__("%{userName}'s avatar"), { userName: user.name });
|
2018-03-27 19:54:05 +05:30
|
|
|
},
|
|
|
|
assigneeUsername(user) {
|
|
|
|
return `@${user.username}`;
|
|
|
|
},
|
|
|
|
shouldRenderCollapsedAssignee(index) {
|
|
|
|
const firstTwo = this.users.length <= 2 && index <= 2;
|
|
|
|
|
|
|
|
return index === 0 || firstTwo;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<div>
|
|
|
|
<div
|
2018-10-15 14:42:47 +05:30
|
|
|
v-tooltip
|
2018-11-08 19:23:39 +05:30
|
|
|
:class="{ 'multiple-users': hasMoreThanOneAssignee }"
|
|
|
|
:title="collapsedTooltipTitle"
|
|
|
|
class="sidebar-collapsed-icon sidebar-collapsed-user"
|
2018-03-27 19:54:05 +05:30
|
|
|
data-container="body"
|
|
|
|
data-placement="left"
|
2018-11-08 19:23:39 +05:30
|
|
|
data-boundary="viewport"
|
2018-03-27 19:54:05 +05:30
|
|
|
>
|
2019-09-30 21:07:59 +05:30
|
|
|
<i v-if="hasNoUsers" :aria-label="__('None')" class="fa fa-user"> </i>
|
2018-03-27 19:54:05 +05:30
|
|
|
<button
|
|
|
|
v-for="(user, index) in users"
|
|
|
|
v-if="shouldRenderCollapsedAssignee(index)"
|
|
|
|
:key="user.id"
|
2018-11-08 19:23:39 +05:30
|
|
|
type="button"
|
|
|
|
class="btn-link"
|
2018-03-27 19:54:05 +05:30
|
|
|
>
|
|
|
|
<img
|
|
|
|
:alt="assigneeAlt(user)"
|
|
|
|
:src="avatarUrl(user)"
|
2018-11-08 19:23:39 +05:30
|
|
|
width="24"
|
|
|
|
class="avatar avatar-inline s24"
|
2018-03-27 19:54:05 +05:30
|
|
|
/>
|
2019-02-15 15:39:39 +05:30
|
|
|
<span class="author"> {{ user.name }} </span>
|
2018-03-27 19:54:05 +05:30
|
|
|
</button>
|
2019-02-15 15:39:39 +05:30
|
|
|
<button v-if="hasMoreThanTwoAssignees" class="btn-link" type="button">
|
|
|
|
<span class="avatar-counter sidebar-avatar-counter"> {{ sidebarAvatarCounter }} </span>
|
2018-03-27 19:54:05 +05:30
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="value hide-collapsed">
|
2019-07-31 22:56:46 +05:30
|
|
|
<span
|
|
|
|
v-if="mergeNotAllowedTooltipMessage"
|
|
|
|
v-tooltip
|
|
|
|
:title="mergeNotAllowedTooltipMessage"
|
|
|
|
data-placement="left"
|
|
|
|
class="float-right cannot-be-merged"
|
|
|
|
>
|
|
|
|
<i aria-hidden="true" data-hidden="true" class="fa fa-exclamation-triangle"></i>
|
|
|
|
</span>
|
2018-03-27 19:54:05 +05:30
|
|
|
<template v-if="hasNoUsers">
|
2019-07-31 22:56:46 +05:30
|
|
|
<span class="assign-yourself no-value qa-assign-yourself">
|
2019-09-30 21:07:59 +05:30
|
|
|
{{ __('None') }}
|
2018-03-27 19:54:05 +05:30
|
|
|
<template v-if="editable">
|
2019-09-30 21:07:59 +05:30
|
|
|
-
|
|
|
|
<button type="button" class="btn-link" @click="assignSelf">
|
|
|
|
{{ __('assign yourself') }}
|
|
|
|
</button>
|
2018-03-27 19:54:05 +05:30
|
|
|
</template>
|
|
|
|
</span>
|
|
|
|
</template>
|
|
|
|
<template v-else-if="hasOneUser">
|
2019-02-15 15:39:39 +05:30
|
|
|
<a :href="assigneeUrl(firstUser)" class="author-link bold">
|
2018-03-27 19:54:05 +05:30
|
|
|
<img
|
|
|
|
:alt="assigneeAlt(firstUser)"
|
|
|
|
:src="avatarUrl(firstUser)"
|
2018-11-08 19:23:39 +05:30
|
|
|
width="32"
|
|
|
|
class="avatar avatar-inline s32"
|
2018-03-27 19:54:05 +05:30
|
|
|
/>
|
2019-02-15 15:39:39 +05:30
|
|
|
<span class="author"> {{ firstUser.name }} </span>
|
|
|
|
<span class="username"> {{ assigneeUsername(firstUser) }} </span>
|
2018-03-27 19:54:05 +05:30
|
|
|
</a>
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<div class="user-list">
|
|
|
|
<div
|
|
|
|
v-for="(user, index) in users"
|
|
|
|
v-if="renderAssignee(index)"
|
|
|
|
:key="user.id"
|
2018-11-08 19:23:39 +05:30
|
|
|
class="user-item"
|
2018-03-27 19:54:05 +05:30
|
|
|
>
|
|
|
|
<a
|
2018-11-08 19:23:39 +05:30
|
|
|
:href="assigneeUrl(user)"
|
|
|
|
:data-title="user.name"
|
2018-03-27 19:54:05 +05:30
|
|
|
class="user-link has-tooltip"
|
|
|
|
data-container="body"
|
|
|
|
data-placement="bottom"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
:alt="assigneeAlt(user)"
|
|
|
|
:src="avatarUrl(user)"
|
2018-11-08 19:23:39 +05:30
|
|
|
width="32"
|
|
|
|
class="avatar avatar-inline s32"
|
2018-03-27 19:54:05 +05:30
|
|
|
/>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
2019-02-15 15:39:39 +05:30
|
|
|
<div v-if="renderShowMoreSection" class="user-list-more">
|
|
|
|
<button type="button" class="btn-link" @click="toggleShowLess">
|
2018-03-27 19:54:05 +05:30
|
|
|
<template v-if="showLess">
|
|
|
|
{{ hiddenAssigneesLabel }}
|
|
|
|
</template>
|
2019-09-30 21:07:59 +05:30
|
|
|
<template v-else>{{ __('- show less') }}</template>
|
2018-03-27 19:54:05 +05:30
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|