I have a vuetify data table as well as a sidebar with all the headers of the table and a filter that should have the following behavior:
- Default is no options are chosen and all records of the table are shown
- If a user selects items in one of the filter drop downs, once they click off the changes are applied and the datatable is filtered to just those selected results and the remaining filters items array (potential selections) are limited just the options available on the screen. So the filters should dwindle down to nothing as more and more options are selected such that they are dynamic in a sense. Similar to excel except with excel by default all options are chosen and you are deselecting unlike mine which is somewhat reversed with options not selected.
What I need to do is basically take the union of a single filters options and then intersect that with every other filters unions.
So far this was my idea but the logic is flawed:
<template>
<v-container>
<v-row class="justify-space-between">
<h1 class="text-h4 pb-10 mx-5">Dashboard</h1>
<v-btn rounded="lg" @click="filterDrawer = !filterDrawer" color="primaryLighten5" class="mx-5" flat>
<template v-slot:prepend>
<v-icon color="primary" icon="mdi-filter" class="text-primaryDarken4"/>
</template>
<div class="text-primaryDarken4">
Filters
</div>
</v-btn>
</v-row>
<v-row>
<v-data-table class="pa-5" :headers="filteredHeaders" :items="displayedReviews"></v-data-table>
<v-navigation-drawer
:location="mdAndUp ? 'right' : 'top'"
floating
width="400"
v-model="filterDrawer"
class="pt-10 px-5 pb-5"
permanent
>
<v-card color="primaryLighten5" class="rounded-xl pa-5 h-100 text-primaryDarken4" flat>
<div class="h-100 overflow-y-auto">
<div class="d-flex justify-end">
<v-btn v-if="!mdAndUp" icon="mdi-close" @click="filterDrawer = !filterDrawer" size="small" variant="tonal"></v-btn>
</div>
<v-card-item>
<v-card-title class="text-center mb-3">Filters</v-card-title>
<div class="d-flex justify-end">
<v-btn flat
color="error"
variant="plain"
>
Clear All
</v-btn>
</div>
<div class="font-weight-normal">
<v-text-field
class="my-5"
append-inner-icon="mdi-magnify"
label="Dynamic Search"
multiple
density="compact"
variant="outlined"
hide-details
single-line
flat
></v-text-field>
<v-autocomplete
v-for="(header,index) in headers"
:key="index"
v-model="header.filtered"
class="my-5"
clearable
chips
:label="header.title"
:items="getUniqueList(reviews,header.key)"
multiple
density="compact"
variant="outlined"
hide-details
single-line
flat
>
<template v-slot:append-inner>
<v-btn @click.prevent="header.visible = !header.visible" flat variant="plain" size="small" :icon="header.visible ? 'mdi-eye' : 'mdi-eye-off'"/>
</template>
</v-autocomplete>
<v-btn @click="applyFilters()" rounded="lg" color="primaryDarken4" class="mt-8" prepend-icon="mdi-filter" size="large" block>Apply</v-btn>
</div>
</v-card-item>
</div>
</v-card>
</v-navigation-drawer>
</v-row>
</v-container>
</template>
<script lang="ts" setup>
import {ref, watch, computed} from 'vue'
import { useDisplay } from 'vuetify'
const { mdAndUp } = useDisplay()
const filterDrawer = ref<boolean>(true)
const headers = ref<any>([
{ title: 'Id', key: 'id', align: 'center', visible: true, filtered: [] },
{ title: 'Tracker', key: 'tracker', align: 'center', visible: true, filtered: [] },
{ title: 'State', key: 'state', align: 'center', visible: true, filtered: [] },
{ title: 'Programs', key: 'program', align: 'center', visible: true, filtered: [] },
{ title: 'Start Date', key: 'start', align: 'center', visible: true, filtered: [] },
{ title: 'End Date', key: 'end', align: 'center', visible: true, filtered: [] },
])
const reviews = ref<any[]>([
{
id: '10001',
tracker: 'test',
state: 'MD',
program: 'Dental',
start: '1/1/2022',
end: '12/31/2022'
},
{
id: '10002',
tracker: 'test2',
state: 'MD',
program: 'Dental',
start: '1/1/2022',
end: '12/31/2022'
},
{
id: '10003',
tracker: 'test3',
state: 'VA',
program: 'Vision',
start: '1/1/2023',
end: '12/31/2024'
},
])
const displayedReviews = ref<any>([])
displayedReviews.value = reviews.value
const applyFilters = () => {
displayedReviews.value = filteredReviews
}
const getUniqueList = (arr: any, property: string ) => {
return [...new Set(arr.map((item: { [x: string]: any; }) => item[property]))].sort()
}
watch(mdAndUp, () => {
if(mdAndUp.value) {
filterDrawer.value = true
} else {
filterDrawer.value = false
}
})
const filteredHeaders = computed(() => {
return headers.value.filter((header: { visible: boolean; }) => header.visible === true)
})
const filteredReviews = computed(() => {
var arr = reviews.value
var atleastOneFilterChecked = false
headers.value.forEach((header) => {
if(header.filtered.length > 0) {
header.filtered.forEach((filter) => {
atleastOneFilterChecked = true
arr = arr.filter((review) => review[header.key] == filter)
});
}
});
return atleastOneFilterChecked ? arr : reviews.value
})
</script>