Draggable.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <script setup lang="ts">
  2. import { PropType, onMounted, ref } from "vue";
  3. const props = defineProps({
  4. name: { type: String, default: "" },
  5. itemKey: { type: String, default: "" },
  6. list: { type: Array as PropType<any[]>, default: () => [] },
  7. componentData: { type: Object, default: () => ({}) },
  8. attributes: { type: Object, default: () => ({}) },
  9. options: { type: Object, default: () => ({}) },
  10. tag: { type: String, default: "div" },
  11. class: { type: String, default: "" }
  12. });
  13. const mounted = ref(false);
  14. onMounted(() => {
  15. mounted.value = true;
  16. });
  17. const emit = defineEmits(["update:list", "start", "end", "update"]);
  18. // When an element starts being dragged
  19. const onDragStart = (itemIndex: number, event: DragEvent) => {
  20. // console.log(111, event);
  21. // Set the effect of moving an element, which by default is clone. Not being used right now
  22. event.dataTransfer.dropEffect = "move";
  23. // Sets the dragging element index, list name and adds a remove function for when this item is moved to a different list
  24. window.draggingItemIndex = itemIndex;
  25. window.draggingItemListName = props.name;
  26. window.draggingItemOnMove = index => {
  27. delete window.draggingItemOnMove;
  28. const list = props.list.slice();
  29. const listItem = list.splice(index, 1)[0];
  30. emit("update:list", list);
  31. return listItem;
  32. };
  33. // Emits the start event to the parent component, indicating that dragging has started
  34. emit("start");
  35. };
  36. // When a dragging element hovers over another draggable element, this gets triggered, usually many times in a second
  37. const onDragOver = (itemIndex: number) => {
  38. // console.log(321, itemIndex);
  39. // The index and list name of the item that is being dragged, stored in window since it can come from another list as well
  40. const fromIndex = window.draggingItemIndex;
  41. const fromList = window.draggingItemListName;
  42. // The new index and list name of the item that is being dragged
  43. const toIndex = itemIndex;
  44. const toList = props.name;
  45. // console.log(3211, fromIndex, fromList, toIndex, toList);
  46. // If the item hasn't changed position in the same list, don't continue
  47. if (fromIndex === toIndex && fromList === toList) return;
  48. // Update the index and list name of the dragged item
  49. window.draggingItemIndex = toIndex;
  50. window.draggingItemListName = props.name;
  51. // If the item comes from another list
  52. if (toList !== fromList) {
  53. // Call the remove function from the dragging element, which removes the item from the previous list and returns it
  54. const item = window.draggingItemOnMove(fromIndex);
  55. // Define a new remove function for the dragging element
  56. window.draggingItemOnMove = index => {
  57. // Deletes the remove function for the dragging element
  58. delete window.draggingItemOnMove;
  59. // Remove the item from the current list and return it
  60. const list = props.list.slice();
  61. const listItem = list.splice(index, 1)[0];
  62. emit("update:list", list);
  63. return listItem;
  64. };
  65. // Add the item to the list at the new index
  66. const list = props.list.slice();
  67. list.splice(toIndex, 0, item);
  68. emit("update:list", list);
  69. }
  70. // If the item is being reordered in the same list
  71. else {
  72. // Remove the item from the old position, and add the item to the new position
  73. const list = props.list.slice();
  74. list.splice(toIndex, 0, list.splice(fromIndex, 1)[0]);
  75. emit("update:list", list);
  76. }
  77. };
  78. // Gets called when the element that is being dragged is released
  79. const onDragEnd = () => {
  80. // Emits the end event to parent component, indicating that dragging has ended
  81. emit("end");
  82. };
  83. // Gets called when an element is dropped on another element
  84. const onDrop = () => {
  85. // Emits the update event to parent component, indicating that the order is now done and ordering/moving is done
  86. emit("update");
  87. };
  88. // Function that gets called for each item and returns attributes
  89. const convertAttributes = item =>
  90. Object.fromEntries(
  91. Object.entries(props.attributes).map(([key, value]) => [
  92. key,
  93. typeof value === "function" ? value(item) : value
  94. ])
  95. );
  96. </script>
  97. <template>
  98. <component
  99. :is="tag"
  100. v-for="(item, itemIndex) in list"
  101. :key="item[itemKey]"
  102. draggable="true"
  103. @dragstart="onDragStart(itemIndex, $event)"
  104. @dragenter.prevent
  105. @dragover.prevent="onDragOver(itemIndex)"
  106. @dragend="onDragEnd()"
  107. @drop.prevent="onDrop()"
  108. :data-index="itemIndex"
  109. :data-list="name"
  110. v-bind="convertAttributes(item)"
  111. >
  112. <slot name="item" :element="item"></slot>
  113. </component>
  114. </template>