Документация/Руководства

Файлы и другие приложения

Собственный drag and drop браузера как драги библиотеки — файлы, ссылки и текст из других приложений в цели и списки, которые освобождают для них место, элементы, вытащенные за пределы страницы, и перетаскивание между окнами вашего приложения.

Редактировать страницу

Страница переведена ИИ и может содержать неточности. Читать английский оригинал

Драги библиотеки живут внутри страницы. У браузера есть собственный drag and drop, который пересекает границы страницы: файлы с рабочего стола, ссылка из другой вкладки, текст из другого приложения и ваши элементы, вытащенные во все эти места. Библиотека ведёт и такие драги — как драги провайдера: цели подсвечиваются, списки освобождают место, события и тексты для скринридеров — обычные.

Live

Загружаем демо…

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

<template>
  <DnDProvider>
    <Board />
  </DnDProvider>
</template>

Извне страницы

Цель принимает такие драги с external, где сказано, что она принимает. Цели без него их игнорируют.

Board.vue
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'); или функция, которую спрашивают о каждом файле. Папки — никогда.
multiplefalse: по одному файлу. Драг нескольких отклоняется.
linksСсылки (text/uri-list): адрес из другой вкладки, ссылка со страницы.
textОбычный текст: выделение из другого приложения.
typesДругие MIME-типы, ровно так, как их предлагают другие приложения.

makeDroppable принимает ту же опцию: зона, которая принимает изображение для аватара, есть в руководстве drag and drop.

Что несёт драг

Браузер держит данные драга при себе, пока его не бросят. Пока драг удерживают, страница узнаёт только, какие виды данных он несёт. active.external и e.external говорят, что известно:

Пока драг удерживаютПри броске
typesMIME-типы, которые он предлагает: Files, text/uri-list…Те же
fileTypesПо одному MIME-типу на файлТе же
filesПустоФайлы, которые принимает цель, как File
urls, text, htmlПустоЕго ссылки, его текст, его HTML
getData(type)''Данные любого типа, который он предлагает
readyfalsetrue

В список

Список освобождает для него место, как для обычного перетаскиваемого элемента, — с createItem, которого спрашивают дважды: пока драг удерживают — об элементе, который показывает, куда он попадёт, и при броске, с данными, — о том, что вставляется. Массив элементов вставляется весь на это место: по карточке на каждое изображение.

cards.ts
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 драг элемента мышью — это собственный драг браузера, поэтому он может покинуть страницу: на рабочий стол, в редактор, в чат, в другую вкладку.

Tile.vue
makeSortableItem(el, {
  item: () => props.card,
  native: { data: () => toOutside(props.card) },
});
cards.ts
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.