marinosoftware/active_storage_drag_and_drop

View on GitHub
app/javascript/active_storage_drag_and_drop/ujs.js

Summary

Maintainability
A
0 mins
Test Coverage
// @flow

import { DragAndDropFormController } from './drag_and_drop_form_controller'
import { placeholderUI, paintDefaultUploadIcon } from './default_ui'
import { dispatchEvent } from './helpers'

let started = false
export let paintUploadIcon = paintDefaultUploadIcon
export let formSubmitted = false // eslint-disable-line prefer-const
const formControllers = new Map()

export function handleProcessUploadQueueEvent (event: Event) {
  console.warn('WARNING! The processs-upload-queue event is deprecated. Call window.ActiveStorageDragAndDrop.processUploadQueue(form, callback) instead.')
  // $FlowFixMe
  const callback = event.detail.callback
  processUploadQueue(event.target, callback)
}

export function processUploadQueue (form: any, callback: (error: Error | null) => void) {
  const formController = findOrInitializeFormController(form)
  formController.start(error => { callback(error) })
}

export function addAttachedFileIcons () {
  document.querySelectorAll("input[type='hidden'][data-direct-upload-id][data-uploaded-file]").forEach(uploadedFile => {
    const dataset = uploadedFile.dataset
    const detail = {
      id: dataset.directUploadId,
      file: JSON.parse(dataset.uploadedFile),
      iconContainer: document.getElementById(dataset.iconContainerId)
    }
    const event = dispatchEvent(uploadedFile, 'dnd-upload:placeholder', { detail })
    placeholderUI(event)
  })
}

export function preventDragover (event: DragEvent) {
  const target = event.target
  if (target instanceof Element && target.closest('.asdndzone')) event.preventDefault()
}

export function handleSubmit (event: Event) {
  if (this.formSubmitted) return

  this.formSubmitted = true
  const formController = findOrInitializeFormController(event.target)
  if (formController.uploadControllers.length === 0) return

  event.preventDefault()
  formController.start(error => {
    if (!error) formController.form.submit()
  })
}

export function removeFileFromQueue (event: Event) {
  const target = event.target
  if (!(target instanceof HTMLElement && target.classList.contains('direct-upload__remove'))) return

  const wrapper = target.closest('[data-direct-upload-id]')
  if (!(wrapper instanceof HTMLElement)) throw new Error('Cannot remove queued upload because there is no enclosing element with a data-direct-upload-id attribute.')

  event.preventDefault()
  const formController = findOrInitializeFormController(target.closest('form'))
  formController.unqueueUpload(parseInt(wrapper.dataset.directUploadId))
}

export function queueUploadsForFileInput (event: Event) {
  const input = event.target
  if (!(input instanceof HTMLInputElement) || input.type !== 'file' || input.dataset.dnd !== 'true') return

  const formController = findOrInitializeFormController(input.form)
  Array.from(input.files).forEach(file => formController.queueUpload(input, file))
  input.value = ''
}

export function queueUploadsForDroppedFiles (event: DragEvent) {
  const { target, dataTransfer } = event
  if (!(target instanceof Element && dataTransfer instanceof DataTransfer)) return

  const asdndz = target.closest('.asdndzone')
  if (!(asdndz instanceof HTMLElement)) return

  // get the input associated with this dndz
  const input = document.getElementById(asdndz.dataset.dndInputId)
  if (!(input instanceof HTMLInputElement)) throw new Error('There is no file input element with the dnd-input-id specified on the drop zone.')

  event.preventDefault()
  const formController = findOrInitializeFormController(input.form)
  Array.from(dataTransfer.files).forEach(file => formController.queueUpload(input, file))
}

export function start (options: { iconPainter?: Function }) {
  if (options && options.iconPainter) paintUploadIcon = options.iconPainter

  if (started) return

  started = true
  document.addEventListener('change', queueUploadsForFileInput)
  document.addEventListener('click', removeFileFromQueue)
  document.addEventListener('drop', queueUploadsForDroppedFiles, true)
  document.addEventListener('submit', handleSubmit)
  document.addEventListener('dnd-uploads:process-upload-queue', handleProcessUploadQueueEvent)
  document.addEventListener('ajax:before', handleSubmit)
  document.addEventListener('dragover', preventDragover)
  addAttachedFileIcons()
}

export function findOrInitializeFormController (form: any) {
  let formController = formControllers.get(form)
  if (formController) return formController

  if (!(form instanceof HTMLFormElement)) throw new Error('Can only initialize form controller with a form element.')

  formController = new DragAndDropFormController(form)
  formControllers.set(form, formController)
  return formController
}