Skip to content

Instantly share code, notes, and snippets.

@achmiral
Forked from lazaronixon/dropzone_controller.js
Created March 24, 2023 05:21
Show Gist options
  • Save achmiral/d678155fa8e4e36fb4426137f04d2092 to your computer and use it in GitHub Desktop.
Save achmiral/d678155fa8e4e36fb4426137f04d2092 to your computer and use it in GitHub Desktop.
Dropzone.js + Stimulus + Active Storage
import { Controller } from "stimulus"
import { DirectUpload } from "@rails/activestorage"
import Dropzone from "dropzone"
import { getMetaValue, findElement, removeElement, insertAfter } from "helpers"
Dropzone.autoDiscover = false
export default class extends Controller {
static targets = [ "input" ]
connect() {
this.dropZone = createDropZone(this)
this.hideFileInput()
this.bindEvents()
}
// Private
hideFileInput() {
this.inputTarget.disabled = true
this.inputTarget.style.display = "none"
}
bindEvents() {
this.dropZone.on("addedfile", (file) => {
setTimeout(() => { file.accepted && createDirectUploadController(this, file).start() }, 500)
})
this.dropZone.on("removedfile", (file) => {
file.controller && removeElement(file.controller.hiddenInput)
})
this.dropZone.on("canceled", (file) => {
file.controller && file.controller.xhr.abort()
})
this.dropZone.on("processing", (file) => {
this.submitButton.disabled = true
})
this.dropZone.on("queuecomplete", (file) => {
this.submitButton.disabled = false
})
}
get headers() { return { "X-CSRF-Token": getMetaValue("csrf-token") } }
get url() { return this.inputTarget.getAttribute("data-direct-upload-url") }
get maxFiles() { return this.data.get("maxFiles") || 1 }
get maxFileSize() { return this.data.get("maxFileSize") || 256 }
get acceptedFiles() { return this.data.get("acceptedFiles") }
get addRemoveLinks() { return this.data.get("addRemoveLinks") || true }
get form() { return this.element.closest("form") }
get submitButton() { return findElement(this.form, "input[type=submit], button[type=submit]") }
}
class DirectUploadController {
constructor(source, file) {
this.directUpload = createDirectUpload(file, source.url, this)
this.source = source
this.file = file
}
start() {
this.file.controller = this
this.hiddenInput = this.createHiddenInput()
this.directUpload.create((error, attributes) => {
if (error) {
removeElement(this.hiddenInput)
this.emitDropzoneError(error)
} else {
this.hiddenInput.value = attributes.signed_id
this.emitDropzoneSuccess()
}
})
}
// Private
createHiddenInput() {
const input = document.createElement("input")
input.type = "hidden"
input.name = this.source.inputTarget.name
insertAfter(input, this.source.inputTarget)
return input
}
directUploadWillStoreFileWithXHR(xhr) {
this.bindProgressEvent(xhr)
this.emitDropzoneUploading()
}
bindProgressEvent(xhr) {
this.xhr = xhr
this.xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
}
uploadRequestDidProgress(event) {
const element = this.source.element
const progress = event.loaded / event.total * 100
findElement(this.file.previewTemplate, ".dz-upload").style.width = `${progress}%`
}
emitDropzoneUploading() {
this.file.status = Dropzone.UPLOADING
this.source.dropZone.emit("processing", this.file)
}
emitDropzoneError(error) {
this.file.status = Dropzone.ERROR
this.source.dropZone.emit("error", this.file, error)
this.source.dropZone.emit("complete", this.file)
}
emitDropzoneSuccess() {
this.file.status = Dropzone.SUCCESS
this.source.dropZone.emit("success", this.file)
this.source.dropZone.emit("complete", this.file)
}
}
// Top level...
function createDirectUploadController(source, file) {
return new DirectUploadController(source, file)
}
function createDirectUpload(file, url, controller) {
return new DirectUpload(file, url, controller)
}
function createDropZone(controller) {
return new Dropzone(controller.element, {
url: controller.url,
headers: controller.headers,
maxFiles: controller.maxFiles,
maxFilesize: controller.maxFileSize,
acceptedFiles: controller.acceptedFiles,
addRemoveLinks: controller.addRemoveLinks,
autoQueue: false
})
}
export function getMetaValue(name) {
const element = findElement(document.head, `meta[name="${name}"]`)
if (element) {
return element.getAttribute("content")
}
}
export function findElement(root, selector) {
if (typeof root == "string") {
selector = root
root = document
}
return root.querySelector(selector)
}
export function removeElement(el) {
if (el && el.parentNode) {
el.parentNode.removeChild(el);
}
}
export function insertAfter(el, referenceNode) {
return referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);
}
<div class="dropzone dropzone-default dz-clickable" data-controller="dropzone" data-dropzone-max-file-size="5" data-dropzone-accepted-files="text/csv">
<%= form.file_field :file, direct_upload: true, data: { target: 'dropzone.input' } %>
<div class="dropzone-msg dz-message needsclick">
<h3 class="dropzone-msg-title">Solte o arquivo aqui ou clique para fazer o upload.</h3>
<span class="dropzone-msg-desc">Use os templates abaixo para criar o arquivo, o tamanho maximo é de 5 MiB.</span>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment