|
@@ -0,0 +1,111 @@
|
|
|
+<script setup lang="ts">
|
|
|
+/* eslint-disable vue/no-mutating-props */
|
|
|
+import { onMounted, ref } from "vue";
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ name: { type: String, default: "" },
|
|
|
+ itemKey: { type: String, default: "" },
|
|
|
+ list: { type: Array, default: () => [] },
|
|
|
+ componentData: { type: Object, default: () => ({}) },
|
|
|
+ options: { type: Object, default: () => ({}) }
|
|
|
+});
|
|
|
+
|
|
|
+const mounted = ref(false);
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ mounted.value = true;
|
|
|
+});
|
|
|
+
|
|
|
+const emit = defineEmits(["start", "end", "update"]);
|
|
|
+
|
|
|
+// When an element starts being dragged
|
|
|
+const onDragStart = (itemIndex, event) => {
|
|
|
+ // Set the effect of moving an element, which by default is clone. Not being used right now
|
|
|
+ event.dataTransfer.dropEffect = "move";
|
|
|
+
|
|
|
+ // Sets the dragging element index, list name and adds a remove function for when this item is moved to a different list
|
|
|
+ window.draggingItemIndex = itemIndex;
|
|
|
+ window.draggingItemListName = props.name;
|
|
|
+ window.draggingItemOnMove = index => {
|
|
|
+ window.draggingItemOnMove = null;
|
|
|
+ return props.list.splice(index, 1)[0];
|
|
|
+ };
|
|
|
+
|
|
|
+ // Emits the start event to the parent component, indicating that dragging has started
|
|
|
+ emit("start");
|
|
|
+};
|
|
|
+
|
|
|
+// When a dragging element hovers over another draggable element, this gets triggered, usually many times in a second
|
|
|
+const onDragOver = itemIndex => {
|
|
|
+ // The index and list name of the item that is being dragged, stored in window since it can come from another list as well
|
|
|
+ const fromIndex = window.draggingItemIndex;
|
|
|
+ const fromList = window.draggingItemList;
|
|
|
+ // The new index and list name of the item that is being dragged
|
|
|
+ const toIndex = itemIndex;
|
|
|
+ const toList = props.name;
|
|
|
+
|
|
|
+ // If the item hasn't changed position in the same list, don't continue
|
|
|
+ if (fromIndex === toIndex && fromList === toList) return;
|
|
|
+
|
|
|
+ // Update the index and list name of the dragged item
|
|
|
+ window.draggingItemIndex = toIndex;
|
|
|
+ window.draggingItemList = props.name;
|
|
|
+
|
|
|
+ // If the item comes from another list
|
|
|
+ if (toList !== fromList) {
|
|
|
+ // Call the remove function from the dragging element, which removes the item from the previous list and returns it
|
|
|
+ const item = window.draggingItemOnMove(fromIndex);
|
|
|
+ // Define a new remove function for the dragging element
|
|
|
+ window.draggingItemOnMove = index => {
|
|
|
+ // Deletes the remove function for the dragging element
|
|
|
+ window.draggingItemOnMove = null;
|
|
|
+ // Remove the item from the current list and return it
|
|
|
+ return props.list.splice(index, 1)[0];
|
|
|
+ };
|
|
|
+ // Add the item to the list at the new index
|
|
|
+ props.list.splice(toIndex, 0, item);
|
|
|
+ }
|
|
|
+ // If the item is being reordered in the same list
|
|
|
+ else {
|
|
|
+ // Remove the item from the old position, and add the item to the new position
|
|
|
+ props.list.splice(toIndex, 0, props.list.splice(fromIndex, 1)[0]);
|
|
|
+ }
|
|
|
+};
|
|
|
+// Gets called when the element that is being dragged is released
|
|
|
+const onDragEnd = () => {
|
|
|
+ // Emits the end event to parent component, indicating that dragging has ended
|
|
|
+ emit("end");
|
|
|
+};
|
|
|
+// Gets called when an element is dropped on another element
|
|
|
+const onDrop = () => {
|
|
|
+ // Emits the update event to parent component, indicating that the order is now done and ordering/moving is done
|
|
|
+ emit("update");
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div
|
|
|
+ v-for="n in list.length"
|
|
|
+ :key="`${name}-${n - 1}`"
|
|
|
+ :id="`${name}-${n - 1}`"
|
|
|
+ ></div>
|
|
|
+
|
|
|
+ <template v-if="mounted">
|
|
|
+ <div v-for="(item, itemIndex) in list" :key="item[itemKey]">
|
|
|
+ <Teleport :to="`#${name}-${itemIndex}`">
|
|
|
+ <div
|
|
|
+ draggable="true"
|
|
|
+ @dragstart="onDragStart(itemIndex, $event)"
|
|
|
+ @dragenter.prevent
|
|
|
+ @dragover.prevent="onDragOver(itemIndex)"
|
|
|
+ @dragend="onDragEnd()"
|
|
|
+ @drop.prevent="onDrop()"
|
|
|
+ :data-index="itemIndex"
|
|
|
+ :data-list="name"
|
|
|
+ >
|
|
|
+ <slot name="item" :element="item"></slot>
|
|
|
+ </div>
|
|
|
+ </Teleport>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+</template>
|