x
1
2
3
4
5
6
<div class="dropzone" data-controller="dropzone" data-dropzone-param-name-value="billboard[images][]" data-dropzone-url-value="https://csszero.lazaronixon.com/lookbook/rails/active_storage/direct_uploads" data-dropzone-accepted-files-value="image/*" data-dropzone-max-files-value="3" data-dropzone-max-filesize-value="0.3">
<div class="dz-default dz-message flex flex-col items-center">
<h5 class="font-semibold">Drop files here or click to upload.</h5>
<p class="text-sm text-subtle">Upload up to 3 images and 300k.</p>
</div>
</div>
1
2
3
4
5
6
<%= tag.div class: "dropzone", data: { controller: "dropzone", dropzone_param_name_value: "billboard[images][]", dropzone_url_value: rails_direct_uploads_url, dropzone_accepted_files_value: "image/*", dropzone_max_files_value: 3, dropzone_max_filesize_value: 0.300 } do %>
<div class="dz-default dz-message flex flex-col items-center">
<h5 class="font-semibold">Drop files here or click to upload.</h5>
<p class="text-sm text-subtle">Upload up to 3 images and 300k.</p>
</div>
<% end %>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@import url("https://esm.sh/dropzone@6.0.0-beta.2/dist/dropzone.css");
.dropzone {
border-radius: var(--rounded-xl);
border: 2px dashed var(--color-border);
padding: var(--size-2);
.dz-preview.dz-image-preview {
background: transparent;
}
.dz-preview .dz-error-message {
background: var(--color-negative);
word-break: break-all;
}
.dz-preview .dz-error-message::after {
border-bottom: 6px solid var(--color-negative);
}
.dz-preview:has(.dz-remove) .dz-error-message {
top: 148px;
}
}
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
90
91
92
93
94
95
96
97
98
import { Controller } from "@hotwired/stimulus"
import { DirectUpload } from "https://esm.sh/@rails/activestorage@8.0.100?standalone"
import Dropzone from "https://esm.sh/dropzone@6.0.0-beta.2?standalone"
export default class extends Controller {
static values = {
url: String,
paramName: String,
maxFiles: { type: Number, default: null },
maxFilesize: { type: Number, default: 256 },
acceptedFiles: { type: String, default: null },
addRemoveLinks: { type: Boolean, default: true }
}
connect() {
this.dropZone = this.#createDropZone()
this.dropZone.enqueueFile = it => this.#upload(it)
}
disconnect() {
this.dropZone.destroy()
}
#createDropZone() {
return new Dropzone(this.element, {
url: this.urlValue,
paramName: this.paramNameValue,
maxFiles: this.maxFilesValue,
maxFilesize: this.maxFilesizeValue,
acceptedFiles: this.acceptedFilesValue,
addRemoveLinks: this.addRemoveLinksValue
})
}
#upload(file) {
new Uploader(file, this.dropZone).start()
}
}
class Uploader {
constructor(file, dropZone) {
this.file = file; this.dropZone = dropZone;
}
start() {
this.#createDirectUpload((error, blob) => {
if (error) {
this.#emitDropzoneError(error)
} else {
this.#createHiddenInput(blob.signed_id)
this.#emitDropzoneSuccess()
}
})
}
directUploadWillStoreFileWithXHR(xhr) {
this.file.xhr = xhr
this.#bindProgress()
this.#emitDropzoneProcessing()
}
#createDirectUpload(callback) {
new DirectUpload(this.file, this.dropZone.options.url, this).create(callback)
}
#createHiddenInput(signedId) {
this.hiddenInput = document.createElement("input")
this.hiddenInput.type = "hidden"
this.hiddenInput.name = this.dropZone.options.paramName
this.hiddenInput.value = signedId
this.file.previewElement.appendChild(this.hiddenInput)
}
#bindProgress() {
this.file.xhr.upload.addEventListener("progress", it => this.#directUploadDidProgress(it))
}
#directUploadDidProgress(event) {
this.dropZone._updateFilesUploadProgress([this.file], this.xhr, event)
}
#emitDropzoneProcessing() {
this.file.status = Dropzone.PROCESSING
this.dropZone.emit("processing", this.file)
}
#emitDropzoneSuccess() {
this.file.status = Dropzone.SUCCESS
this.dropZone.emit("success", this.file)
this.dropZone.emit("complete", this.file)
}
#emitDropzoneError(error) {
this.file.status = Dropzone.ERROR
this.dropZone.emit("error", this.file, error)
this.dropZone.emit("complete", this.file)
}
}