feat: create new popup component to handle popups generally

This commit is contained in:
kolaente 2021-11-02 21:59:14 +01:00
parent 9250f4e76b
commit 86efb07f09
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
6 changed files with 88 additions and 88 deletions

View File

@ -6,39 +6,42 @@
> >
{{ $t('filters.clear') }} {{ $t('filters.clear') }}
</x-button> </x-button>
<filters <popup>
:class="{'is-open': visibleInternal}" <template #trigger="{toggle}">
class="filter-popup" <x-button
v-model="value" @click.prevent.stop="toggle()"
ref="filters" type="secondary"
/> icon="filter"
>
{{ $t('filters.title') }}
</x-button>
</template>
<template #default>
<filters
v-model="value"
ref="filters"
/>
</template>
</popup>
</template> </template>
<script> <script>
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import Filters from '../../../components/list/partials/filters' import Filters from '../../../components/list/partials/filters'
import {getDefaultParams} from '../../tasks/mixins/taskList' import {getDefaultParams} from '../../tasks/mixins/taskList'
import Popup from '../../misc/popup'
export default { export default {
name: 'filter-popup', name: 'filter-popup',
components: { components: {
Popup,
Filters, Filters,
}, },
props: { props: {
modelValue: { modelValue: {
required: true, required: true,
}, },
visible: {
type: Boolean,
default: false,
},
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
data() {
return {
visibleInternal: false,
}
},
computed: { computed: {
value: { value: {
get() { get() {
@ -65,12 +68,6 @@ export default {
return JSON.stringify(filterParams) !== JSON.stringify(def) return JSON.stringify(filterParams) !== JSON.stringify(def)
}, },
}, },
mounted() {
document.addEventListener('click', this.hidePopup)
},
beforeUnmount() {
document.removeEventListener('click', this.hidePopup)
},
watch: { watch: {
modelValue: { modelValue: {
handler(value) { handler(value) {
@ -78,43 +75,11 @@ export default {
}, },
immediate: true, immediate: true,
}, },
visible() {
this.visibleInternal = !this.visibleInternal
},
}, },
methods: { methods: {
hidePopup(e) {
if (!this.visibleInternal) {
return
}
closeWhenClickedOutside(e, this.$refs.filters.$el, () => {
this.visibleInternal = false
})
},
clearFilters() { clearFilters() {
this.value = {...getDefaultParams()} this.value = {...getDefaultParams()}
}, },
}, },
} }
</script> </script>
<style lang="scss">
.filter-popup {
transition: opacity $transition;
opacity: 0;
height: 0;
overflow: hidden;
position: absolute;
top: 2rem;
margin: 0 !important;
border-width: 0 !important;
&.is-open {
opacity: 1;
height: auto;
margin: 1rem 0 !important;
border-width: 1px !important;
}
}
</style>

View File

@ -0,0 +1,63 @@
<template>
<slot name="trigger" :isOpen="open" :toggle="toggle"></slot>
<div class="popup" :class="{'is-open': open}">
<slot ref="popupContent"/>
</div>
</template>
<script>
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {onBeforeUnmount, onMounted, ref} from 'vue'
export default {
setup() {
const open = ref(false)
const popupContent = ref(null)
const toggle = () => {
open.value = !open.value
}
const hidePopup = e => {
if (!open.value) {
return
}
closeWhenClickedOutside(e, popupContent.$el, () => {
open.value = false
})
}
onMounted(() => {
document.addEventListener('click', hidePopup)
})
onBeforeUnmount(() => {
document.removeEventListener('click', hidePopup)
})
return {
open,
toggle,
}
},
}
</script>
<style scoped lang="scss">
.popup {
transition: opacity $transition;
opacity: 0;
height: 0;
overflow: hidden;
position: absolute;
top: 1rem;
margin: 0 !important;
&.is-open {
opacity: 1;
height: auto;
margin: 1rem 0 !important;
}
}
</style>

View File

@ -1,16 +1,14 @@
import TaskCollectionService from '@/services/taskCollection' import TaskCollectionService from '@/services/taskCollection'
// FIXME: merge with DEFAULT_PARAMS in filters.vue // FIXME: merge with DEFAULT_PARAMS in filters.vue
const DEFAULT_PARAMS = { export const getDefaultParams = () => ({
sort_by: ['position', 'id'], sort_by: ['position', 'id'],
order_by: ['asc', 'desc'], order_by: ['asc', 'desc'],
filter_by: ['done'], filter_by: ['done'],
filter_value: ['false'], filter_value: ['false'],
filter_comparator: ['equals'], filter_comparator: ['equals'],
filter_concat: 'and', filter_concat: 'and',
} })
export const getDefaultParams = () => ({...DEFAULT_PARAMS})
/** /**
* This mixin provides a base set of methods and properties to get tasks on a list. * This mixin provides a base set of methods and properties to get tasks on a list.

View File

@ -2,16 +2,9 @@
<div class="kanban-view"> <div class="kanban-view">
<div class="filter-container" v-if="isSavedFilter"> <div class="filter-container" v-if="isSavedFilter">
<div class="items"> <div class="items">
<x-button
@click.prevent.stop="toggleFilterPopup"
icon="filter"
type="secondary"
>
{{ $t('filters.title') }}
</x-button>
<filter-popup <filter-popup
:visible="showFilters"
v-model="params" v-model="params"
@update:modelValue="loadBuckets"
/> />
</div> </div>
</div> </div>
@ -300,7 +293,6 @@ export default {
filter_comparator: [], filter_comparator: [],
filter_concat: 'and', filter_concat: 'and',
}, },
showFilters: false,
} }
}, },
created() { created() {
@ -359,10 +351,6 @@ export default {
}, },
methods: { methods: {
toggleFilterPopup() {
this.showFilters = !this.showFilters
},
loadBuckets() { loadBuckets() {
// Prevent trying to load buckets if the task popup view is active // Prevent trying to load buckets if the task popup view is active
if (this.$route.name !== 'list.kanban') { if (this.$route.name !== 'list.kanban') {

View File

@ -41,15 +41,7 @@
v-if="!showTaskSearch" v-if="!showTaskSearch"
/> />
</div> </div>
<x-button
@click.prevent.stop="showTaskFilter = !showTaskFilter"
type="secondary"
icon="filter"
>
{{ $t('filters.title') }}
</x-button>
<filter-popup <filter-popup
:visible="showTaskFilter"
v-model="params" v-model="params"
@update:modelValue="loadTasks()" @update:modelValue="loadTasks()"
/> />
@ -154,6 +146,7 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
import {HAS_TASKS} from '@/store/mutation-types' import {HAS_TASKS} from '@/store/mutation-types'
import Nothing from '@/components/misc/nothing.vue' import Nothing from '@/components/misc/nothing.vue'
import Pagination from '@/components/misc/pagination.vue' import Pagination from '@/components/misc/pagination.vue'
import Popup from '@/components/misc/popup'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import {calculateItemPosition} from '../../../helpers/calculateItemPosition' import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
@ -197,6 +190,7 @@ export default {
taskList, taskList,
], ],
components: { components: {
Popup,
Nothing, Nothing,
FilterPopup, FilterPopup,
SingleTaskInList, SingleTaskInList,

View File

@ -3,21 +3,13 @@
<div class="filter-container"> <div class="filter-container">
<div class="items"> <div class="items">
<x-button <x-button
@click.prevent.stop="() => {showActiveColumnsFilter = !showActiveColumnsFilter; showTaskFilter = false}" @click.prevent.stop="showActiveColumnsFilter = !showActiveColumnsFilter"
icon="th" icon="th"
type="secondary" type="secondary"
> >
{{ $t('list.table.columns') }} {{ $t('list.table.columns') }}
</x-button> </x-button>
<x-button
@click.prevent.stop="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}"
icon="filter"
type="secondary"
>
{{ $t('filters.title') }}
</x-button>
<filter-popup <filter-popup
:visible="showTaskFilter"
v-model="params" v-model="params"
@update:modelValue="loadTasks()" @update:modelValue="loadTasks()"
/> />