Skip to content

VirtualSelect

Поле выбора с поиском на основе виртуализированного выпадающего списка. Обрабатывает сотни тысяч опций без нагрузки на DOM.

Пропы

options

T[]

Объекты опций.

modelValue

T | null · по умолчанию: null

Текущая выбранная опция.

labelField

string · по умолчанию: 'label'

Поле, отображаемое в триггере и выпадающем списке.

valueField

string · по умолчанию: 'value'

Поле, используемое для сравнения на равенство — также используется как ключ строки выпадающего списка, поэтому должно быть уникальным среди options.

placeholder

string · по умолчанию: 'Select an option…'

Текст плейсхолдера.

disabled

boolean · по умолчанию: false

Отключить select.

clearable

boolean · по умолчанию: false

Показывать кнопку очистки, когда значение выбрано.

searchable

boolean · по умолчанию: true

Показывать поле поиска при открытии выпадающего списка.

estimatedItemSize

number · по умолчанию: 36

Оценочная высота строки опции.

maxVisibleRows

number · по умолчанию: 8

Максимум строк, показываемых до прокрутки списка.

motionBlur

boolean · по умолчанию: false

Применить CSS-размытие, масштабируемое по скорости прокрутки при быстрой прокрутке.

remote

boolean · по умолчанию: false

Пропустить клиентскую фильтрацию — options рендерится как есть; вы обновляете его сами в ответ на search.

debounceMs

number · по умолчанию: 0

Задержка перед срабатыванием события search после остановки ввода (объединяет нажатия клавиш для запроса к серверу). 0 сохраняет текущее синхронное поведение.

isLoading

boolean · по умолчанию: false

Показывает слот #loading в выпадающем списке, проверяется до слота #empty, чтобы выполняющийся удалённый поиск не мигал "No options".

Emits

update:modelValue

Payload: T | null

change

Payload: T | null

Payload: string

Слоты

default

Область видимости: { option: T, index: number, selected: boolean }

Кастомная строка опции.

empty

Область видимости: нет

Показывается, когда filteredOptions пуст и загрузки нет.

loading

Область видимости: нет

Показывается, пока isLoading истинно, вместо списка опций.

Публичный API

open()

Открывает выпадающий список.

close()

Закрывает выпадающий список.

getScrollElement()

Возвращает элемент, который реально прокручивается — сочетайте с VirtualScrollbar.

ts
selectRef.value?.open()
selectRef.value?.close()
selectRef.value?.getScrollElement() // сочетайте с VirtualScrollbar

Пример

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VirtualSelect } from 'vue-virtual-scroller-kit'

interface Country {
  value: string
  label: string
  flag: string
}

const countries: Country[] = [
  { value: 'us', label: 'United States', flag: '🇺🇸' },
  { value: 'de', label: 'Germany', flag: '🇩🇪' },
  // … сотни других
]

const selected = ref<Country | null>(null)
</script>

<template>
  <VirtualSelect
    v-model="selected"
    :options="countries"
    label-field="label"
    value-field="value"
    clearable
    style="width: 300px"
  >
    <template #default="{ option }"> {{ option.flag }} {{ option.label }} </template>
  </VirtualSelect>
</template>

Асинхронный/удалённый поискoptions заполняется с сервера, фильтрация происходит там, а не на клиенте:

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VirtualSelect } from 'vue-virtual-scroller-kit'

interface Country {
  value: string
  label: string
}

const selected = ref<Country | null>(null)
const results = ref<Country[]>([])
const isLoading = ref(false)
let requestId = 0

async function onSearch(query: string) {
  const id = ++requestId
  isLoading.value = true
  try {
    const res = await fetch(`/api/countries?q=${encodeURIComponent(query)}`)
    const data: Country[] = await res.json()
    if (id !== requestId) return // более новое нажатие клавиши уже запустило другой запрос
    results.value = data
  } finally {
    if (id === requestId) isLoading.value = false
  }
}
</script>

<template>
  <VirtualSelect
    v-model="selected"
    :options="results"
    remote
    :debounce-ms="300"
    :is-loading="isLoading"
    label-field="label"
    value-field="value"
    style="width: 300px"
    @search="onSearch"
  >
    <template #loading>Searching…</template>
  </VirtualSelect>
</template>