Skip to content

Commit

Permalink
Improve usability of dragging dropping files (#590)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiobayona authored Jan 12, 2025
1 parent e0cea93 commit 89ca36e
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 8 deletions.
16 changes: 16 additions & 0 deletions app/assets/stylesheets/application.tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,19 @@ input:focus {
nav {
-webkit-touch-callout: none !important;
}

#drag-n-drop-shade {
outline: 8px dashed #000;
outline-offset: -10px;
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 99999;
}

.dark #drag-n-drop-shade {
outline-color: #888;
}
51 changes: 48 additions & 3 deletions app/javascript/stimulus/image_upload_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@ export default class extends Controller {
static targets = [ "file", "content", "preview" ]

connect() {
this.dragCounter = 0
this.fileTarget.addEventListener("change", this.boundPreviewUpdate)
this.contentTarget.addEventListener("drop", this.boundDropped)
this.element.addEventListener("drop", this.boundDropped)
this.contentTarget.addEventListener("paste", this.boundPasted)
this.element.addEventListener("dragenter", this.boundDragEnter)
this.element.addEventListener("dragover", this.boundDragOver)
this.element.addEventListener("dragleave", this.boundDragLeave)
}

disconnect() {
this.fileTarget.removeEventListener("change", this.boundPreviewUpdate)
this.contentTarget.removeEventListener("drop", this.boundDropped)
this.element.removeEventListener("drop", this.boundDropped)
this.contentTarget.removeEventListener("paste", this.boundPasted)
this.element.removeEventListener("dragenter", this.boundDragEnter)
this.element.removeEventListener("dragover", this.boundDragOver)
this.element.removeEventListener("dragleave", this.boundDragLeave)
}

boundPreviewUpdate = () => { this.previewUpdate() }
Expand All @@ -39,12 +46,40 @@ export default class extends Controller {

boundDropped = (event) => { this.dropped(event) }
dropped(event) {
event.preventDefault() // w/o this chrome opens a new browser tab w/ the image
event.preventDefault()
this.dragCounter = 0
const shade = this.element.querySelector("#drag-n-drop-shade")
if (shade) shade.remove()

let files = event.dataTransfer.files
this.fileTarget.files = files
this.previewUpdate()
}

boundDragOver = (event) => this.dragOver(event)
dragOver(event) {
event.preventDefault()
this.displayDragnDropShade()
}

boundDragLeave = (event) => this.dragLeave(event)
dragLeave(event) {
event.preventDefault()
this.dragCounter--
if (this.dragCounter <= 0) {
this.dragCounter = 0
const shade = this.element.querySelector("#drag-n-drop-shade")
if (shade) shade.remove()
}
}

boundDragEnter = (event) => this.dragEnter(event)
dragEnter(event) {
event.preventDefault()
this.dragCounter++
this.displayDragnDropShade()
}

boundPasted = async (event) => { this.pasted(event) }
async pasted(event) {
const clipboardData =
Expand Down Expand Up @@ -75,6 +110,16 @@ export default class extends Controller {
})
}

displayDragnDropShade() {
const existing = this.element.querySelector("#drag-n-drop-shade")
if (existing) return

this.element.insertAdjacentHTML(
'beforeend',
'<div id="drag-n-drop-shade"></div>'
);
}

addImageToFileInput(dataURL, fileType) {
const fileList = new DataTransfer()
const blob = this.dataURLtoBlob(dataURL, fileType)
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
class="fixed inset-0 bg-black z-20 bg-opacity-40 md:hidden nav-closed:hidden "
data-action="click->transition#toggleClass"
></div>
<main id="main-container" class="flex flex-1 h-full w-full bg-white dark:bg-gray-800 text-gray-950 dark:text-gray-100 px-safe">
<main id="main-container" class="flex flex-1 h-full w-full bg-white dark:bg-gray-800 text-gray-950 dark:text-gray-100 px-safe" data-controller="image-upload">
<%= content_for?(:main) ? yield(:main) : yield %>
</main>
<%= render "layouts/alerts" %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/messages/_main_column.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
pl-6 pr-6 mx-auto mb-6
w-full md:max-w-none lg:max-w-[700px] xl:max-w-[810px]
"
data-controller="composer image-upload"
data-controller="composer"
data-composer-speaker-outlet="[data-controller~='speaker']"
>
<%= button_tag type: "button",
Expand Down Expand Up @@ -173,7 +173,7 @@
data-role="preview"
>
<%= button_tag type: "button",
class: "rounded-full absolute -top-2 -right-2 border border-gray-300 bg-gray-200 hidden group-hover:block",
class: "rounded-full absolute -top-2 -right-2 border border-gray-300 bg-gray-200 dark:bg-gray-800 hidden group-hover:block",
data: {
action: "image-upload#remove",
role: "preview-remove"
Expand Down
2 changes: 0 additions & 2 deletions app/views/messages/_message.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ end %>
border-2 border-gray-100 dark:border-gray-400
rounded-md
|,
width: 1,
height: 1,
data: {
image_loader_target: "image",
action: %|
Expand Down

0 comments on commit 89ca36e

Please sign in to comment.