Add collapsing kanban buckets

This commit is contained in:
kolaente 2021-07-07 21:58:29 +02:00
parent 304ba797a0
commit ac6082a670
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 81 additions and 8 deletions

View File

@ -0,0 +1,30 @@
const key = 'collapsedBuckets'
const getAllState = () => {
const saved = localStorage.getItem(key)
if (saved === null) {
return {}
}
return JSON.parse(saved)
}
export const saveCollapsedBucketState = (listId, collapsedBuckets) => {
const state = getAllState()
state[listId] = collapsedBuckets
for (const bucketId in state[listId]) {
if (!state[listId][bucketId]) {
delete state[listId][bucketId]
}
}
localStorage.setItem(key, JSON.stringify(state))
}
export const getCollapsedBucketState = listId => {
const state = getAllState()
if (typeof state[listId] !== 'undefined') {
return state[listId]
}
return {}
}

View File

@ -243,7 +243,8 @@
"deleteBucketText2": "This will not delete any tasks but move them into the default bucket.",
"deleteBucketSuccess": "The bucket has been deleted successfully.",
"bucketTitleSavedSuccess": "The bucket title has been saved successfully.",
"bucketLimitSavedSuccess": "The bucket limit been saved successfully."
"bucketLimitSavedSuccess": "The bucket limit been saved successfully.",
"collapse": "Collapse this bucket"
}
},
"namespace": {

View File

@ -2,6 +2,8 @@ $bucket-background: $grey-100;
$task-background: $white;
$ease-out: all .3s cubic-bezier(0.23, 1, 0.32, 1);
$bucket-width: 300px;
$bucket-header-height: 60px;
$bucket-right-margin: 1rem;
$crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1rem - 1.5rem - 11px';
$crazy-height-calculation-tasks: '#{$crazy-height-calculation} - 1rem - 2.5rem - 2rem - #{$button-height} - 1rem';
@ -31,7 +33,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
position: relative;
flex: 0 0 $bucket-width;
margin: 0 1rem 0 0;
margin: 0 $bucket-right-margin 0 0;
max-height: 100%;
min-height: 20px;
max-width: $bucket-width;
@ -233,6 +235,18 @@ $filter-container-height: '1rem - #{$switch-view-height}';
a.dropdown-item {
padding-right: 1rem;
}
&.is-collapsed {
transform: rotate(90deg) translateX($bucket-width / 2 - $bucket-header-height / 2);
// Using negative margins instead of translateY here to make all other buckets fill the empty space
margin-left: ($bucket-width / 2 - $bucket-header-height / 2) * -1;
margin-right: calc(#{($bucket-width / 2 - $bucket-header-height / 2) * -1} + #{$bucket-right-margin});
cursor: pointer;
.tasks, .bucket-footer {
display: none;
}
}
}
.bucket-header {
@ -240,6 +254,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
align-items: center;
justify-content: space-between;
padding: .5rem;
height: $bucket-header-height;
.limit {
padding-left: .5rem;

View File

@ -17,8 +17,13 @@
/>
</div>
<div :class="{ 'is-loading': loading && !oneTaskUpdating}" class="kanban loader-container">
<div :key="`bucket${bucket.id}`" class="bucket" v-for="bucket in buckets">
<div class="bucket-header">
<div
:key="`bucket${bucket.id}`"
class="bucket"
:class="{'is-collapsed': collapsedBuckets[bucket.id]}"
v-for="bucket in buckets"
>
<div class="bucket-header" @click="() => unCollapseBucket(bucket)">
<span
v-if="bucket.isDoneBucket"
class="icon is-small has-text-success mr-2"
@ -31,7 +36,7 @@
@focusout="() => saveBucketTitle(bucket.id)"
@keydown.enter.prevent.stop="() => saveBucketTitle(bucket.id)"
class="title input"
:contenteditable="canWrite"
:contenteditable="canWrite && !collapsedBuckets[bucket.id]"
spellcheck="false">{{ bucket.title }}</h2>
<span
:class="{'is-max': bucket.tasks.length >= bucket.limit}"
@ -41,7 +46,7 @@
</span>
<dropdown
class="is-right options"
v-if="canWrite"
v-if="canWrite && !collapsedBuckets[bucket.id]"
trigger-icon="ellipsis-v"
@close="() => showSetLimitInput = false"
>
@ -77,7 +82,7 @@
</template>
</a>
<a
@click="toggleDoneBucket(bucket)"
@click.stop="toggleDoneBucket(bucket)"
class="dropdown-item"
v-tooltip="$t('list.kanban.doneBucketHintExtended')"
>
@ -85,9 +90,15 @@
icon="check-double"/></span>
{{ $t('list.kanban.doneBucket') }}
</a>
<a
class="dropdown-item"
@click.stop="() => collapseBucket(bucket)"
>
{{ $t('list.kanban.collapse') }}
</a>
<a
:class="{'is-disabled': buckets.length <= 1}"
@click="() => deleteBucketModal(bucket.id)"
@click.stop="() => deleteBucketModal(bucket.id)"
class="dropdown-item has-text-danger"
v-tooltip="buckets.length <= 1 ? $t('list.kanban.deleteLast') : ''"
>
@ -283,6 +294,7 @@ import FilterPopup from '@/components/list/partials/filter-popup'
import Dropdown from '@/components/misc/dropdown'
import {playPop} from '@/helpers/playPop'
import createTask from '@/components/tasks/mixins/createTask'
import {getCollapsedBucketState, saveCollapsedBucketState} from '@/helpers/saveCollapsedBucketState'
export default {
name: 'Kanban',
@ -315,6 +327,7 @@ export default {
showNewBucketInput: false,
newTaskError: {},
showSetLimitInput: false,
collapsedBuckets: {},
// We're using this to show the loading animation only at the task when updating it
taskUpdating: {},
@ -369,6 +382,8 @@ export default {
return
}
this.collapsedBuckets = getCollapsedBucketState(this.$route.params.listId)
console.debug(`Loading buckets, loadedListId = ${this.loadedListId}, $route.params =`, this.$route.params)
this.filtersChanged = false
@ -612,6 +627,18 @@ export default {
bucket.isDoneBucket = !bucket.isDoneBucket
})
},
collapseBucket(bucket) {
this.$set(this.collapsedBuckets, bucket.id, true)
saveCollapsedBucketState(this.$route.params.listId, this.collapsedBuckets)
},
unCollapseBucket(bucket) {
if (!this.collapsedBuckets[bucket.id]) {
return
}
this.$set(this.collapsedBuckets, bucket.id, false)
saveCollapsedBucketState(this.$route.params.listId, this.collapsedBuckets)
},
},
}
</script>