Vue — Swap two items
Use swap in Vue to exchange only two items and inspect the completion event.
Different from insertion sorting
Section titled “Different from insertion sorting”Dropping A on the center of C in [A, B, C, D] produces [C, B, A, D]. Only the two items exchange positions; B and D remain. Intervening items are not shifted as they would be for insertion.
- Drag the A handle to the center of C and release.
- Check JSON for
status: "dropped",change.operation: "swap",change.itemId: "A", andchange.swapItemId: "C". - Start another drag and cancel with Escape; order remains unchanged.
- Reset returns to A, B, C, D.
Public options
Section titled “Public options”| Option | Value/purpose |
|---|---|
swap |
true — exchanges source and target within the same area. |
direction |
"vertical" — this example uses one vertical list. |
itemKey or data-sortable-id |
Supplies identity retained across movement. |
handle |
".task-handle" — restricts pointer input to handles. |
Vue state commit
Section titled “Vue state commit”SortableRoot → SortableArea, #item · v-model (modelValue / update:modelValue)
Commit the adapter’s next array to state immediately. Do not mutate the original array or use index-based keys. Check both the onAfterDrag completion result and the displayed order.
Uses Vue SFC generic inference in 0.1.6. SortableArea infers item and slot types from modelValue without a component type assertion.
This is a same-area, single-item swap. Grid collision, multiDrag, threshold/invertSwap combinations, and cross-area delivery remain separate scenarios. This pointer-based example does not claim full keyboard reordering simply because its handle is focusable.
Related documentation
Section titled “Related documentation”Advanced sorting guide · Core lifecycle · Vanilla instance · Ordinary list transfer
Try it live
Section titled “Try it live”<script setup lang="ts">import { ref } from 'vue';import { SortableRoot, SortableArea } from 'comins-sortable/vue';import type { AfterDragResult } from 'comins-sortable/core';import 'comins-sortable/styles.css';const props = withDefaults(defineProps<{ lang?: string }>(), { lang: 'ko' });const ko = props.lang === 'ko';type Item = { id: string };const initial: Item[] = ['A','B','C','D'].map(id => ({ id }));const items = ref<Item[]>([...initial]);const result = ref<AfterDragResult | null>(null);function afterDrag(event: AfterDragResult) { result.value = event; }function reset() { items.value = [...initial]; result.value = null; }</script><template> <div class="demo" data-demo="swap-vue"> <div class="demo-toolbar"><span>{{ ko ? 'A의 손잡이를 C의 중앙에 놓으십시오.' : 'Drop the A handle onto the center of C.' }}</span><button @click="reset">{{ ko ? '초기화' : 'Reset' }}</button></div> <SortableRoot :on-after-drag="afterDrag"> <SortableArea v-model="items" area-id="items" item-key="id" swap direction="vertical" handle=".task-handle" :component-props="{ class: 'task-list swap-list' }"> <template #item="{ item }"><div class="task" :data-swap-id="item.id"><button class="task-handle" :aria-label="`Drag ${item.id}`">⠿</button><span>{{ item.id }}</span></div></template> </SortableArea> </SortableRoot> <p role="status" data-swap-order>{{ items.map(item => item.id).join(', ') }}</p> <pre data-swap-result>{{ JSON.stringify(result, null, 2) }}</pre> </div></template>