Vue — 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 setup lang="ts">import { ref } from 'vue';import { SortableRoot, SortableArea } from 'comins-sortable/vue';const props = withDefaults(defineProps<{ lang?: string }>(), { lang: 'ko' });const ko = props.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' }];const todo = ref<Item[]>([...initial]);const done = ref<Item[]>([]);function reset() { todo.value = [...initial]; done.value = []; }</script><template> <div class="demo" data-demo="sortable-vue"> <div class="demo-toolbar"><span>{{ ko ? '손잡이를 잡고 다른 목록으로 이동하세요.' : 'Drag a handle into the other list.' }}</span><button @click="reset">{{ ko ? '초기화' : 'Reset' }}</button></div> <SortableRoot> <div class="transfer-board"> <section class="transfer-column"> <div class="list-heading">{{ ko ? '진행 예정' : 'To do' }}<span>{{ todo.length }}</span></div> <SortableArea v-model="todo" area-id="todo" group="tasks" item-key="id" handle=".task-handle" :component-props="{ class: 'task-list', 'aria-label': ko ? '진행 예정' : 'To do' }"> <template #item="{ item }"><div class="task" :data-task="item.id"><button class="task-handle" :aria-label="`${ko ? '드래그' : 'Drag'}: ${item.label}`">⠿</button><span>{{ item.label }}</span></div></template> </SortableArea> </section> <section class="transfer-column"> <div class="list-heading">{{ ko ? '완료' : 'Done' }}<span>{{ done.length }}</span></div> <SortableArea v-model="done" area-id="done" group="tasks" item-key="id" handle=".task-handle" :component-props="{ class: 'task-list', 'aria-label': ko ? '완료' : 'Done' }"> <template #item="{ item }"><div class="task" :data-task="item.id"><button class="task-handle" :aria-label="`${ko ? '드래그' : 'Drag'}: ${item.label}`">⠿</button><span>{{ item.label }}</span></div></template> </SortableArea> </section> </div> </SortableRoot> <p role="status" class="demo-status">{{ ko ? '완료' : 'Done' }}: {{ done.length }} / 3</p> </div></template>Vue state contract
Section titled “Vue state contract”Import SortableRoot and SortableArea from comins-sortable/vue. v-model connects modelValue with update:modelValue. Render items through the #item slot receiving { item, index }. React items/onItemsChange props do not apply to this adapter.
Handles use touch-action: none and user-select: none to avoid competing touch scroll and text selection during a drag.
Uses Vue SFC generic inference in 0.1.6. SortableArea infers item and slot types from modelValue without a component type assertion.
Search terms
Section titled “Search terms”Transfer, move between lists, drag and drop, reorder, shared scope.