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"> <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 as many files as you want.</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 } 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 as many files as you want.</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) }}