feat: example of dataTableFacetedFilters virtualization

This commit is contained in:
romanhrynevych 2024-07-10 21:51:17 +03:00
parent 1f1e6f339f
commit a1fcbf8ea9
2 changed files with 57 additions and 43 deletions

View File

@ -2,7 +2,8 @@
import type { Column } from '@tanstack/vue-table' import type { Column } from '@tanstack/vue-table'
import type { Component } from 'vue' import type { Component } from 'vue'
import { computed } from 'vue' import { computed } from 'vue'
import { type Task } from '../data/schema' import { useVirtualList } from '@vueuse/core'
import type { Task } from '../data/schema'
import PlusCircledIcon from '~icons/radix-icons/plus-circled' import PlusCircledIcon from '~icons/radix-icons/plus-circled'
import CheckIcon from '~icons/radix-icons/check' import CheckIcon from '~icons/radix-icons/check'
@ -30,8 +31,16 @@ interface DataTableFacetedFilter {
const props = defineProps<DataTableFacetedFilter>() const props = defineProps<DataTableFacetedFilter>()
function repeat<T>(array: T[], times: number) {
return Array.from({ length: times }, () => array).flat()
}
const facets = computed(() => props.column?.getFacetedUniqueValues()) const facets = computed(() => props.column?.getFacetedUniqueValues())
const selectedValues = computed(() => new Set(props.column?.getFilterValue() as string[])) const selectedValues = computed(() => new Set(props.column?.getFilterValue() as string[]))
const options = computed(() => repeat(props.options, 100))
const { list, containerProps, wrapperProps } = useVirtualList(options, {
itemHeight: 32,
})
</script> </script>
<template> <template>
@ -73,48 +82,53 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="w-[200px] p-0" align="start"> <PopoverContent class="w-[200px] p-0" align="start">
<Command <Command>
:filter-function="(list: DataTableFacetedFilter['options'], term) => list.filter(i => i.label.toLowerCase()?.includes(term)) "
>
<CommandInput :placeholder="title" /> <CommandInput :placeholder="title" />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>
<CommandGroup> No results found.
<CommandItem </CommandEmpty>
v-for="option in options" <div v-bind="containerProps" class="max-h-52">
:key="option.value" <div v-bind="wrapperProps">
:value="option" <CommandGroup>
@select="() => { <CommandItem
const isSelected = selectedValues.has(option.value) v-for="option in list"
if (isSelected) { :key="option.index"
selectedValues.delete(option.value) style="height: 32px"
} :value="option.data"
else { @select="() => {
selectedValues.add(option.value) const isSelected = selectedValues.has(option.data.value)
} if (isSelected) {
const filterValues = Array.from(selectedValues) selectedValues.delete(option.data.value)
column?.setFilterValue( }
filterValues.length ? filterValues : undefined, else {
) selectedValues.add(option.data.value)
}" }
> const filterValues = Array.from(selectedValues)
<div column?.setFilterValue(
:class="cn( filterValues.length ? filterValues : undefined,
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary', )
selectedValues.has(option.value) }"
? 'bg-primary text-primary-foreground' >
: 'opacity-50 [&_svg]:invisible', <div
)" :class="cn(
> 'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
<CheckIcon :class="cn('h-4 w-4')" /> selectedValues.has(option.data.value)
</div> ? 'bg-primary text-primary-foreground'
<component :is="option.icon" v-if="option.icon" class="mr-2 h-4 w-4 text-muted-foreground" /> : 'opacity-50 [&_svg]:invisible',
<span>{{ option.label }}</span> )"
<span v-if="facets?.get(option.value)" class="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs"> >
{{ facets.get(option.value) }} <CheckIcon :class="cn('h-4 w-4')" />
</span> </div>
</CommandItem> <component :is="option.data.icon" v-if="option.data.icon" class="mr-2 h-4 w-4 text-muted-foreground" />
</CommandGroup> <span>{{ option.data.label }}</span>
<span v-if="facets?.get(option.data.value)" class="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
{{ facets.get(option.data.value) }}
</span>
</CommandItem>
</CommandGroup>
</div>
</div>
<template v-if="selectedValues.size > 0"> <template v-if="selectedValues.size > 0">
<CommandSeparator /> <CommandSeparator />

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { type Table } from '@tanstack/vue-table' import type { Table } from '@tanstack/vue-table'
import { computed } from 'vue' import { computed } from 'vue'
import { type Task } from '../data/schema' import type { Task } from '../data/schema'
import { priorities, statuses } from '../data/data' import { priorities, statuses } from '../data/data'
import DataTableFacetedFilter from './DataTableFacetedFilter.vue' import DataTableFacetedFilter from './DataTableFacetedFilter.vue'