Merge branch 'master' into enh/archiveAllCardsFromStack
This commit is contained in:
@@ -23,13 +23,33 @@
|
||||
<template>
|
||||
<div class="board-wrapper">
|
||||
<Controls :board="board" />
|
||||
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-if="loading" key="loading" class="emptycontent">
|
||||
<div class="icon icon-loading" />
|
||||
<h2>{{ t('deck', 'Loading board') }}</h2>
|
||||
<p />
|
||||
</div>
|
||||
<div v-else-if="board && !loading" key="board" class="board">
|
||||
<EmptyContent v-else-if="isEmpty" key="empty" icon="icon-deck">
|
||||
{{ t('deck', 'No lists available') }}
|
||||
<template #desc>
|
||||
{{ t('deck', 'Create a new list to add cards to this board') }}
|
||||
<form @submit.prevent="addNewStack()">
|
||||
<input id="new-stack-input-main"
|
||||
v-model="newStackTitle"
|
||||
v-focus
|
||||
type="text"
|
||||
class="no-close"
|
||||
:placeholder="t('deck', 'List name')"
|
||||
required>
|
||||
<input v-tooltip="t('deck', 'Add new list')"
|
||||
class="icon-confirm"
|
||||
type="submit"
|
||||
value="">
|
||||
</form>
|
||||
</template>
|
||||
</EmptyContent>
|
||||
<div v-else-if="!isEmpty && !loading" key="board" class="board">
|
||||
<Container lock-axix="y"
|
||||
orientation="horizontal"
|
||||
:drag-handle-selector="dragHandleSelector"
|
||||
@@ -54,6 +74,7 @@ import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Controls from '../Controls'
|
||||
import Stack from './Stack'
|
||||
import { EmptyContent } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'Board',
|
||||
@@ -62,6 +83,7 @@ export default {
|
||||
Container,
|
||||
Draggable,
|
||||
Stack,
|
||||
EmptyContent,
|
||||
},
|
||||
inject: [
|
||||
'boardApi',
|
||||
@@ -72,9 +94,10 @@ export default {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data: function() {
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
newStackTitle: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -91,6 +114,9 @@ export default {
|
||||
dragHandleSelector() {
|
||||
return this.canEdit ? null : '.no-drag'
|
||||
},
|
||||
isEmpty() {
|
||||
return this.stacksByBoard.length === 0
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
id: 'fetchData',
|
||||
@@ -117,13 +143,13 @@ export default {
|
||||
this.$store.dispatch('orderStack', { stack: this.stacksByBoard[removedIndex], removedIndex, addedIndex })
|
||||
},
|
||||
|
||||
createStack() {
|
||||
addNewStack() {
|
||||
const newStack = {
|
||||
title: 'FooBar',
|
||||
title: this.newStackTitle,
|
||||
boardId: this.id,
|
||||
order: this.stacksByBoard().length,
|
||||
}
|
||||
this.$store.dispatch('createStack', newStack)
|
||||
this.newStackTitle = ''
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -131,12 +157,25 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@import "../../css/animations.scss";
|
||||
@import '../../css/animations.scss';
|
||||
|
||||
$board-spacing: 15px;
|
||||
$stack-spacing: 10px;
|
||||
$stack-width: 300px;
|
||||
|
||||
form {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.board-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@@ -89,6 +89,12 @@ export default {
|
||||
li {
|
||||
display: flex;
|
||||
height: 44px;
|
||||
|
||||
&:hover, &:active, &.focus {
|
||||
button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -119,12 +125,5 @@ export default {
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
}
|
||||
li {
|
||||
&:hover, &:active, &.focus {
|
||||
button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -178,20 +178,25 @@ export default {
|
||||
#shareWithList {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#shareWithList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.username {
|
||||
padding: 12px 9px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.board-owner-label {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.avatarLabel {
|
||||
padding: 6px
|
||||
}
|
||||
|
||||
.avatardiv {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 16px;
|
||||
|
||||
@@ -25,21 +25,24 @@
|
||||
<div class="stack">
|
||||
<div v-click-outside="stopCardCreation" class="stack--header">
|
||||
<transition name="fade" mode="out-in">
|
||||
<h3 v-if="!canManage">
|
||||
<h3 v-if="!canManage || isArchived">
|
||||
{{ stack.title }}
|
||||
</h3>
|
||||
<h3 v-else-if="!editing" @click="startEditing(stack)">
|
||||
<h3 v-else-if="!editing"
|
||||
v-tooltip="stack.title"
|
||||
class="stack--title"
|
||||
@click="startEditing(stack)">
|
||||
{{ stack.title }}
|
||||
</h3>
|
||||
<form v-else @submit.prevent="finishedEdit(stack)">
|
||||
<input v-model="copiedStack.title" v-focus type="text">
|
||||
<input v-tooltip="t('deck', 'Add a new stack')"
|
||||
<input v-tooltip="t('deck', 'Add a new list')"
|
||||
class="icon-confirm"
|
||||
type="submit"
|
||||
value="">
|
||||
</form>
|
||||
</transition>
|
||||
<Actions v-if="canManage" :force-menu="true">
|
||||
<Actions v-if="canManage && !isArchived" :force-menu="true">
|
||||
<ActionButton icon="icon-archive" @click="modalArchivAllCardsShow=true">
|
||||
{{ t('deck', 'Archive all cards') }}
|
||||
</ActionButton>
|
||||
@@ -47,7 +50,7 @@
|
||||
{{ t('deck', 'Delete list') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions v-if="canEdit && !showArchived">
|
||||
<Actions v-if="canEdit && !showArchived && !isArchived">
|
||||
<ActionButton icon="icon-add" @click.stop="showAddCard=true">
|
||||
{{ t('deck', 'Add card') }}
|
||||
</ActionButton>
|
||||
@@ -57,7 +60,7 @@
|
||||
<Modal v-if="modalArchivAllCardsShow" @close="modalArchivAllCardsShow=false">
|
||||
<div class="modal__content">
|
||||
<h3>{{ t('deck', 'Archive all cards in this list') }}</h3>
|
||||
<progress :value="archiveAllCardsProgress" :max="stackLen" />
|
||||
<progress :value="stackTransfer.current" :max="stackTransfer.total" />
|
||||
<button class="primary" @click="archiveAllCardsFromStack(stack)">
|
||||
{{ t('deck', 'Archive all cards') }}
|
||||
</button>
|
||||
@@ -112,7 +115,9 @@
|
||||
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
|
||||
import { Actions, ActionButton, Modal } from '@nextcloud/vue'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import CardItem from '../cards/CardItem'
|
||||
|
||||
export default {
|
||||
@@ -141,14 +146,17 @@ export default {
|
||||
stateCardCreating: false,
|
||||
animate: false,
|
||||
modalArchivAllCardsShow: false,
|
||||
archiveAllCardsProgress: null,
|
||||
stackLen: 0,
|
||||
stackTransfer: {
|
||||
total: 0,
|
||||
current: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'canManage',
|
||||
'canEdit',
|
||||
'isArchived',
|
||||
]),
|
||||
...mapState({
|
||||
showArchived: state => state.showArchived,
|
||||
@@ -200,9 +208,9 @@ export default {
|
||||
},
|
||||
archiveAllCardsFromStack(stack) {
|
||||
|
||||
this.stackLen = this.cardsByStack.length
|
||||
this.stackTransfer.total = this.cardsByStack.length
|
||||
this.cardsByStack.forEach((card, index) => {
|
||||
this.archiveAllCardsProgress = index
|
||||
this.stackTransfer.current = index
|
||||
this.$store.dispatch('archiveUnarchiveCard', { ...card, archived: true })
|
||||
})
|
||||
this.modalArchivAllCardsShow = false
|
||||
@@ -234,7 +242,7 @@ export default {
|
||||
})
|
||||
this.$router.push({ name: 'card', params: { cardId: newCard.id } })
|
||||
} catch (e) {
|
||||
OCP.Toast.error('Could not create card: ' + e.response.data.message)
|
||||
showError('Could not create card: ' + e.response.data.message)
|
||||
} finally {
|
||||
this.stateCardCreating = false
|
||||
}
|
||||
@@ -263,12 +271,14 @@ export default {
|
||||
margin: 3px -3px;
|
||||
margin-right: -10px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 3px;
|
||||
background-color: var(--color-main-background-translucent);
|
||||
cursor: grab;
|
||||
|
||||
h3, form {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
cursor: inherit;
|
||||
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
@@ -276,6 +286,13 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.stack--title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: calc($stack-width - 60px);
|
||||
}
|
||||
|
||||
.stack--card-add {
|
||||
position: sticky;
|
||||
top: 52px;
|
||||
@@ -310,6 +327,7 @@ export default {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.stack .smooth-dnd-container.vertical {
|
||||
margin-top: 3px;
|
||||
}
|
||||
@@ -322,6 +340,7 @@ export default {
|
||||
.slide-top-leave-active {
|
||||
transition: all 100ms ease;
|
||||
}
|
||||
|
||||
.slide-top-enter, .slide-top-leave-to {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
@@ -334,10 +353,6 @@ export default {
|
||||
min-height: 100px;
|
||||
text-align: center;
|
||||
margin: 20px 20px 20px 20px;
|
||||
|
||||
.multiselect {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal__content button {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="labels">
|
||||
<li v-for="label in labels" :key="label.id" :class="{editing: (editingLabelId === label.id)}">
|
||||
<li v-for="label in labelsSorted" :key="label.id" :class="{editing: (editingLabelId === label.id)}">
|
||||
<!-- Edit Tag -->
|
||||
<template v-if="editingLabelId === label.id">
|
||||
<form class="label-form" @submit.prevent="updateLabel(label)">
|
||||
@@ -14,24 +14,30 @@
|
||||
type="submit"
|
||||
value=""
|
||||
class="icon-confirm">
|
||||
<input v-tooltip="t('deck', 'Cancel')"
|
||||
value=""
|
||||
class="icon-close"
|
||||
@click="editingLabelId = null">
|
||||
<Actions>
|
||||
<ActionButton v-tooltip="{content: missingDataLabel, show: !editLabelObjValidated, trigger: 'manual' }"
|
||||
:disabled="!editLabelObjValidated"
|
||||
icon="icon-close"
|
||||
@click="editingLabelId = null">
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</form>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :style="{ backgroundColor: `#${label.color}`, color:textColor(label.color) }" class="label-title">
|
||||
<span>{{ label.title }}</span>
|
||||
<div class="label-title" @click="clickEdit(label)">
|
||||
<span :style="{ backgroundColor: `#${label.color}`, color: textColor(label.color) }">{{ label.title }}</span>
|
||||
</div>
|
||||
<button v-if="canManage"
|
||||
v-tooltip="t('deck', 'Edit')"
|
||||
class="icon-rename"
|
||||
@click="clickEdit(label)" />
|
||||
<button v-if="canManage"
|
||||
v-tooltip="t('deck', 'Delete')"
|
||||
class="icon-delete"
|
||||
@click="deleteLabel(label.id)" />
|
||||
<Actions v-if="canManage && !isArchived">
|
||||
<ActionButton icon="icon-rename" @click="clickEdit(label)">
|
||||
{{ t('deck', 'Edit') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions v-if="canManage && !isArchived">
|
||||
<ActionButton icon="icon-delete" @click="deleteLabel(label.id)">
|
||||
{{ t('deck', 'Delete') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</template>
|
||||
</li>
|
||||
|
||||
@@ -48,14 +54,15 @@
|
||||
type="submit"
|
||||
value=""
|
||||
class="icon-confirm">
|
||||
<input v-tooltip="t('deck', 'Cancel')"
|
||||
value=""
|
||||
class="icon-close"
|
||||
@click="addLabel=false">
|
||||
<Actions>
|
||||
<ActionButton icon="icon-close" @click="addLabel=false">
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</form>
|
||||
</template>
|
||||
</li>
|
||||
<button v-if="canManage" @click="clickShowAddLabel()">
|
||||
<button v-if="canManage && !isArchived" @click="clickShowAddLabel()">
|
||||
<span class="icon-add" />{{ t('deck', 'Add a new tag') }}
|
||||
</button>
|
||||
</ul>
|
||||
@@ -66,12 +73,14 @@
|
||||
|
||||
import { mapGetters } from 'vuex'
|
||||
import Color from '../../mixins/color'
|
||||
import { ColorPicker } from '@nextcloud/vue'
|
||||
import { ColorPicker, Actions, ActionButton } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'TagsTabSidebar',
|
||||
components: {
|
||||
ColorPicker,
|
||||
Actions,
|
||||
ActionButton,
|
||||
},
|
||||
mixins: [Color],
|
||||
data() {
|
||||
@@ -88,6 +97,7 @@ export default {
|
||||
...mapGetters({
|
||||
labels: 'currentBoardLabels',
|
||||
canManage: 'canManage',
|
||||
isArchived: 'isArchived',
|
||||
}),
|
||||
addLabelObjValidated() {
|
||||
if (this.addLabelObj.title === '') {
|
||||
@@ -111,6 +121,9 @@ export default {
|
||||
|
||||
return true
|
||||
},
|
||||
labelsSorted() {
|
||||
return [...this.labels].sort((a, b) => (a.title < b.title) ? -1 : 1)
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
@@ -145,7 +158,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
$clickable-area: 37px;
|
||||
$clickable-area: 44px;
|
||||
|
||||
.labels li {
|
||||
display: flex;
|
||||
@@ -153,12 +166,23 @@ export default {
|
||||
align-items: stretch;
|
||||
height: $clickable-area;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-hover);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.label-title {
|
||||
flex-grow: 1;
|
||||
border-radius: 3px;
|
||||
padding: 7px;
|
||||
padding: 10px;
|
||||
|
||||
&:hover, span:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
border-radius: 15px;
|
||||
padding: 7px 12px;
|
||||
}
|
||||
}
|
||||
&:not(.editing) button {
|
||||
@@ -191,15 +215,11 @@ export default {
|
||||
display: flex;
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
margin: 5px;
|
||||
}
|
||||
input[type=submit] {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
button,
|
||||
input:not([type='text']):last-child {
|
||||
min-width: $clickable-area;
|
||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||
margin-left: -1px;
|
||||
width: 35px;
|
||||
background-color: var(--color-main-background);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user