Skip to content

GroupedVirtualList

Рендерит элементы, сгруппированные под сворачиваемыми заголовками секций. Каждая группа может раскрываться или сворачиваться с плавной CSS-анимацией.

Пропы

ПропТипПо умолчаниюОписание
groupsGroupDef<T>[]Массив определений групп
estimatedItemSizenumber50Оценочная высота строк элементов
estimatedGroupHeaderSizenumber40Оценочная высота строк заголовков групп
overscannumber3Дополнительные строки, рендерящиеся вне вьюпорта
keyFieldstring'id'Поле, используемое как ключ элемента
motionBlurbooleanfalseПрименить CSS-размытие, масштабируемое по скорости прокрутки при быстрой прокрутке
stickyGroupHeadersbooleanfalseДержать заголовок текущей группы прикреплённым сверху, пока её элементы прокручиваются под ним (оверлей, см. ниже)

stickyGroupHeaders рендерится как постоянный оверлей над списком, переиспользуя слот #group-header для той группы, что сейчас находится вверху вьюпорта — это не настоящая CSS-строка position: sticky, что здесь невозможно, поскольку виртуализированные строки — position: absolute. Он мгновенно перескакивает к следующей группе (без анимации «выталкивания»).

GroupDef<T>

ts
interface GroupDef<T> {
  key: string // уникальный идентификатор группы
  label: string // отображаемая метка
  items: T[] // элементы в этой группе
  collapsed?: boolean // начальное состояние свёрнутости
}

Слоты

СлотОбласть видимостиОписание
#group-header{ group: GroupDef<T>, toggle: () => void, isCollapsed: boolean }Кастомный заголовок группы
#default{ item: T, index: number, groupKey: string }Содержимое строки элемента
#emptyПоказывается, когда все группы пусты

Emits

Те же, что у VirtualList: scroll, visible-range-change.

Публичный API (GroupedVirtualListExpose)

ts
import type { GroupedVirtualListExpose } from 'vue-virtual-scroller-kit'

const listRef = ref<GroupedVirtualListExpose | null>(null)

listRef.value?.toggle('group-key') // раскрыть/свернуть группу
listRef.value?.scrollTo(index) // прокрутить к плоскому индексу строки
listRef.value?.scrollTo(index, 'start', { behavior: 'smooth' })
listRef.value?.getScrollElement() // сочетайте с VirtualScrollbar

Пример

vue
<script setup lang="ts">
import { ref } from 'vue'
import { GroupedVirtualList } from 'vue-virtual-scroller-kit'
import type { GroupDef, GroupedVirtualListExpose } from 'vue-virtual-scroller-kit'

interface Contact {
  id: number
  name: string
  email: string
}

const groups = ref<GroupDef<Contact>[]>([
  {
    key: 'a',
    label: 'A',
    items: [
      { id: 1, name: 'Alice', email: 'alice@example.com' },
      { id: 2, name: 'Aaron', email: 'aaron@example.com' },
    ],
  },
  {
    key: 'b',
    label: 'B',
    items: [{ id: 3, name: 'Bob', email: 'bob@example.com' }],
    collapsed: true,
  },
])

const listRef = ref<GroupedVirtualListExpose | null>(null)

function expandAll() {
  groups.value = groups.value.map((g) => ({ ...g, collapsed: false }))
}
function collapseAll() {
  groups.value = groups.value.map((g) => ({ ...g, collapsed: true }))
}
</script>

<template>
  <button @click="expandAll">Expand all</button>
  <button @click="collapseAll">Collapse all</button>

  <GroupedVirtualList
    ref="listRef"
    :groups="groups"
    :estimated-item-size="56"
    style="height: 500px"
  >
    <template #group-header="{ group, toggle, isCollapsed }">
      <div class="group-header" @click="toggle">
        {{ isCollapsed ? '▶' : '▼' }} {{ group.label }}
        <span>({{ group.items.length }})</span>
      </div>
    </template>

    <template #default="{ item }">
      <div class="contact-row">
        <strong>{{ item.name }}</strong>
        <span>{{ item.email }}</span>
      </div>
    </template>
  </GroupedVirtualList>
</template>