Browse Source

Fix add and drag drop attachments to minicards and card.

Thanks to xet7 !

Fixes #5946,
fixes #5436,
fixes #2936,
fixes #1926,
fixes #300,
fixes #142
Lauri Ojansivu 4 days ago
parent
commit
b06daff4c7

+ 7 - 0
client/components/boards/boardBody.css

@@ -496,3 +496,10 @@
   font-size: 25px;
   cursor: pointer;
 }
+
+/* Global file drag over state for board canvas */
+.board-canvas.file-drag-over {
+  background-color: rgba(0, 123, 255, 0.05) !important;
+  border: 2px dashed #007bff !important;
+  transition: all 0.2s ease;
+}

+ 25 - 0
client/components/boards/boardBody.js

@@ -721,6 +721,31 @@ BlazeComponent.extendComponent({
           }
         },
         'click .js-empty-board-add-swimlane': Popup.open('swimlaneAdd'),
+        // Global drag and drop file upload handlers for better visual feedback
+        'dragover .board-canvas'(event) {
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            // Add visual indicator that files can be dropped
+            $('.board-canvas').addClass('file-drag-over');
+          }
+        },
+        'dragleave .board-canvas'(event) {
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            // Only remove class if we're leaving the board canvas entirely
+            if (!event.currentTarget.contains(event.relatedTarget)) {
+              $('.board-canvas').removeClass('file-drag-over');
+            }
+          }
+        },
+        'drop .board-canvas'(event) {
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            $('.board-canvas').removeClass('file-drag-over');
+          }
+        },
       },
     ];
   },

+ 1 - 1
client/components/cards/attachments.js

@@ -343,7 +343,7 @@ export function handleFileUpload(card, files) {
   }
 
   // Check if user can modify the card
-  if (!card.canModifyCard()) {
+  if (!Utils.canModifyCard()) {
     if (process.env.DEBUG === 'true') {
       console.warn('User does not have permission to modify this card');
     }

+ 39 - 36
client/components/cards/cardDetails.js

@@ -504,55 +504,58 @@ BlazeComponent.extendComponent({
         },
         // Drag and drop file upload handlers
         'dragover .js-card-details'(event) {
-          event.preventDefault();
-          event.stopPropagation();
+          // Only prevent default for file drags to avoid interfering with other drag operations
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+          }
         },
         'dragenter .js-card-details'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          const card = this.data();
-          const board = card.board();
-          // Only allow drag-and-drop if user can modify card and board allows attachments
-          if (card.canModifyCard() && board && board.allowsAttachments) {
-            // Check if the drag contains files
-            const dataTransfer = event.originalEvent.dataTransfer;
-            if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            const card = this.data();
+            const board = card.board();
+            // Only allow drag-and-drop if user can modify card and board allows attachments
+            if (Utils.canModifyCard() && board && board.allowsAttachments) {
               $(event.currentTarget).addClass('is-dragging-over');
             }
           }
         },
         'dragleave .js-card-details'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          $(event.currentTarget).removeClass('is-dragging-over');
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            $(event.currentTarget).removeClass('is-dragging-over');
+          }
         },
         'drop .js-card-details'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          $(event.currentTarget).removeClass('is-dragging-over');
-
-          const card = this.data();
-          const board = card.board();
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            $(event.currentTarget).removeClass('is-dragging-over');
 
-          // Check permissions
-          if (!card.canModifyCard() || !board || !board.allowsAttachments) {
-            return;
-          }
+            const card = this.data();
+            const board = card.board();
 
-          // Check if this is a file drop (not a checklist item reorder)
-          const dataTransfer = event.originalEvent.dataTransfer;
-          if (!dataTransfer || !dataTransfer.files || dataTransfer.files.length === 0) {
-            return;
-          }
+            // Check permissions
+            if (!Utils.canModifyCard() || !board || !board.allowsAttachments) {
+              return;
+            }
 
-          // Check if the drop contains files (not just text/HTML)
-          if (!dataTransfer.types.includes('Files')) {
-            return;
-          }
+            // Check if this is a file drop (not a checklist item reorder)
+            if (!dataTransfer.files || dataTransfer.files.length === 0) {
+              return;
+            }
 
-          const files = dataTransfer.files;
-          if (files && files.length > 0) {
-            handleFileUpload(card, files);
+            const files = dataTransfer.files;
+            if (files && files.length > 0) {
+              handleFileUpload(card, files);
+            }
           }
         },
       },

+ 39 - 36
client/components/cards/minicard.js

@@ -111,55 +111,58 @@ BlazeComponent.extendComponent({
         'click .js-open-minicard-details-menu': Popup.open('minicardDetailsActions'),
         // Drag and drop file upload handlers
         'dragover .minicard'(event) {
-          event.preventDefault();
-          event.stopPropagation();
+          // Only prevent default for file drags to avoid interfering with sortable
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+          }
         },
         'dragenter .minicard'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          const card = this.data();
-          const board = card.board();
-          // Only allow drag-and-drop if user can modify card and board allows attachments
-          if (card.canModifyCard() && board && board.allowsAttachments) {
-            // Check if the drag contains files
-            const dataTransfer = event.originalEvent.dataTransfer;
-            if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            const card = this.data();
+            const board = card.board();
+            // Only allow drag-and-drop if user can modify card and board allows attachments
+            if (Utils.canModifyCard() && board && board.allowsAttachments) {
               $(event.currentTarget).addClass('is-dragging-over');
             }
           }
         },
         'dragleave .minicard'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          $(event.currentTarget).removeClass('is-dragging-over');
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            $(event.currentTarget).removeClass('is-dragging-over');
+          }
         },
         'drop .minicard'(event) {
-          event.preventDefault();
-          event.stopPropagation();
-          $(event.currentTarget).removeClass('is-dragging-over');
-
-          const card = this.data();
-          const board = card.board();
+          const dataTransfer = event.originalEvent.dataTransfer;
+          if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+            event.preventDefault();
+            event.stopPropagation();
+            $(event.currentTarget).removeClass('is-dragging-over');
 
-          // Check permissions
-          if (!card.canModifyCard() || !board || !board.allowsAttachments) {
-            return;
-          }
+            const card = this.data();
+            const board = card.board();
 
-          // Check if this is a file drop (not a card reorder)
-          const dataTransfer = event.originalEvent.dataTransfer;
-          if (!dataTransfer || !dataTransfer.files || dataTransfer.files.length === 0) {
-            return;
-          }
+            // Check permissions
+            if (!Utils.canModifyCard() || !board || !board.allowsAttachments) {
+              return;
+            }
 
-          // Check if the drop contains files (not just text/HTML)
-          if (!dataTransfer.types.includes('Files')) {
-            return;
-          }
+            // Check if this is a file drop (not a card reorder)
+            if (!dataTransfer.files || dataTransfer.files.length === 0) {
+              return;
+            }
 
-          const files = dataTransfer.files;
-          if (files && files.length > 0) {
-            handleFileUpload(card, files);
+            const files = dataTransfer.files;
+            if (files && files.length > 0) {
+              handleFileUpload(card, files);
+            }
           }
         },
       }

+ 9 - 0
client/components/lists/list.js

@@ -157,6 +157,15 @@ BlazeComponent.extendComponent({
       } else {
         $cards.sortable({
           handle: '.minicard',
+          // Prevent sortable from interfering with file drag and drop
+          start: function(event, ui) {
+            // Check if this is a file drag operation
+            const dataTransfer = event.originalEvent?.dataTransfer;
+            if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
+              // Cancel sortable for file drags
+              return false;
+            }
+          },
         });
       }