Skip to content

⬅️ Back to Table of Contents

📄 useDropZone

📊 Analysis Summary

Metric Count
🔧 Functions 6
📦 Imports 7
📐 Interfaces 2

📚 Table of Contents

🛠️ File Location:

📂 packages/core/useDropZone/index.ts

📦 Imports

Name Source
MaybeRef vue
MaybeRefOrGetter vue
ShallowRef vue
isClient @vueuse/shared
shallowRef vue
unref vue
useEventListener ../useEventListener

Functions

useDropZone(target: MaybeRefOrGetter<HTMLElement | Document…, options: UseDropZoneOptions | UseDropZoneOptions…): UseDropZoneReturn

Parameters:

  • target MaybeRefOrGetter<HTMLElement | Document | null | undefined>
  • options UseDropZoneOptions | UseDropZoneOptions['onDrop']

Returns: UseDropZoneReturn

Calls:

  • shallowRef (from vue)
  • Array.from
  • unref (from vue)
  • dataTypes
  • types.every
  • dataTypes.some
  • type.includes
  • _options.checkValidity
  • Array.from(items ?? []).map
  • checkDataTypes
  • /^(?:(?!chrome|android).)*safari/i.test
  • checkValidity
  • event.preventDefault
  • isSafari
  • getFiles
  • _options.onEnter
  • _options.onOver
  • _options.onLeave
  • _options.onDrop
  • useEventListener (from ../useEventListener)
  • handleDragEvent
Code
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) => {
      if (_options.checkValidity) {
        return _options.checkValidity(items)
      }

      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,
  }
}

Internal helpers

Declared inside another function in this file.

getFiles(event: DragEvent): File[]

Parameters:

  • event DragEvent

Returns: File[]

Calls:

  • Array.from
Code
(event: DragEvent) => {
      const list = Array.from(event.dataTransfer?.files ?? [])
      return list.length === 0 ? null : (multiple ? list : [list[0]])
    }

checkDataTypes(types: string[]): any

Parameters:

  • types string[]

Returns: any

Calls:

  • unref (from vue)
  • dataTypes
  • types.every
  • dataTypes.some
  • type.includes
Code
(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)),
      )
    }

checkValidity(items: DataTransferItemList): boolean

Parameters:

  • items DataTransferItemList

Returns: boolean

Calls:

  • _options.checkValidity
  • Array.from(items ?? []).map
  • checkDataTypes
Code
(items: DataTransferItemList) => {
      if (_options.checkValidity) {
        return _options.checkValidity(items)
      }

      const types = Array.from(items ?? []).map(item => item.type)

      const dataTypesValid = checkDataTypes(types)
      const multipleFilesValid = multiple || items.length <= 1

      return dataTypesValid && multipleFilesValid
    }

isSafari(): boolean

Returns: boolean

Code
() => (
      /^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent)
      && !('chrome' in window)
    )

handleDragEvent(event: DragEvent, eventType: 'enter' | 'over' | 'leave' | 'drop'): void

Parameters:

  • event DragEvent
  • eventType 'enter' | 'over' | 'leave' | 'drop'

Returns: void

Calls:

  • checkValidity
  • event.preventDefault
  • isSafari
  • getFiles
  • _options.onEnter
  • _options.onOver
  • _options.onLeave
  • _options.onDrop
Code
(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
      }
    }

Interfaces

UseDropZoneReturn

Interface Code
export interface UseDropZoneReturn {
  files: ShallowRef<File[] | null>
  isOverDropZone: ShallowRef<boolean>
}

Properties

Name Type Optional Description
files ShallowRef<File[] \| null> not shown
isOverDropZone ShallowRef<boolean> not shown

UseDropZoneOptions

Interface Code
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)
  /**
   * Similar to dataTypes, but exposes the DataTransferItemList for custom validation.
   * If provided, this function takes precedence over dataTypes.
   */
  checkValidity?: (items: DataTransferItemList) => 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
}

Properties

Name Type Optional Description
dataTypes MaybeRef<readonly string[]> \| ((types: readonly string[]) => boolean) not shown
checkValidity (items: DataTransferItemList) => boolean not shown
onDrop (files: File[] \| null, event: DragEvent) => void not shown
onEnter (files: File[] \| null, event: DragEvent) => void not shown
onLeave (files: File[] \| null, event: DragEvent) => void not shown
onOver (files: File[] \| null, event: DragEvent) => void not shown
multiple boolean not shown
preventDefaultForUnhandled boolean not shown

Generated by Syntax Scribe