SortableList
Component for enabling sorting of items accompanied by the flip animation based on Drag'n'Drop API.
Props
items
- type:
SortabeListItem[] - default:
() => []
Sortable items
itemKey
- type:
string - default:
'id'
Name of id field of an item
threshold
- type:
number - default:
null
Threshold in pixels
startDelay
- type:
number - default:
null
Delay before drag start
pending
- type:
boolean
Loading state
Events
| Event name | Properties | Description |
|---|---|---|
| update:items | Update of items | |
| sort | Sorting of items | |
| select | Item selection |
Slots
| Name | Description | Bindings |
|---|---|---|
| default |
Brief description
The logic of the component depends upon the CSS-based flip animation provided by Vue.
As there is no hook to listen to the end of that animation, trick with @transitionend
was necessary. In fact, sorting works as follows:
- User grabs
SortabeListTriggercomponent. Then the closest.sortable-list__itemis searched and once it's found, its clone is created with fixed position and the corresponding list item becomes invisible. - Next, global listeners for
pointerup,pointermove,pointercancelare set up. As the mouse moves across the screen, so does the cloned item. While moving, component checks if the cloned item hovers over another list item. If true, it triggers reordering of the list locally by updating duplicated list of items. - While animation is played, next reordering is disabled. Once it's finished, reordering gets enabled. Animation end is detected by
@transitionendlisteners attached to each affected item. Vue doesn't provide any hooks for that. - Once user has released mouse button, clone of the active list item is destroyed, all global listeners are removed too and event
sortis sent with the following argument type:
interface SortEventArgument<Item extends Record<string, any>> {
updatedIds: (number|string)[];
updatedItems: Item[]
oldItems: Item[];
oldIds: (number|string)[];
start: () => void;
rollback: () => void;
}
start function should be called before the request to save new order is sent.
That function updates the list of items by emitting event update:items with updatedItems.
In case of request failure, rollback needs to be called. It restores the initial value of
items by emitting event update:items with oldItems.
Events
update:items
- type
Array<Record<string, any>>
Items update
sort
- type:
SortEventArgument
Sort saving event
Scoped slots
default
- required:
true - prop:
SortableListTrigger- component that triggers sort. - prop:
triggerListeners- listeners for sort triggering. - prop:
item- currently iterated list item. - prop:
activeItem- picked list item if it exists. - prop:
activeKey- id of the picked list item if it exists. - prop:
pending- pending flag.
Example
<template>
<SortableList
:items.sync="items"
@sort="onSort"
>
<template #default="{ item, SortableListTrigger, triggerListeners }">
<Component
:is="SortableListTrigger"
v-on="triggerListeners"
>
<div>
{{ item.name }}
</div>
</Component>
</template>
</SortableList>
</template>
<script>
import flatry from 'flatry';
import SortableList from '@/components/sortable-list';
// @vue/component
export default {
components: {
SortableList
},
data() {
return {
items: [
{
id: 1,
name: 'Item 1'
},
{
id: 2,
name: 'Item 2'
},
{
id: 3,
name: 'Item 3'
},
]
}
},
methods: {
async onSort({
updatedIds,
start,
rollback
}) {
start();
const [err] = await flatry(this.$axios.$patch('v1/templates/ord', {
templateIds: updatedIds
}));
if (err) {
rollback();
this.$toat.erro(err.serverError || 'Failed sort update !');
}
}
}
}
</script>