Документация/API

makeSortableList

Делает контейнер сортируемым списком — черновик перестраивается во время драга и записывается обратно при броске.

Редактировать страницу

Страница переведена ИИ и может содержать неточности. Читать английский оригинал

function makeSortableList<T>(  target: MaybeElementRef,  options: SortableListOptions<T>,): SortableListReturn<T>;

Делает контейнер сортируемым списком. Рендерите возвращённый items (порядок черновика), а не свой массив: бросок записывается обратно в options.items после onDrop, если только event.preventDefault() не оставляет event.result вам.

const list = useTemplateRef<HTMLElement>('list');
const { items } = makeSortableList(list, { items: tasks, group: 'board' });

Строки списка вызывают makeSortableItem. См. сортируемые списки и списки с общими элементами.

Live

Загружаем демо…

<script setup lang="ts">
import { ref } from 'vue';
import { DnDProvider } from '@vue-dnd-kit/core';
import Segmented from './Segmented.vue';
import Tiles from './Tiles.vue';

const strategy = ref<'vertical' | 'horizontal' | 'grid'>('grid');
const placeholder = ref<'item' | 'indicator'>('item');
const behavior = ref<'sort' | 'swap'>('sort');
</script>

<template>
  <DnDProvider>
    <div class="demo">
      <div class="switches">
        <Segmented v-model="strategy" name="strategy" :options="['vertical', 'horizontal', 'grid']" />
        <Segmented v-model="placeholder" name="placeholder" :options="['item', 'indicator']" />
        <Segmented v-model="behavior" name="behavior" :options="['sort', 'swap']" />
      </div>
      <code class="call">makeSortableList(el, { strategy: '{{ strategy }}', placeholder: '{{ placeholder }}', behavior: '{{ behavior }}' })</code>
      <Tiles :strategy="strategy" :placeholder="placeholder" :behavior="behavior" />
    </div>
  </DnDProvider>
</template>

<style scoped>
.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  max-width: 100%;
}

.switches {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
}

.call {
  max-width: 100%;
  padding: 6px 10px;
  overflow-x: auto;
  border-radius: 8px;
  background: color-mix(in srgb, currentColor 6%, transparent);
  font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
  white-space: nowrap;
  color: #42b883;
}
</style>

Опции

Опции makeSortableList. Компонент может создавать несколько списков (и деревьев): каждый элемент, отрендеренный в нём, относится к самому внутреннему из них, чей DOM-элемент его содержит.

Опции принимают значение, ref или геттер (overlay, overOverlay, dropAnimation, overDropAnimation, animation — значение или ref) и читаются, когда они нужны списку, поэтому изменение вступает в силу не позже следующего драга. Исключения: id и group читаются один раз, при создании списка; items — это сама модель; функции (getKey, filter, clone, createItem, колбэки) передаются как есть.

SortableItems<T>

Источник истины. Записываемый ref получает новый массив при броске, сразу после onDrop. Источник только для чтения (геттер, readonly ref, computed) так не может: вызовите event.preventDefault() и примените event.result сами. Если оставить такой бросок библиотеке, он ничего не изменит, и dev-сборки об этом предупредят.

(item: T) => Key

По умолчанию — item.id или сам элемент для примитивов. Также задаёт ключ для makeSortableItem({ item }).

(item: T) => boolean

Показывает только элементы, для которых возвращает true (поиск, тег): они в items (возвращаемом), и драг сортирует среди них. Остальные сохраняют свои места в источнике: бросок записывает весь массив, и каждый скрытый элемент по-прежнему стоит после показанного, за которым шёл (после ближайшего оставшегося на месте, если тот двигался). Реактивна: то, что она читает (ref с запросом), применяется сразу. e.to.index и activeIndex считают показанные элементы.

Key

По умолчанию — useId(), совпадает при рендере на сервере и на клиенте. Читается один раз.

stringзначение · ref · геттер

Название списка для людей: тексты для скринридеров называют его вместо id (position 2 of 5 in Garden).

unknownзначение · ref · геттер

Любые сведения о списке как о цели: события передают их как over.data, пока над ним драг.

ActivationOptionsзначение · ref · геттер

Как нажатие на элементы этого списка начинает драг, вместо activation провайдера (собственная опция элемента важнее). Читается в начале нажатия.

Componentзначение · ref

Компонент оверлея для элементов этого списка (собственный overlay элемента важнее). Получает OverlayProps и сохраняется на весь драг, в том числе над другими списками, если только у цели под драгом нет собственного оверлея (overOverlay). Ref, переключённый посреди драга, сразу его меняет.

Componentзначение · ref

Что показывает оверлей, пока над этим списком драг, который список принимает, из какого бы списка он ни пришёл: компонент, который получает OverlayProps и собственный оверлей драга как слот по умолчанию (см. DroppableOptions.overOverlay). Читается на лету.

DropAnimationзначение · ref

Как приземляется оверлей, когда заканчивается драг элементов этого списка, вместо dropAnimation провайдера (собственная у элемента важнее; у цели — важнее обеих). Читается при броске.

DropAnimationзначение · ref

Как приземляется оверлей, когда его бросают в этот список, из какого бы списка он ни пришёл: важнее dropAnimation перетаскиваемого элемента и провайдера. Читается при броске.

Modifier[]значение · ref · геттер

Модификаторы для драгов элементов этого списка; применяются после modifiers провайдера (собственные modifiers элемента важнее). Например, [restrictToVerticalAxis], чтобы элементы вертикального списка не сходили со своей оси. Читается в начале драга.

'x' | 'y'значение · ref · геттер

Фиксирует драги элементов этого списка по одной оси: 'y' для вертикального списка, 'x' для горизонтального (собственный axis элемента важнее). Фиксация идёт после модификаторов, поэтому держится, что бы они ни делали. Читается в начале драга.

string | SortableGroupOptions<T>

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

AcceptModeзначение · ref · геттер

Мульти-драг элементов нескольких типов: принять (group.put) нужно все элементы ('all', по умолчанию) или некоторые ('some').

SortableStrategyзначение · ref · геттер

По умолчанию 'vertical'.

SortableBehaviorзначение · ref · геттер

'swap': перетаскиваемый элемент на лету меняется местами с тем, на который наведён; если вернуться на него или уйти со списков, обмен откатывается. Списки одной группы, где оба используют обмен, обмениваются элементами между собой: элемент под драгом переезжает на место перетаскиваемого, поэтому он должен уметь покидать свой список (group.pull: true) и попадать в список перетаскиваемого (group.put). Мульти-драг вместо этого сортирует. По умолчанию 'sort'.

SortablePlaceholderзначение · ref · геттер

'indicator': список не двигается, а indicator показывает, куда пойдёт бросок. По умолчанию 'item'.

MultipleModeзначение · ref · геттер

Режим мульти-драга для драгов, которые начинаются в этом списке. По умолчанию 'group'. Читается в начале драга.

booleanзначение · ref · геттер

false: элементы нельзя переставлять внутри этого списка (палитры).

(item: T) => T

Создаёт копии (pull: 'clone', клавиша-модификатор копирования). Обязательна для копирования.

(active: Active) => T | T[]

Принимает обычные перетаскиваемые элементы извне: создаёт элемент для вставки. С клавиатуры стрелка в сторону списка забирает перетаскиваемый элемент в список (на его уровне, как при переносе между списками), и созданный элемент дальше движется по слотам, как любой другой.

Для драга извне страницы (external) её спрашивают дважды: пока драг удерживают — об элементе, который показывает, куда он попадёт (тогда известны только active.external.fileTypes и types), и при броске, с его данными (active.external.ready), — о том, что вставляется. Массив элементов вставляется весь туда, где был слот: по элементу на файл.

ExternalAcceptзначение · ref · геттер

Принимает драги извне страницы: файлы, ссылки, текст (см. ExternalAccept). Список освобождает для них место, как для обычного перетаскиваемого элемента, с createItem — она ему нужна. Элементам его группы, перетащенным из другого окна приложения (native), не нужна ни одна из этих опций: они попадают в список как есть. Значение, ref или геттер, читается вживую.

DropOutsideзначение · ref · геттер

Бросок вне всех целей: оставить элементы там, где плейсхолдер, отменить или удалить их. По умолчанию 'placeholder' ('cancel' при placeholder: 'indicator'). Читается при броске.

LayoutAnimationзначение · ref

FLIP-анимация этого списка; по умолчанию — animation провайдера. Значение или ref (функция — это своя анимация).

AutoAnimationзначение · ref · геттер

Анимирует изменения items, сделанные не драгом: добавленные, удалённые, перемещённые и отфильтрованные элементы (см. AutoAnimation). По умолчанию — animateChanges провайдера; выключено, если там не задано. Читается при изменении items.

booleanзначение · ref · геттер

Собственный размер списка следует за его элементами: когда драг приносит элемент в список или уносит из него (а также при броске и при изменениях, которые показывает animateChanges), список растёт или сжимается до нового размера с длительностью и easing из animation, а не скачком, и то, что вокруг, сдвигается вместе с ним. По умолчанию true; false оставляет скачок. Читается при каждом изменении.

boolean | AutoScrollOptionsзначение · ref · геттер

Переопределяет autoScroll провайдера для скролл-контейнера этого списка. Читается в начале драга.

MaybeElementRef

Элемент, который принимает броски для списка; по умолчанию — сам элемент списка. Например, вся колонка вокруг короткого списка карточек: тогда карточка, отпущенная ниже последней, всё равно попадает в список (в его конец). Элементы остаются в элементе списка.

booleanзначение · ref · геттер

true: его элементы нельзя перетащить, и он не принимает элементы.

( event: SortableDropEvent<T>, ) => MaybePromise<BeforeDropResult>

Вызывается перед броском в этот список и перед тем, как dropOutside: 'remove' уберёт его элементы: false отклоняет бросок (элементы улетают обратно), список ключей принимает только эти элементы. Может быть асинхронным: оверлей ждёт там, где его отпустили. Ошибка тоже отклоняет бросок. Про result и preventDefault — см. SortableDropEvent.

(event: SortableDropEvent<T>) => void

Вызывается, когда бросок в этот список принят, но ещё не записан: в items всё ещё старый порядок, в event.result — новый. Синхронный: запись следует сразу после возврата, если не был вызван event.preventDefault(). Также вызывается у списка, откуда пришли элементы, когда их удаляют (to.index равен -1).

Возвращает

Что возвращает makeSortableList.

Key

Id списка: опция id или созданный для него id.

ComputedRef<T[]>

Порядок черновика: рендерите его, а не исходный массив.

ComputedRef<boolean>

Над списком драг.

ComputedRef<boolean>

Что-то перетаскивается, и этот список это принимает.

ComputedRef<SortableIndicator | null>

Режим индикатора: куда в этом списке попадёт бросок; null, если не сюда.

ComputedRef<Key | null>

Строка, которую виртуализатор должен держать смонтированной: слот, пока он в этом списке (копия, созданный элемент), иначе перетаскиваемый элемент; null без драга.

ComputedRef<number>

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

ComputedRef<Size | null>

Размер перетаскиваемого элемента в начале драга: например, для размера виртуальной строки, которую он занимает.

() => void

Анимирует изменения раскладки, которые вызвала не библиотека (например, повторный замер виртуализатора).

Группы

Группа списка: списки, которые обмениваются элементами, и могут ли элементы покидать этот список и попадать в него.

string

Списки и деревья с одинаковым именем обмениваются элементами.

boolean | 'clone'

Могут ли элементы покидать этот список: перемещением, копированием ('clone', палитра) или никак.

boolean | string | string[] | ((context: SortablePutContext<T>) => boolean)

Могут ли элементы попадать в этот список: true (по умолчанию), false, тип или типы, которые должны быть у элементов (как accept цели: с acceptMode — все или некоторые), или функция от драга.

С чем вызывается group.put, заданный функцией: элементы, которые вошли бы в список.

Key[]

Ключи элементов.

T[]

Элементы (для обычного перетаскиваемого элемента — элемент, созданный createItem).

string[]

Типы элементов, вместе.

Key | null

Список-источник, null для внешних перетаскиваемых элементов.

Key

Id списка, который спрашивают.

Событие броска

Что получают onBeforeDrop и onDrop.

Бросок на сортируемый список или дерево. Его хуки выполняются по порядку: onBeforeDrop принимает решение, onDrop получает уведомление, затем действие по умолчанию записывает result в items каждого списка, который в нём указан, и оверлей приземляется. preventDefault() в любом из хуков пропускает эту запись, чтобы вы применили result сами.

Key[]

Ключи перетаскиваемых элементов.

T[]

Элементы, которые приземляются: перетаскиваемые, их копии (mode: 'copy') или элемент, созданный createItem ('insert').

Key[]

Ключи, которые проходят: все, если только список или onBeforeDrop не принимает лишь часть.

Key[]

Ключи, которые улетают обратно, туда, где были.

{ list: Key | null; index: number }

Где начался драг: список и индекс активного элемента. list равен null для обычного перетаскиваемого элемента извне.

{ list: Key; index: number }

index равен -1, если ничего не приземлилось: элементы покинули свои списки (e.source.remove(), dropOutside: 'remove').

boolean

Бросок остаётся в списке, где начался драг.

DragMode

Что делает бросок (см. DragMode).

{ key: Key; item: T; from: { list: Key; index: number }; to: { list: Key; index: number } }

mode: 'swap': элемент, с которым обменялся перетаскиваемый. Он сдвинулся в обратную сторону: from — место, которое занял перетаскиваемый элемент (to выше), to — место, откуда перетаскиваемый элемент начал (from выше); между списками — в список перетаскиваемого элемента.

ReadonlyMap<Key, T[]>

Новые элементы каждого списка, который меняет бросок, по id списка: ровно то, что запись по умолчанию кладёт в их items, в виде новых массивов. Сюда входят перемещения, копии, созданные элементы, частичный приём, обе стороны обмена и удалённые элементы (e.source.remove() в цели внутри элемента, зона-корзина, dropOutside: 'remove'); списка, который бросок оставляет как есть, здесь нет. Запись дерева — новый массив корней: узлы на изменённых путях — копии (withChildren), нетронутые поддеревья общие, ваши узлы никогда не мутируются. В onBeforeDrop это бросок в том виде, в каком он есть до ответа хука (если вернуть часть ключей, он изменится); onDrop получает окончательный.

() => void

Пропускает запись по умолчанию — во все списки из result (запись только в некоторые потеряла бы или задублировала элементы). Примените result сами в onDrop или до того, как разрешится асинхронный onBeforeDrop, — и оверлей приземлится уже в новом порядке. Если нигде его не применить, списки остаются как были, а оверлей улетает обратно (копии и созданные элементы, у которых ещё нет места, растворяются).

boolean

preventDefault() был вызван — в onBeforeDrop или onDrop.

ExternalData

Драг извне страницы (external): что он нёс, вместе с данными (бросок состоялся).

Индикатор

Что содержит indicator в режиме placeholder: 'indicator'.

Куда попадёт бросок при placeholder: 'indicator': список не двигается, линию рисуете вы.

Key

Id списка (или дерева), куда попадёт бросок.

number

Позиция вставки в списке в том виде, как он отрендерен.

Key | null

Элемент, к которому привязан индикатор; null в пустом списке.

'before' | 'after' | 'inside' | 'swap'

С какой стороны от key рисуется линия. 'inside' (деревья): драг наведён на середину строки, которая принимает перетаскиваемые узлы, и бросок идёт в неё первым дочерним узлом. 'swap' (behavior: 'swap'): key — элемент, с которым бросок поменяет местами; отмечайте сам элемент.

number

Деревья: глубина, которую получат брошенные узлы (у корней 0), например чтобы сдвинуть линию отступом.

Key | null

Деревья: родитель, которого получат брошенные узлы; null на корневом уровне.

Значения

Источник элементов сортируемого списка. Записываемый ref: библиотека записывает в него каждый бросок. Источник только для чтения (геттер, readonly ref, computed: иммутабельный стор, серверное состояние, стор, который меняется только через экшены): каждый бросок вы применяете сами из event.result после event.preventDefault() (см. SortableDropEvent).

type SortableItems = Ref<T[]> | Readonly<Ref<readonly T[]>> | (() => readonly T[])

Как список раскладывает свои элементы: колонкой ('vertical'), строкой ('horizontal') или сеткой с переносом ('grid').

type SortableStrategy = 'vertical' | 'horizontal' | 'grid'

Что происходит при наведении на элемент: 'sort' освобождает место (элементы между ними сдвигаются), 'swap' меняет два элемента местами.

type SortableBehavior = 'sort' | 'swap'

Как драг показывает, куда он попадёт: 'item' — слот перетаскиваемого элемента движется по списку; 'indicator' — список остаётся как есть, а indicator говорит, куда попадёт бросок (линию рисуете вы).

type SortablePlaceholder = 'item' | 'indicator'

Что делают остальные выделенные элементы при мульти-драге:

  • 'group': покидают свои места и едут вместе с активным элементом сплошным рядом плейсхолдеров, поэтому при наведении список уже показывает результат броска;
  • 'gaps': остаются на местах до броска;
  • 'stack': схлопываются в оверлей.

Стандартный оверлей показывает перетаскиваемые элементы столбиком вдоль списка ('group', 'gaps'; строкой в горизонтальных списках, стопкой за активным в сетках) или активный элемент со счётчиком ('stack').

type MultipleMode = 'group' | 'gaps' | 'stack'

Что делает бросок вне всех целей: 'placeholder' оставляет элементы там, где их слот, 'cancel' возвращает их обратно, 'remove' убирает их из списка.

type DropOutside = 'placeholder' | 'cancel' | 'remove'