Sorting Lists
Vue DnD Kit provides powerful capabilities for creating sortable lists with drag and drop functionality, allowing items to be reordered within a single list or moved between different lists.
Overview
Vue DnD Kit offers several key features for working with sortable lists:
- Internal sorting: Reorder items within the same list
- Cross-list transfers: Move items between different lists
- Transition animations: Smooth animations during item movement
- Automatic updates: Built-in operations for data management
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Implementation
Draggable Component
Create a draggable component for list items:
vue
<script setup lang="ts">
import { useDraggable } from '@vue-dnd-kit/core';
import { computed } from 'vue';
const { index, source } = defineProps<{
index: number;
source: any[];
}>();
// Using computed for index and source ensures reactivity
// This is especially important when working with nested lists
const { elementRef, handleDragStart, isOvered, isDragging } = useDraggable({
data: computed(() => ({
index,
source,
})),
});
</script>
<template>
<div
ref="elementRef"
@pointerdown="handleDragStart"
:class="{
'is-over': isOvered,
'is-dragging': isDragging,
}"
class="draggable"
>
<slot />
</div>
</template>
<style>
.draggable {
padding: 10px 16px;
border: 1px solid #3eaf7c;
border-radius: 6px;
cursor: move;
background-color: #3eaf7c;
color: white;
font-weight: 500;
font-size: 14px;
width: 100%;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(62, 175, 124, 0.2);
user-select: none;
touch-action: none;
opacity: 0.5;
}
.draggable:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(62, 175, 124, 0.3);
}
.is-dragging {
opacity: 0.2;
}
.is-over {
border-color: #3eaf7c;
box-shadow: 0 0 0 3px rgba(62, 175, 124, 0.3);
background-color: #44c98d;
opacity: 1;
}
</style>
Example Component
Main component showcasing sortable lists functionality:
vue
<script setup lang="ts">
import { ref } from 'vue';
import Draggable from './Draggable.vue';
import { useDroppable, DnDOperations } from '@vue-dnd-kit/core';
const firstColumn = ref([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]);
const secondColumn = ref([
{ id: 4, name: 'Item 4' },
{ id: 5, name: 'Item 5' },
{ id: 6, name: 'Item 6' },
]);
const { elementRef: firstColumnRef } = useDroppable({
data: {
source: firstColumn.value,
},
events: {
onDrop: DnDOperations.applyTransfer,
},
});
const { elementRef: secondColumnRef } = useDroppable({
data: {
source: secondColumn.value,
},
events: {
onDrop: DnDOperations.applyTransfer,
},
});
</script>
<template>
<div class="container">
<div
class="dropzone"
ref="firstColumnRef"
>
<TransitionGroup name="list">
<Draggable
v-for="(item, index) in firstColumn"
:key="item.id"
:index="index"
:source="firstColumn"
>
{{ item.name }}
</Draggable>
</TransitionGroup>
</div>
<div
class="dropzone"
ref="secondColumnRef"
>
<TransitionGroup name="list">
<Draggable
v-for="(item, index) in secondColumn"
:key="item.id"
:index="index"
:source="secondColumn"
>
{{ item.name }}
</Draggable>
</TransitionGroup>
</div>
</div>
</template>
<style>
.container {
display: flex;
flex-direction: column;
gap: 1rem;
}
.dropzone {
position: relative;
width: 100%;
height: 100%;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
border: 1px dashed rgba(62, 175, 124, 0.3);
border-radius: 6px;
background-color: rgba(62, 175, 124, 0.1);
}
.list-move {
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.list-enter-active,
.list-leave-active {
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(-20px);
}
.list-leave-active {
position: absolute;
pointer-events: none;
}
</style>
Key Features
Automatic Transfer Operations
Vue DnD Kit includes built-in operations for handling common list operations:
js
import { DnDOperations } from '@vue-dnd-kit/core';
// In your droppable configuration
const { elementRef } = useDroppable({
events: {
onDrop: DnDOperations.applyTransfer, // Handles item movement automatically
},
});
Transition Animations
Implement smooth animations using Vue's transition system:
vue
<template>
<div class="dropzone">
<TransitionGroup name="list">
<Draggable
v-for="(item, index) in items"
:key="item.id"
:index="index"
:source="items"
>
{{ item.name }}
</Draggable>
</TransitionGroup>
</div>
</template>
<style>
.list-move {
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.list-enter-active,
.list-leave-active {
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(-20px);
}
</style>
Visual Feedback
Add visual cues for drag and drop interactions:
css
.dropzone {
padding: 1rem;
border: 1px dashed rgba(62, 175, 124, 0.3);
border-radius: 6px;
background-color: rgba(62, 175, 124, 0.1);
}
.draggable {
cursor: move;
transition: all 0.2s ease;
}
.is-dragging {
opacity: 0.2;
}
.is-over {
background-color: rgba(62, 175, 124, 0.2);
}
Best Practices
- Unique Keys: Always provide unique keys for list items to ensure proper rendering and animations
- Computed Properties: Use computed properties for dynamic data in draggable components
- Touch Support: Include touch-action CSS property for better mobile support
- Accessibility: Add appropriate ARIA attributes and keyboard navigation
- Animation Performance: Use transform and opacity for smooth animations