-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
index.ts
89 lines (81 loc) · 2.85 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
import type { MaybeRef, Ref } from 'vue-demi'
// eslint-disable-next-line no-restricted-imports
import { ref, shallowRef, unref } from 'vue-demi'
import type { MaybeRefOrGetter } from '@vueuse/shared'
import { isClient, notNullish } from '@vueuse/shared'
import { useEventListener } from '../useEventListener'
export interface UseDropZoneReturn {
files: Ref<File[] | null>
isOverDropZone: Ref<boolean>
}
export interface UseDropZoneOptions {
/**
* Allowed data types, if not set, all data types are allowed.
* Also can be a function to check the data types.
*/
dataTypes?: MaybeRef<string[]> | ((types: readonly string[]) => boolean)
onDrop?: (files: File[] | null, event: DragEvent) => void
onEnter?: (files: File[] | null, event: DragEvent) => void
onLeave?: (files: File[] | null, event: DragEvent) => void
onOver?: (files: File[] | null, event: DragEvent) => void
}
export function useDropZone(
target: MaybeRefOrGetter<HTMLElement | null | undefined>,
options: UseDropZoneOptions | UseDropZoneOptions['onDrop'] = {},
): UseDropZoneReturn {
const isOverDropZone = ref(false)
const files = shallowRef<File[] | null>(null)
let counter = 0
let isDataTypeIncluded = true
if (isClient) {
const _options = typeof options === 'function' ? { onDrop: options } : options
const getFiles = (event: DragEvent) => {
const list = Array.from(event.dataTransfer?.files ?? [])
return (files.value = list.length === 0 ? null : list)
}
useEventListener<DragEvent>(target, 'dragenter', (event) => {
const types = Array.from(event?.dataTransfer?.items || [])
.map(i => i.kind === 'file' ? i.type : null)
.filter(notNullish)
if (_options.dataTypes && event.dataTransfer) {
const dataTypes = unref(_options.dataTypes)
isDataTypeIncluded = typeof dataTypes === 'function'
? dataTypes(types)
: dataTypes
? dataTypes.some(item => types.includes(item))
: true
if (!isDataTypeIncluded)
return
}
event.preventDefault()
counter += 1
isOverDropZone.value = true
_options.onEnter?.(getFiles(event), event)
})
useEventListener<DragEvent>(target, 'dragover', (event) => {
if (!isDataTypeIncluded)
return
event.preventDefault()
_options.onOver?.(getFiles(event), event)
})
useEventListener<DragEvent>(target, 'dragleave', (event) => {
if (!isDataTypeIncluded)
return
event.preventDefault()
counter -= 1
if (counter === 0)
isOverDropZone.value = false
_options.onLeave?.(getFiles(event), event)
})
useEventListener<DragEvent>(target, 'drop', (event) => {
event.preventDefault()
counter = 0
isOverDropZone.value = false
_options.onDrop?.(getFiles(event), event)
})
}
return {
files,
isOverDropZone,
}
}