-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
Copy pathFilters.vue
103 lines (92 loc) · 2.92 KB
/
Filters.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<script setup>
import { createReusableTemplate, useMediaQuery, useUrlSearchParams, watchDebounced } from '@vueuse/core'
import { safeDestr } from 'destr'
import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { VList } from 'virtua/vue'
const emit = defineEmits(['change'])
const [TriggerTemplate, TriggerComponent] = createReusableTemplate()
const [FilterTemplate, FilterComponent] = createReusableTemplate()
const isDesktop = useMediaQuery('(min-width: 640px)')
const links = ref([])
const isOpen = ref(false)
const selectedLinks = ref([])
async function getLinks() {
links.value = await useAPI('/api/link/search')
}
onMounted(() => {
getLinks()
})
watchDebounced(selectedLinks, (value) => {
emit('change', 'slug', value.join(','))
}, { debounce: 500, maxWait: 1000 })
function restoreFilters() {
const searchParams = useUrlSearchParams('history')
if (searchParams.filters) {
const filters = safeDestr(searchParams.filters)
if (filters.slug) {
selectedLinks.value = filters.slug.split(',')
}
}
}
onBeforeMount(() => {
restoreFilters()
})
</script>
<template>
<TriggerTemplate>
<Button
variant="outline"
role="combobox"
:aria-expanded="isOpen"
class="flex justify-between px-3 w-full sm:w-48"
>
<div class="flex-1 text-left truncate" :class="selectedLinks.length ? 'text-foreground' : 'text-muted-foreground'">
{{ selectedLinks.length ? selectedLinks.join(', ') : $t('dashboard.filter_placeholder') }}
</div>
<ChevronsUpDown class="ml-2 w-4 h-4 opacity-50 shrink-0" />
</Button>
</TriggerTemplate>
<FilterTemplate>
<Command v-model="selectedLinks" multiple>
<CommandInput :placeholder="selectedLinks.length ? selectedLinks.join(', ') : $t('dashboard.filter_placeholder')" />
<CommandEmpty>No link found.</CommandEmpty>
<CommandList :class="{ 'max-h-none': !isDesktop }">
<CommandGroup>
<VList
v-slot="{ item: link }"
:data="links"
:style="{ height: isDesktop ? '292px' : '420px' }"
>
<CommandItem
:value="link.slug"
>
<Check
:class="cn(
'mr-2 h-4 w-4',
selectedLinks.includes(link.slug) ? 'opacity-100' : 'opacity-0',
)"
/>
{{ link.slug }}
</CommandItem>
</VList>
</CommandGroup>
</CommandList>
</Command>
</FilterTemplate>
<Popover v-if="isDesktop" v-model:open="isOpen">
<PopoverTrigger as-child>
<TriggerComponent />
</PopoverTrigger>
<PopoverContent class="p-0 w-full sm:w-48">
<FilterComponent />
</PopoverContent>
</Popover>
<Drawer v-else v-model:open="isOpen">
<DrawerTrigger as-child>
<TriggerComponent />
</DrawerTrigger>
<DrawerContent class="h-[500px]">
<FilterComponent />
</DrawerContent>
</Drawer>
</template>