chore: copy useDropZone from vueuse
This commit is contained in:
parent
3f1f92c410
commit
abbf2ce183
|
|
@ -122,6 +122,7 @@
|
|||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"@vue/tsconfig": "0.8.1",
|
||||
"@vueuse/shared": "^14.0.0",
|
||||
"autoprefixer": "10.4.22",
|
||||
"browserslist": "4.28.0",
|
||||
"caniuse-lite": "1.0.30001754",
|
||||
|
|
|
|||
|
|
@ -227,6 +227,9 @@ importers:
|
|||
'@vue/tsconfig':
|
||||
specifier: 0.8.1
|
||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
||||
'@vueuse/shared':
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0(vue@3.5.24(typescript@5.9.3))
|
||||
autoprefixer:
|
||||
specifier: 10.4.22
|
||||
version: 10.4.22(postcss@8.5.6)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
import type { MaybeRef, MaybeRefOrGetter, ShallowRef } from 'vue'
|
||||
import { isClient } from '@vueuse/shared'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { shallowRef, unref } from 'vue'
|
||||
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
|
||||
export interface UseDropZoneReturn {
|
||||
files: ShallowRef<File[] | null>
|
||||
isOverDropZone: ShallowRef<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<readonly 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
|
||||
/**
|
||||
* Allow multiple files to be dropped. Defaults to true.
|
||||
*/
|
||||
multiple?: boolean
|
||||
/**
|
||||
* Prevent default behavior for unhandled events. Defaults to false.
|
||||
*/
|
||||
preventDefaultForUnhandled?: boolean
|
||||
}
|
||||
|
||||
export function useDropZone(
|
||||
target: MaybeRefOrGetter<HTMLElement | Document | null | undefined>,
|
||||
options: UseDropZoneOptions | UseDropZoneOptions['onDrop'] = {},
|
||||
): UseDropZoneReturn {
|
||||
const isOverDropZone = shallowRef(false)
|
||||
const files = shallowRef<File[] | null>(null)
|
||||
let counter = 0
|
||||
let isValid = true
|
||||
|
||||
if (isClient) {
|
||||
const _options = typeof options === 'function' ? { onDrop: options } : options
|
||||
const multiple = _options.multiple ?? true
|
||||
const preventDefaultForUnhandled = _options.preventDefaultForUnhandled ?? false
|
||||
|
||||
const getFiles = (event: DragEvent) => {
|
||||
const list = Array.from(event.dataTransfer?.files ?? [])
|
||||
return list.length === 0 ? null : (multiple ? list : [list[0]])
|
||||
}
|
||||
|
||||
const checkDataTypes = (types: string[]) => {
|
||||
const dataTypes = unref(_options.dataTypes)
|
||||
|
||||
if (typeof dataTypes === 'function')
|
||||
return dataTypes(types)
|
||||
|
||||
if (!dataTypes?.length)
|
||||
return true
|
||||
|
||||
if (types.length === 0)
|
||||
return false
|
||||
|
||||
return types.every(type =>
|
||||
dataTypes.some(allowedType => type.includes(allowedType)),
|
||||
)
|
||||
}
|
||||
|
||||
const checkValidity = (items: DataTransferItemList) => {
|
||||
const types = Array.from(items ?? []).map(item => item.type)
|
||||
|
||||
const dataTypesValid = checkDataTypes(types)
|
||||
const multipleFilesValid = multiple || items.length <= 1
|
||||
|
||||
return dataTypesValid && multipleFilesValid
|
||||
}
|
||||
|
||||
const isSafari = () => (
|
||||
/^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
&& !('chrome' in window)
|
||||
)
|
||||
|
||||
const handleDragEvent = (event: DragEvent, eventType: 'enter' | 'over' | 'leave' | 'drop') => {
|
||||
const dataTransferItemList = event.dataTransfer?.items
|
||||
isValid = (dataTransferItemList && checkValidity(dataTransferItemList)) ?? false
|
||||
|
||||
if (preventDefaultForUnhandled) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (!isSafari() && !isValid) {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'none'
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'copy'
|
||||
}
|
||||
|
||||
const currentFiles = getFiles(event)
|
||||
|
||||
switch (eventType) {
|
||||
case 'enter':
|
||||
counter += 1
|
||||
isOverDropZone.value = true
|
||||
_options.onEnter?.(null, event)
|
||||
break
|
||||
case 'over':
|
||||
_options.onOver?.(null, event)
|
||||
break
|
||||
case 'leave':
|
||||
counter -= 1
|
||||
if (counter === 0)
|
||||
isOverDropZone.value = false
|
||||
_options.onLeave?.(null, event)
|
||||
break
|
||||
case 'drop':
|
||||
counter = 0
|
||||
isOverDropZone.value = false
|
||||
if (isValid) {
|
||||
files.value = currentFiles
|
||||
_options.onDrop?.(currentFiles, event)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
useEventListener<DragEvent>(target, 'dragenter', event => handleDragEvent(event, 'enter'))
|
||||
useEventListener<DragEvent>(target, 'dragover', event => handleDragEvent(event, 'over'))
|
||||
useEventListener<DragEvent>(target, 'dragleave', event => handleDragEvent(event, 'leave'))
|
||||
useEventListener<DragEvent>(target, 'drop', event => handleDragEvent(event, 'drop'))
|
||||
}
|
||||
|
||||
return {
|
||||
files,
|
||||
isOverDropZone,
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue