forked from vikunja/frontend
feat: create new popup component to handle popups generally
This commit is contained in:
parent
9250f4e76b
commit
86efb07f09
|
@ -6,39 +6,42 @@
|
|||
>
|
||||
{{ $t('filters.clear') }}
|
||||
</x-button>
|
||||
<filters
|
||||
:class="{'is-open': visibleInternal}"
|
||||
class="filter-popup"
|
||||
v-model="value"
|
||||
ref="filters"
|
||||
/>
|
||||
<popup>
|
||||
<template #trigger="{toggle}">
|
||||
<x-button
|
||||
@click.prevent.stop="toggle()"
|
||||
type="secondary"
|
||||
icon="filter"
|
||||
>
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<template #default>
|
||||
<filters
|
||||
v-model="value"
|
||||
ref="filters"
|
||||
/>
|
||||
</template>
|
||||
</popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import Filters from '../../../components/list/partials/filters'
|
||||
import {getDefaultParams} from '../../tasks/mixins/taskList'
|
||||
import Popup from '../../misc/popup'
|
||||
|
||||
export default {
|
||||
name: 'filter-popup',
|
||||
components: {
|
||||
Popup,
|
||||
Filters,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
required: true,
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
visibleInternal: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
|
@ -65,12 +68,6 @@ export default {
|
|||
return JSON.stringify(filterParams) !== JSON.stringify(def)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hidePopup)
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('click', this.hidePopup)
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(value) {
|
||||
|
@ -78,43 +75,11 @@ export default {
|
|||
},
|
||||
immediate: true,
|
||||
},
|
||||
visible() {
|
||||
this.visibleInternal = !this.visibleInternal
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hidePopup(e) {
|
||||
if (!this.visibleInternal) {
|
||||
return
|
||||
}
|
||||
|
||||
closeWhenClickedOutside(e, this.$refs.filters.$el, () => {
|
||||
this.visibleInternal = false
|
||||
})
|
||||
},
|
||||
clearFilters() {
|
||||
this.value = {...getDefaultParams()}
|
||||
},
|
||||
},
|
||||
}
|
||||
</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>
|
||||
|
|
|
@ -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>
|
|
@ -1,16 +1,14 @@
|
|||
import TaskCollectionService from '@/services/taskCollection'
|
||||
|
||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||
const DEFAULT_PARAMS = {
|
||||
export const getDefaultParams = () => ({
|
||||
sort_by: ['position', 'id'],
|
||||
order_by: ['asc', 'desc'],
|
||||
filter_by: ['done'],
|
||||
filter_value: ['false'],
|
||||
filter_comparator: ['equals'],
|
||||
filter_concat: 'and',
|
||||
}
|
||||
|
||||
export const getDefaultParams = () => ({...DEFAULT_PARAMS})
|
||||
})
|
||||
|
||||
/**
|
||||
* This mixin provides a base set of methods and properties to get tasks on a list.
|
||||
|
|
|
@ -2,16 +2,9 @@
|
|||
<div class="kanban-view">
|
||||
<div class="filter-container" v-if="isSavedFilter">
|
||||
<div class="items">
|
||||
<x-button
|
||||
@click.prevent.stop="toggleFilterPopup"
|
||||
icon="filter"
|
||||
type="secondary"
|
||||
>
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
<filter-popup
|
||||
:visible="showFilters"
|
||||
v-model="params"
|
||||
@update:modelValue="loadBuckets"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -300,7 +293,6 @@ export default {
|
|||
filter_comparator: [],
|
||||
filter_concat: 'and',
|
||||
},
|
||||
showFilters: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -359,10 +351,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
toggleFilterPopup() {
|
||||
this.showFilters = !this.showFilters
|
||||
},
|
||||
|
||||
loadBuckets() {
|
||||
// Prevent trying to load buckets if the task popup view is active
|
||||
if (this.$route.name !== 'list.kanban') {
|
||||
|
|
|
@ -41,15 +41,7 @@
|
|||
v-if="!showTaskSearch"
|
||||
/>
|
||||
</div>
|
||||
<x-button
|
||||
@click.prevent.stop="showTaskFilter = !showTaskFilter"
|
||||
type="secondary"
|
||||
icon="filter"
|
||||
>
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
<filter-popup
|
||||
:visible="showTaskFilter"
|
||||
v-model="params"
|
||||
@update:modelValue="loadTasks()"
|
||||
/>
|
||||
|
@ -154,6 +146,7 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
|||
import {HAS_TASKS} from '@/store/mutation-types'
|
||||
import Nothing from '@/components/misc/nothing.vue'
|
||||
import Pagination from '@/components/misc/pagination.vue'
|
||||
import Popup from '@/components/misc/popup'
|
||||
|
||||
import draggable from 'vuedraggable'
|
||||
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
||||
|
@ -197,6 +190,7 @@ export default {
|
|||
taskList,
|
||||
],
|
||||
components: {
|
||||
Popup,
|
||||
Nothing,
|
||||
FilterPopup,
|
||||
SingleTaskInList,
|
||||
|
|
|
@ -3,21 +3,13 @@
|
|||
<div class="filter-container">
|
||||
<div class="items">
|
||||
<x-button
|
||||
@click.prevent.stop="() => {showActiveColumnsFilter = !showActiveColumnsFilter; showTaskFilter = false}"
|
||||
@click.prevent.stop="showActiveColumnsFilter = !showActiveColumnsFilter"
|
||||
icon="th"
|
||||
type="secondary"
|
||||
>
|
||||
{{ $t('list.table.columns') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click.prevent.stop="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}"
|
||||
icon="filter"
|
||||
type="secondary"
|
||||
>
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
<filter-popup
|
||||
:visible="showTaskFilter"
|
||||
v-model="params"
|
||||
@update:modelValue="loadTasks()"
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue