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)