Hey Elixir Forum, this is my first post.
I am trying to use phx-drop-target to allow users to drag and drop entire directories. My setup works fine when clicking the file input, but it fails when dragging and dropping a folder.
Here is my input setup:
<.live_file_input upload={@uploads.folder_uploads} webkitdirectory />
After looking into the LiveView source code for the drop event, it appears it only looks at dataTransfer.files. From what I could tell, directories show up, but only as 0 byte files with no contents.
I was able to around this issue by creating a hook to intercept the drop event, but it feels quite hacky.
Here is my current hook:
Drop = {
mounted() {
this.inputEl = document.getElementById(this.el.dataset.inputId);
this.handleDrop = async (e) => {
e.preventDefault();
e.stopPropagation();
const items = e.dataTransfer.items;
const fileArray = [];
const queue = [];
for (const item of items) {
const entry = item.webkitGetAsEntry();
if (entry) {
queue.push(this.traverseEntry(entry, fileArray));
}
}
await Promise.all(queue);
const dataTransfer = new DataTransfer();
fileArray.forEach(file => dataTransfer.items.add(file));
this.inputEl.files = dataTransfer.files;
this.inputEl.dispatchEvent(new Event("input", { bubbles: true }));
this.inputEl.dispatchEvent(new Event("change", { bubbles: true }));
};
this.el.addEventListener("drop", this.handleDrop, true);
},
destroyed() {
this.el.removeEventListener("drop", this.handleDrop, true);
},
traverseEntry(entry, fileList, path = "") {
return new Promise((resolve) => {
if (entry.isFile) {
entry.file((file) => {
Object.defineProperty(file, "webkitRelativePath", {
value: path + file.name,
writable: false
});
fileList.push(file);
resolve();
});
} else if (entry.isDirectory) {
const dirReader = entry.createReader();
const dirPath = path + entry.name + "/";
const readBatch = () => {
dirReader.readEntries(async (entries) => {
if (entries.length === 0) {
resolve();
} else {
const childPromises = entries.map(child => this.traverseEntry(child, fileList, dirPath));
await Promise.all(childPromises);
readBatch();
}
});
};
readBatch();
} else {
resolve();
}
});
}
};
Is there a more native way to handle dropped directories that I missed?
Thanks.






















