Jake Vanderwerf
2025-11-10 e9967fa22781d922ba4eb8fb44fe72d200ac4b14
assets/js/concise/UploadManager.js
@@ -99,6 +99,20 @@
         'failed_permanent': 'Upload failed permanently'
      };
      // Sortable configuration
      this.sortableInstances = new Map();
      this.sortableConfig = {
         animation: 150,
         draggable: '.item',
         handle: '.select-item-label, img', // Can drag by image or checkbox label
         ghostClass: 'sortable-ghost',
         chosenClass: 'sortable-chosen',
         dragClass: 'sortable-drag',
         onEnd: (evt) => {
            this.handleReorder(evt);
         }
      };
      this.init();
   }
@@ -210,6 +224,9 @@
      if (config.destination === 'post_group' && !this.dragController) {
         this.initGroupFeatures();
      }
      if (config.type !== 'single') {
         this.initSortable(field);
      }
      return fieldId;
   }
@@ -356,6 +373,76 @@
      });
   }
   initSortable(field) {
      if (!window.Sortable) return;
      // Main grid
      const mainGrid = field.element.querySelector('.item-grid:not(.group)');
      if (mainGrid) {
         this.sortableInstances.set(`${field.id}-main`,
            new Sortable(mainGrid, {
               ...this.sortableConfig,
               group: {
                  name: field.id,
                  pull: true,
                  put: true
               }
            })
         );
      }
      // Group grids (for selection mode with grouping)
      const groupGrids = field.element.querySelectorAll('.item-grid.group');
      groupGrids.forEach((grid, index) => {
         this.sortableInstances.set(`${field.id}-group-${index}`,
            new Sortable(grid, {
               ...this.sortableConfig,
               group: {
                  name: field.id,
                  pull: true,
                  put: true
               }
            })
         );
      });
   }
// Add reorder handler
   handleReorder(evt) {
      const grid = evt.to;
      const fieldWrapper = grid.closest('.field, .upload');
      if (!fieldWrapper) return;
      const form = fieldWrapper.closest('form');
      if (!form) return;
      // Get form config if available
      const formId = form.dataset.formId;
      if (formId && window.jvbForms) {
         const formConfig = window.jvbForms.forms?.get(formId);
         if (formConfig?.options.autosave) {
            // Trigger autosave after reordering
            window.jvbForms.scheduleSave(formConfig, 1000);
         }
      }
      // Announce for accessibility
      if (window.jvbA11y) {
         window.jvbA11y.announce('Item reordered');
      }
      // Trigger custom event
      fieldWrapper.dispatchEvent(new CustomEvent('jvb-items-reordered', {
         detail: {
            from: evt.from,
            to: evt.to,
            oldIndex: evt.oldIndex,
            newIndex: evt.newIndex
         },
         bubbles: true
      }));
   }
   /*******************************************************************************
    * EXTERNAL FILE DROP HANDLERS (for new uploads from desktop)
    *******************************************************************************/
@@ -2936,6 +3023,12 @@
      this.selectionHandlers.clear();
      this.cleanupAllPreviewUrls();
      this.sortableInstances.forEach(instance => {
         if (instance?.destroy) {
            instance.destroy();
         }
      });
      this.sortableInstances.clear();
      // Clear data
      this.fields.clear();
@@ -2945,6 +3038,20 @@
      this.subscribers.clear();
   }
   destroySortable(fieldName) {
      // Destroy all sortable instances for this field
      const instances = Array.from(this.sortableInstances.keys())
         .filter(key => key.startsWith(fieldName));
      instances.forEach(key => {
         const instance = this.sortableInstances.get(key);
         if (instance?.destroy) {
            instance.destroy();
         }
         this.sortableInstances.delete(key);
      });
   }
   cleanupRestore() {
      this.restoreModal.handleClose();
      this.restoreSelection.destroy();