Documentación/API

DnDProvider

El componente que gestiona el arrastre — la entrada, el overlay, la selección, el desplazamiento automático y la región viva — para todo lo que se renderiza dentro de él.

Editar esta página

Esta página la ha traducido una IA y puede contener imprecisiones. Leer el original en inglés

const DnDProvider: Component<DnDProviderProps>;

Gestiona los arrastres de todo lo que se renderiza dentro: la entrada, el overlay, la selección, el desplazamiento automático y los anuncios para lectores de pantalla (consulta DnDProviderProps).

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

<template>
  <DnDProvider :animation="{ duration: 200 }" @drag-end="save">
    <RouterView />
    <template #overlay="{ items, over, accepted }">
      <!-- optional: a custom overlay for every drag -->
    </template>
  </DnDProvider>
</template>

Todo composable debe ejecutarse en un componente renderizado dentro de un provider. El objetivo es un único provider para toda la app: las partes de la página reciben sus propias reglas con makeDragArea. Consulta la guía del provider.

La mayoría de las props se leen cuando el provider las necesita: cambia una y el siguiente arrastre la usará.

Live

Cargando la demo…

<script setup lang="ts">
import { computed, ref } from 'vue';
import { DnDProvider, type ActivationOptions } from '@vue-dnd-kit/core';
import PropRow from './PropRow.vue';
import DensityList from './DensityList.vue';

const ACTIVATIONS: Record<string, ActivationOptions | undefined> = {
  default: undefined,
  '{ mouse: { delay: 300 } }': { mouse: { delay: 300, tolerance: 5 } },
};

const overlayFit = ref<'slot' | 'source'>('slot');
const activation = ref('default');
const keyboard = ref<'true' | 'false'>('true');
const copyModifier = ref<'Alt' | 'Shift' | 'null'>('Alt');

// The provider reads its props when it needs them: a change applies to the next drag.
const props = computed(() => ({
  overlayFit: overlayFit.value,
  activation: ACTIVATIONS[activation.value],
  // `false` turns keyboard drags off; an object sets their keys and steps.
  keyboard: keyboard.value === 'true' ? undefined : (false as const),
  copyModifier: copyModifier.value === 'null' ? null : copyModifier.value,
}));

const compact = ref([
  { id: 'copy', title: 'Write copy' },
  { id: 'login', title: 'Fix login' },
]);
const roomy = ref([
  { id: 'sprint', title: 'Plan the sprint' },
  { id: 'review', title: 'Review the PR' },
]);
</script>

<template>
  <DnDProvider v-bind="props">
    <div class="demo">
      <div class="props" role="group" aria-label="DnDProvider props">
        <PropRow v-model="overlayFit" name="overlay-fit" :options="['slot', 'source']" />
        <PropRow v-model="activation" name=":activation" :options="Object.keys(ACTIVATIONS)" />
        <PropRow v-model="keyboard" name=":keyboard" :options="['true', 'false']" />
        <PropRow v-model="copyModifier" name="copy-modifier" :options="['Alt', 'Shift', 'null']" />
      </div>
      <div class="lists">
        <DensityList v-model="compact" title="Compact" compact />
        <DensityList v-model="roomy" title="Roomy" />
      </div>
    </div>
  </DnDProvider>
</template>

<style scoped>
/* Room for every row in one list: the lists grow downward and nothing moves under the pointer. */
.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  min-height: 430px;
}

.props {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  gap: 6px 14px;
  max-width: 100%;
  padding: 12px 14px;
  border: 1px solid color-mix(in srgb, currentColor 12%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, currentColor 3%, Canvas);
}

.lists {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
}

@media (max-width: 480px) {
  .props {
    grid-template-columns: minmax(0, 1fr);
  }
}
</style>

Dónde se renderiza el overlay

overlayTo saca el overlay de body: a un <dialog> modal, que está por encima de todo lo demás en la página. Desactiva el interruptor y las filas que arrastras desaparecen bajo el dialog. La guía del overlay explica cuándo hace falta.

Live

Cargando la demo…

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

const columns = ref(['Name', 'Status', 'Owner', 'Due date', 'Priority']);

const dialog = useTemplateRef<HTMLDialogElement>('dialog');
const open = ref(false);
const intoDialog = ref(true);

// A modal <dialog> sits on the top layer, above everything in <body>, the overlay
// included. While it is open, the overlay renders inside it.
const overlayTo = computed(() => (open.value && intoDialog.value ? '#columns-dialog' : 'body'));

const show = () => {
  dialog.value?.showModal();
  open.value = true;
};
</script>

<template>
  <DnDProvider :overlay-to="overlayTo">
    <div class="sheet">
      <table>
        <thead>
          <tr>
            <th v-for="column in columns" :key="column">{{ column }}</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="row in 2" :key="row">
            <td v-for="column in columns" :key="column"><span class="skeleton" /></td>
          </tr>
        </tbody>
      </table>
      <button type="button" class="primary" @click="show">Edit columns</button>
    </div>

    <dialog id="columns-dialog" ref="dialog" @close="open = false">
      <h3>Columns</h3>
      <p>Drag to change their order.</p>
      <ColumnList v-model:columns="columns" />
      <footer>
        <label class="switch">
          <input v-model="intoDialog" type="checkbox" />
          Overlay inside the dialog
        </label>
        <button type="button" class="primary" @click="dialog?.close()">Done</button>
      </footer>
    </dialog>
  </DnDProvider>
</template>

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

table {
  width: min(100%, 520px);
  border-collapse: collapse;
  font-size: 12px;
}

th {
  padding: 8px;
  border-bottom: 1px solid color-mix(in srgb, currentColor 15%, transparent);
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}

td {
  padding: 10px 8px;
  border-bottom: 1px solid color-mix(in srgb, currentColor 8%, transparent);
}

.skeleton {
  display: block;
  height: 8px;
  border-radius: 4px;
  background: color-mix(in srgb, currentColor 10%, transparent);
}

button {
  padding: 7px 14px;
  border: 0;
  border-radius: 8px;
  font: 600 12px/1.2 system-ui, sans-serif;
  cursor: pointer;
}

.primary {
  background: #42b883;
  color: #04130c;
}

button:focus-visible {
  outline: 2px solid #42b883;
  outline-offset: 2px;
}

/* No transform on the dialog: it would move the overlay's fixed position. */
dialog {
  width: min(340px, calc(100vw - 32px));
  box-sizing: border-box;
  margin: auto;
  padding: 18px;
  border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  border-radius: 16px;
  background: Canvas;
  color: CanvasText;
  box-shadow: 0 30px 60px -20px rgb(0 0 0 / 0.6);
}

dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}

h3 {
  margin: 0;
  font-size: 15px;
}

p {
  margin: 4px 0 14px;
  font-size: 12px;
  opacity: 0.6;
}

footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 16px;
}

.switch {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
}

.switch input {
  accent-color: #42b883;
}
</style>

Props

Props de DnDProvider. La mayoría se leen cuando el provider las necesita: cambia una y el siguiente arrastre la usa.

CollisionDetection

Cómo se elige el destino bajo el arrastre. Por defecto pointerFirst; otra detección integrada (closestCorners) o la tuya.

Modifier[]

Modificadores para cada arrastre, aplicados primero: los de las áreas de arrastre, los elementos arrastrables y las listas van después.

LayoutAnimation

Animación FLIP por defecto para las listas ordenables.

DropAnimation

Cómo aterriza el overlay al soltar y vuelve a su sitio al cancelar, cuando nada más cercano define una: primero va la overDropAnimation del destino de drop, luego la propia del ítem arrastrado (o la de su lista) y luego esta.

AutoAnimation

animateChanges por defecto de las listas y los árboles ordenables: los cambios de sus ítems que no vienen de un arrastre. Desactivado por defecto.

boolean | AutoScrollOptions

Desplaza los contenedores y la página cuando un arrastre se acerca a sus bordes. Por defecto true; las opciones lo ajustan, false lo desactiva. Las listas, los árboles y las áreas de arrastre pueden sobrescribirlo.

ActivationOptions

Lo que debe hacer una pulsación antes de convertirse en arrastre, por tipo de puntero (consulta ActivationOptions para los valores por defecto). Las áreas de arrastre, las listas, los árboles, los ítems y los elementos arrastrables pueden sobrescribirlo.

KeyboardOptions | false

Los arrastres con teclado: sus teclas y su paso. false los desactiva; Escape sigue cancelando cualquier arrastre.

Sensor[]

Lo que convierte la entrada en arrastres. Por defecto [pointerSensor, keyboardSensor]: quita uno, envuélvelo o sustitúyelo, o añade el tuyo (defineSensor). El sensor de teclado también cancela cualquier arrastre con Escape; keyboard: false desactiva sus arrastres pero mantiene eso. La lista puede cambiar en cualquier momento: los sensores que salen de ella se detienen y cancelan su arrastre.

boolean | HapticsOptions

Vibración para los arrastres táctiles. Por defecto: una corta cuando un arrastre empieza tras la pulsación larga; un objeto añade otras (enter, drop, refuse); false las desactiva todas.

string | null

Tecla (KeyboardEvent.key) que convierte un movimiento en una copia. 'Alt' por defecto; null la desactiva.

string | HTMLElement

Dónde se renderiza el overlay: un selector o un elemento. Por defecto 'body'. Un <dialog> modal queda por encima del resto de la página: los arrastres dentro de uno lo necesitan ahí.

'slot' | 'source'

Tamaño del overlay mientras arrastras. 'slot' (por defecto): sigue al hueco en el que aterrizará el ítem, así que sobre otra lista toma el tamaño de los ítems de esa lista, y el drop no necesita cambiar de tamaño. Cambia de tamaño alrededor del puntero, que se queda en la misma proporción de la caja. 'source': mantiene el tamaño del elemento arrastrado. El contenido del overlay por defecto se vuelve a maquetar con el nuevo tamaño; nunca se escala.

number | 'all'

Multi-drag: cuántos ítems arrastrados reciben un clon al agarrar, el agarrado y los demás junto a él en el orden del drop. 5 por defecto; 'all' clona todos los que están renderizados. El overlay por defecto muestra los clones y cuenta el resto (+N); en un layout propio, DragPreview renderiza su slot para un ítem sin clon. Todos los clones vuelan: de vuelta a su ítem al cancelar, a su hueco en un drop, dentro de la zona en la que entra un drop, uno tras otro. Los clones se toman juntos, con un solo layout por pasada de snapshot para todos, pero cada uno cuesta leer sus estilos: cuidado con los ítems largos en móviles lentos.

boolean | TransitionProps

Cómo cambia el overlay de un componente a otro durante un arrastre (el overOverlay de un destino de drop toma el relevo, se cambia una ref de overlay): true (por defecto) hace un fundido cruzado entre ellos en 150 ms, false los cambia al instante, y un objeto son las props de un <Transition> de Vue alrededor de la raíz del contenido (name, mode, hooks de JS para GSAP o motion). Sin mode: 'out-in' los dos se ven a la vez: dale al que sale position: absolute (.<name>-leave-active) para que se apilen en la caja.

string | Record<string, boolean> | Array<string | Record<string, boolean>>

Clases para el contenido del overlay ([data-dnd-overlay-content]), el elemento que llena la caja del overlay: el que hay que animar como un todo mientras la biblioteca mueve y dimensiona la caja. Una transición en él arranca desde @starting-style al agarrar, y vuelve al aterrizar ([data-dnd-overlay='dropping']): el drop la espera. El CSS normal llega a él fuera de <style scoped> (o con :global(...)), Tailwind con starting: y in-data-[dnd-overlay=dragging]:.

Partial<Announcements> | false

Textos propios para algunos eventos (consulta Announcements); false mantiene en silencio la región viva.

string

Cómo arrastrar con el teclado, leído por los lectores de pantalla para cada elemento arrastrable (su aria-describedby). Por defecto: instrucciones en inglés para Espacio, Enter, las flechas y Escape.

boolean

Inyecta el aspecto por defecto: CSS en capas y con especificidad cero que atenúa los placeholders y los ítems que un multi-drag deja en su sitio (opacity: 0.4). Por defecto true; false deja el aspecto en tus manos. Lo que necesitan los arrastres se inyecta en cualquier caso: sin selección de texto ni anclaje del scroll durante el arrastre (las listas virtuales dependen de ello), el cursor de arrastre (--dnd-cursor) y sin el menú de enlace en una pulsación larga en iOS.

Eventos

EventoPayloadCuándo
drag-startDnDEventHa empezado un arrastre (tras su activación).
drag-moveDnDEventEl puntero se ha movido durante un arrastre (como mucho una vez por frame).
drag-overDnDEventHa cambiado el destino bajo el arrastre.
drag-endDnDEndEventUn drop se ha completado y se ha escrito.
drag-cancelDnDEventEl arrastre se ha cancelado o el drop se ha rechazado.
drag-resumeDnDEventonBeforeDrop ha respondido 'continue': no hubo drop, y el arrastre sigue.
drag-settleDnDEndEvent o DnDEventEl overlay del arrastre ha aterrizado: lo que recibió drag-end o drag-cancel.

Los mismos eventos, más onBeforeDrop, están disponibles en cualquier lugar bajo el provider con useDnDMonitor. Sus payloads están en la página de eventos.

Slot #overlay

Renderiza lo que sigue al puntero, para cada arrastre que no tiene un componente de overlay propio (consulta la guía del overlay). Si no renderiza nada, se vuelve al clon.

Lo que muestra el overlay: las props del slot #overlay del provider y de los componentes de overlay (opción overlay de los elementos arrastrables, los ítems ordenables, las listas y los árboles).

Active

El arrastre.

DragItem[]

Todos los ítems arrastrados, empezando por el principal: un solo overlay para todo el multi-drag.

Over | null

El destino de drop bajo el arrastre. Mientras se anima un drop ya decidido, el destino en el que aterriza (tal como estaba al soltar); null mientras un arrastre cancelado vuelve a su sitio.

boolean

Si over acepta los ítems arrastrados; false cuando no hay nada debajo.

DnDStatus

'dragging', y luego 'dropping' mientras el overlay aterriza (o vuelve a su sitio).

DragMode

Lo que haría un drop ahora (consulta DnDEvent.mode): p. ej., mostrar un más mientras copia.

DropDestination | null

Dónde pondría ahora un drop los ítems arrastrados, en una lista o un árbol ordenables: por ejemplo, para teñir el overlay según la carpeta en la que entraría un arrastre de árbol. Fuera de todos los destinos es el sitio del hueco, donde los deja dropOutside: 'placeholder' (el valor por defecto). null cuando el drop no los pondría en ninguna lista: sobre un destino de drop simple, sobre una lista que los rechaza, fuera con dropOutside: 'cancel' o 'remove'. Mientras se anima un drop ya decidido, el sitio en el que aterriza.

Teclado

Los arrastres con teclado (keyboard del provider): sus teclas y el paso de los arrastres libres.

{ start?: string[]; drop?: string[]; cancel?: string[] }

Las teclas que agarran (start), sueltan y cancelan, como KeyboardEvent.code ('Space', 'KeyW') o .key ('Enter', 'w'). Por defecto: Espacio y Enter agarran y sueltan, Escape cancela. Las flechas siempre mueven.

number

Píxeles por pulsación de flecha en los arrastres libres (no ordenables). 25 por defecto.

Desplazamiento automático

Cómo se desplazan los contenedores cuando un arrastre se acerca a sus bordes (autoScroll del provider).

number

Zona del borde como fracción del tamaño del contenedor. 0.2 por defecto (limitado a 24–96 px).

number

Velocidad máxima en px por frame de 16 ms.

number

Velocidad en px por fotograma con el puntero en el borde del contenedor: más lenta dentro de la zona del borde, más rápida más allá del borde, hasta maxSpeed. Por defecto 10.

Vibración

Cuándo vibran los arrastres táctiles. Solo vibran los que se inician con el dedo; donde el navegador no puede vibrar (Safari en iOS) o lo bloquea (aún no se ha tocado la página), no pasa nada.

HapticPattern

Empieza el arrastre, tras la pulsación larga. true por defecto.

HapticPattern

El arrastre entra en un destino de drop (una lista, una zona) que lo acepta. false por defecto.

HapticPattern

El drop se completa. false por defecto.

HapticPattern

El drop se rechaza (por un destino o por onBeforeDrop) y los ítems vuelven a su sitio. false por defecto.

Una vibración (navigator.vibrate): su duración en ms o un patrón de encendido/apagado. true es la de por defecto (10 ms; [20, 40, 20] para refuse), false o 0 ninguna.

type HapticPattern = boolean | number | number[]

Anuncios

Textos para lectores de pantalla, por evento. Devuelve undefined para el texto integrado, '' para ninguno. context tiene las posiciones que anuncian los textos integrados de las listas ordenables.

Un texto lo dice la región viva del provider (los movimientos, cuando el arrastre se queda quieto 500 ms); undefined deja el evento a la siguiente fuente (los textos de integraciones como las posiciones ordenables, y luego los predeterminados). No se anuncia un movimiento para el destino encontrado al agarrar: aún no se ha movido nada.

(event: DnDEvent, context: AnnouncementContext) => string | undefined

Empezó un arrastre.

(event: DnDEvent, context: AnnouncementContext) => string | undefined

El arrastre pasó sobre otro destino, o a otro puesto de una lista (cuando se queda quieto 500 ms).

(event: DnDEndEvent, context: AnnouncementContext) => string | undefined

El drop se llevó a cabo, o el arrastre terminó fuera de todos los destinos.

(event: DnDEvent, context: AnnouncementContext) => string | undefined

El arrastre se canceló o su drop se rechazó.

(event: DnDEvent, context: AnnouncementContext) => string | undefined

onBeforeDrop respondió 'continue': el arrastre continúa.

De qué trata un anuncio, más allá del evento: para textos propios (otro idioma, otras palabras) que digan lo que dicen los integrados.

AnnouncedPosition

En una lista o un árbol ordenable: dónde están los ítems arrastrados. Al empezar, dónde se agarraron; durante el movimiento, dónde aterrizarían (ninguna posición fuera de las listas); al terminar, dónde aterrizaron, o adónde volvieron.

{ key: Key; label?: string; position: AnnouncedPosition }

Un intercambio: el ítem intercambiado con el arrastrado, y dónde está ahora.

number

Cuántos ítems lleva el arrastre.

boolean

El arrastre copia (group.pull: 'clone', la tecla de copia).

Un sitio en una lista o un árbol ordenable (sus filas visibles): index cuenta desde 0, sobre un total de length.

Key

Id de la lista o del árbol.

string

El label de la lista, el nombre que le da la gente.

number

El puesto, desde 0: los textos integrados dicen index + 1.

number

Cuántos puestos tiene la lista.