filters
Filters with async server-side search
Filters with async server-side search
Загрузка превью…
src/filters/c-filters-12.vue
<!--
ПОРТ c-filters-12 ("Filters with async server-side search").
Апстрим гоняет `loadOptions` через `setTimeout(400ms)` на каждый запрос,
симулируя серверный поиск по 10000-строчному "каталогу". Как и в
c-filters-11, попапы значений не раскрываются на статичном скриншоте, так
что `loadOptions` не вызывается при монтировании — но код остаётся
типобезопасным и по смыслу верным апстриму: реальная задержка заменена
синхронной микрозадачей, детерминированный 10000-элементный каталог и
логика "top 50 совпадений" перенесены дословно.
-->
<script setup lang="ts">
import { h, ref } from "vue"
import {
createFilter,
Filters,
type Filter,
type FilterFieldConfig,
type FilterOption,
} from "@/components/reui/filters"
function UserSearchIcon() {
return h(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "size-3.5",
},
[
h("circle", { cx: "10", cy: "7", r: "4" }),
h("path", { d: "M10.3 15H7a4 4 0 0 0-4 4v2" }),
h("circle", { cx: "17", cy: "17", r: "3" }),
h("path", { d: "m21 21-1.9-1.9" }),
]
)
}
const FIRST_NAMES = [
"Alex",
"Bailey",
"Casey",
"Dana",
"Emerson",
"Finley",
"Gray",
"Harper",
"Indira",
"Jordan",
"Kai",
"Logan",
"Morgan",
"Noor",
"Parker",
"Quinn",
"Riley",
"Sasha",
"Taylor",
"Umi",
"Val",
"Wren",
"Xan",
"Yuki",
"Zephyr",
]
const LAST_NAMES = [
"Ahmed",
"Brooks",
"Chen",
"Diaz",
"Evans",
"Ferreira",
"Gupta",
"Hansen",
"Ito",
"Johnson",
"Kowalski",
"Lopez",
"Mensah",
"Novak",
"Okafor",
"Park",
]
// Simulate a large remote directory that cannot be fully prefetched.
const DIRECTORY: FilterOption<string>[] = Array.from({ length: 10000 }, (_, index) => {
const first = FIRST_NAMES[index % FIRST_NAMES.length]
const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length]
return {
value: `user-${index + 1}`,
label: `${first} ${last} #${index + 1}`,
}
})
const demoFields: FilterFieldConfig<string>[] = [
{
key: "assignee",
label: "Assignee",
icon: UserSearchIcon,
type: "multiselect",
// Seed only the initially selected value so its chip stays labelled.
options: [DIRECTORY[0]!],
// Server-side search: return only the top matches for the query. Real
// network delay replaced by a plain microtask (ponytail: no fake timer
// may gate the first render; the async shape of `loadOptions` itself and
// the "top 50 matches" slicing are preserved verbatim).
loadOptions: async (query: string) => {
await Promise.resolve()
const q = query.trim().toLowerCase()
const matches = q
? DIRECTORY.filter((option) => option.label.toLowerCase().includes(q))
: DIRECTORY
return matches.slice(0, 50)
},
},
]
const filters = ref<Filter<string>[]>([createFilter("assignee", "is_any_of", ["user-1"])])
function handleFiltersChange(next: Filter<string>[]) {
filters.value = next
}
</script>
<template>
<div class="flex grow content-start items-start self-start">
<Filters :filters="filters" :fields="demoFields" @change="handleFiltersChange" />
</div>
</template>
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-filters-12.jsonЗависимости реестра
Источник: порт из ReUI (Keenthemes, MIT)