Commit 7b935cc8 authored by Clement Ho's avatar Clement Ho

[skip ci] refactor down into one component

parent 025fd9ce
import eventHub from '../event_hub';
export default {
name: 'Assignees',
data() {
return {
defaultRenderCount: 5,
defaultMaxCounter: 99,
showLess: true,
};
},
props: {
rootPath: {
type: String,
required: true,
},
users: {
type: Array,
required: true,
},
},
computed: {
renderShowMoreSection() {
return this.users.length > this.defaultRenderCount;
},
numberOfHiddenAssignees() {
return this.users.length - this.defaultRenderCount;
},
isHiddenAssignees() {
return this.numberOfHiddenAssignees > 0;
},
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`);
}
return names.join(', ');
},
sidebarAvatarCounter() {
let counter = `+${this.users.length - 1}`;
if (this.users.length > this.defaultMaxCounter) {
counter = `${this.defaultMaxCounter}+`;
}
return counter;
},
},
methods: {
assignSelf() {
eventHub.$emit('addCurrentUser');
},
toggleShowLess() {
this.showLess = !this.showLess;
},
renderAssignee(index) {
return !this.showLess || (index < this.defaultRenderCount && this.showLess);
},
assigneeUrl(user) {
return `${this.rootPath}${user.username}`;
},
assigneeAlt(user) {
return `${user.name}'s avatar`;
},
},
template: `
<div>
<div
class="sidebar-collapsed-icon sidebar-collapsed-user"
:class="{ 'multiple-users': users.length > 1, 'has-tooltip': users.length > 0}"
data-container="body"
data-placement="left"
:title="collapsedTooltipTitle"
>
<i
v-if="users.length === 0"
aria-hidden="true"
class="fa fa-user"
/>
<button
type="button"
class="btn-link"
v-for="(user, index) in users"
v-if="index === 0 || users.length <= 2 && index <= 2"
>
<img
width="24"
class="avatar avatar-inline s24"
:alt="assigneeAlt(user)"
:src="user.avatarUrl"
>
<span class="author">{{user.name}}</span>
</button>
<button
v-if="users.length > 2"
class="btn-link"
type="button"
>
<span
class="avatar-counter sidebar-avatar-counter"
>
{{sidebarAvatarCounter}}
</span>
</button>
</div>
<div class="value hide-collapsed">
<template v-if="users.length === 0">
<span class="assign-yourself no-value">
No assignee -
<button
type="button"
class="btn-link"
@click="assignSelf"
>
assign yourself
</button>
</span>
</template>
<template v-else-if="users.length === 1">
<a
class="author_link bold"
:href="assigneeUrl(users[0])"
>
<img
width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(users[0])"
:src="users[0].avatarUrl"
>
<span class="author">{{users[0].name}}</span>
<span class="username">@{{users[0].username}}</span>
</a>
</template>
<template v-else>
<div class="user-list">
<div
class="user-item"
v-for="(user, index) in users"
v-if="renderAssignee(index)"
>
<a
class="user-link has-tooltip"
data-placement="bottom"
:href="assigneeUrl(user)"
:data-title="user.name"
>
<img
width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(user)"
:src="user.avatarUrl"
/>
</a>
</div>
</div>
<div
v-if="renderShowMoreSection"
class="user-list-more"
>
<button
type="button"
class="btn-link"
@click="toggleShowLess"
>
<template v-if="showLess">
+ {{numberOfHiddenAssignees}} more
</template>
<template v-else>
- show less
</template>
</button>
</div>
</template>
</div>
</div>
`,
};
import CollapsedAvatar from './avatar';
export default {
name: 'CollapsedAssignees',
data() {
return {
defaultRenderCount: 5,
defaultMaxCounter: 99,
};
},
props: {
users: {
type: Array,
required: true,
},
},
computed: {
title() {
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`);
}
return names.join(', ');
},
counter() {
let counter = `+${this.users.length - 1}`;
if (this.users.length > this.defaultMaxCounter) {
counter = `${this.defaultMaxCounter}+`;
}
return counter;
},
hasNoAssignees() {
return this.users.length === 0;
},
hasTwoAssignees() {
return this.users.length === 2;
},
moreThanOneAssignees() {
return this.users.length > 1;
},
moreThanTwoAssignees() {
return this.users.length > 2;
},
},
components: {
'collapsed-avatar': CollapsedAvatar,
},
template: `
<div>
<div v-if="hasNoAssignees" class="sidebar-collapsed-icon sidebar-collapsed-user">
<i aria-hidden="true" class="fa fa-user"></i>
</div>
<div v-else class="sidebar-collapsed-icon sidebar-collapsed-user has-tooltip"
:class="{'multiple-users': moreThanOneAssignees}"
data-container="body"
data-placement="left"
:title="title" >
<collapsed-avatar
:name="users[0].name"
:avatarUrl="users[0].avatarUrl"
/>
<collapsed-avatar
v-if="hasTwoAssignees"
:name="users[1].name"
:avatarUrl="users[1].avatarUrl"
/>
<button class="btn-link" type="button" v-if="moreThanTwoAssignees">
<span class="avatar-counter sidebar-avatar-counter">{{counter}}</span>
</button>
</div>
</div>
`,
};
export default {
name: 'CollapsedAvatar',
props: {
name: {
type: String,
required: true,
},
avatarUrl: {
type: String,
required: true,
},
},
computed: {
alt() {
return `${this.name}'s avatar`;
},
},
template: `
<button class="btn-link" type="button">
<img width="24"
class="avatar avatar-inline s24"
:alt="alt"
:src="avatarUrl" >
<span class="author">{{name}}</span>
</button>
`,
};
export default {
name: 'MultipleAssignees',
data() {
return {
defaultRenderCount: 5,
showLess: true,
};
},
props: {
rootPath: {
type: String,
required: true,
},
users: {
type: Array,
required: true,
},
},
computed: {
renderShowMoreSection() {
return this.users.length > this.defaultRenderCount;
},
numberOfHiddenAssignees() {
return this.users.length - this.defaultRenderCount;
},
isHiddenAssignees() {
return this.numberOfHiddenAssignees > 0;
},
},
methods: {
toggleShowLess() {
this.showLess = !this.showLess;
},
renderAssignee(index) {
return !this.showLess || (index < this.defaultRenderCount && this.showLess);
},
assigneeUrl(username) {
return `${this.rootPath}${username}`;
},
assigneeAlt(name) {
return `${name}'s avatar`;
},
},
template: `
<div class="hide-collapsed">
<div class="user-list">
<div class="user-item" v-for="(user, index) in users"
v-if="renderAssignee(index)" >
<a class="user-link has-tooltip"
data-placement="bottom"
:href="assigneeUrl(user.username)"
:data-title="user.name" >
<img width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(user.name)"
:src="user.avatarUrl" >
</a>
</div>
</div>
<div class="user-list-more" v-if="renderShowMoreSection">
<button type="button" class="btn-link" @click="toggleShowLess">
<template v-if="showLess">
+ {{numberOfHiddenAssignees}} more
</template>
<template v-else>
- show less
</template>
</button>
</div>
</div>
`,
};
import eventHub from '../../event_hub';
export default {
name: 'NoAssignee',
methods: {
assignSelf() {
eventHub.$emit('addCurrentUser');
},
},
template: `
<div class="hide-collapsed">
<span class="assign-yourself no-value">
No assignee -
<button type="button" class="btn-link" @click="assignSelf">
assign yourself
</button>
</span>
</div>
`,
};
export default {
name: 'SingleAssignee',
props: {
rootPath: {
type: String,
required: true,
},
user: {
type: Object,
required: true,
},
},
computed: {
userUrl() {
return `${this.rootPath}${this.user.username}`;
},
username() {
return `@${this.user.username}`;
},
avatarAlt() {
return `${this.user.name}'s avatar`;
},
},
template: `
<div class="hide-collapsed">
<a class="author_link bold" :href="userUrl">
<img width="32"
class="avatar avatar-inline s32"
:alt="avatarAlt"
:src="user.avatarUrl" >
<span class="author">{{user.name}}</span>
<span class="username">{{username}}</span>
</a>
</div>
`,
};
......@@ -2,7 +2,6 @@ import Vue from 'vue';
import VueResource from 'vue-resource';
import '../../vue_shared/vue_resource_interceptor';
Vue.http.options.emulateJSON = true;
Vue.use(VueResource);
export default class SidebarAssigneesService {
......@@ -14,6 +13,8 @@ export default class SidebarAssigneesService {
update(userIds) {
return Vue.http.put(this.path, {
[this.field]: userIds,
}, { emulateJSON: true });
}, {
emulateJSON: true,
});
}
}
......@@ -3,11 +3,7 @@
import eventHub from './event_hub';
import AssigneeTitle from './components/assignee_title';
import NoAssignee from './components/expanded/no_assignee';
import SingleAssignee from './components/expanded/single_assignee';
import MultipleAssignees from './components/expanded/multiple_assignees';
import CollapsedAssignees from './components/collapsed/assignees';
import Assignees from './components/assignees';
import SidebarAssigneesService from './services/sidebar_assignees_service';
import SidebarAssigneesStore from './stores/sidebar_assignees_store';
......@@ -55,6 +51,10 @@ export default {
methods: {
addCurrentUser() {
this.store.addCurrentUserId();
// Notify gl dropdown that we are now assigning to current user
this.$el.parentElement.dispatchEvent(new Event('assignYourself'));
this.saveUsers();
},
saveUsers() {
......@@ -63,41 +63,30 @@ export default {
.then((response) => {
this.loading = false;
this.store.setUsers(response.data.assignees);
}).catch(() => {
})
.catch(() => {
this.loading = false;
return new Flash('An error occured while saving assignees');
});
},
},
components: {
'no-assignee': NoAssignee,
'single-assignee': SingleAssignee,
'multiple-assignees': MultipleAssignees,
'assignee-title': AssigneeTitle,
'collapsed-assignees': CollapsedAssignees,
'assignees': Assignees,
},
template: `
<div>
<assignee-title
:numberOfAssignees="store.userIds.length"
:numberOfAssignees="store.selectedUserIds.length"
:loading="loading"
:editable="store.editable"
/>
<collapsed-assignees :users="store.users"/>
<div class="value" v-if="!store.loading">
<no-assignee v-if="numberOfAssignees === 0" />
<single-assignee
v-else-if="numberOfAssignees === 1"
:rootPath="store.rootPath"
:user="store.users[0]"
/>
<multiple-assignees
v-else
:rootPath="store.rootPath"
:users="store.users"
/>
</div>
<assignees
class="value"
v-if="!store.loading"
:rootPath="store.rootPath"
:users="store.renderedUsers"
/>
</div>
`,
};
......@@ -5,47 +5,46 @@ export default class SidebarAssigneesStore {
this.currentUserId = currentUserId;
this.rootPath = rootPath;
// Tracks the selected users
this.userIds = [];
// Tracks the rendered users
this.users = [];
this.selectedUserIds = [];
this.renderedUsers = [];
this.loading = false;
this.editable = editable;
this.setUsers(assignees);
}
this.userIds = assignees.map(a => a.id);
addCurrentUserId() {
this.addUserId(this.currentUserId);
}
addUserId(id) {
this.userIds.push(id);
// Prevent duplicate user id's from being added
if (this.selectedUserIds.indexOf(id) === -1) {
this.selectedUserIds.push(id);
}
}
removeUserId(id) {
this.userIds = this.userIds.filter(uid => uid !== id);
this.selectedUserIds = this.selectedUserIds.filter(uid => uid !== id);
}
removeAllUserIds() {
this.userIds = [];
}
addCurrentUserId() {
this.addUserId(this.currentUserId);
this.selectedUserIds = [];
}
getUserIds() {
// If there are no ids, that means we have to unassign (which is id = 0)
return this.userIds.length > 0 ? this.userIds : [0];
return this.selectedUserIds.length > 0 ? this.selectedUserIds : [0];
}
setUsers(users) {
this.users = users.map((u) => ({
this.renderedUsers = users.map((u) => ({
id: u.id,
name: u.name,
username: u.username,
avatarUrl: u.avatar_url,
}));
this.selectedUserIds = users.map(u => u.id);
}
}
......@@ -54,6 +54,24 @@ import eventHub from './sidebar_assignees/event_hub';
$collapsedSidebar = $block.find('.sidebar-collapsed-user');
$loading = $block.find('.block-loading').fadeOut();
$block[0].addEventListener('assignYourself', () => {
// Remove unassigned selected from the DOM
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');
input.value = _this.currentUser.id;
$dropdown.before(input);
});
var getSelected = function() {
return $selectbox
.find(`input[name="${$dropdown.data('field-name')}"]`)
......
......@@ -66,7 +66,7 @@
.selectbox.hide-collapsed
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { name: assignee.name, username: assignee.username, 'avatar-url' => assignee.avatar_url }
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil
- options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment