Skip to content

Перетаскивание для сортировки

useDraggableList() — composable для drag-to-reorder на pointer-событиях. Показывает призрачный элемент с фиксированным позиционированием, следующий за курсором, анимирует соседние элементы через translateY и автоматически прокручивает контейнер при перетаскивании у краёв.

ts
function useDraggableList<T>(options: UseDraggableListOptions<T>): UseDraggableListReturn

Опции

items

Ref<T[]>

Реактивный массив элементов.

onReorder

(newItems, from, to) => void

Вызывается после успешного сброса с переупорядоченным массивом.

isDragDisabled

(item, index) => boolean

Верните true, чтобы запретить перетаскивание элемента.

scrollContainer

HTMLElement | Ref<HTMLElement | null>

Контейнер для авто-прокрутки при перетаскивании у его краёв.

Возвращаемое значение

dragIndex

Readonly<Ref<number>>

Индекс перетаскиваемого элемента, -1 в состоянии покоя.

overIndex

Readonly<Ref<number>>

Индекс текущей цели сброса.

isDragging

Readonly<Ref<boolean>>

Идёт ли сейчас перетаскивание.

ghostStyle

Readonly<Ref<CSSProperties>>

Стили с фиксированным позиционированием для призрачного элемента.

getItemStyle

(index: number) => CSSProperties

Стили для каждого элемента: opacity:0 для плейсхолдера, translateY для анимируемых соседей.

getItemProps

(index: number) => DraggableItemProps

Пропы для распространения на каждый перетаскиваемый элемент (data-drag-index, onPointerdown, CSS-классы). Перетаскивание полностью основано на pointer-событиях — это никогда не выставляет нативный HTML5-атрибут draggable, поэтому не провоцирует встроенное в браузер поведение drag-ghost/выделения текста.

CSS-классы, добавляемые getItemProps

КлассКогда
vvsk-drag--draggingПрименяется к плейсхолдеру (перетаскиваемому элементу)
vvsk-drag--overПрименяется к текущей цели сброса
vvsk-drag--disabledПрименяется, когда isDragDisabled возвращает true

Авто-прокрутка

Когда указан scrollContainer, список автоматически прокручивается вверх или вниз, когда курсор попадает в зону 60 px у краёв контейнера. Скорость прокрутки пропорциональна расстоянию (максимум 14 px за кадр).

Пример

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

interface Card {
  id: number
  label: string
}

const cards = ref<Card[]>(Array.from({ length: 50 }, (_, i) => ({ id: i, label: `Card ${i + 1}` })))
const listRef = ref<HTMLElement | null>(null)

const { isDragging, dragIndex, ghostStyle, getItemStyle, getItemProps } = useDraggableList({
  items: cards,
  scrollContainer: listRef,
  onReorder: (newItems) => {
    cards.value = newItems
  },
})
</script>

<template>
  <div
    ref="listRef"
    style="display: flex; flex-direction: column; gap: 6px; overflow-y: auto; height: 500px"
  >
    <div
      v-for="(card, index) in cards"
      :key="card.id"
      v-bind="getItemProps(index)"
      :style="getItemStyle(index)"
      class="card"
    >
      ⣿ {{ card.label }}
    </div>
  </div>

  <Teleport to="body">
    <div v-if="isDragging && dragIndex >= 0" class="card card--ghost" :style="ghostStyle">
      ⣿ {{ cards[dragIndex]?.label }}
    </div>
  </Teleport>
</template>

<style>
.card {
  padding: 12px 16px;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 6px;
  cursor: grab;
  user-select: none;
}
.card--ghost {
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.3);
  transform: scale(1.02);
  pointer-events: none;
}
</style>