Svelte — Transfer between two lists
Reorder items within one list or transfer them to another. Both areas share one scope and group="tasks", with unique areaId values. Item IDs remain stable during transfer.
Try it
Section titled “Try it”Drag the left handle into the empty list on the right. The done count increases and the item leaves the first list. Reset returns all items to their original order. Transfer in this example uses pointer interaction.
<script lang="ts"> import { onDestroy } from 'svelte'; import { createSortableScope, sortable } from 'comins-sortable/svelte'; export let lang = 'ko'; const ko = lang === 'ko'; type Item = { id: string; label: string }; const initial = [{ id: 'plan', label: ko ? '문서 설계' : 'Design the docs' }, { id: 'build', label: ko ? '예제 구현' : 'Build an example' }, { id: 'review', label: ko ? '코드 검토' : 'Review the code' }]; let todo: Item[] = [...initial]; let done: Item[] = []; const scope = createSortableScope<Item>(); onDestroy(() => scope.destroy()); $: todoOptions = { scope, areaId: 'todo', group: 'tasks', items: todo, itemKey: 'id' as const, handle: '.task-handle', onItemsChange: (next: readonly Item[]) => { todo = [...next]; } }; $: doneOptions = { scope, areaId: 'done', group: 'tasks', items: done, itemKey: 'id' as const, handle: '.task-handle', onItemsChange: (next: readonly Item[]) => { done = [...next]; } };</script><div class="demo" data-demo="sortable-svelte"> <div class="demo-toolbar"><span>{ko ? '손잡이를 잡고 다른 목록으로 이동하세요.' : 'Drag a handle into the other list.'}</span><button on:click={() => { todo = [...initial]; done = []; }}>{ko ? '초기화' : 'Reset'}</button></div> <div class="transfer-board"> <section class="transfer-column"> <div class="list-heading">{ko ? '진행 예정' : 'To do'}<span>{todo.length}</span></div> <div class="task-list" aria-label={ko ? '진행 예정' : 'To do'} use:sortable={todoOptions}> {#each todo as item (item.id)}<div class="task" data-task={item.id}><button class="task-handle" aria-label={`${ko ? '드래그' : 'Drag'}: ${item.label}`}>⠿</button><span>{item.label}</span></div>{/each} </div> </section> <section class="transfer-column"> <div class="list-heading">{ko ? '완료' : 'Done'}<span>{done.length}</span></div> <div class="task-list" aria-label={ko ? '완료' : 'Done'} use:sortable={doneOptions}> {#each done as item (item.id)}<div class="task" data-task={item.id}><button class="task-handle" aria-label={`${ko ? '드래그' : 'Drag'}: ${item.label}`}>⠿</button><span>{item.label}</span></div>{/each} </div> </section> </div> <p role="status" class="demo-status">{ko ? '완료' : 'Done'}: {done.length} / 3</p></div>Svelte state contract
Section titled “Svelte state contract”Pass the same scope from createSortableScope<Item>() to both use:sortable actions. Without it, each action owns an independent scope and cannot transfer between the lists. Commit arrays in onItemsChange and destroy the shared scope on disposal.
Handles use touch-action: none and user-select: none to avoid competing touch scroll and text selection during a drag.
Search terms
Section titled “Search terms”Transfer, move between lists, drag and drop, reorder, shared scope.