文档/指南

文件与其他应用

浏览器自己的拖放,作为本库的拖拽运行——来自其他应用的文件、链接和文本拖进放置目标和为它们腾出位置的列表,被拖出页面的项,以及在你的应用的各个窗口之间拖拽。

编辑此页

本页由 AI 翻译,可能存在不准确之处。 阅读英文原文

本库的拖拽发生在页面之内。浏览器有它自己的拖放,可以跨越页面的边界:来自桌面的文件、来自另一个标签页的链接、来自另一个应用的文本,以及你的项被拖出到所有这些地方。本库也运行这些拖拽,把它们作为 provider 的拖拽:目标会亮起,列表会腾出位置,事件和屏幕阅读器文本都和平常一样。

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 接受同样的选项:接收头像图片的放置区见拖放。

拖拽携带了什么

在放下之前,浏览器不会交出拖拽的数据。拖拽进行时,页面只能知道它携带了哪些种类的数据。active.external 和 e.external 说明已知的内容:

拖拽进行时放置时
types它提供的 MIME 类型: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 作为文本携带。
  • 在页面上,这次拖拽和其他拖拽一样工作:目标、腾出位置的列表、放置。拖拽的图像由浏览器绘制,代替浮层,元素留在原地(使用 overlay: false 时 transform 保持为 null)。
  • 对该项的触摸拖拽和键盘拖拽仍是本库的拖拽:浏览器没有用于它们的拖拽。
  • effect(effectAllowed)规定它被放到的地方可以做什么:默认为 'copyMove'。放到别处时,拖拽以 droppedOutside 结束,值为 'move'、'copy' 或 'link',在 onDragEnd 中。移动会把这些项从它们的列表中拿走;复制则保留它们。

makeDraggable 以同样的方式接受 native。

在窗口之间

带 native 的项还会携带它自身:你的应用另一个窗口中同一分组的列表会接收它,就像它来自它们自己的某个列表一样。在第二个窗口中打开这个情绪板,然后把一张卡片拖过去。

  • 拖拽进行时,另一个窗口已经拿到了这个项:它的列表会在相应位置显示它。
  • 放置时它被放进去,而在这里它离开自己的列表(这次放置是移动)。按住 Ctrl(Mac 上为 Option)则改为复制。
  • 带 createItem 的列表也会为这类项被询问,此时 active.external.group 已设置,项在 active.items[].item 中:返回它们,或者返回带有那个窗口的 key 的副本。没有 createItem 时,它们按原样放进去。
  • 项以 JSON 形式传递:日期会变成字符串,函数会被丢弃。

限制

  • 数据只随放置到来,绝不会更早:拖拽进行时显示的项要设计成不依赖这些数据。
  • 拖拽文件只适用于鼠标和触控板用户。也请在放置区旁边提供一个 <input type="file">。
  • 页面上只有一个 provider 接收来自外部的拖拽:第一个拥有可接收它们的目标的 provider。
  • 在 Firefox 中,一个项在拖拽期间被移进另一个列表、然后被放到另一个窗口中时,它不会得知这次放置:它在这里留在原来的位置。
  • JSON 超过 16 KB 的项仍然可以去往另一个窗口,但那个窗口只在放置时才显示它。

API 见 makeDroppable 和 makeDraggable。