Docs/Guides
Files and other apps
The browser's own drag and drop, as drags of the library — files, links and text from other apps into drop targets and lists that make room for them, items dragged out of the page, and between windows of your app.
The library's drags live inside the page. The browser has a drag and drop of its own that crosses the page's edges: files from the desktop, a link from another tab, text from another app, and items of yours dragged out to all of those. The library runs those drags too, as drags of the provider: targets light up, lists make room, the events and the screen reader texts are the usual ones.
Loading the demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Board from './Board.vue';
</script>
<template>
<DnDProvider>
<Board />
</DnDProvider>
</template>From outside the page
A target takes such drags with external, saying what it takes. Targets without it ignore
them.
const { items } = makeSortableList(grid, {
items: cards,
group: 'moodboard',
strategy: 'grid',
external: { files: 'image/*', links: true, text: true },
createItem: fromOutside,
});
external | Takes |
|---|---|
files | Files: true any, MIME types and extensions as <input accept> reads them ('image/*, .md'), or a function asked file by file. Folders never. |
multiple | false: one file at a time. A drag of several is refused. |
links | Links (text/uri-list): an address from another tab, a link from a page. |
text | Plain text: a selection from another app. |
types | Other MIME types, exactly as other apps offer them. |
makeDroppable takes the same option: a zone that takes an avatar image is in
drag and drop.
What the drag carries
The browser keeps the data of a drag to itself until it is dropped. While it is held, the
page only learns what kinds of data it carries. active.external and e.external say
what is known:
| While held | At the drop | |
|---|---|---|
types | The MIME types it offers: Files, text/uri-list… | The same |
fileTypes | One MIME type per file | The same |
files | Empty | The files the target takes, as Files |
urls, text, html | Empty | Its links, its text, its HTML |
getData(type) | '' | The data of any type it offers |
ready | false | true |
Into a list
A list makes room for it as for a plain draggable: with createItem, which is asked twice:
while the drag is held, for the item that shows where it goes, and at the drop, with the
data, for what goes in. A list of items inserts them all at that place: a card per image.
export const fromOutside = (active: Active): Card | Card[] | undefined => {
const { external } = active;
if (!external) return undefined;
// While it is held: a tile that says what is coming.
if (!external.ready) return { id: 'incoming', kind: 'incoming', label: incoming(external) };
// At the drop: a card each.
if (external.files.length) {
return external.files.map((file) => ({ id: uid(), kind: 'image', src: URL.createObjectURL(file), name: file.name }));
}
if (external.urls.length) return external.urls.map((url) => ({ id: uid(), kind: 'link', url, title: host(url) }));
const text = external.text.trim();
return text ? { id: uid(), kind: 'note', text } : undefined;
};
- The incoming tile is a row of your list like any other: the list renders it, the rows
around it slide out of its way,
onDropgets the real ones. - Trees take such drags too, one node at a time: a folder node with a child per file holds several.
- The drop event has
e.externalwith the data, in the list'sonDropas in the monitors'.
Out of the page
With native, a mouse drag of an item is the browser's own drag, so it can leave the page:
onto the desktop, into an editor, a chat, another tab.
makeSortableItem(el, {
item: () => props.card,
native: { data: () => toOutside(props.card) },
});
case 'link':
return { 'text/uri-list': card.url, 'text/plain': card.url };
case 'note':
return { 'text/plain': card.text };
datagives other apps the item as MIME types and text:text/plain,text/uri-list,text/html. Read when the drag starts. Without it,native: truecarries thelabelas text.- On the page, the drag works as any other: targets, lists that make room, the drop. The
browser draws the dragged image instead of the overlay, and the element stays in place
(
transformstaysnullwithoverlay: false). - Touch and keyboard drags of the item stay the library's: the browser has no drag of its own for them.
effect(effectAllowed) says what the place it is dropped on may do:'copyMove'by default. A drop elsewhere ends the drag withdroppedOutside,'move','copy'or'link', inonDragEnd. A move takes the items out of their list; a copy leaves them.
makeDraggable takes native the same way.
Between windows
An item with native also carries itself: the lists of its group in another window of your
app take it as if it came from one of their own lists. Open the moodboard in a second window
and drag a card across.
- While it is held, the other window already has the item: its list shows it in place.
- At the drop it goes in, and here it leaves its list (the drop was a move). Hold Ctrl (Option on a Mac) to copy it instead.
- A list with
createItemis asked for such items too, withactive.external.groupset and the items inactive.items[].item: return them, or copies with keys of that window. WithoutcreateItem, they go in as they are. - Items travel as JSON: dates become strings, functions are left out.
Limits
- The data comes with the drop only, never before: plan the item that shows while a drag is held without it.
- Dragging files is for mouse and trackpad users. Offer an
<input type="file">next to the zone too. - One provider of the page takes drags from outside: the first that has a target for them.
- In Firefox, an item moved into another list during the drag and then dropped in another window doesn't hear about that drop: it stays where it was here.
- An item whose JSON passes 16 KB still goes to another window, but that window shows it only at the drop.
The API is in makeDroppable and makeDraggable.