Components/SortableList

SortableList

A list that reorders by drag or keyboard.

VerifiedSince 0.3.4

Usage

Basic

Every item needs a stable string id; the item slot's dragging flag marks the carried row.

Loading demo...

Drag Modes

The default pointer mode carries the row while the others spring aside; use dragMode="native" when items must leave the list, as in board columns.

<template>
  <TxSortableList v-model="column.cards" drag-mode="native" />
</template>

Drag Handle Mode

handle requires a drag to start inside [data-tx-sort-handle="true"]. Default rows include a grip; a custom slot spreads handleAttrs onto its own.

<template>
  <TxSortableList v-model="steps" handle @reorder="saveOrder">
    <template #item="{ item, handleAttrs }">
      <div>
        <button type="button" v-bind="handleAttrs" aria-label="Drag item">☰</button>
        <span>{{ item.title }}</span>
      </div>
    </template>
  </TxSortableList>
</template>

Persisting Reorder

<script setup lang="ts">
const tasks = ref([{ id: 'draft' }, { id: 'review' }, { id: 'ship' }])

async function persistOrder({ items }: { items: Array<{ id: string }> }) {
  tasks.value = items
  await api.saveTaskOrder(items.map(item => item.id))
}
</script>

<template>
  <TxSortableList v-model="tasks" @reorder="persistOrder" />
</template>

Best Practices

  • Use stable persisted ids, never array indexes.
  • Update the local array with v-model; persist from reorder when server order matters.
  • Turn on handle for rows with buttons, links, inputs, or selectable text, and for long touch lists so rows don't capture swipes.
  • Pass itemLabel when ids aren't human-readable, or screen readers announce ids like plugin-a1b3.
  • Keep lists modest; for very long ones, pair a different drag strategy with virtualization.

API Reference

Props

PropTypeDefaultDescription
modelValueSortableListItem[]requiredOrdered list data; every item needs a stable string id.
disabledbooleanfalseDisables dragging, keyboard reordering, and reorder emits.
handlebooleanfalseRequires a drag to start from [data-tx-sort-handle="true"].
dragMode'pointer' | 'native''pointer'pointer carries the row as others step aside; native uses HTML5 drag and drop, letting items leave.
ariaLabelstring-Accessible name of the list.
itemLabel(item) => string-Item name in announcements; defaults to the id.
labelsSortableListLabels-Templates for grabbed, moved, dropped, cancelled, and the built-in handle, with {item}, {position}, {size}.

Events

EventPayloadDescription
update:modelValueSortableListItem[]Once on release for pointer drags; on every step for native drags and the keyboard.
reorder{ from: number, to: number, items: SortableListItem[] }Once when the interaction ends, with the start and end indexes.

Slots

SlotPropsDescription
item{ item, dragging, grabbed, index, handleAttrs }Custom item renderer, defaulting to the id; spread handleAttrs onto the element that starts a drag.

SortableListItem

FieldTypeDescription
idstringStable identity for keys and drag state; other fields are preserved.

Overview

  • The root is role="list" and each item role="listitem".
  • The component owns the preview, so rows move whether or not the host writes modelValue back; reorders emit a shallow copy that keeps item objects.
  • A pointer drag starts after 4px of travel and swallows the release click; inputs and editable regions never start one; Escape or pointercancel cancels without emitting.
  • A native drag reorders as dragover crosses rows, and a drop outside the list still counts.
  • Keyboard: the list is one tab stop. Space or Enter picks a row up, arrows move it, and pressing again drops it; Escape restores the order, and blur drops it. A role="status" region announces each step.
  • Under reduced motion nothing lifts and springs take no time; the carried row still follows the pointer.

Technologies

  • Travel and lift use the separate translate and scale properties so each keeps its own timing; resolveTransition compiles the springs to CSS linear() curves.
  • Source: packages/tuffex/packages/components/src/sortable-list/.
查看源码
packages/tuffex/packages/components/src/sortable-list/index.ts