/
index.ts
151 lines (134 loc) · 3.88 KB
/
index.ts
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { computed, ref } from 'vue-demi'
import type { MaybeComputedRef } from '@vueuse/shared'
import { isClient, resolveUnref, toRefs } from '@vueuse/shared'
import { useEventListener } from '../useEventListener'
import type { PointerType, Position } from '../types'
import { defaultWindow } from '../_configurable'
export interface UseDraggableOptions {
/**
* Only start the dragging when click on the element directly
*
* @default false
*/
exact?: MaybeComputedRef<boolean>
/**
* Prevent events defaults
*
* @default false
*/
preventDefault?: MaybeComputedRef<boolean>
/**
* Prevent events propagation
*
* @default false
*/
stopPropagation?: MaybeComputedRef<boolean>
/**
* Element to attach `pointermove` and `pointerup` events to.
*
* @default window
*/
draggingElement?: MaybeComputedRef<HTMLElement | SVGElement | Window | Document | null | undefined>
/**
* Handle that triggers the drag event
*
* @default target
*/
handle?: MaybeComputedRef<HTMLElement | SVGElement | null | undefined>
/**
* Pointer types that listen to.
*
* @default ['mouse', 'touch', 'pen']
*/
pointerTypes?: PointerType[]
/**
* Initial position of the element.
*
* @default { x: 0, y: 0 }
*/
initialValue?: MaybeComputedRef<Position>
/**
* Callback when the dragging starts. Return `false` to prevent dragging.
*/
onStart?: (position: Position, event: PointerEvent) => void | false
/**
* Callback during dragging.
*/
onMove?: (position: Position, event: PointerEvent) => void
/**
* Callback when dragging end.
*/
onEnd?: (position: Position, event: PointerEvent) => void
}
/**
* Make elements draggable.
*
* @see https://vueuse.org/useDraggable
* @param target
* @param options
*/
export function useDraggable(target: MaybeComputedRef<HTMLElement | SVGElement | null | undefined>, options: UseDraggableOptions = {}) {
const draggingElement = options.draggingElement ?? defaultWindow
const draggingHandle = options.handle ?? target
const position = ref<Position>(resolveUnref(options.initialValue) ?? { x: 0, y: 0 })
const pressedDelta = ref<Position>()
const filterEvent = (e: PointerEvent) => {
if (options.pointerTypes)
return options.pointerTypes.includes(e.pointerType as PointerType)
return true
}
const handleEvent = (e: PointerEvent) => {
if (resolveUnref(options.preventDefault))
e.preventDefault()
if (resolveUnref(options.stopPropagation))
e.stopPropagation()
}
const start = (e: PointerEvent) => {
if (!filterEvent(e))
return
if (resolveUnref(options.exact) && e.target !== resolveUnref(target))
return
const rect = resolveUnref(target)!.getBoundingClientRect()
const pos = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
}
if (options.onStart?.(pos, e) === false)
return
pressedDelta.value = pos
handleEvent(e)
}
const move = (e: PointerEvent) => {
if (!filterEvent(e))
return
if (!pressedDelta.value)
return
position.value = {
x: e.clientX - pressedDelta.value.x,
y: e.clientY - pressedDelta.value.y,
}
options.onMove?.(position.value, e)
handleEvent(e)
}
const end = (e: PointerEvent) => {
if (!filterEvent(e))
return
if (!pressedDelta.value)
return
pressedDelta.value = undefined
options.onEnd?.(position.value, e)
handleEvent(e)
}
if (isClient) {
useEventListener(draggingHandle, 'pointerdown', start, true)
useEventListener(draggingElement, 'pointermove', move, true)
useEventListener(draggingElement, 'pointerup', end, true)
}
return {
...toRefs(position),
position,
isDragging: computed(() => !!pressedDelta.value),
style: computed(() => `left:${position.value.x}px;top:${position.value.y}px;`),
}
}
export type UseDraggableReturn = ReturnType<typeof useDraggable>