@bagelink/vue 1.15.433 → 1.15.440
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DragOver.vue.d.ts.map +1 -1
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/draggable/Draggable.vue.d.ts.map +1 -1
- package/dist/components/draggable/index.d.ts +1 -0
- package/dist/components/draggable/index.d.ts.map +1 -1
- package/dist/components/draggable/reorder.d.ts +5 -0
- package/dist/components/draggable/reorder.d.ts.map +1 -0
- package/dist/components/draggable/useDraggable.d.ts +5 -1
- package/dist/components/draggable/useDraggable.d.ts.map +1 -1
- package/dist/components/draggable/vDraggable.d.ts.map +1 -1
- package/dist/components/file-drop/useFileDropzone.d.ts +23 -0
- package/dist/components/file-drop/useFileDropzone.d.ts.map +1 -0
- package/dist/components/file-drop/useFileDropzone.test.d.ts +2 -0
- package/dist/components/file-drop/useFileDropzone.test.d.ts.map +1 -0
- package/dist/components/form/inputs/ArrayInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/Panel.vue.d.ts.map +1 -1
- package/dist/components/layout/Resizable.vue.d.ts.map +1 -1
- package/dist/components/layout/appLayoutContext.d.ts +1 -1
- package/dist/components/layout/appLayoutContext.d.ts.map +1 -1
- package/dist/composables/index.d.ts +6 -0
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/resizeGeometry.d.ts +20 -0
- package/dist/composables/resizeGeometry.d.ts.map +1 -0
- package/dist/composables/useBoundedRectDrag.d.ts +26 -0
- package/dist/composables/useBoundedRectDrag.d.ts.map +1 -0
- package/dist/composables/usePointerSession.d.ts +17 -0
- package/dist/composables/usePointerSession.d.ts.map +1 -0
- package/dist/composables/useResizableLayout.d.ts +1 -2
- package/dist/composables/useResizableLayout.d.ts.map +1 -1
- package/dist/composables/useResizeObserver.d.ts +5 -4
- package/dist/composables/useResizeObserver.d.ts.map +1 -1
- package/dist/core.css +1 -1
- package/dist/directives/vResize.d.ts +2 -1
- package/dist/directives/vResize.d.ts.map +1 -1
- package/dist/index.cjs +42 -51
- package/dist/index.css +1 -1
- package/dist/index.mjs +10269 -9989
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/DragOver.vue +6 -41
- package/src/components/dataTable/DataTable.vue +11 -7
- package/src/components/draggable/Draggable.vue +5 -6
- package/src/components/draggable/index.ts +1 -0
- package/src/components/draggable/reorder.ts +17 -0
- package/src/components/draggable/useDraggable.ts +73 -71
- package/src/components/draggable/vDraggable.ts +18 -8
- package/src/components/file-drop/useFileDropzone.test.ts +108 -0
- package/src/components/file-drop/useFileDropzone.ts +83 -0
- package/src/components/form/inputs/ArrayInput.vue +3 -5
- package/src/components/form/inputs/Upload/UploadInput.vue +11 -21
- package/src/components/index.ts +4 -1
- package/src/components/layout/AppLayout.design.md +13 -15
- package/src/components/layout/AppLayout.vue +57 -38
- package/src/components/layout/Panel.vue +19 -1
- package/src/components/layout/Resizable.vue +2 -5
- package/src/components/layout/appLayoutContext.ts +2 -1
- package/src/composables/index.ts +6 -0
- package/src/composables/resizeGeometry.ts +31 -0
- package/src/composables/useBoundedRectDrag.ts +69 -0
- package/src/composables/usePointerSession.ts +70 -0
- package/src/composables/useResizableLayout.ts +66 -55
- package/src/composables/useResizeObserver.ts +32 -27
- package/src/directives/vResize.ts +86 -165
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Icon } from '@bagelink/vue'
|
|
3
|
-
import {
|
|
3
|
+
import { useFileDropzone } from './file-drop/useFileDropzone'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
multiple?: boolean
|
|
@@ -13,32 +13,10 @@ const emit = defineEmits<{
|
|
|
13
13
|
(e: 'addFiles', files: FileList): void
|
|
14
14
|
}>()
|
|
15
15
|
|
|
16
|
-
const isDragging =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (props.disabled) { return }
|
|
21
|
-
e.preventDefault()
|
|
22
|
-
e.stopPropagation()
|
|
23
|
-
dragCounter.value++
|
|
24
|
-
isDragging.value = true
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function handleDragOver(e: DragEvent) {
|
|
28
|
-
if (props.disabled) { return }
|
|
29
|
-
e.preventDefault()
|
|
30
|
-
e.stopPropagation()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function handleDragLeave(e: DragEvent) {
|
|
34
|
-
if (props.disabled) { return }
|
|
35
|
-
e.preventDefault()
|
|
36
|
-
e.stopPropagation()
|
|
37
|
-
dragCounter.value--
|
|
38
|
-
if (dragCounter.value === 0) {
|
|
39
|
-
isDragging.value = false
|
|
40
|
-
}
|
|
41
|
-
}
|
|
16
|
+
const { isDragging, handlers } = useFileDropzone({
|
|
17
|
+
disabled: () => props.disabled,
|
|
18
|
+
onDrop: files => emit('addFiles', files),
|
|
19
|
+
})
|
|
42
20
|
|
|
43
21
|
function handleClick(e: MouseEvent) {
|
|
44
22
|
if (!props.browse || props.disabled) {
|
|
@@ -59,16 +37,6 @@ function handleClick(e: MouseEvent) {
|
|
|
59
37
|
input.click()
|
|
60
38
|
}
|
|
61
39
|
|
|
62
|
-
function handleDrop(e: DragEvent) {
|
|
63
|
-
if (props.disabled) { return }
|
|
64
|
-
e.preventDefault()
|
|
65
|
-
e.stopPropagation()
|
|
66
|
-
dragCounter.value = 0
|
|
67
|
-
isDragging.value = false
|
|
68
|
-
|
|
69
|
-
if (!e.dataTransfer) { return }
|
|
70
|
-
emit('addFiles', e.dataTransfer.files)
|
|
71
|
-
}
|
|
72
40
|
</script>
|
|
73
41
|
|
|
74
42
|
<template>
|
|
@@ -78,10 +46,7 @@ function handleDrop(e: DragEvent) {
|
|
|
78
46
|
'is-dragging': isDragging,
|
|
79
47
|
'is-disabled': disabled,
|
|
80
48
|
}"
|
|
81
|
-
|
|
82
|
-
@dragover="handleDragOver"
|
|
83
|
-
@dragleave="handleDragLeave"
|
|
84
|
-
@drop="handleDrop"
|
|
49
|
+
v-on="handlers"
|
|
85
50
|
@click="handleClick"
|
|
86
51
|
>
|
|
87
52
|
<slot />
|
|
@@ -94,14 +94,18 @@ const dragHandle = computed(() => {
|
|
|
94
94
|
const tbodyRef = ref<HTMLTableSectionElement>()
|
|
95
95
|
|
|
96
96
|
const { initDraggableContainer } = useDraggable({
|
|
97
|
-
handle
|
|
97
|
+
get handle() { return dragHandle.value },
|
|
98
|
+
get disabled() { return !isSortable.value },
|
|
98
99
|
mode: 'line',
|
|
99
|
-
items
|
|
100
|
+
get items() { return list.value.map(entry => entry.data) },
|
|
100
101
|
onEnd: (event: DraggableEvent) => {
|
|
101
102
|
if (event.oldIndex === event.newIndex) return
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
103
|
+
const source = list.value[event.oldIndex]
|
|
104
|
+
const target = list.value[event.newIndex]
|
|
105
|
+
if (!source || !target) return
|
|
106
|
+
const cleanItem = cleanTransformedData(source.data)
|
|
107
|
+
// Virtual indices include the synthetic header at index zero.
|
|
108
|
+
emit('reorder', { oldIndex: source.index - 1, newIndex: target.index - 1, item: cleanItem })
|
|
105
109
|
}
|
|
106
110
|
})
|
|
107
111
|
|
|
@@ -112,7 +116,7 @@ function reinitDrag() {
|
|
|
112
116
|
})
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
watch(
|
|
119
|
+
watch(list, reinitDrag, { flush: 'post' })
|
|
116
120
|
watch(isSortable, (val) => { if (val) nextTick(reinitDrag) })
|
|
117
121
|
onMounted(reinitDrag)
|
|
118
122
|
|
|
@@ -225,7 +229,7 @@ const headerFontSize = computed(() => {
|
|
|
225
229
|
</div>
|
|
226
230
|
</td>
|
|
227
231
|
</tr>
|
|
228
|
-
<tr v-if="props.onLastItemVisible !== undefined" ref="lastItemEl" style="height: 1px;" />
|
|
232
|
+
<tr v-if="props.onLastItemVisible !== undefined" ref="lastItemEl" data-draggable="false" style="height: 1px;" />
|
|
229
233
|
</tbody>
|
|
230
234
|
</table>
|
|
231
235
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { DraggableEvent } from './useDraggable'
|
|
3
3
|
import { ref, watch, onMounted, nextTick } from 'vue'
|
|
4
4
|
import { useDraggable } from './useDraggable'
|
|
5
|
+
import { reorderItems } from './reorder'
|
|
5
6
|
|
|
6
7
|
const props = withDefaults(defineProps<{
|
|
7
8
|
modelValue?: any[]
|
|
@@ -22,7 +23,7 @@ const emit = defineEmits<{
|
|
|
22
23
|
|
|
23
24
|
const dragContainer = ref<HTMLElement>()
|
|
24
25
|
|
|
25
|
-
const { initDraggableContainer } = useDraggable({
|
|
26
|
+
const { initDraggableContainer } = useDraggable(() => ({
|
|
26
27
|
group: props.group,
|
|
27
28
|
handle: props.handle,
|
|
28
29
|
mode: props.mode,
|
|
@@ -30,9 +31,7 @@ const { initDraggableContainer } = useDraggable({
|
|
|
30
31
|
items: props.modelValue,
|
|
31
32
|
onStart: (event) => { emit('start', event) },
|
|
32
33
|
onEnd: (event: DraggableEvent) => {
|
|
33
|
-
const newList =
|
|
34
|
-
const [removed] = newList.splice(event.oldIndex, 1)
|
|
35
|
-
newList.splice(event.newIndex, 0, removed)
|
|
34
|
+
const newList = reorderItems(props.modelValue ?? [], event.oldIndex, event.newIndex)
|
|
36
35
|
emit('update:modelValue', newList)
|
|
37
36
|
|
|
38
37
|
emit('end', event)
|
|
@@ -42,13 +41,13 @@ const { initDraggableContainer } = useDraggable({
|
|
|
42
41
|
}
|
|
43
42
|
})
|
|
44
43
|
}
|
|
45
|
-
})
|
|
44
|
+
}))
|
|
46
45
|
|
|
47
46
|
watch(() => props.modelValue, () => {
|
|
48
47
|
if (dragContainer.value) {
|
|
49
48
|
initDraggableContainer(dragContainer.value)
|
|
50
49
|
}
|
|
51
|
-
}, { deep: true })
|
|
50
|
+
}, { deep: true, flush: 'post' })
|
|
52
51
|
|
|
53
52
|
onMounted(() => {
|
|
54
53
|
if (dragContainer.value) {
|
|
@@ -2,3 +2,4 @@ export { default as Draggable } from './Draggable.vue'
|
|
|
2
2
|
export { useDraggable } from './useDraggable'
|
|
3
3
|
export type { DraggableEvent, DraggableOptions } from './useDraggable'
|
|
4
4
|
export { vDraggable } from './vDraggable'
|
|
5
|
+
export { insertionIndexToFinalIndex, reorderItems } from './reorder'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Convert a boundary in the original list to an index after removing the source. */
|
|
2
|
+
export function insertionIndexToFinalIndex(oldIndex: number, insertionIndex: number, length: number): number {
|
|
3
|
+
if (!Number.isInteger(oldIndex) || oldIndex < 0 || oldIndex >= length
|
|
4
|
+
|| !Number.isInteger(insertionIndex) || insertionIndex < 0 || insertionIndex > length) return -1
|
|
5
|
+
return insertionIndex > oldIndex ? insertionIndex - 1 : insertionIndex
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Return a reordered copy; both indices refer to item positions, not boundaries. */
|
|
9
|
+
export function reorderItems<T>(items: readonly T[], oldIndex: number, newIndex: number): T[] {
|
|
10
|
+
const result = [...items]
|
|
11
|
+
if (!Number.isInteger(oldIndex) || !Number.isInteger(newIndex)
|
|
12
|
+
|| oldIndex < 0 || newIndex < 0 || oldIndex >= result.length || newIndex >= result.length
|
|
13
|
+
|| oldIndex === newIndex) return result
|
|
14
|
+
const [item] = result.splice(oldIndex, 1)
|
|
15
|
+
result.splice(newIndex, 0, item!)
|
|
16
|
+
return result
|
|
17
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ref, onUnmounted } from 'vue'
|
|
1
|
+
import { ref, computed, toValue, getCurrentInstance, onUnmounted, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import { insertionIndexToFinalIndex } from './reorder'
|
|
2
3
|
|
|
3
4
|
export interface DraggableEvent extends MouseEvent {
|
|
4
5
|
oldIndex: number
|
|
6
|
+
/** Final array index after removing the source item, in both line and ghost modes. */
|
|
5
7
|
newIndex: number
|
|
6
8
|
item?: any
|
|
7
9
|
}
|
|
@@ -28,7 +30,7 @@ export interface DraggableElement extends HTMLElement {
|
|
|
28
30
|
__drag_data?: any
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
export function useDraggable(options: DraggableOptions = {}) {
|
|
33
|
+
export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
32
34
|
const isDragging = ref(false)
|
|
33
35
|
const dragElement = ref<DraggableElement | null>(null)
|
|
34
36
|
const ghostElement = ref<HTMLElement | null>(null)
|
|
@@ -55,14 +57,14 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
const defaultOptions
|
|
60
|
+
const defaultOptions = computed(() => ({
|
|
59
61
|
animation: 150,
|
|
60
62
|
mode: 'line',
|
|
61
63
|
ghostClass: 'draggable-ghost',
|
|
62
64
|
dragClass: 'draggable-dragging',
|
|
63
65
|
dropIndicatorClass: 'draggable-drop-indicator',
|
|
64
|
-
...options
|
|
65
|
-
}
|
|
66
|
+
...toValue(options)
|
|
67
|
+
}))
|
|
66
68
|
|
|
67
69
|
let startX = 0
|
|
68
70
|
let startY = 0
|
|
@@ -87,11 +89,11 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
87
89
|
element.style.margin = '0'
|
|
88
90
|
element.style.padding = '0'
|
|
89
91
|
element.style.border = '0'
|
|
90
|
-
element.showPopover()
|
|
92
|
+
element.showPopover?.()
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
function removeFromTopLayer(element: HTMLElement) {
|
|
94
|
-
if (element.matches(':popover-open')) element.hidePopover()
|
|
96
|
+
if (element.hidePopover && element.matches(':popover-open')) element.hidePopover()
|
|
95
97
|
element.parentNode?.removeChild(element)
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -110,15 +112,15 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
110
112
|
ghost.style.zIndex = '9999'
|
|
111
113
|
ghost.style.cursor = 'grabbing'
|
|
112
114
|
ghost.style.willChange = 'transform'
|
|
113
|
-
ghost.classList.add(defaultOptions.ghostClass!)
|
|
115
|
+
ghost.classList.add(defaultOptions.value.ghostClass!)
|
|
114
116
|
appendToTopLayer(ghost)
|
|
115
117
|
return ghost
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
function createDropIndicator(
|
|
120
|
+
function createDropIndicator() {
|
|
119
121
|
if (!isBrowser) { return null }
|
|
120
122
|
const line = document.createElement('div')
|
|
121
|
-
line.classList.add(defaultOptions.dropIndicatorClass!)
|
|
123
|
+
line.classList.add(defaultOptions.value.dropIndicatorClass!)
|
|
122
124
|
line.style.position = 'fixed'
|
|
123
125
|
line.style.pointerEvents = 'none'
|
|
124
126
|
line.style.zIndex = '9999'
|
|
@@ -179,7 +181,7 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
// function resetDragStyles(dragEl: DraggableElement) {
|
|
182
|
-
// if (defaultOptions.mode === 'ghost') {
|
|
184
|
+
// if (defaultOptions.value.mode === 'ghost') {
|
|
183
185
|
// dragEl.style.opacity = ''
|
|
184
186
|
// dragEl.style.transform = ''
|
|
185
187
|
// } else {
|
|
@@ -258,26 +260,13 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
258
260
|
document.removeEventListener('keydown', onKeyDown)
|
|
259
261
|
document.removeEventListener('touchmove', onTouchMove)
|
|
260
262
|
document.removeEventListener('touchend', onTouchEnd)
|
|
261
|
-
document.removeEventListener('touchcancel',
|
|
263
|
+
document.removeEventListener('touchcancel', cleanup)
|
|
262
264
|
window.removeEventListener('blur', cleanup)
|
|
263
265
|
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
setTimeout(() => {
|
|
269
|
-
if (ghostElement.value) removeFromTopLayer(ghostElement.value)
|
|
270
|
-
ghostElement.value = null
|
|
271
|
-
}, 150)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (dropIndicator.value) {
|
|
275
|
-
dropIndicator.value.style.opacity = '0'
|
|
276
|
-
setTimeout(() => {
|
|
277
|
-
if (dropIndicator.value) removeFromTopLayer(dropIndicator.value)
|
|
278
|
-
dropIndicator.value = null
|
|
279
|
-
}, 150)
|
|
280
|
-
}
|
|
266
|
+
if (ghostElement.value) removeFromTopLayer(ghostElement.value)
|
|
267
|
+
if (dropIndicator.value) removeFromTopLayer(dropIndicator.value)
|
|
268
|
+
ghostElement.value = null
|
|
269
|
+
dropIndicator.value = null
|
|
281
270
|
|
|
282
271
|
// Reset all elements
|
|
283
272
|
if (dragElement.value) {
|
|
@@ -287,7 +276,7 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
287
276
|
el.style.transform = ''
|
|
288
277
|
el.style.opacity = ''
|
|
289
278
|
})
|
|
290
|
-
dragElement.value.classList.remove(defaultOptions.dragClass!)
|
|
279
|
+
dragElement.value.classList.remove(defaultOptions.value.dragClass!)
|
|
291
280
|
dragElement.value = null
|
|
292
281
|
}
|
|
293
282
|
|
|
@@ -317,16 +306,16 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
317
306
|
}
|
|
318
307
|
|
|
319
308
|
function onDragStart(e: MouseEvent) {
|
|
320
|
-
if (!isBrowser || defaultOptions.disabled) { return }
|
|
309
|
+
if (!isBrowser || isDragging.value || defaultOptions.value.disabled) { return }
|
|
321
310
|
if (e.button !== 0) { return } // Only left mouse button
|
|
322
311
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) { return }
|
|
323
312
|
|
|
324
313
|
const dragEl = findDraggableParent(e.target as Element)
|
|
325
|
-
if (!dragEl || dragEl.dataset.draggable === 'false') { return }
|
|
314
|
+
if (!dragEl || dragEl.dataset.draggable === 'false' || e.currentTarget !== dragEl) { return }
|
|
326
315
|
|
|
327
316
|
// If handle is specified, check if clicked element matches
|
|
328
|
-
if (defaultOptions.handle) {
|
|
329
|
-
const handleEl = (e.target as Element).closest(defaultOptions.handle)
|
|
317
|
+
if (defaultOptions.value.handle) {
|
|
318
|
+
const handleEl = (e.target as Element).closest(defaultOptions.value.handle)
|
|
330
319
|
if (!handleEl || !dragEl.contains(handleEl)) { return }
|
|
331
320
|
}
|
|
332
321
|
|
|
@@ -349,17 +338,17 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
349
338
|
}
|
|
350
339
|
|
|
351
340
|
// Only create drop indicator in line mode
|
|
352
|
-
if (defaultOptions.mode === 'line') {
|
|
353
|
-
dropIndicator.value = createDropIndicator(
|
|
341
|
+
if (defaultOptions.value.mode === 'line') {
|
|
342
|
+
dropIndicator.value = createDropIndicator()
|
|
354
343
|
}
|
|
355
344
|
|
|
356
345
|
// Apply mode-specific styles and get shift amount
|
|
357
|
-
dragEl.classList.add(defaultOptions.dragClass!)
|
|
358
|
-
shiftAmount = defaultOptions.mode === 'ghost'
|
|
346
|
+
dragEl.classList.add(defaultOptions.value.dragClass!)
|
|
347
|
+
shiftAmount = defaultOptions.value.mode === 'ghost'
|
|
359
348
|
? handleGhostMode(dragEl)
|
|
360
349
|
: handleLineMode(dragEl)
|
|
361
350
|
|
|
362
|
-
defaultOptions.onStart?.(e)
|
|
351
|
+
defaultOptions.value.onStart?.(e)
|
|
363
352
|
|
|
364
353
|
// Prevent text selection during drag
|
|
365
354
|
if (isBrowser) {
|
|
@@ -430,7 +419,8 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
430
419
|
const newDropTarget = findDraggableParent(elementAtPoint)
|
|
431
420
|
|
|
432
421
|
// Validate drop target
|
|
433
|
-
if (!newDropTarget || newDropTarget
|
|
422
|
+
if (!newDropTarget || !draggedList.includes(newDropTarget)) {
|
|
423
|
+
dropTarget.value = null
|
|
434
424
|
if (dropIndicator.value) {
|
|
435
425
|
dropIndicator.value.style.display = 'none'
|
|
436
426
|
}
|
|
@@ -445,32 +435,33 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
445
435
|
dropTarget.value.__intended_index = dropIndex
|
|
446
436
|
|
|
447
437
|
// Update line indicator
|
|
448
|
-
if (dropIndicator.value && defaultOptions.mode === 'line') {
|
|
438
|
+
if (dropIndicator.value && defaultOptions.value.mode === 'line') {
|
|
449
439
|
const targetIndex = draggedList.indexOf(newDropTarget)
|
|
450
440
|
const isAfter = dropIndex > targetIndex
|
|
451
441
|
updateDropIndicator(newDropTarget, isAfter)
|
|
452
442
|
}
|
|
453
443
|
|
|
454
444
|
// Update element positions
|
|
445
|
+
const finalIndex = insertionIndexToFinalIndex(dragIndex, dropIndex, draggedList.length)
|
|
455
446
|
const shift = shiftAmount
|
|
456
447
|
draggedList.forEach((el) => {
|
|
457
448
|
// Use transform3d for better performance
|
|
458
|
-
el.style.transition = `transform ${defaultOptions.animation}ms cubic-bezier(0.2, 0, 0, 1)`
|
|
449
|
+
el.style.transition = `transform ${defaultOptions.value.animation}ms cubic-bezier(0.2, 0, 0, 1)`
|
|
459
450
|
el.style.transform = 'translate3d(0, 0, 0)'
|
|
460
451
|
|
|
461
452
|
if (el === dragElement.value) {
|
|
462
|
-
if (defaultOptions.mode === 'ghost') {
|
|
463
|
-
const offset = (
|
|
453
|
+
if (defaultOptions.value.mode === 'ghost') {
|
|
454
|
+
const offset = (finalIndex - dragIndex) * shift
|
|
464
455
|
el.style.transform = `translate3d(0, ${offset}px, 0)`
|
|
465
456
|
}
|
|
466
457
|
} else {
|
|
467
458
|
const currentIndex = draggedList.indexOf(el)
|
|
468
|
-
if (dragIndex <
|
|
469
|
-
if (currentIndex > dragIndex && currentIndex <=
|
|
459
|
+
if (dragIndex < finalIndex) {
|
|
460
|
+
if (currentIndex > dragIndex && currentIndex <= finalIndex) {
|
|
470
461
|
el.style.transform = `translate3d(0, -${shift}px, 0)`
|
|
471
462
|
}
|
|
472
|
-
} else if (dragIndex >
|
|
473
|
-
if (currentIndex >=
|
|
463
|
+
} else if (dragIndex > finalIndex) {
|
|
464
|
+
if (currentIndex >= finalIndex && currentIndex < dragIndex) {
|
|
474
465
|
el.style.transform = `translate3d(0, ${shift}px, 0)`
|
|
475
466
|
}
|
|
476
467
|
}
|
|
@@ -482,6 +473,11 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
482
473
|
}
|
|
483
474
|
|
|
484
475
|
function onDragEnd(e: MouseEvent) {
|
|
476
|
+
if (defaultOptions.value.disabled) { cleanup(); return }
|
|
477
|
+
if (isDragging.value) {
|
|
478
|
+
lastUpdateTime = 0
|
|
479
|
+
onDragMove(e)
|
|
480
|
+
}
|
|
485
481
|
if (!isDragging.value || !dragElement.value || !dropTarget.value) {
|
|
486
482
|
cleanup()
|
|
487
483
|
return
|
|
@@ -495,9 +491,9 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
495
491
|
|
|
496
492
|
const draggedList = list.filter(el => el.dataset.draggable !== 'false')
|
|
497
493
|
const actualDragIndex = draggedList.indexOf(dragElement.value)
|
|
498
|
-
const actualDropIndex = dropTarget.value.__intended_index
|
|
494
|
+
const actualDropIndex = insertionIndexToFinalIndex(actualDragIndex, dropTarget.value.__intended_index!, draggedList.length)
|
|
499
495
|
|
|
500
|
-
if (
|
|
496
|
+
if (actualDropIndex !== -1 && actualDragIndex !== actualDropIndex) {
|
|
501
497
|
list.forEach((el) => {
|
|
502
498
|
el.style.transition = 'none'
|
|
503
499
|
el.style.transform = ''
|
|
@@ -508,16 +504,18 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
508
504
|
const dragEvent = new MouseEvent('mouseup', e) as DraggableEvent
|
|
509
505
|
dragEvent.oldIndex = actualDragIndex
|
|
510
506
|
dragEvent.newIndex = actualDropIndex
|
|
511
|
-
dragEvent.item = dragElement.value.__drag_data
|
|
507
|
+
dragEvent.item = defaultOptions.value.items?.[actualDragIndex] ?? dragElement.value.__drag_data
|
|
512
508
|
|
|
513
|
-
|
|
509
|
+
cleanup()
|
|
510
|
+
defaultOptions.value.onEnd?.(dragEvent)
|
|
511
|
+
return
|
|
514
512
|
}
|
|
515
513
|
|
|
516
514
|
cleanup()
|
|
517
515
|
}
|
|
518
516
|
|
|
519
517
|
function onTouchStart(e: TouchEvent) {
|
|
520
|
-
if (!isBrowser || defaultOptions.disabled || !e.target) { return }
|
|
518
|
+
if (!isBrowser || defaultOptions.value.disabled || !e.target) { return }
|
|
521
519
|
|
|
522
520
|
const touch = e.touches[0]
|
|
523
521
|
if (!touch) { return }
|
|
@@ -525,9 +523,9 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
525
523
|
const target = e.target as Element
|
|
526
524
|
const draggableParent = findDraggableParent(target)
|
|
527
525
|
|
|
528
|
-
if (!draggableParent) { return }
|
|
526
|
+
if (!draggableParent || e.currentTarget !== draggableParent || isDragging.value) { return }
|
|
529
527
|
|
|
530
|
-
const { handle } = defaultOptions
|
|
528
|
+
const { handle } = defaultOptions.value
|
|
531
529
|
if (handle && !target.closest(handle)) { return }
|
|
532
530
|
|
|
533
531
|
e.preventDefault() // Prevent scrolling
|
|
@@ -548,17 +546,17 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
548
546
|
}
|
|
549
547
|
|
|
550
548
|
// Only create drop indicator in line mode
|
|
551
|
-
if (defaultOptions.mode === 'line') {
|
|
552
|
-
dropIndicator.value = createDropIndicator(
|
|
549
|
+
if (defaultOptions.value.mode === 'line') {
|
|
550
|
+
dropIndicator.value = createDropIndicator()
|
|
553
551
|
}
|
|
554
552
|
|
|
555
553
|
// Apply mode-specific styles and get shift amount
|
|
556
|
-
dragElement.value.classList.add(defaultOptions.dragClass!)
|
|
557
|
-
shiftAmount = defaultOptions.mode === 'ghost'
|
|
554
|
+
dragElement.value.classList.add(defaultOptions.value.dragClass!)
|
|
555
|
+
shiftAmount = defaultOptions.value.mode === 'ghost'
|
|
558
556
|
? handleGhostMode(dragElement.value)
|
|
559
557
|
: handleLineMode(dragElement.value)
|
|
560
558
|
|
|
561
|
-
defaultOptions.onStart?.(e as unknown as MouseEvent)
|
|
559
|
+
defaultOptions.value.onStart?.(e as unknown as MouseEvent)
|
|
562
560
|
|
|
563
561
|
if (isBrowser) {
|
|
564
562
|
// Prevent text selection during drag
|
|
@@ -569,7 +567,8 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
569
567
|
window.addEventListener('blur', cleanup)
|
|
570
568
|
document.addEventListener('touchmove', onTouchMove, { passive: false })
|
|
571
569
|
document.addEventListener('touchend', onTouchEnd)
|
|
572
|
-
document.addEventListener('touchcancel',
|
|
570
|
+
document.addEventListener('touchcancel', cleanup)
|
|
571
|
+
document.addEventListener('keydown', onKeyDown)
|
|
573
572
|
}
|
|
574
573
|
}
|
|
575
574
|
|
|
@@ -601,7 +600,7 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
601
600
|
onDragEnd(mouseEvent)
|
|
602
601
|
document.removeEventListener('touchmove', onTouchMove)
|
|
603
602
|
document.removeEventListener('touchend', onTouchEnd)
|
|
604
|
-
document.removeEventListener('touchcancel',
|
|
603
|
+
document.removeEventListener('touchcancel', cleanup)
|
|
605
604
|
}
|
|
606
605
|
|
|
607
606
|
function initDraggableContainer(container: HTMLElement) {
|
|
@@ -624,17 +623,15 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
624
623
|
})
|
|
625
624
|
|
|
626
625
|
let currentIndex = 0
|
|
627
|
-
elements.forEach((el
|
|
626
|
+
elements.forEach((el) => {
|
|
628
627
|
if (el.dataset.draggable !== 'false') {
|
|
629
628
|
el.style.userSelect = 'none'
|
|
630
629
|
el.style.webkitUserSelect = 'none'
|
|
631
|
-
el.__drag_group =
|
|
630
|
+
el.__drag_group = defaultOptions.value.group
|
|
632
631
|
el.__drag_index = currentIndex++
|
|
633
632
|
el.__drag_list = elements.filter(e => e.dataset.draggable !== 'false')
|
|
634
633
|
|
|
635
|
-
|
|
636
|
-
el.__drag_data = options.items[index]
|
|
637
|
-
}
|
|
634
|
+
el.__drag_data = defaultOptions.value.items?.[el.__drag_index]
|
|
638
635
|
|
|
639
636
|
el.addEventListener('mousedown', onDragStart)
|
|
640
637
|
el.addEventListener('touchstart', onTouchStart)
|
|
@@ -642,6 +639,7 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
642
639
|
})
|
|
643
640
|
|
|
644
641
|
cleanupFn = () => {
|
|
642
|
+
cleanup()
|
|
645
643
|
elements.forEach((el) => {
|
|
646
644
|
el.removeEventListener('mousedown', onDragStart)
|
|
647
645
|
el.removeEventListener('touchstart', onTouchStart)
|
|
@@ -653,14 +651,18 @@ export function useDraggable(options: DraggableOptions = {}) {
|
|
|
653
651
|
}
|
|
654
652
|
}
|
|
655
653
|
|
|
656
|
-
|
|
657
|
-
onUnmounted(() => {
|
|
654
|
+
function destroy() {
|
|
658
655
|
cleanupFn?.()
|
|
659
|
-
|
|
656
|
+
cleanupFn = undefined
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (getCurrentInstance()) onUnmounted(destroy)
|
|
660
660
|
|
|
661
661
|
return {
|
|
662
662
|
isDragging,
|
|
663
663
|
dragElement,
|
|
664
|
-
initDraggableContainer
|
|
664
|
+
initDraggableContainer,
|
|
665
|
+
destroy,
|
|
666
|
+
cancel: cleanup
|
|
665
667
|
}
|
|
666
668
|
}
|
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { shallowRef, type Directive } from 'vue'
|
|
2
2
|
import type { DraggableOptions } from './useDraggable'
|
|
3
3
|
import { useDraggable } from './useDraggable'
|
|
4
4
|
|
|
5
|
+
const instances = new WeakMap<HTMLElement, {
|
|
6
|
+
options: ReturnType<typeof shallowRef<DraggableOptions>>
|
|
7
|
+
draggable: ReturnType<typeof useDraggable>
|
|
8
|
+
}>()
|
|
9
|
+
|
|
5
10
|
export const vDraggable: Directive<HTMLElement, DraggableOptions> = {
|
|
6
11
|
mounted(el, binding) {
|
|
7
|
-
const
|
|
8
|
-
|
|
12
|
+
const options = shallowRef(binding.value || {})
|
|
13
|
+
const draggable = useDraggable(options)
|
|
14
|
+
instances.set(el, { options, draggable })
|
|
15
|
+
draggable.initDraggableContainer(el)
|
|
9
16
|
},
|
|
10
17
|
updated(el, binding) {
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const instance = instances.get(el)
|
|
19
|
+
if (!instance) return
|
|
20
|
+
instance.options.value = binding.value || {}
|
|
21
|
+
instance.draggable.initDraggableContainer(el)
|
|
22
|
+
},
|
|
23
|
+
beforeUnmount(el) {
|
|
24
|
+
instances.get(el)?.draggable.destroy()
|
|
25
|
+
instances.delete(el)
|
|
16
26
|
}
|
|
17
27
|
}
|