From 7ebb5ce8ef276e6d23d818c216be6ef2974369cc Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 23 Jul 2026 19:46:59 +0200 Subject: Turn the asset upload forms into file drop targets Dropped files land in the existing file input and submit through the unchanged form. An empty name field is prefilled from the basename. --- app/views/assets/edit.html.erb | 7 ++++++- app/views/assets/new.html.erb | 7 ++++++- public/javascripts/admin_interface.js | 38 +++++++++++++++++++++++++++++++++++ public/stylesheets/admin.css | 11 ++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/app/views/assets/edit.html.erb b/app/views/assets/edit.html.erb index 0c1dd8fe..d0a39aa3 100644 --- a/app/views/assets/edit.html.erb +++ b/app/views/assets/edit.html.erb @@ -14,7 +14,12 @@ <% end %>
<%= f.label :upload, "Replace file" %>
-
<%= f.file_field :upload %>
+
+
+ <%= f.file_field :upload %> +

…or drop a file here.

+
+
<%= f.label :creator %>
<%= f.text_field :creator %>
diff --git a/app/views/assets/new.html.erb b/app/views/assets/new.html.erb index 7bded949..8a7a6094 100644 --- a/app/views/assets/new.html.erb +++ b/app/views/assets/new.html.erb @@ -9,7 +9,12 @@
<%= f.text_field :name %>
<%= f.label :upload, "File" %>
-
<%= f.file_field :upload %>
+
+
+ <%= f.file_field :upload %> +

…or drop a file here.

+
+
<%= f.label :creator %>
<%= f.text_field :creator %>
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index 5514f3b3..284d2b72 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js @@ -85,6 +85,44 @@ $(document).ready(function () { desktop_mq.addEventListener('change', sync_metadata); } + var dropzone = document.getElementById('asset_dropzone'); + if (dropzone) { + var input = dropzone.querySelector('input[type=file]'); + + // Without these, a drop that misses the zone navigates the browser + // to the file, losing the half-filled form. + ['dragover', 'drop'].forEach(function(ev) { + window.addEventListener(ev, function(e) { e.preventDefault(); }); + }); + + ['dragenter', 'dragover'].forEach(function(ev) { + dropzone.addEventListener(ev, function(e) { + e.preventDefault(); dropzone.classList.add('dragging'); + }); + }); + ['dragleave', 'drop'].forEach(function(ev) { + dropzone.addEventListener(ev, function(e) { + e.preventDefault(); dropzone.classList.remove('dragging'); + }); + }); + + dropzone.addEventListener('drop', function(e) { + if (!e.dataTransfer.files.length) return; + var dt = new DataTransfer(); + dt.items.add(e.dataTransfer.files[0]); + input.files = dt.files; + input.dispatchEvent(new Event('change', { bubbles: true })); + }); + + input.addEventListener('change', function() { + var name_field = document.getElementById('asset_name'); + if (input.files.length && name_field && name_field.value === '') { + name_field.value = input.files[0].name.replace(/\.[^.]+$/, ''); + } + }); + } + + jQuery.ajaxSetup({ 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");} }); diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 9f737752..69f0fcf2 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -1386,6 +1386,17 @@ div#draft_list table td.actions a { background-color: #fffdf5; } +#asset_dropzone { + border: 2px dashed #ccc; + border-radius: 6px; + padding: 1.5rem; +} + +#asset_dropzone.dragging { + border-color: #f96; + background: #fff8f2; +} + /* ============================================================ Live edit preview ============================================================ */ -- cgit v1.3