Implement stack drag-and-drop and use proper data from store

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl
2018-12-29 22:53:53 +01:00
parent 0f46b283b7
commit 35d9b48117
3 changed files with 71 additions and 72 deletions

View File

@@ -23,7 +23,7 @@
<template> <template>
<div class="controls"> <div class="controls">
<div id="app-navigation-toggle-custom" class="icon-menu" v-on:click="toggleNav" /> <div id="app-navigation-toggle-custom" class="icon-menu" @click="toggleNav" />
<div class="breadcrumb"> <div class="breadcrumb">
<div class="crumb svg last"> <div class="crumb svg last">
<router-link to="/boards" class="icon-home" title="All Boards"> <router-link to="/boards" class="icon-home" title="All Boards">
@@ -35,8 +35,26 @@
<a href="#todo">{{ board.title }}</a> <a href="#todo">{{ board.title }}</a>
<span style="display: inline;" class="icon-shared" /> <span style="display: inline;" class="icon-shared" />
</div> </div>
<div class="board-actions"> <div v-if="board" class="board-actions">
<router-link :to="{name: 'board.details'}" v-tooltip="t('deck', 'Board settings')" class="icon-settings" tag="button"></router-link> <div id="stack-add">
<form>
<label for="new-stack-input-main" class="hidden-visually">Add a new stack</label>
<input type="text" class="no-close" id="new-stack-input-main" placeholder="Add a new stack">
<button class="button-inline icon icon-add" type="submit" title="Submit">
<span class="hidden-visually">Submit</span>
</button>
</form>
</div>
<button title="Show archived cards">
<i class="icon icon-archive"></i>
<span class="hidden-visually">Show archived cards</span>
</button>
<button title="Toggle compact mode">
<i class="icon icon-toggle-compact-expanded"></i>
<span class="hidden-visually">Toggle compact mode</span>
</button>
<router-link v-tooltip="t('deck', 'Board settings')" :to="{name: 'board.details'}" class="icon-settings"
tag="button" />
</div> </div>
</div> </div>

View File

@@ -24,23 +24,28 @@
<div> <div>
<Controls :board="board" /> <Controls :board="board" />
<div v-if="board"> <div v-if="board">
<!-- example for external drop zone -->
<!-- <container :should-accept-drop="() => true" style="border:1px solid #aaa;" /> -->
<container lock-axix="y" orientation="horizontal" @drop="onDropStack"> <container lock-axix="y" orientation="horizontal" @drop="onDropStack">
<draggable v-for="stack in stacks" :key="stack.id" class="stack"> <draggable v-for="stack in stacksByBoard" :key="stack.id" class="stack">
<h3>{{ stack.title }}</h3> <h3>{{ stack.title }}</h3>
<container :get-child-payload="payload(stack.id)" group-name="stack" @drop="($event) => onDropCard(stack.id, $event)"> <container :get-child-payload="payloadForCard(stack.id)" group-name="stack" @drop="($event) => onDropCard(stack.id, $event)">
<draggable v-for="card in stack.cards" :key="card.id"> <draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
<card-item :id="card.id" /> <card-item v-if="card" :id="card.id" />
</draggable> </draggable>
</container> </container>
<div class="card create">
<div title="Add card">
<i class="icon icon-add"></i>
<span class="hidden-visually">Add card</span>
</div>
</div>
</draggable> </draggable>
</container> </container>
</div> </div>
<div v-else class="emptycontent"> <div v-else class="emptycontent">
<div class="icon icon-deck"></div> <div class="icon icon-deck" />
<h2>{{ t('deck', 'Board not found')}}</h2> <h2>{{ t('deck', 'Board not found') }}</h2>
<p></p> <p />
</div> </div>
</div> </div>
</template> </template>
@@ -48,37 +53,10 @@
<script> <script>
import { Container, Draggable } from 'vue-smooth-dnd' import { Container, Draggable } from 'vue-smooth-dnd'
import { mapState } from 'vuex' import { mapState, mapGetters } from 'vuex'
import Controls from '../Controls' import Controls from '../Controls'
import CardItem from '../cards/CardItem' import CardItem from '../cards/CardItem'
const applyDrag = (arr, dragResult) => {
const { removedIndex, addedIndex, payload } = dragResult
if (removedIndex === null && addedIndex === null) return arr
const result = [...arr]
let itemToAdd = payload
if (removedIndex !== null) {
itemToAdd = result.splice(removedIndex, 1)[0]
}
if (addedIndex !== null) {
result.splice(addedIndex, 0, itemToAdd)
}
return result
}
const dummyCard = function(i) {
return {
id: i,
order: 0,
title: 'card ' + i,
stackId: 1
}
}
export default { export default {
name: 'Board', name: 'Board',
components: { components: {
@@ -98,50 +76,53 @@ export default {
}, },
data: function() { data: function() {
return { return {
loading: true, loading: true
stacks: [
{ id: 1, title: 'abc', cards: [dummyCard(1), dummyCard(2), dummyCard(3), dummyCard(4), dummyCard(5)] },
{ id: 2, title: '234', cards: [dummyCard(6), dummyCard(7)] }
]
} }
}, },
computed: { computed: {
...mapState({ ...mapState({
board: state => state.currentBoard board: state => state.currentBoard
}), }),
orderedCards() { stacksByBoard() {
// return (stack) => _.orderBy(this.stacks[stack].cards, 'order') return this.$store.getters.stacksByBoard(this.board.id)
},
cardsByStack() {
return (id) => this.$store.getters.cardsByStack(id)
} }
}, },
created: function() { watch: {
this.boardApi.loadById(this.id) '$route': 'fetchData'
.then((board) => { },
this.$store.dispatch('setCurrentBoard', board) created() {
this.loading = false this.fetchData()
})
}, },
methods: { methods: {
onDropStack(dropResult) { fetchData() {
// TODO: persist new order in order field this.boardApi.loadById(this.id)
this.stacks = applyDrag(this.stacks, dropResult) .then((board) => {
this.$store.dispatch('setCurrentBoard', board)
this.$store.dispatch('loadStacks', board)
this.loading = false
})
}, },
onDropCard(stackId, dropResult) { onDropStack({ removedIndex, addedIndex }) {
if (dropResult.removedIndex !== null || dropResult.addedIndex !== null) { this.$store.dispatch('orderStack', { stack: this.stacksByBoard[removedIndex], removedIndex, addedIndex })
// TODO: persist new order in order field
const stacks = this.stacks
const stack = stacks.filter(p => p.id === stackId)[0]
const stackIndex = stacks.indexOf(stack)
const newStack = Object.assign({}, stack)
newStack.cards = applyDrag(newStack.cards, dropResult)
stacks.splice(stackIndex, 1, newStack)
this.stacks = stacks
}
}, },
payload(stackId) { onDropCard({ removedIndex, addedIndex }) {
},
payloadForCard(stackId) {
return index => { return index => {
return this.stacks.find(stack => stack.id === stackId).cards[index] return this.cardsByStack(stackId)[index]
} }
},
createStack() {
let newStack = {
title: 'FooBar',
boardId: this.id,
order: this.stacksByBoard().length
}
this.$store.dispatch('createStack', newStack)
} }
} }
} }

View File

@@ -23,7 +23,7 @@
<template> <template>
<div @click="openCard" tag="div" class="card"> <div @click="openCard" tag="div" class="card">
<div class="card-upper"> <div class="card-upper">
<h3>Card {{ id }}</h3> <h3>{{ card.title }}</h3>
<ul class="labels"> <ul class="labels">
<li v-for="label in labels" :key="label.id" :style="labelStyle(label)"><span>{{ label.title }}</span></li> <li v-for="label in labels" :key="label.id" :style="labelStyle(label)"><span>{{ label.title }}</span></li>
</ul> </ul>
@@ -67,7 +67,7 @@ export default {
return false return false
}, },
card() { card() {
return this.id return this.$store.getters.cardById(this.id)
}, },
menu() { menu() {
return [] return []