Documentation/API

DnDProvider

Le composant qui gère le glissement — les entrées, l'overlay, la sélection, le défilement automatique et la région live — pour tout ce qui est rendu à l'intérieur.

Modifier cette page

Cette page a été traduite par IA et peut contenir des inexactitudes. Lire l'original en anglais

const DnDProvider: Component<DnDProviderProps>;

Gère les glissements de tout ce qui est rendu à l'intérieur : les entrées, l'overlay, la sélection, le défilement automatique et les annonces pour lecteurs d'écran (voir 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>

Chaque composable doit s'exécuter dans un composant rendu à l'intérieur d'un provider. L'objectif est un seul provider pour toute l'application : des parties de la page reçoivent leurs propres règles avec makeDragArea. Voir le guide du provider.

La plupart des props sont lues au moment où le provider en a besoin : changez-en une, et le glissement suivant l'utilise.

Live

Chargement de la démo…

<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>

Où l'overlay est rendu

overlayTo sort l'overlay de body : dans un <dialog> modal, qui se trouve au-dessus de tout le reste de la page. Désactivez l'interrupteur, et les lignes que vous faites glisser disparaissent sous le dialog. Le guide de l'overlay explique quand c'est nécessaire.

Live

Chargement de la démo…

<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

Les props de DnDProvider. La plupart sont lues quand le provider en a besoin : changez-en une, et le glissement suivant l'utilise.

CollisionDetection

Comment la cible sous le glissement est choisie. Par défaut pointerFirst ; une autre détection intégrée (closestCorners) ou la vôtre.

Modifier[]

Les modificateurs de chaque glissement, appliqués en premier : ceux des zones de glissement, des éléments déplaçables et des listes viennent après.

LayoutAnimation

Animation FLIP par défaut des listes triables.

DropAnimation

Comment l'overlay atterrit au dépôt et revient à sa place en cas d'annulation, quand rien de plus proche n'en définit une : l'overDropAnimation de la cible de dépôt passe en premier, puis celle propre à l'item glissé (ou celle de sa liste), puis celle-ci.

AutoAnimation

animateChanges par défaut des listes et des arbres triables : les changements de leurs items qui ne viennent pas d'un glissement. Désactivé par défaut.

boolean | AutoScrollOptions

Fait défiler les conteneurs et la page quand un glissement approche de leurs bords. Par défaut true ; des options le règlent, false le désactive. Les listes, les arbres et les zones de glissement peuvent le redéfinir.

ActivationOptions

Ce qu'un appui doit faire avant de devenir un glissement, par type de pointeur (voir ActivationOptions pour les valeurs par défaut). Les zones de glissement, les listes, les arbres, les items et les éléments déplaçables peuvent le redéfinir.

KeyboardOptions | false

Les glissements au clavier : leurs touches et leur pas. false les désactive ; Échap annule toujours n'importe quel glissement.

Sensor[]

Ce qui transforme les entrées en glissements. Par défaut [pointerSensor, keyboardSensor] : retirez-en un, enveloppez-le ou remplacez-le, ou ajoutez le vôtre (defineSensor). Le capteur clavier annule aussi n'importe quel glissement avec Escape ; keyboard: false désactive ses glissements mais conserve ce comportement. La liste peut changer à tout moment : les capteurs qui en sortent s'arrêtent, en annulant leur glissement.

boolean | HapticsOptions

Vibration des glissements tactiles. Par défaut : une vibration courte quand un glissement commence après l'appui long ; un objet en ajoute d'autres (enter, drop, refuse) ; false les désactive toutes.

string | null

La touche (KeyboardEvent.key) qui transforme un déplacement en copie. 'Alt' par défaut ; null désactive.

string | HTMLElement

Où l'overlay est rendu : un sélecteur ou un élément. 'body' par défaut. Un <dialog> modal se place au-dessus du reste de la page : les glissements à l'intérieur en ont besoin là.

'slot' | 'source'

Taille de l'overlay pendant le glissement. 'slot' (par défaut) : il suit l'emplacement où l'item va atterrir ; au-dessus d'une autre liste, il prend donc la taille des items de cette liste, et le dépôt n'a pas besoin de redimensionnement. Il se redimensionne autour du pointeur, qui reste à la même proportion de la boîte. 'source' : il garde la taille de l'élément glissé. La mise en page du contenu de l'overlay par défaut est recalculée à la nouvelle taille ; il n'est jamais mis à l'échelle.

number | 'all'

Multi-drag : combien d'items glissés reçoivent un clone à la saisie, celui qui a été saisi et les autres à côté de lui dans l'ordre du dépôt. 5 par défaut ; 'all' clone tous ceux qui sont rendus. L'overlay par défaut affiche les clones et compte le reste (+N) ; dans une mise en page à vous, DragPreview rend son slot pour un item sans clone. Chaque clone vole : de retour vers son item lors d'une annulation, vers son emplacement lors d'un dépôt, dans la zone où entre un dépôt, l'un après l'autre. Les clones sont pris ensemble, avec une seule mise en page par passe de snapshot pour tous, mais chacun coûte la lecture de ses styles : attention aux items longs sur les téléphones lents.

boolean | TransitionProps

Comment l'overlay passe d'un composant à un autre pendant un glissement (l'overOverlay d'une cible de dépôt prend le relais, une ref d'overlay est changée) : true (par défaut) les enchaîne en fondu en 150 ms, false les échange aussitôt, et un objet donne les props d'une <Transition> Vue autour de la racine du contenu (name, mode, hooks JS pour GSAP ou motion). Sans mode: 'out-in', les deux s'affichent en même temps : donnez à celui qui part position: absolute (.<name>-leave-active) pour qu'ils s'empilent dans la boîte.

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

Classes pour le contenu de l'overlay ([data-dnd-overlay-content]), l'élément qui remplit la boîte de l'overlay : celui à animer d'un bloc pendant que la bibliothèque déplace et dimensionne la boîte. Une transition sur lui part de @starting-style à la saisie, et revient à l'atterrissage ([data-dnd-overlay='dropping']) : le dépôt l'attend. Le CSS simple l'atteint en dehors de <style scoped> (ou avec :global(...)), Tailwind avec starting: et in-data-[dnd-overlay=dragging]:.

Partial<Announcements> | false

Vos propres textes pour certains événements (voir Announcements) ; false rend la région live muette.

string

Comment glisser au clavier, lu par les lecteurs d'écran pour chaque élément déplaçable (son aria-describedby). Par défaut : des instructions en anglais pour Espace, Entrée, les flèches et Échap.

boolean

Injecte l'apparence par défaut : du CSS en couches, de spécificité nulle, qui estompe les placeholders et les items qu'un multi-drag laisse en place (opacity: 0.4). Par défaut true ; false vous laisse l'apparence. Ce dont les glissements ont besoin est injecté dans tous les cas : pas de sélection de texte ni d'ancrage du défilement pendant le glissement (les listes virtuelles en dépendent), le curseur de glissement (--dnd-cursor), et pas de menu de lien lors d'un appui long sur iOS.

Événements

ÉvénementPayloadQuand
drag-startDnDEventUn glissement a commencé (après son activation).
drag-moveDnDEventLe pointeur a bougé pendant un glissement (au plus une fois par frame).
drag-overDnDEventLa cible sous le glissement a changé.
drag-endDnDEndEventUn dépôt a abouti et a été écrit.
drag-cancelDnDEventLe glissement a été annulé ou le dépôt refusé.
drag-resumeDnDEventonBeforeDrop a répondu 'continue' : il n'y a pas eu de dépôt, et le glissement continue.
drag-settleDnDEndEvent ou DnDEventL'overlay du glissement a atterri : ce qu'a reçu drag-end ou drag-cancel.

Les mêmes événements, plus onBeforeDrop, sont disponibles n'importe où sous le provider avec useDnDMonitor. Leurs payloads sont décrits sur la page des événements.

Slot #overlay

Rend ce qui suit le pointeur, pour chaque glissement qui n'a pas son propre composant d'overlay (voir le guide de l'overlay). S'il ne rend rien, le glissement se rabat sur le clone.

Ce que montre l'overlay : les props du slot #overlay du provider et des composants d'overlay (option overlay des éléments déplaçables, des items triables, des listes et des arbres).

Active

Le glissement.

DragItem[]

Tous les items glissés, en commençant par l'item principal : un seul overlay pour tout le multi-drag.

Over | null

La cible de dépôt sous le glissement. Pendant qu'un dépôt décidé s'anime, la cible sur laquelle il atterrit (telle qu'elle était au dépôt) ; null pendant qu'un glissement annulé revient à sa place.

boolean

Si over accepte les items glissés ; false au-dessus de rien.

DnDStatus

'dragging', puis 'dropping' pendant que l'overlay atterrit (ou revient à sa place).

DragMode

Ce que ferait un dépôt maintenant (voir DnDEvent.mode) : par exemple afficher un plus pendant une copie.

DropDestination | null

L'endroit où un dépôt mettrait maintenant les items glissés, dans une liste ou un arbre triable : par exemple pour teinter l'overlay selon le dossier dans lequel irait un glissement dans un arbre. En dehors de toute cible, c'est la place de l'emplacement, où dropOutside: 'placeholder' (la valeur par défaut) les garde. null quand le dépôt ne les mettrait dans aucune liste : au-dessus d'une cible de dépôt simple, au-dessus d'une liste qui les refuse, à l'extérieur avec dropOutside: 'cancel' ou 'remove'. Pendant l'animation d'un dépôt décidé, la place où il atterrit.

Clavier

Les glissements au clavier (keyboard du provider) : leurs touches, et le pas des glissements libres.

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

Les touches qui saisissent (start), déposent et annulent, sous forme de KeyboardEvent.code ('Space', 'KeyW') ou de .key ('Enter', 'w'). Par défaut : Espace et Entrée saisissent et déposent, Échap annule. Les flèches déplacent toujours.

number

Pixels par appui sur une flèche pour les glissements libres (non triables). 25 par défaut.

Défilement automatique

Comment les conteneurs défilent quand un glissement approche de leurs bords (autoScroll du provider).

number

Zone de bord, en fraction de la taille du conteneur. 0.2 par défaut (limitée à 24–96 px).

number

Vitesse maximale en px par frame de 16 ms.

number

La vitesse en px par image quand le pointeur est au bord du conteneur : plus lente dans la zone du bord, plus rapide au-delà du bord, jusqu'à maxSpeed. 10 par défaut.

Vibrations

Quand les glissements tactiles vibrent. Seuls les glissements lancés au toucher vibrent ; là où le navigateur ne peut pas vibrer (Safari sur iOS) ou le bloque (pas encore de tap sur la page), rien ne se passe.

HapticPattern

Le glissement commence, après l'appui long. true par défaut.

HapticPattern

Le glissement entre dans une cible de dépôt (une liste, une zone) qui l'accepte. false par défaut.

HapticPattern

Le dépôt a lieu. false par défaut.

HapticPattern

Le dépôt est refusé (par une cible ou par onBeforeDrop) et les items reviennent à leur place. false par défaut.

Une vibration (navigator.vibrate) : sa durée en ms ou un motif marche/arrêt. true correspond à la vibration par défaut (10 ms ; [20, 40, 20] pour refuse), false ou 0 à aucune.

type HapticPattern = boolean | number | number[]

Annonces

Les textes pour les lecteurs d'écran, par événement. Renvoyez undefined pour le texte intégré, '' pour aucun texte. context contient les positions qu'énoncent les textes intégrés des listes triables.

Un texte est lu par la région live du provider (les déplacements, une fois que le glissement reste immobile 500 ms) ; undefined laisse l'événement à la source suivante (les textes des intégrations, comme les positions triables, puis ceux par défaut). Aucun déplacement n'est annoncé pour la cible trouvée à la saisie : rien n'a encore bougé.

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

Un glissement a commencé.

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

Le glissement est passé sur une autre cible, ou à une autre place dans une liste (une fois immobile pendant 500 ms).

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

Le dépôt a abouti, ou le glissement s'est terminé hors de toute cible.

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

Le glissement a été annulé, ou son dépôt refusé.

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

onBeforeDrop a répondu 'continue' : le glissement continue.

Ce dont parle une annonce, au-delà de l'événement : pour vos propres textes (une autre langue, d'autres mots) qui disent ce que disent les textes intégrés.

AnnouncedPosition

Dans une liste ou un arbre triable : où se trouvent les items glissés. Au début, là où ils ont été saisis ; pendant le déplacement, là où ils atterriraient (aucune position en dehors des listes) ; à la fin, là où ils ont atterri, ou là où ils sont revenus.

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

Un échange : l'item échangé avec l'item glissé, et où il se trouve maintenant.

number

Combien d'items le glissement transporte.

boolean

Le glissement copie (group.pull: 'clone', la touche de copie).

Une place dans une liste ou un arbre triable (ses lignes visibles) : index compte à partir de 0, sur un total de length.

Key

Id de la liste ou de l'arbre.

string

Le label de la liste, le nom que les gens lui donnent.

number

La place, à partir de 0 : les textes intégrés disent index + 1.

number

Le nombre de places de la liste.