feat: improve tabs behavior & style

Signed-off-by: Luka Trovic <luka@nextcloud.com>
This commit is contained in:
Luka Trovic
2022-02-16 09:14:59 +01:00
parent 021d55226b
commit 69896d5cca
6 changed files with 121 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-if="activeTab === 'attachment'" class="section-details"> <div v-if="activeTabs.includes('attachment')" class="section-details">
<AttachmentList <AttachmentList
:card-id="card.id" :card-id="card.id"
:removable="true" :removable="true"
@@ -18,9 +18,9 @@ export default {
type: Object, type: Object,
default: null, default: null,
}, },
activeTab: { activeTabs: {
type: String, type: Array,
default: null, default: () => [],
}, },
}, },
data() { data() {

View File

@@ -31,23 +31,23 @@
</p> </p>
</div> </div>
<div class="tabs"> <div class="tabs">
<div class="tab members" :class="{active: activeTab === 'members'}" @click="activeTab = 'members'"> <div class="tab members" :class="{active: activeTabs.includes('members')}" @click="changeActiveTab('members')">
<i class="icon-user icon" /> <i class="icon-user icon" />
{{ t('deck', 'Members') }} {{ t('deck', 'Members') }}
</div> </div>
<div class="tab tags" :class="{active: activeTab === 'tags'}" @click="activeTab = 'tags'"> <div class="tab tags" :class="{active: activeTabs.includes('tags')}" @click="changeActiveTab('tags')">
<i class="icon icon-tag" /> <i class="icon icon-tag" />
{{ t('deck', 'Tags') }} {{ t('deck', 'Tags') }}
</div> </div>
<div class="tab due-date" :class="{active: activeTab === 'duedate'}" @click="activeTab = 'duedate'"> <div class="tab due-date" :class="{active: activeTabs.includes('duedate')}" @click="changeActiveTab('duedate')">
<i class="icon icon-calendar-dark" /> <i class="icon icon-calendar-dark" />
{{ t('deck', 'Due date') }} {{ t('deck', 'Due date') }}
</div> </div>
<div class="tab project" :class="{active: activeTab === 'project'}" @click="activeTab = 'project'"> <div class="tab project" :class="{active: activeTabs.includes('project')}" @click="changeActiveTab('project')">
<i class="icon icon-deck" /> <i class="icon icon-deck" />
{{ t('deck', 'Project') }} {{ t('deck', 'Project') }}
</div> </div>
<div class="tab attachments" :class="{active: activeTab === 'attachment'}" @click="activeTab = 'attachment'"> <div class="tab attachments" :class="{active: activeTabs.includes('attachment')}" @click="changeActiveTab('attachment')">
<i class="icon-attach icon icon-attach-dark" /> <i class="icon-attach icon icon-attach-dark" />
{{ t('deck', 'Attachments') }} {{ t('deck', 'Attachments') }}
</div> </div>
@@ -56,28 +56,36 @@
<div class="content-tabs"> <div class="content-tabs">
<MembersTab <MembersTab
:card="currentCard" :card="currentCard"
:active-tab="activeTab" :active-tabs="activeTabs"
@click="activeTab = 'members'" :current-tab="currentTab"
@active-tab="changeActiveTab" /> @click="activeTabs.push('members')"
@active-tab="changeActiveTab"
@remove-active-tab="removeActiveTab" />
<TagsTab <TagsTab
:active-tab="activeTab" :active-tabs="activeTabs"
:card="currentCard" :card="currentCard"
@click="activeTab = 'tags'" :current-tab="currentTab"
@active-tab="changeActiveTab" /> @click="activeTabs.push('tags')"
@active-tab="changeActiveTab"
@remove-active-tab="removeActiveTab" />
<DueDateTab <DueDateTab
:active-tab="activeTab" :active-tabs="activeTabs"
:card="currentCard" :card="currentCard"
@click="activeTab = 'duedate'" :current-tab="currentTab"
@active-tab="changeActiveTab" /> @click="activeTabs.push('duedate')"
@active-tab="changeActiveTab"
@remove-active-tab="removeActiveTab" />
<ProjectTab <ProjectTab
:active-tab="activeTab" :active-tabs="activeTabs"
:card="currentCard" :card="currentCard"
@click="activeTab = 'project'" :current-tab="currentTab"
@click="activeTabs.push('project')"
@active-tab="changeActiveTab" /> @active-tab="changeActiveTab" />
<AttachmentsTab <AttachmentsTab
:active-tab="activeTab" :active-tabs="activeTabs"
:card="currentCard" :card="currentCard"
@click="activeTab = 'attachment'" :current-tab="currentTab"
@click="activeTabs.push('attachment')"
@active-tab="changeActiveTab" /> @active-tab="changeActiveTab" />
</div> </div>
<Description :key="currentCard.id" :card="currentCard" @change="descriptionChanged" /> <Description :key="currentCard.id" :card="currentCard" @change="descriptionChanged" />
@@ -86,29 +94,17 @@
<h2 class="activities-title"> <h2 class="activities-title">
<div class="icon-activity" /> {{ t('deck', 'Activity') }} <div class="icon-activity" /> {{ t('deck', 'Activity') }}
</h2> </h2>
<div class="comments"> <CardSidebarTabComments :card="currentCard" :tab-query="tabQuery" />
<Avatar :user="currentUser.uid" /> <ActivityList v-if="hasActivity"
<CommentForm v-model="newComment" @submit="createComment" /> filter="deck"
</div> :object-id="currentBoard.id"
<div class="activities-logs"> object-type="deck"
<CommentItem v-if="replyTo" type="deck" />
:comment="replyTo"
:reply="true"
:preview="true"
@cancel="cancelReply" />
<ul v-if="getCommentsForCard(currentCard.id).length > 0" id="commentsFeed">
<CommentItem v-for="cmt in getCommentsForCard(currentCard.id)"
:key="cmt.id"
:comment="cmt"
@doReload="loadComments" />
</ul>
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { Avatar } from '@nextcloud/vue'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import relativeDate from '../../mixins/relativeDate' import relativeDate from '../../mixins/relativeDate'
@@ -120,24 +116,23 @@ import DueDateTab from './DueDateTab.vue'
import Description from './Description.vue' import Description from './Description.vue'
import ProjectTab from './ProjectTab.vue' import ProjectTab from './ProjectTab.vue'
import AttachmentsTab from './AttachmentsTab.vue' import AttachmentsTab from './AttachmentsTab.vue'
import CommentForm from './CommentForm' import CardSidebarTabComments from './CardSidebarTabComments'
import CommentItem from './CommentItem'
import moment from '@nextcloud/moment' import moment from '@nextcloud/moment'
import ActivityList from '../ActivityList'
const capabilities = window.OC.getCapabilities() const capabilities = window.OC.getCapabilities()
export default { export default {
name: 'CardModal', name: 'CardModal',
components: { components: {
Avatar,
MembersTab, MembersTab,
Description, Description,
TagsTab, TagsTab,
DueDateTab, DueDateTab,
ProjectTab, ProjectTab,
AttachmentsTab, AttachmentsTab,
CommentForm, CardSidebarTabComments,
CommentItem, ActivityList,
}, },
filters: { filters: {
fromNow(value) { fromNow(value) {
@@ -169,7 +164,8 @@ export default {
hasActivity: capabilities && capabilities.activity, hasActivity: capabilities && capabilities.activity,
currentUser: getCurrentUser(), currentUser: getCurrentUser(),
comment: '', comment: '',
activeTab: null, currentTab: null,
activeTabs: [],
} }
}, },
computed: { computed: {
@@ -276,7 +272,20 @@ export default {
}, },
changeActiveTab(tab) { changeActiveTab(tab) {
this.activeTab = tab this.currentTab = tab
this.activeTabs = this.activeTabs.filter((item) => !['project', 'attachment'].includes(item))
if (!this.activeTabs.includes(tab)) {
this.activeTabs.push(tab)
}
},
removeActiveTab(tab) {
const index = this.activeTabs.indexOf(tab)
if (index > -1) {
this.activeTabs = this.activeTabs.splice(index, 1)
}
}, },
}, },
} }

View File

@@ -1,6 +1,6 @@
<template> <template>
<div v-if="activeTab === 'duedate' || (copiedCard && copiedCard.duedate)" <div v-if="activeTabs.includes('duedate') || (copiedCard && copiedCard.duedate)"
v-show="!['project', 'attachment'].includes(activeTab)" v-show="!['project', 'attachment'].includes(currentTab)"
class="section-details"> class="section-details">
<div @click="$emit('active-tab', 'duedate')"> <div @click="$emit('active-tab', 'duedate')">
<DatetimePicker v-model="duedate" <DatetimePicker v-model="duedate"
@@ -20,7 +20,6 @@
</Actions> </Actions>
</div> </div>
</template> </template>
<script> <script>
import { DatetimePicker, Actions, ActionButton } from '@nextcloud/vue' import { DatetimePicker, Actions, ActionButton } from '@nextcloud/vue'
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
@@ -41,9 +40,13 @@ export default {
type: Object, type: Object,
default: null, default: null,
}, },
activeTab: { activeTabs: {
type: Array,
default: () => [],
},
currentTab: {
type: String, type: String,
default: null, default: '',
}, },
}, },
data() { data() {
@@ -133,10 +136,20 @@ export default {
watch: { watch: {
card() { card() {
this.initialize() this.initialize()
if (this.copiedCard.duedate) {
this.$emit('active-tab', 'duedate')
} else {
this.$emit('remove-active-tab', 'duedate')
}
}, },
}, },
mounted() { mounted() {
this.initialize() this.initialize()
if (this.copiedCard.duedate) {
this.$emit('active-tab', 'duedate')
} else {
this.$emit('remove-active-tab', 'duedate')
}
}, },
methods: { methods: {
async initialize() { async initialize() {
@@ -155,7 +168,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.section-details { .section-details{
margin-right: 5px; margin-right: 5px;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
@@ -163,7 +176,7 @@ export default {
</style> </style>
<style> <style>
.section-details .mx-input { .section-details .mx-input{
height: 36px !important; height: 36px !important;
margin: 0; margin: 0;
} }

View File

@@ -1,6 +1,6 @@
<template> <template>
<div v-show="!['project', 'attachment'].includes(activeTab)" <div v-if="activeTabs.includes('members') || (assignedUsers && assignedUsers.length > 0)"
v-if="activeTab === 'members' || (assignedUsers && assignedUsers.length > 0)" v-show="!['project', 'attachment'].includes(currentTab)"
class="section-details"> class="section-details">
<div v-if="showSelelectMembers" @mouseleave="showSelelectMembers = false"> <div v-if="showSelelectMembers" @mouseleave="showSelelectMembers = false">
<Multiselect v-if="canEdit" <Multiselect v-if="canEdit"
@@ -65,9 +65,13 @@ export default {
type: Object, type: Object,
default: null, default: null,
}, },
activeTab: { activeTabs: {
type: Array,
default: () => [],
},
currentTab: {
type: String, type: String,
default: null, default: '',
}, },
}, },
data() { data() {
@@ -110,6 +114,13 @@ export default {
card() { card() {
this.initialize() this.initialize()
}, },
assignedUsers(value) {
if (value.length > 0) {
this.$emit('active-tab', 'members')
} else {
this.$emit('remove-active-tab', 'members')
}
},
}, },
mounted() { mounted() {
this.initialize() this.initialize()
@@ -172,8 +183,8 @@ export default {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
height: 32px; height: 32px;
width: 32px; width: 34px;
padding: 9px; padding: 5px 9px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-left: 5px; margin-left: 5px;

View File

@@ -1,6 +1,6 @@
<template> <template>
<div v-if="activeTab === 'project'" class="section-details"> <div v-if="activeTabs.includes('project')" class="section-details">
<div class="section-wrapper" @click="$emit('active-tab', 'project')"> <div class="section-wrapper">
<CollectionList v-if="card.id" <CollectionList v-if="card.id"
:id="`${card.id}`" :id="`${card.id}`"
:name="card.title" :name="card.title"
@@ -19,21 +19,11 @@ export default {
type: Object, type: Object,
default: null, default: null,
}, },
activeTab: { activeTabs: {
type: String, type: Array,
default: null, default: () => [],
}, },
}, },
data() {
return {
}
},
computed: {
},
mounted() {
},
methods: {
},
} }
</script> </script>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div v-show="!['project', 'attachment'].includes(activeTab)" <div v-if="activeTabs.includes('tags') || card.labels.length > 0"
v-if="activeTab === 'tags' || card.labels.length > 0" v-show="!['project', 'attachment'].includes(currentTab)"
class="section-details"> class="section-details">
<div v-if="showSelelectTags || card.labels.length <= 0" @mouseleave="showSelelectTags = false"> <div v-if="showSelelectTags || card.labels.length <= 0" @mouseleave="showSelelectTags = false">
<Multiselect v-model="assignedLabels" <Multiselect v-model="assignedLabels"
@@ -53,9 +53,13 @@ export default {
type: Object, type: Object,
default: null, default: null,
}, },
activeTab: { activeTabs: {
type: Array,
default: () => [],
},
currentTab: {
type: String, type: String,
default: null, default: '',
}, },
}, },
data() { data() {
@@ -74,8 +78,20 @@ export default {
return [...this.currentBoard.labels].sort((a, b) => (a.title < b.title) ? -1 : 1) return [...this.currentBoard.labels].sort((a, b) => (a.title < b.title) ? -1 : 1)
}, },
}, },
watch: {
card(value) {
if (value.labels.length > 0) {
this.$emit('active-tab', 'tags')
} else {
this.$emit('remove-active-tab', 'tags')
}
},
},
mounted() { mounted() {
this.initialize() this.initialize()
if (this.card.labels.length > 0) {
this.$emit('active-tab', 'tags')
}
}, },
methods: { methods: {
add() { add() {
@@ -140,11 +156,11 @@ export default {
.select-tag { .select-tag {
height: 32px; height: 32px;
width: 32px; width: 34px;
padding: 5px 7px; padding: 5px 8px;
} }
.tag { .tag{
padding: 0px 5px; padding: 0px 5px;
border-radius: 15px; border-radius: 15px;
} }