feat(assignees): improve avatar list consistency
Resolves https://kolaente.dev/vikunja/frontend/issues/3354
This commit is contained in:
parent
270e32290a
commit
f63c39a578
@ -11,7 +11,12 @@
|
||||
class="input-wrapper input"
|
||||
:class="{'has-multiple': hasMultiple}"
|
||||
>
|
||||
<template v-if="Array.isArray(internalValue)">
|
||||
<slot
|
||||
v-if="Array.isArray(internalValue)"
|
||||
name="items"
|
||||
:items="internalValue"
|
||||
:remove="remove"
|
||||
>
|
||||
<template v-for="(item, key) in internalValue">
|
||||
<slot name="tag" :item="item">
|
||||
<span :key="`item${key}`" class="tag ml-2 mt-2">
|
||||
@ -20,7 +25,7 @@
|
||||
</span>
|
||||
</slot>
|
||||
</template>
|
||||
</template>
|
||||
</slot>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
@ -85,7 +90,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance, type PropType} from 'vue'
|
||||
import {
|
||||
computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance, type PropType,
|
||||
watchEffect,
|
||||
} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
|
87
src/components/tasks/partials/assigneeList.vue
Normal file
87
src/components/tasks/partials/assigneeList.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import User from '@/components/misc/user.vue'
|
||||
import {computed} from 'vue'
|
||||
|
||||
type removeFunction = (item: any) => {}
|
||||
|
||||
const {
|
||||
assignees,
|
||||
remove,
|
||||
disabled,
|
||||
avatarSize = 30,
|
||||
inline = false,
|
||||
} = defineProps<{
|
||||
assignees: IUser[],
|
||||
remove?: removeFunction,
|
||||
disabled?: boolean,
|
||||
avatarSize?: number,
|
||||
inline?: boolean,
|
||||
}>()
|
||||
|
||||
const hasDelete = computed(() => typeof remove !== 'undefined' && !disabled)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="assignees-list" :class="{'is-inline': inline}">
|
||||
<span class="assignee" v-for="user in assignees">
|
||||
<User
|
||||
:avatar-size="avatarSize"
|
||||
:show-username="false"
|
||||
:user="user"
|
||||
:class="{'m-2': hasDelete, 'mr-3': !hasDelete}"
|
||||
/>
|
||||
<BaseButton
|
||||
v-if="hasDelete"
|
||||
@click="remove(user)"
|
||||
class="remove-assignee"
|
||||
>
|
||||
<icon icon="times"/>
|
||||
</BaseButton>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.assignees-list {
|
||||
display: flex;
|
||||
|
||||
&.is-inline :deep(.user) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
&:hover .assignee:not(:first-child) {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.assignee {
|
||||
position: relative;
|
||||
transition: all $transition;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: -1.5rem;
|
||||
}
|
||||
|
||||
:deep(.user img) {
|
||||
border: 2px solid var(--white);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-assignee {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 2px;
|
||||
color: var(--danger);
|
||||
background: var(--white);
|
||||
padding: 0 4px;
|
||||
display: block;
|
||||
border-radius: 100%;
|
||||
font-size: .75rem;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
@ -11,13 +11,8 @@
|
||||
v-model="assignees"
|
||||
:autocomplete-enabled="false"
|
||||
>
|
||||
<template #tag="{item: user}">
|
||||
<span class="assignee">
|
||||
<user :avatar-size="32" :show-username="false" :user="user" class="m-2"/>
|
||||
<BaseButton @click="removeAssignee(user)" class="remove-assignee" v-if="!disabled">
|
||||
<icon icon="times"/>
|
||||
</BaseButton>
|
||||
</span>
|
||||
<template #items="{items, remove}">
|
||||
<assignee-list :assignees="items" :remove="removeAssignee"/>
|
||||
</template>
|
||||
<template #searchResult="{option: user}">
|
||||
<user :avatar-size="24" :show-username="true" :user="user"/>
|
||||
@ -40,6 +35,7 @@ import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import {getDisplayName} from '@/models/user'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
@ -120,34 +116,3 @@ async function findUser(query: string) {
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.assignee {
|
||||
position: relative;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: -1.5rem;
|
||||
}
|
||||
|
||||
:deep(.user img) {
|
||||
border: 2px solid var(--white);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.remove-assignee {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 2px;
|
||||
color: var(--danger);
|
||||
background: var(--white);
|
||||
padding: 0 4px;
|
||||
display: block;
|
||||
border-radius: 100%;
|
||||
font-size: .75rem;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
@ -49,15 +49,13 @@
|
||||
<div class="footer">
|
||||
<labels :labels="task.labels"/>
|
||||
<priority-label :priority="task.priority" :done="task.done"/>
|
||||
<div class="assignees" v-if="task.assignees.length > 0">
|
||||
<user
|
||||
v-for="u in task.assignees"
|
||||
:avatar-size="24"
|
||||
:key="task.id + 'assignee' + u.id"
|
||||
:show-username="false"
|
||||
:user="u"
|
||||
/>
|
||||
</div>
|
||||
<assignee-list
|
||||
v-if="task.assignees.length > 0"
|
||||
:assignees="task.assignees"
|
||||
:avatar-size="24"
|
||||
class="ml-1"
|
||||
:inline="true"
|
||||
/>
|
||||
<checklist-summary :task="task"/>
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<icon icon="paperclip"/>
|
||||
@ -91,6 +89,7 @@ import AttachmentService from '@/services/attachment'
|
||||
import {formatDateLong, formatISO, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
|
@ -49,14 +49,12 @@
|
||||
:labels="task.labels"
|
||||
/>
|
||||
|
||||
<User
|
||||
v-for="(a, i) in task.assignees"
|
||||
:avatar-size="27"
|
||||
:is-inline="true"
|
||||
:key="task.id + 'assignee' + a.id + i"
|
||||
:show-username="false"
|
||||
:user="a"
|
||||
class="m-2"
|
||||
<assignee-list
|
||||
v-if="task.assignees.length > 0"
|
||||
:assignees="task.assignees"
|
||||
:avatar-size="25"
|
||||
class="ml-1"
|
||||
:inline="true"
|
||||
/>
|
||||
|
||||
<!-- FIXME: use popup -->
|
||||
@ -152,6 +150,7 @@ import {success} from '@/message'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
|
||||
const {
|
||||
theTask,
|
||||
|
@ -33,13 +33,12 @@
|
||||
:labels="task.labels"
|
||||
/>
|
||||
|
||||
<User
|
||||
v-for="(a, i) in task.assignees"
|
||||
<assignee-list
|
||||
v-if="task.assignees.length > 0"
|
||||
:assignees="task.assignees"
|
||||
:avatar-size="20"
|
||||
:key="task.id + 'assignee' + a.id + i"
|
||||
:show-username="false"
|
||||
:user="a"
|
||||
class="avatar"
|
||||
class="ml-1"
|
||||
:inline="true"
|
||||
/>
|
||||
|
||||
<span
|
||||
@ -98,6 +97,7 @@ import ColorBubble from '@/components/misc/colorBubble.vue'
|
||||
import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatDate'
|
||||
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
|
||||
const {
|
||||
task,
|
||||
|
@ -143,13 +143,12 @@
|
||||
<labels :labels="t.labels"/>
|
||||
</td>
|
||||
<td v-if="activeColumns.assignees">
|
||||
<user
|
||||
:avatar-size="27"
|
||||
:is-inline="true"
|
||||
:key="t.id + 'assignee' + a.id + i"
|
||||
:show-username="false"
|
||||
:user="a"
|
||||
v-for="(a, i) in t.assignees"
|
||||
<assignee-list
|
||||
v-if="t.assignees.length > 0"
|
||||
:assignees="t.assignees"
|
||||
:avatar-size="28"
|
||||
class="ml-1"
|
||||
:inline="true"
|
||||
/>
|
||||
</td>
|
||||
<date-table-cell :date="t.dueDate" v-if="activeColumns.dueDate"/>
|
||||
@ -201,6 +200,7 @@ import {useTaskList} from '@/composables/useTaskList'
|
||||
import type {SortBy} from '@/composables/useTaskList'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
|
||||
const ACTIVE_COLUMNS_DEFAULT = {
|
||||
index: true,
|
||||
|
Loading…
x
Reference in New Issue
Block a user