From e9967fa22781d922ba4eb8fb44fe72d200ac4b14 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 10 Nov 2025 21:04:10 +0000
Subject: [PATCH] =IconsManager.php update

---
 assets/js/concise/UploadManager.js |  107 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 107 insertions(+), 0 deletions(-)

diff --git a/assets/js/concise/UploadManager.js b/assets/js/concise/UploadManager.js
index 0fd8f6e..b59101c 100644
--- a/assets/js/concise/UploadManager.js
+++ b/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();

--
Gitblit v1.10.0