Rudimentary kanban using sortable

This commit is contained in:
kolaente 2021-07-28 16:54:30 +02:00
parent 7f3cfccfbb
commit cf49adb381
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
2 changed files with 95 additions and 65 deletions

View File

@ -61,10 +61,11 @@ $filter-container-height: '1rem - #{$switch-view-height}';
-ms-user-select: none; // Internet Explorer/Edge -ms-user-select: none; // Internet Explorer/Edge
user-select: none; // Non-prefixed version, currently supported by Chrome, Opera and Firefox user-select: none; // Non-prefixed version, currently supported by Chrome, Opera and Firefox
transition: $ease-out; //transition: $ease-out;
cursor: pointer; cursor: pointer;
box-shadow: $shadow-xs; box-shadow: $shadow-xs;
display: block; display: block;
border: 3px solid transparent;
font-size: .9rem; font-size: .9rem;
padding: .5rem; padding: .5rem;
@ -205,7 +206,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
} }
} }
.drop-preview { .ghost {
border-radius: $radius; border-radius: $radius;
margin: 0 .5rem .5rem; margin: 0 .5rem .5rem;
background: transparent; background: transparent;
@ -213,6 +214,13 @@ $filter-container-height: '1rem - #{$switch-view-height}';
} }
} }
.move-card-move {
transition: transform 0s;
}
.no-move {
transition: transform 0s;
}
h2 { h2 {
font-size: 1rem; font-size: 1rem;
margin: 0; margin: 0;

View File

@ -108,28 +108,43 @@
</dropdown> </dropdown>
</div> </div>
<div :ref="`tasks-container${bucket.id}`" class="tasks"> <div :ref="`tasks-container${bucket.id}`" class="tasks">
<!-- Make the component either a div or a draggable component based on the user rights --> {{ drag }}
<component <draggable
:animation-duration="150" v-model="bucket.tasks"
:drop-placeholder="dropPlaceholderOptions" @start="() => drag = true"
:get-child-payload="getTaskPayload(bucket.id)" @end="e => onDrop(bucket.id, e)"
:is="canWrite ? 'Container' : 'div'" group="buckets"
:should-accept-drop="() => shouldAcceptDrop(bucket)" v-bind="dragOptions"
@drop="e => onDrop(bucket.id, e)"
drag-class="ghost-task"
drag-class-drop="ghost-task-drop"
drag-handle-selector=".task.draggable"
group-name="buckets"
> >
<transition-group type="transition" :name="!drag ? 'move-card': null">
<!-- Make the component either a div or a draggable component based on the user rights --> <!-- Make the component either a div or a draggable component based on the user rights -->
<component <!-- <component-->
:is="canWrite ? 'Draggable' : 'div'" <!-- :animation-duration="150"-->
<!-- :drop-placeholder="dropPlaceholderOptions"-->
<!-- :get-child-payload="getTaskPayload(bucket.id)"-->
<!-- :is="canWrite ? 'Container' : 'div'"-->
<!-- :should-accept-drop="() => shouldAcceptDrop(bucket)"-->
<!-- @drop="e => onDrop(bucket.id, e)"-->
<!-- drag-class="ghost-task"-->
<!-- drag-class-drop="ghost-task-drop"-->
<!-- drag-handle-selector=".task.draggable"-->
<!-- group-name="buckets"-->
<!-- >-->
<!-- &lt;!&ndash; Make the component either a div or a draggable component based on the user rights &ndash;&gt;-->
<!-- <component-->
<!-- :is="canWrite ? 'Draggable' : 'div'"-->
<!-- :key="`bucket${bucket.id}-task${task.id}`"-->
<!-- v-for="task in bucket.tasks"-->
<!-- >-->
<kanban-card
:key="`bucket${bucket.id}-task${task.id}`" :key="`bucket${bucket.id}-task${task.id}`"
v-for="task in bucket.tasks" v-for="task in bucket.tasks"
> :task="task"
<kanban-card :task="task"/> />
</component> </transition-group>
</component> <!-- </component>-->
<!-- </component>-->
</draggable>
</div> </div>
<div class="bucket-footer" v-if="canWrite"> <div class="bucket-footer" v-if="canWrite">
<div class="field" v-if="showNewTaskInput[bucket.id]"> <div class="field" v-if="showNewTaskInput[bucket.id]">
@ -248,6 +263,11 @@ export default {
animationDuration: 150, animationDuration: 150,
showOnTop: true, showOnTop: true,
}, },
drag: false,
dragOptions: {
animation: 150,
ghostClass: 'ghost',
},
sourceBucket: 0, sourceBucket: 0,
showBucketDeleteModal: false, showBucketDeleteModal: false,
@ -345,54 +365,56 @@ export default {
}) })
}, },
onDrop(bucketId, dropResult) { onDrop(bucketId, dropResult) {
console.log('drop', bucketId, dropResult)
this.drag = false
// Note: A lot of this example comes from the excellent kanban example on https://github.com/kutlugsahin/vue-smooth-dnd/blob/master/demo/src/pages/cards.vue // Note: A lot of this example comes from the excellent kanban example on https://github.com/kutlugsahin/vue-smooth-dnd/blob/master/demo/src/pages/cards.vue
const bucketIndex = filterObject(this.buckets, b => b.id === bucketId) // const bucketIndex = filterObject(this.buckets, b => b.id === bucketId)
//
if (dropResult.removedIndex !== null || dropResult.addedIndex !== null) { // if (dropResult.removedIndex !== null || dropResult.addedIndex !== null) {
//
// FIXME: This is probably not the best solution and more of a naive brute-force approach // // FIXME: This is probably not the best solution and more of a naive brute-force approach
//
// Duplicate the buckets to avoid stuff moving around without noticing // // Duplicate the buckets to avoid stuff moving around without noticing
const buckets = Object.assign({}, this.buckets) // const buckets = Object.assign({}, this.buckets)
// Get the index of the bucket and the bucket itself // // Get the index of the bucket and the bucket itself
const bucket = buckets[bucketIndex] // const bucket = buckets[bucketIndex]
//
// Rebuild the tasks from the bucket, removing/adding the moved task // // Rebuild the tasks from the bucket, removing/adding the moved task
bucket.tasks = applyDrag(bucket.tasks, dropResult) // bucket.tasks = applyDrag(bucket.tasks, dropResult)
// Update the bucket in the list of all buckets // // Update the bucket in the list of all buckets
delete buckets[bucketIndex] // delete buckets[bucketIndex]
buckets[bucketIndex] = bucket // buckets[bucketIndex] = bucket
// Set the buckets, triggering a state update in vue // // Set the buckets, triggering a state update in vue
// FIXME: This seems to set some task attributes (like due date) wrong. Commented out, but seems to still work? // // FIXME: This seems to set some task attributes (like due date) wrong. Commented out, but seems to still work?
// Not sure what to do about this. // // Not sure what to do about this.
// this.$store.commit('kanban/setBuckets', buckets) // // this.$store.commit('kanban/setBuckets', buckets)
} // }
//
if (dropResult.addedIndex !== null) { // if (dropResult.addedIndex !== null) {
//
const taskIndex = dropResult.addedIndex // const taskIndex = dropResult.addedIndex
const taskBefore = typeof this.buckets[bucketIndex].tasks[taskIndex - 1] === 'undefined' ? null : this.buckets[bucketIndex].tasks[taskIndex - 1] // const taskBefore = typeof this.buckets[bucketIndex].tasks[taskIndex - 1] === 'undefined' ? null : this.buckets[bucketIndex].tasks[taskIndex - 1]
const taskAfter = typeof this.buckets[bucketIndex].tasks[taskIndex + 1] === 'undefined' ? null : this.buckets[bucketIndex].tasks[taskIndex + 1] // const taskAfter = typeof this.buckets[bucketIndex].tasks[taskIndex + 1] === 'undefined' ? null : this.buckets[bucketIndex].tasks[taskIndex + 1]
const task = this.buckets[bucketIndex].tasks[taskIndex] // const task = this.buckets[bucketIndex].tasks[taskIndex]
this.$set(this.taskUpdating, task.id, true) // this.$set(this.taskUpdating, task.id, true)
this.oneTaskUpdating = true // this.oneTaskUpdating = true
//
task.kanbanPosition = calculateItemPosition(taskBefore !== null ? taskBefore.kanbanPosition : null, taskAfter !== null ? taskAfter.kanbanPosition : null) // task.kanbanPosition = calculateItemPosition(taskBefore !== null ? taskBefore.kanbanPosition : null, taskAfter !== null ? taskAfter.kanbanPosition : null)
console.log(task.kanbanPosition) // console.log(task.kanbanPosition)
//
task.bucketId = bucketId // task.bucketId = bucketId
//
this.$store.dispatch('tasks/update', task) // this.$store.dispatch('tasks/update', task)
.catch(e => { // .catch(e => {
this.error(e) // this.error(e)
}) // })
.finally(() => { // .finally(() => {
this.$set(this.taskUpdating, task.id, false) // this.$set(this.taskUpdating, task.id, false)
this.oneTaskUpdating = false // this.oneTaskUpdating = false
}) // })
} // }
}, },
getTaskPayload(bucketId) { getTaskPayload(bucketId) {
return index => { return index => {