useSortable
A composable that leverages the HTML Drag and Drop API to provide a simple and flexible way to reorder items.
Import
import useSortable from '@leaflink/stash/useSortable';
Usage
By just informing the parent reference and the list of items, the composable will automatically handle drag and drop events.
INFO
The consumer component must define which children are draggable by setting the draggable="true"
attribute.
<script setup lang="ts">
import { ref } from 'vue';
import useSortable from '../../../src/composables/useSortable/useSortable';
const items = ref(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
const containerRef = ref<HTMLElement | null>(null);
useSortable({ ref: containerRef, list: items });
</script>
<template>
<div class="tw-space-y-4">
<div ref="containerRef" class="tw-space-y-2">
<div
v-for="item in items"
:key="`dragging_classes_${item}`"
class="tw-cursor-move tw-rounded tw-border tw-bg-white tw-p-4 hover:tw-border-blue-500"
draggable="true"
>
{{ item }}
</div>
</div>
<div>
<p>Items list</p>
<div>{{ JSON.stringify(items, null, 2) }}</div>
</div>
</div>
</template>
Items list
Dragging classes
The ghostClass
and chosenClass
options allow you to customize the appearance of the ghost and chosen elements.
Items list
[ "Item 1", "Item 2", "Item 3", "Item 4" ]<script setup lang="ts">
import { ref } from 'vue';
import useSortable from '../../../src/composables/useSortable/useSortable';
const items = ref(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
const containerRef = ref<HTMLElement | null>(null);
useSortable({
ref: containerRef,
list: items,
ghostClass: 'tw-border-blue tw-border-4',
chosenClass: 'tw-opacity-0',
});
</script>
<template>
<div class="tw-space-y-4">
<div ref="containerRef" class="tw-space-y-2">
<div
v-for="item in items"
:key="`dragging_classes_${item}`"
class="tw-cursor-move tw-rounded tw-border tw-bg-white tw-p-4 hover:tw-border-blue-500"
draggable="true"
>
{{ item }}
</div>
</div>
<div>
<p>Items list</p>
{{ JSON.stringify(items, null, 2) }}
</div>
</div>
</template>
Disabling dragging
The composable requires the parent element to define which children are draggable by setting the draggable="true"
attribute. If you want to disable dragging for a specific element, just add draggable="false"
or remove the attribute from the element.
Items list
<script setup lang="ts">
import { ref } from 'vue';
import useSortable from '../../../src/composables/useSortable/useSortable';
const items = ref(['Item 1', 'Item 2']);
const containerRef = ref<HTMLElement | null>(null);
useSortable({ ref: containerRef, list: items });
</script>
<template>
<div class="tw-space-y-4">
<div ref="containerRef" class="tw-space-y-2">
<div
v-for="drag in items"
:key="drag"
class="tw-cursor-move tw-rounded tw-border tw-bg-white tw-p-4 hover:tw-border-blue-500"
draggable="true"
>
{{ drag }}
</div>
<div
v-for="disabledDrag in 2"
:key="`disabled-drag_${disabledDrag}`"
class="tw-rounded tw-border tw-bg-white tw-p-4"
>
Not Draggable {{ disabledDrag }}
</div>
</div>
<div>
<p>Items list</p>
<div>{{ JSON.stringify(items, null, 2) }}</div>
</div>
</div>
</template>
Sort in place
By default sortInPlace
is set to true
, which means the list will be sorted in place. If you want to disable this behavior, set it to false
.
Items list
<script setup lang="ts">
import { ref } from 'vue';
import useSortable from '../../../src/composables/useSortable/useSortable';
const items = ref(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
const containerRef = ref<HTMLElement | null>(null);
useSortable({
ref: containerRef,
list: items,
sortInPlace: false,
ghostClass: 'tw-border-blue tw-border-4',
chosenClass: 'tw-opacity-0',
});
</script>
<template>
<div class="tw-space-y-4">
<div ref="containerRef" class="tw-space-y-2">
<div
v-for="item in items"
:key="`dragging_classes_${item}`"
class="tw-cursor-move tw-rounded tw-border tw-bg-white tw-p-4 hover:tw-border-blue-500"
draggable="true"
>
{{ item }}
</div>
</div>
<div>
<p>Items list</p>
<div>{{ JSON.stringify(items, null, 2) }}</div>
</div>
</div>
</template>
Config
export interface UseSortableOptions<SortableItem> {
/**
* The parent element of the sortable elements
*/
ref: Ref<HTMLElement | null>;
/**
* The list of sortable elements to be sorted
*/
list: Ref<SortableItem[]>;
/**
* Enables/Disables drag and drop sorting
* @default true
*/
isEnabled?: boolean;
/**
* Class name for the ghost element
* @default ''
*/
ghostClass?: string;
/**
* Class name for the chosen element
* @default ''
*/
chosenClass?: string;
/**
* Sort the list in place
* @default true
*/
sortInPlace?: boolean;
/**
* Callback when the dragging starts
*/
onDragStart?: (e: SortableOnDragStartEvent) => void;
/**
* Callback when the dragging ends
*/
onDragEnd?: (e: SortableOnDragEndEvent) => void;
}
export interface SortableDragEvent extends DragEvent {
/**
* The old index of the dragged element
*/
oldIndex: number;
/**
* The new index of the dragged element
*/
newIndex: number;
}
export type SortableOnDragStartEvent = Omit<SortableDragEvent, 'newIndex'>;
export type SortableOnDragEndEvent = SortableDragEvent;
export interface UseSortableReturn {
/**
* Whether the element is currently being dragged
*/
isDragging: Ref<boolean>;
/**
* The new position of the dragged element
*/
newIndex: Ref<number>;
/**
* The original position of the dragged element
*/
oldIndex: Ref<number>;
}