Skip to content

Commit

Permalink
drag and drop into file field
Browse files Browse the repository at this point in the history
  • Loading branch information
al3rez committed Jan 19, 2024
1 parent a02dbc0 commit 1973a6f
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 10 deletions.
84 changes: 84 additions & 0 deletions app/javascript/controllers/image_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
static targets = ["file", "content"];

connect() {
this.dropHandler();
this.pasteHandler();
}

dropHandler() {
this.fileTarget.addEventListener("dragover", (event) =>
this.preventDefaults(event),
);
this.fileTarget.addEventListener("dragenter", (event) =>
this.preventDefaults(event),
);
this.fileTarget.addEventListener("drop", (event) => this.drop(event));
}

preventDefaults(event) {
event.preventDefault();
event.stopPropagation();
}

drop(event) {
this.preventDefaults(event);
let files = event.dataTransfer.files;
this.fileTarget.files = files;
}

pasteHandler() {
this.contentTarget.addEventListener("paste", async (event) => {
const clipboardData =
event.clipboardData || event.originalEvent.clipboardData;

for (const item of clipboardData.items) {
if (item.kind === "file") {
const blob = item.getAsFile();
if (blob) {
try {
const dataURL = await this.readBlobAsDataURL(blob);
this.addImageToFileInput(dataURL, blob.type);
} catch (error) {
console.error("Error reading pasted image:", error);
}
}
}
}
});
}

async readBlobAsDataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(event.target.result);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(blob);
});
}

addImageToFileInput(dataURL, fileType) {
const fileList = new DataTransfer();
const blob = this.dataURLtoBlob(dataURL, fileType);
fileList.items.add(
new File([blob], "pasted-image.png", { type: fileType }),
);
this.fileTarget.files = fileList.files;
}

dataURLtoBlob(dataURL, fileType) {
const binaryString = atob(dataURL.split(",")[1]);
const arrayBuffer = new ArrayBuffer(binaryString.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < binaryString.length; i++) {
uint8Array[i] = binaryString.charCodeAt(i);
}
return new Blob([uint8Array], { type: fileType });
}
}
17 changes: 7 additions & 10 deletions app/views/shared/_message_input.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@

<div class='flex bottom-0 inset-x-0 mx-auto w-full px-4 py-6 justify-center items-center bg-[#343541]'>
<%= form_with(url: chats_path, method: :post, data: { remote: "true" }, class: "border border-gray-300 flex justify-center items-center space-x-2 shadow-md rounded px-2 w-1/2") do |f| %>
<%= f.file_field :image %>
<%= f.text_field :content, class: "flex-1 bg-[#343541] text-white p-2 border-0 focus:outline-none" %>
<button type="submit">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="text-black cursor-pointer bg-white rounded dark:text-black"><path d="M7 11L12 6L17 11M12 18V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</button>
<% end %>
</div>
<%= form_with(url: chats_path, method: :post, data: { controller: 'image', remote: 'true' }, class: 'border border-gray-300 flex justify-center items-center space-x-2 shadow-md rounded px-2 w-1/2') do |f| %>
<%= f.file_field :image, data: { image_target: 'file' } %>
<%= f.text_field :content, data: { image_target: 'content'}, class: 'flex-1 bg-[#343541] text-white p-2 border-0 focus:outline-none' %>
<button type='submit'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' class='text-black cursor-pointer bg-white rounded dark:text-black'><path d='M7 11L12 6L17 11M12 18V7' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'></path></svg>
</button>
<% end %>

0 comments on commit 1973a6f

Please sign in to comment.