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

useDnD и мониторы

Состояние драга и его события в любом месте под провайдером — и сам провайдер, переданный другому Vue-приложению.

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

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

Live

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

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

<template>
  <DnDProvider>
    <div class="demo">
      <TokenScene />
      <StateInspector />
    </div>
  </DnDProvider>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 16px;
}
</style>

useDnD

function useDnD(): DnDState;

Состояние драга только для чтения — для своего UI (подсветка подходящих целей, спиннеры, прогресс).

const { status, active, over, selection, cancel } = useDnD();
const dragging = computed(() => status.value === 'dragging');

Состояние драга провайдера, реактивное. active и over очищаются, как только бросок решён, пока оверлей ещё приземляется.

Состояние драга заканчивается, как только бросок решён: active, over и pointer равны null (и зоны сброса перестают принимать), пока оверлей ещё анимируется к своему месту. Производный UI никогда не смешивает старый драг с новыми данными. Пока асинхронный onBeforeDrop принимает решение, состояние остаётся таким, каким было в момент отпускания, но страница выходит из режима драга: возвращаются её собственный курсор и выделение текста (диалог подтверждения работает как обычно), а оверлей ждёт там, где его отпустили.

status равен 'dragging', пока идёт драг, иначе 'dropping', пока решается бросок или ещё приземляется оверлей, иначе 'idle' (не считая 'activating' и 'selecting'). Новый драг не ждёт приземлений: он начинается сразу, а они завершаются вокруг него.

Readonly<Ref<DnDStatus>>

Что делает провайдер (см. DnDStatus).

Readonly<Ref<Active | null>>

Текущий драг; null, если драга нет, и сразу, как только его бросок решён.

Readonly<Ref<Over | null>>

Цель под драгом; null, если драг ни над чем.

Readonly<Ref<Coordinates | null>>

Указатель в координатах вьюпорта, после модификаторов; null без драга.

Readonly<Ref<Coordinates | null>>

Где сейчас находится левый верхний угол перетаскиваемого блока в over: DnDEvent.position при каждом движении. null над пустым местом. Компонент оверлея читает его, чтобы показать, куда он приземлится (время в календаре).

{ progress: Readonly<Ref<number>> }

progress: насколько нажатие продвинулось к своему условию активации, от 0 до 1, пока status равен 'activating': например, кольцо, которое заполняется во время долгого нажатия.

Readonly<Ref<boolean>>

Автоскролл прокручивает контейнер.

DnDSelection

Выделенные перетаскиваемые элементы (см. DnDSelection).

() => void

Отменяет драг (он улетает обратно) или ожидающую активацию.

Выделение

Выделенные перетаскиваемые элементы, по id: драг одного из них несёт их все (мульти-драг). Выделение хранится по id и поэтому переживает размонтирование (виртуальные списки).

ReadonlySet<Key>

Выделенные id, реактивно.

(id: Key) => boolean

Выделен ли id.

(...ids: Key[]) => void

Выделяет эти id.

(...ids: Key[]) => void

Снимает выделение с этих id.

(id: Key, force?: boolean) => void

Выделяет id или снимает с него выделение; force: true выделяет, false снимает выделение.

(ids: Iterable<Key>) => void

Выделяет эти id и только их.

() => void

Снимает выделение со всех id.

useDnDMonitor

function useDnDMonitor(monitor: DnDMonitor): void;

Подписывается на события каждого драга под провайдером, пока живёт вызвавший компонент (или effect scope).

useDnDMonitor({
  onDragEnd: (e) => (e.dropped ? save(e) : undefined),
  onBeforeDrop: async (e) => ((await confirm()) ? 'accept' : 'reject'),
});

Ваши мониторы спрашиваются о броске (onBeforeDrop) раньше цели. Данные событий — на странице событий.

Хуки для каждого драга под провайдером (useDnDMonitor).

(event: DnDEvent) => void

Драг начался, после своей активации.

(event: DnDEvent) => void

Драг сдвинулся (не чаще раза за кадр).

(event: DnDEvent) => void

Цель под драгом сменилась.

(event: DnDEvent) => MaybePromise<BeforeDropResult>

Вызывается до того, как бросок применён (см. BeforeDropResult): 'reject' отклоняет его, список id принимает часть элементов.

(event: DnDEndEvent) => void

Уведомление после броска: сортируемые списки уже вызвали свой onDrop и записали результат.

(event: DnDEvent) => void

Драг отменён или его бросок отклонён: элементы улетают обратно.

(event: DnDEvent) => void

onBeforeDrop ответил 'continue': броска не было, и драг продолжается.

(event: DnDEvent | DnDEndEvent) => void

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

(event: KeyboardEvent, context: KeyboardMoveContext) => MaybePromise<Coordinates | undefined>

Перетаскивание с клавиатуры: следующая позиция указателя для клавиши-стрелки (например, следующий слот сортировки).

С чем вызывается getKeyboardCoordinates для клавиши-стрелки.

Active

Драг.

Coordinates

Где сейчас драг, в координатах вьюпорта.

Coordinates

Единичный вектор нажатой стрелки.

useDnDContext и provideDnDContext

function useDnDContext(): DnDContext;

Провайдер над этим компонентом — как непрозрачный дескриптор для provideDnDContext: он пробрасывает провайдер в shadow root и отдельные Vue-приложения.

function provideDnDContext(app: App, context: DnDContext): void;

Делится провайдером (из useDnDContext()) с другим Vue-приложением, например смонтированным внутри shadow root.

// In a component under the provider:
const context = useDnDContext();
// A second Vue app (a widget mounted in a shadow root) joins the same drag:
const widget = createApp(Widget);
provideDnDContext(widget, context);

Провайдер в том виде, в каком его возвращает useDnDContext(), — чтобы передать его в provideDnDContext(): тогда другое Vue-приложение (например, смонтированное в shadow root) работает с тем же провайдером. Непрозрачный: у него нет публичных членов, поэтому он не меняется вместе с внутренностями библиотеки.