文档/API

makeSortableList

让容器成为可排序列表——一份拖拽时重新排序的草稿,在放置时写回。

编辑此页

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

function makeSortableList<T>(  target: MaybeElementRef,  options: SortableListOptions<T>,): SortableListReturn<T>;

让容器成为可排序列表。渲染返回的 items(草稿顺序),而不是你的数组:放置结果会在 onDrop 之后写回 options.items,除非 event.preventDefault() 把 event.result 留给你处理。

const list = useTemplateRef<HTMLElement>('list');
const { items } = makeSortableList(list, { items: tasks, group: 'board' });

列表的行调用 makeSortableItem。参见可排序列表和共享项的列表。

Live

正在加载演示…

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

const strategy = ref<'vertical' | 'horizontal' | 'grid'>('grid');
const placeholder = ref<'item' | 'indicator'>('item');
const behavior = ref<'sort' | 'swap'>('sort');
</script>

<template>
  <DnDProvider>
    <div class="demo">
      <div class="switches">
        <Segmented v-model="strategy" name="strategy" :options="['vertical', 'horizontal', 'grid']" />
        <Segmented v-model="placeholder" name="placeholder" :options="['item', 'indicator']" />
        <Segmented v-model="behavior" name="behavior" :options="['sort', 'swap']" />
      </div>
      <code class="call">makeSortableList(el, { strategy: '{{ strategy }}', placeholder: '{{ placeholder }}', behavior: '{{ behavior }}' })</code>
      <Tiles :strategy="strategy" :placeholder="placeholder" :behavior="behavior" />
    </div>
  </DnDProvider>
</template>

<style scoped>
.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  max-width: 100%;
}

.switches {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
}

.call {
  max-width: 100%;
  padding: 6px 10px;
  overflow-x: auto;
  border-radius: 8px;
  background: color-mix(in srgb, currentColor 6%, transparent);
  font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
  white-space: nowrap;
  color: #42b883;
}
</style>

选项

makeSortableList 的选项。一个组件可以创建多个列表(和树):组件中渲染的每个项都会加入元素包含它的最内层列表。

选项接受值、ref 或 getter(overlay、overOverlay、dropAnimation、overDropAnimation、animation:值或 ref),并在列表需要时读取,所以变化最迟在下一次拖拽时生效。例外:id 和 group 只在创建列表时读取一次;items 就是模型本身;函数(getKey、filter、clone、createItem、各个回调)按原样传入。

SortableItems<T>

数据的唯一来源。可写的 ref 会在放置时、紧接在 onDrop 之后接收新数组。只读的数据源(getter、只读 ref、computed)则不能:调用 event.preventDefault(),自己应用 event.result。如果那里的放置被留给库处理,开发构建会发出警告,而这样的放置什么都不会改变。

(item: T) => Key

默认:item.id;对于原始值,则是项本身。也用于为 makeSortableItem({ item }) 生成 key。

(item: T) => boolean

只显示它返回 true 的项(搜索、标签):items(返回的那个)包含这些项,拖拽在它们之间排序。其余项在源数组中保持原位:放置会写入整个数组,每个隐藏项仍跟在它原先跟随的显示项之后(那一项被移动时,跟在最近的留在原处的项之后)。响应式:它读取的东西(搜索词的 ref)立即生效。e.to.index 和 activeIndex 按显示的项计数。

Key

默认:useId(),在服务端和客户端渲染之间保持稳定。只读取一次。

string值 · ref · getter

人们对列表的称呼:屏幕阅读器文本会说出它,而不是列表的 id(position 2 of 5 in Garden)。

unknown值 · ref · getter

关于这个列表作为放置目标的任何信息:拖拽位于它上方时,事件以 over.data 携带它。

ActivationOptions值 · ref · getter

在这个列表的项上按下时如何开始拖拽,替代 provider 的 activation(项自己的优先)。在按下开始时读取。

Component值 · ref

这个列表中项的浮层组件(项自己的 overlay 优先)。它接收 OverlayProps,并在整个拖拽过程中保持不变,经过其他列表时也是如此,除非拖拽下方的目标有自己的浮层(overOverlay)。在拖拽过程中切换的 ref 会立即替换它。

Component值 · ref

当这个列表接受的拖拽位于它上方时浮层显示什么,无论拖拽来自哪个列表:一个获得 OverlayProps、并把拖拽自己的浮层作为默认插槽的组件(见 DroppableOptions.overOverlay)。实时读取。

DropAnimation值 · ref

这个列表中项的拖拽结束时浮层如何落位,替代 provider 的 dropAnimation(项自己的优先;放置目标的优先于两者)。在放置时读取。

DropAnimation值 · ref

浮层被放进这个列表时如何落位,无论它来自哪个列表:优先于被拖拽项和 provider 的 dropAnimation。在放置时读取。

Modifier[]值 · ref · getter

这个列表中项的拖拽修饰器,在 provider 的 modifiers 之后应用(项自己的 modifiers 优先):例如用 [restrictToVerticalAxis] 让垂直列表的项保持在轴上。在拖拽开始时读取。

'x' | 'y'值 · ref · getter

把这个列表中项的拖拽锁定在一个轴上:垂直列表用 'y',水平列表用 'x'(项自己的 axis 优先)。锁定在修饰器之后执行,所以无论修饰器做什么,锁定都成立。在拖拽开始时读取。

string | SortableGroupOptions<T>

分组名称相同的列表会互相交换项。默认:自成一组。只读取一次。

AcceptMode值 · ref · getter

多种类型项的多项拖拽:全部('all',默认)还是部分('some')项必须被接受(group.put)。

SortableStrategy值 · ref · getter

默认为 'vertical'。

SortableBehavior值 · ref · getter

'swap':被拖拽的项与被悬停的项实时交换位置;移回它上方或移出列表会还原。同一分组中都设置了交换的列表之间会互相交换项:被悬停的项移到被拖拽项的位置,所以它必须能离开自己的列表(group.pull: true)并进入被拖拽项的列表(group.put)。多项拖拽会排序而不是交换。默认为 'sort'。

SortablePlaceholder值 · ref · getter

'indicator':列表不移动,由 indicator 指出放置的位置。默认为 'item'。

MultipleMode值 · ref · getter

从这个列表开始的拖拽的多项拖拽模式。默认为 'group'。在拖拽开始时读取。

boolean值 · ref · getter

false:项不能在这个列表内部重新排序(面板)。

(item: T) => T

创建副本(pull: 'clone'、复制修饰键)。复制时必需。

(active: Active) => T | T[]

接收来自外部的普通可拖拽元素:创建要插入的项。使用键盘时,朝列表方向按方向键即可把可拖拽元素放进来(与它平齐,就像在列表之间移动那样),之后新创建的项会像其他项一样逐个位置移动。

来自页面之外的拖拽(external)会被询问两次:拖拽进行时,询问用来显示它将去往哪里的项(此时只知道 active.external.fileTypes 和 types);放置时,带着它的数据(active.external.ready),询问要插入的内容。返回一组项时,它们会全部插入到占位所在的位置:每个文件一项。

ExternalAccept值 · ref · getter

接收来自页面之外的拖拽:文件、链接、文本(见 ExternalAccept)。列表像为普通可拖拽元素那样为它们腾出位置,这需要 createItem。从应用另一个窗口拖进来的同一分组的项(native)两个选项都不需要:它们以自身进入。值、ref 或 getter,实时读取。

DropOutside值 · ref · getter

放在所有目标之外时:把项留在占位所在的地方、取消,或者移除它们。默认为 'placeholder'(使用 placeholder: 'indicator' 时为 'cancel')。在放置时读取。

LayoutAnimation值 · ref

这个列表的 FLIP 动画;默认:provider 的 animation。值或 ref(函数即自定义动画)。

AutoAnimation值 · ref · getter

为非拖拽引起的 items 变化添加动画:被添加、删除、移动和过滤的项(参见 AutoAnimation)。默认:provider 的 animateChanges,除非在那里设置,否则关闭。在 items 变化时读取。

boolean值 · ref · getter

列表自身的尺寸跟随它的项:当拖拽带入或带走一个项时(以及放置时,和 animateChanges 展示的变化中),它以 animation 的时长和缓动增大或缩小到新尺寸,而不是跳变,周围的内容也随之移动。默认 true;false 让它跳变。每次变化时读取。

boolean | AutoScrollOptions值 · ref · getter

为这个列表的滚动容器覆盖 provider 的 autoScroll。在拖拽开始时读取。

MaybeElementRef

替列表接收放置的元素,默认是列表元素本身:例如包围一个短卡片列表的整列,这样在最后一张卡片下方松开的卡片仍然会落进列表(落到末尾)。项仍然留在列表元素中。

boolean值 · ref · getter

true:它的项不能被拖拽,它也不接受任何项。

( event: SortableDropEvent<T>, ) => MaybePromise<BeforeDropResult>

在放置进入这个列表之前、以及 dropOutside: 'remove' 移出它的项之前询问:false 拒绝(项飞回原处),返回部分 key 则只接受这些项。可以是异步的:浮层停在被放下的位置等待。抛出错误也会拒绝这次放置。关于 result 和 preventDefault,参见 SortableDropEvent。

(event: SortableDropEvent<T>) => void

放置进入这个列表并被接受后、写入之前调用:items 仍是旧顺序,event.result 是新顺序。同步执行:它返回后紧接着就会写入,除非它调用了 event.preventDefault()。当项被移除时(to.index 为 -1),也会在项所来自的列表上调用。

返回值

makeSortableList 的返回值。

Key

列表的 id:id 选项,或为它生成的 id。

ComputedRef<T[]>

草稿顺序:渲染它,而不是源数组。

ComputedRef<boolean>

有拖拽位于列表上方。

ComputedRef<boolean>

正在拖拽某个东西,并且这个列表接受它。

ComputedRef<SortableIndicator | null>

指示器模式:放置在这个列表中的位置;不在这里时为 null。

ComputedRef<Key | null>

虚拟化器必须保持挂载的行:位置在这个列表中时(副本、新创建的项)是该位置,否则是被拖拽的项;没有拖拽时为 null。

ComputedRef<number>

当前项在这个列表草稿中的索引,不在这里时为 -1。

ComputedRef<Size | null>

拖拽开始时被拖拽项的尺寸:例如用作它所占虚拟行的尺寸。

() => void

为库没有引起的布局变化添加动画(例如虚拟化器重新测量)。

分组

列表的分组:相互交换项的列表,以及项能否离开和进入这个列表。

string

名称相同的列表和树相互交换项。

boolean | 'clone'

项能否离开这个列表:移动、复制('clone',面板)或完全不能。

boolean | string | string[] | ((context: SortablePutContext<T>) => boolean)

项能否进入这个列表:true(默认)、false、项必须具有的一个或多个类型(与放置目标的 accept 相同:通过 acceptMode 决定全部还是部分),或者一个关于拖拽的函数。

函数形式的 group.put 被调用时收到的参数:将要进入列表的项。

Key[]

这些项的 key。

T[]

这些项(对于普通的可拖拽元素,是 createItem 创建的项)。

string[]

这些项的类型合在一起。

Key | null

来源列表,对于外部的可拖拽元素为 null。

Key

被询问的列表的 id。

放置事件

onBeforeDrop 和 onDrop 收到的内容。

可排序列表或树上的一次放置。它的钩子按顺序运行:onBeforeDrop 做决定,onDrop 收到通知,然后默认行为把 result 写入它所列出的每个列表的 items,最后浮层落位。在任一钩子中调用 preventDefault() 都会跳过这次写入,这样你就可以自己应用 result。

Key[]

被拖拽项的 key。

T[]

落位的项:被拖拽的项、它们的副本(mode: 'copy'),或 createItem 创建的项('insert')。

Key[]

放置生效的 key:全部,除非列表或 onBeforeDrop 只接受了一部分。

Key[]

飞回原处的 key。

{ list: Key | null; index: number }

拖拽从哪里开始:当前项所在的列表和索引。对于来自外部的普通可拖拽元素,list 为 null。

{ list: Key; index: number }

没有任何东西落下时,index 为 -1:项离开了它们的列表(e.source.remove()、dropOutside: 'remove')。

boolean

放置仍在拖拽开始的那个列表中。

DragMode

放置会做什么(见 DragMode)。

{ key: Key; item: T; from: { list: Key; index: number }; to: { list: Key; index: number } }

mode: 'swap':与被拖拽项交换的那个项。它朝相反的方向移动,from 是被拖拽项占据的位置(上面的 to),to 是被拖拽项开始时的位置(上面的 from);在列表之间交换时,它会移入被拖拽项所在的列表。

ReadonlyMap<Key, T[]>

这次放置改变的每个列表的新项,以列表 id 为键:正是默认写入会放进它们 items 中的内容,以新数组的形式提供。移动、复制、新创建的项、部分接受、交换的双方,以及被移除的项(项内部目标中的 e.source.remove()、回收区、dropOutside: 'remove')都包含在其中;放置后保持不变的列表则不包含。树的条目是一个新的根数组:变化路径上的节点是副本(withChildren),未受影响的子树是共享的,你的节点永远不会被修改。在 onBeforeDrop 中,它是钩子做出回答之前的放置结果(返回部分 key 会改变它);onDrop 收到的是最终结果。

() => void

跳过默认写入,对 result 中的每个列表都跳过(只写入其中一部分会丢失或重复项)。在 onDrop 中,或在异步的 onBeforeDrop resolve 之前自己应用 result,浮层就会按新的顺序落位。如果哪里都没有应用,列表保持原样,浮层飞回原处(副本和新创建的项还没有位置,会淡出)。

boolean

在 onBeforeDrop 或 onDrop 中调用过 preventDefault()。

ExternalData

来自页面之外的拖拽(external):它携带的内容,包括它的数据(它已被放下)。

指示器

placeholder: 'indicator' 模式下 indicator 包含的内容。

使用 placeholder: 'indicator' 时放置的位置:列表不移动,由你来画线。

Key

放置进入的列表(或树)的 id。

number

在渲染出的列表中的插入位置。

Key | null

指示器所依附的项;在空列表中为 null。

'before' | 'after' | 'inside' | 'swap'

插入线位于 key 的哪一侧。'inside'(树):悬停在一个接收被拖拽节点的行的中间,放置会进入这一行,成为它的第一个子节点。'swap'(behavior: 'swap'):key 是放置时与之交换的项;标记该项本身。

number

树:被放下的节点将得到的深度(根节点为 0),例如用来缩进插入线。

Key | null

树:被放下的节点将得到的父节点,在根层级时为 null。

取值

可排序项的数据源。可写的 ref:库会把每次放置写入其中。只读的数据源(getter、只读 ref、computed:不可变的 store、服务端状态、只能通过 action 修改的 store):在 event.preventDefault() 之后,由你根据 event.result 应用每次放置(参见 SortableDropEvent)。

type SortableItems = Ref<T[]> | Readonly<Ref<readonly T[]>> | (() => readonly T[])

列表如何排布它的项:一列('vertical')、一行('horizontal')或自动换行的网格('grid')。

type SortableStrategy = 'vertical' | 'horizontal' | 'grid'

悬停在某个项上时会发生什么:'sort' 腾出位置(中间的项平移),'swap' 交换这两个项。

type SortableBehavior = 'sort' | 'swap'

拖拽如何显示它将去往哪里:'item',被拖拽项的位置在列表中移动;'indicator',列表保持不动,由 indicator 指出放置的位置(由你来画线)。

type SortablePlaceholder = 'item' | 'indicator'

多项拖拽中其他选中项的行为:

  • 'group':它们离开原位置,作为连续的占位随当前项一起移动,因此悬停时列表已经显示出放置的结果;
  • 'gaps':它们留在原处,直到放置;
  • 'stack':它们收进浮层中。

默认浮层会把被拖拽的项沿列表方向排成一列('group'、'gaps';在水平列表中是一行,在网格中是当前项后面的一叠),或者显示当前项加上数量('stack')。

type MultipleMode = 'group' | 'gaps' | 'stack'

在所有目标之外放置时会发生什么:'placeholder' 把项留在它们的位置所在处,'cancel' 把它们送回原处,'remove' 把它们从所在列表中移除。

type DropOutside = 'placeholder' | 'cancel' | 'remove'