forked from MaxLeiter/sortablejs-vue3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSortable.vue
129 lines (118 loc) · 4 KB
/
Sortable.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<script setup lang="ts">
import { ref, PropType, watch, onUnmounted, computed, useAttrs, Ref } from "vue";
import Sortable, { SortableOptions } from "sortablejs";
import type { AutoScrollOptions } from "sortablejs/plugins";
type SortableOptionsProp = Omit<
SortableOptions | AutoScrollOptions,
| "onUnchoose"
| "onChoose"
| "onStart"
| "onEnd"
| "onAdd"
| "onUpdate"
| "onSort"
| "onRemove"
| "onFilter"
| "onMove"
| "onClone"
| "onChange"
>;
const props = defineProps({
/** All SortableJS options are supported; events are handled by the `defineEmits` below and should be used with v-on */
options: {
type: Object as PropType<SortableOptionsProp>,
default: null,
required: false,
},
/** Your list of items **/
list: {
type: [Array, Object] as PropType<any[]>,
default: [],
required: true,
},
/** The name of the key present in each item in the list that corresponds to a unique value. */
itemKey: {
type: [String, Function] as PropType<
string | ((item: any) => string | number | Symbol)
>,
default: "",
required: true,
},
/** The element type to render as. */
tag: {
type: String as PropType<string>,
default: "div",
required: false,
},
});
const emit = defineEmits<{
(eventName: "choose", evt: Sortable.SortableEvent): void;
(eventName: "unchoose", evt: Sortable.SortableEvent): void;
(eventName: "start", evt: Sortable.SortableEvent): void;
(eventName: "end", evt: Sortable.SortableEvent): void;
(eventName: "add", evt: Sortable.SortableEvent): void;
(eventName: "update", evt: Sortable.SortableEvent): void;
(eventName: "sort", evt: Sortable.SortableEvent): void;
(eventName: "remove", evt: Sortable.SortableEvent): void;
(eventName: "filter", evt: Sortable.SortableEvent): void;
(eventName: "move", evt: Sortable.MoveEvent, originalEvent: Event): void;
(eventName: "clone", evt: Sortable.SortableEvent): void;
(eventName: "change", evt: Sortable.SortableEvent): void;
}>();
const attrs = useAttrs();
const containerRef = ref<HTMLElement | null>(null);
const sortable = ref<Sortable | null>(null);
const getKey = computed(() => {
if (typeof props.itemKey === "string")
return (item: any) => item[props.itemKey as string];
return props.itemKey;
});
defineExpose({ containerRef, sortable: <Ref<Sortable | null>>sortable });
watch(containerRef, (newDraggable) => {
if (newDraggable) {
sortable.value = new Sortable(newDraggable, {
...props.options,
onChoose: (event) => emit("choose", event),
onUnchoose: (event) => emit("unchoose", event),
onStart: (event) => emit("start", event),
onEnd: (event) => emit("end", event),
onAdd: (event) => emit("add", event),
onUpdate: (event) => emit("update", event),
onSort: (event) => emit("sort", event),
onRemove: (event) => emit("remove", event),
onFilter: (event) => emit("filter", event),
// See https://github.com/MaxLeiter/sortablejs-vue3/pull/56 for context on `attrs`.
onMove: (event, originalEvent) => "onMoveCapture" in attrs ? (<(event: Sortable.MoveEvent, originalEvent: Event) => void>attrs.onMoveCapture)(event, originalEvent) : emit("move", event, originalEvent),
onClone: (event) => emit("clone", event),
onChange: (event) => emit("change", event),
});
}
});
watch(
() => props.options,
(options) => {
if (options && sortable?.value) {
for (const property in options) {
sortable.value.option(
property as keyof SortableOptionsProp,
options[property as keyof SortableOptionsProp]
);
}
}
}
);
onUnmounted(() => {
if (sortable.value) {
sortable.value.destroy();
containerRef.value = null;
sortable.value = null;
}
});
</script>
<template>
<component ref="containerRef" :is="$props.tag" :class="$props.class">
<slot name="header"></slot>
<slot v-for="(item, index) of list" :key="getKey(item)" :element="item" :index="index" name="item"></slot>
<slot name="footer"></slot>
</component>
</template>