Vue — 두 항목 위치 교환
Vue에서 두 항목만 교환하는 swap과 완료 이벤트를 확인합니다.
삽입 정렬과 다른 결과
섹션 제목: “삽입 정렬과 다른 결과”[A, B, C, D]에서 A를 C 중앙에 놓으면 [C, B, A, D]가 됩니다. 두 항목만 위치를 교환하고 B와 D는 유지합니다. 일반 삽입 정렬처럼 중간 항목을 밀어내는 동작이 아닙니다.
- A 손잡이를 C 중앙까지 드래그하여 놓습니다.
- 결과 JSON에서
status: "dropped",change.operation: "swap",change.itemId: "A",change.swapItemId: "C"를 확인합니다. - 드래그를 시작한 뒤 Escape로 취소하면 순서가 유지됩니다.
- 초기화하면 A, B, C, D 순서로 돌아옵니다.
공개 옵션
섹션 제목: “공개 옵션”| 옵션 | 값/역할 |
|---|---|
swap |
true — 같은 영역에서 출발 항목과 대상 항목만 교환합니다. |
direction |
"vertical" — 이 예제의 단일 세로 목록 기준입니다. |
itemKey 또는 data-sortable-id |
이동 전후 유지되는 안정 ID를 지정합니다. |
handle |
".task-handle" — 포인터 입력을 손잡이로 제한합니다. |
Vue 상태 반영
섹션 제목: “Vue 상태 반영”SortableRoot → SortableArea, #item · v-model (modelValue / update:modelValue)
어댑터가 전달하는 다음 배열을 즉시 상태에 반영합니다. 원본 배열을 직접 바꾸거나 index를 key로 사용하지 않습니다. onAfterDrag의 완료 결과와 표시 순서를 함께 확인합니다.
0.1.6의 Vue SFC 제네릭 추론을 사용합니다. SortableArea는 modelValue에서 항목·슬롯 타입을 추론하므로 별도 컴포넌트 타입 단언이 필요하지 않습니다.
적용 범위
섹션 제목: “적용 범위”이 예제는 같은 영역의 단일 항목 Swap입니다. Grid collision, multiDrag, threshold/invertSwap와의 조합, 여러 영역 사이의 전달은 별도 시나리오입니다. 포인터 기반 예제이며 드래그 handle의 focus가 전체 키보드 정렬 지원을 뜻하지 않습니다.
관련 문서
섹션 제목: “관련 문서”고급 정렬 가이드 · Core 생명주기 · Vanilla 인스턴스 · 일반 목록 이동
직접 실행
섹션 제목: “직접 실행”<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>