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.
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).
<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á.
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.
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.
CollisionDetectionCó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.
LayoutAnimationAnimación FLIP por defecto para las listas ordenables.
DropAnimationCó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.
AutoAnimationanimateChanges por defecto de las listas y los árboles ordenables: los cambios de sus ítems que no vienen de un arrastre. Desactivado por defecto.
boolean | AutoScrollOptionsDesplaza 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.
ActivationOptionsLo 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 | falseLos 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 | HapticsOptionsVibració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 | nullTecla (KeyboardEvent.key) que convierte un movimiento en una copia. 'Alt' por defecto; null la desactiva.
string | HTMLElementDó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 | TransitionPropsCó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> | falseTextos propios para algunos eventos (consulta Announcements); false mantiene en silencio la región viva.
stringCó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.
booleanInyecta 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
| Evento | Payload | Cuándo |
|---|---|---|
drag-start | DnDEvent | Ha empezado un arrastre (tras su activación). |
drag-move | DnDEvent | El puntero se ha movido durante un arrastre (como mucho una vez por frame). |
drag-over | DnDEvent | Ha cambiado el destino bajo el arrastre. |
drag-end | DnDEndEvent | Un drop se ha completado y se ha escrito. |
drag-cancel | DnDEvent | El arrastre se ha cancelado o el drop se ha rechazado. |
drag-resume | DnDEvent | onBeforeDrop ha respondido 'continue': no hubo drop, y el arrastre sigue. |
drag-settle | DnDEndEvent o DnDEvent | El 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).
ActiveEl arrastre.
DragItem[]Todos los ítems arrastrados, empezando por el principal: un solo overlay para todo el multi-drag.
Over | nullEl 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.
booleanSi 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).
DragModeLo que haría un drop ahora (consulta DnDEvent.mode): p. ej., mostrar un más mientras copia.
DropDestination | nullDó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.
numberPí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).
numberZona del borde como fracción del tamaño del contenedor. 0.2 por defecto (limitado a 24–96 px).
numberVelocidad máxima en px por frame de 16 ms.
numberVelocidad 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.
HapticPatternEmpieza el arrastre, tras la pulsación larga. true por defecto.
HapticPatternEl arrastre entra en un destino de drop (una lista, una zona) que lo acepta. false por defecto.
HapticPatternEl drop se completa. false por defecto.
HapticPatternEl 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 | undefinedEmpezó un arrastre.
(event: DnDEvent, context: AnnouncementContext) => string | undefinedEl arrastre pasó sobre otro destino, o a otro puesto de una lista (cuando se queda quieto 500 ms).
(event: DnDEndEvent, context: AnnouncementContext) => string | undefinedEl drop se llevó a cabo, o el arrastre terminó fuera de todos los destinos.
(event: DnDEvent, context: AnnouncementContext) => string | undefinedEl arrastre se canceló o su drop se rechazó.
(event: DnDEvent, context: AnnouncementContext) => string | undefinedonBeforeDrop 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.
AnnouncedPositionEn 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.
numberCuántos ítems lleva el arrastre.
booleanEl 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.