Документация/Руководства
Файлы и другие приложения
Собственный drag and drop браузера как драги библиотеки — файлы, ссылки и текст из других приложений в цели и списки, которые освобождают для них место, элементы, вытащенные за пределы страницы, и перетаскивание между окнами вашего приложения.
Страница переведена ИИ и может содержать неточности. Читать английский оригинал
Драги библиотеки живут внутри страницы. У браузера есть собственный drag and drop, который пересекает границы страницы: файлы с рабочего стола, ссылка из другой вкладки, текст из другого приложения и ваши элементы, вытащенные во все эти места. Библиотека ведёт и такие драги — как драги провайдера: цели подсвечиваются, списки освобождают место, события и тексты для скринридеров — обычные.
Загружаем демо…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Board from './Board.vue';
</script>
<template>
<DnDProvider>
<Board />
</DnDProvider>
</template>Извне страницы
Цель принимает такие драги с external, где сказано, что она принимает. Цели без него их
игнорируют.
const { items } = makeSortableList(grid, {
items: cards,
group: 'moodboard',
strategy: 'grid',
external: { files: 'image/*', links: true, text: true },
createItem: fromOutside,
});
external | Принимает |
|---|---|
files | Файлы: true — любые; MIME-типы и расширения, как их читает <input accept> ('image/*, .md'); или функция, которую спрашивают о каждом файле. Папки — никогда. |
multiple | false: по одному файлу. Драг нескольких отклоняется. |
links | Ссылки (text/uri-list): адрес из другой вкладки, ссылка со страницы. |
text | Обычный текст: выделение из другого приложения. |
types | Другие MIME-типы, ровно так, как их предлагают другие приложения. |
makeDroppable принимает ту же опцию: зона, которая принимает изображение для аватара, есть в
руководстве drag and drop.
Что несёт драг
Браузер держит данные драга при себе, пока его не бросят. Пока драг удерживают, страница
узнаёт только, какие виды данных он несёт. active.external и e.external говорят, что
известно:
| Пока драг удерживают | При броске | |
|---|---|---|
types | MIME-типы, которые он предлагает: Files, text/uri-list… | Те же |
fileTypes | По одному MIME-типу на файл | Те же |
files | Пусто | Файлы, которые принимает цель, как File |
urls, text, html | Пусто | Его ссылки, его текст, его HTML |
getData(type) | '' | Данные любого типа, который он предлагает |
ready | false | true |
В список
Список освобождает для него место, как для обычного перетаскиваемого элемента, — с
createItem, которого спрашивают дважды: пока драг удерживают — об элементе, который показывает,
куда он попадёт, и при броске, с данными, — о том, что вставляется. Массив элементов
вставляется весь на это место: по карточке на каждое изображение.
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;
};
- Плитка входящего драга — строка вашего списка, как любая другая: список рендерит её,
соседние строки отъезжают с её пути, а
onDropполучает настоящие элементы. - Деревья тоже принимают такие драги, по одному узлу: узел-папка с потомком на каждый файл вмещает несколько.
- В событии броска есть
e.externalс данными — и вonDropсписка, и в мониторах.
За пределы страницы
С native драг элемента мышью — это собственный драг браузера, поэтому он может покинуть
страницу: на рабочий стол, в редактор, в чат, в другую вкладку.
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 };
dataотдаёт элемент другим приложениям в виде MIME-типов и текста:text/plain,text/uri-list,text/html. Читается, когда драг начинается. Без неёnative: trueнесётlabelкак текст.- На странице драг работает как любой другой: цели, списки, которые освобождают место, бросок.
Браузер рисует перетаскиваемое изображение вместо оверлея, а элемент остаётся на месте
(
transformостаётсяnullприoverlay: false). - Драги элемента касанием и с клавиатуры остаются драгами библиотеки: своего драга для них у браузера нет.
effect(effectAllowed) говорит, что может сделать место, куда его бросили: по умолчанию'copyMove'. Бросок в другом месте завершает драг сdroppedOutside—'move','copy'или'link'— вonDragEnd. Перемещение забирает элементы из их списка; копирование оставляет их.
makeDraggable принимает native так же.
Между окнами
Элемент с native несёт ещё и самого себя: списки его группы в другом окне вашего приложения
принимают его так, будто он пришёл из одного из их собственных списков. Откройте мудборд во
втором окне и перетащите туда карточку.
- Пока драг удерживают, в другом окне элемент уже есть: его список показывает его на месте.
- При броске он попадает туда, а здесь покидает свой список (бросок был перемещением). Зажмите Ctrl (Option на Mac), чтобы вместо этого скопировать его.
- Список с
createItemспрашивают и о таких элементах — с заданнымactive.external.groupи элементами вactive.items[].item: верните их или копии с ключами этого окна. БезcreateItemони попадают в список как есть. - Элементы передаются как JSON: даты становятся строками, функции отбрасываются.
Ограничения
- Данные приходят только с броском, никогда раньше: продумайте элемент, который показывается, пока драг удерживают, без них.
- Перетаскивание файлов — для пользователей мыши и тачпада. Поставьте рядом с зоной ещё и
<input type="file">. - Драги извне принимает один провайдер на странице: первый, у которого есть для них цель.
- В Firefox элемент, который во время драга перенесли в другой список, а потом бросили в другом окне, не узнаёт об этом броске: здесь он остаётся там, где был.
- Элемент, чей JSON больше 16 КБ, всё равно попадает в другое окно, но это окно показывает его только при броске.
API — в makeDroppable и makeDraggable.