Add drag-and-drop functionality for service reordering with localStorage support
This commit is contained in:
168
js/drag-drop.js
Normal file
168
js/drag-drop.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
* drag-drop.js - Drag and drop service reordering
|
||||||
|
*/
|
||||||
|
|
||||||
|
const STORAGE_KEY_ORDER = 'services-order';
|
||||||
|
|
||||||
|
// Load saved order from localStorage
|
||||||
|
function loadSavedOrder() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(STORAGE_KEY_ORDER);
|
||||||
|
return saved ? JSON.parse(saved) : {};
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error loading saved order:', e);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save order to localStorage
|
||||||
|
function saveSavedOrder(orderMap) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEY_ORDER, JSON.stringify(orderMap));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error saving order:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply saved order to service groups
|
||||||
|
function applySavedOrder() {
|
||||||
|
const orderMap = loadSavedOrder();
|
||||||
|
|
||||||
|
document.querySelectorAll('.service-group').forEach(group => {
|
||||||
|
const groupName = group.querySelector('h2')?.textContent;
|
||||||
|
if (!groupName || !orderMap[groupName]) return;
|
||||||
|
|
||||||
|
const container = group.querySelector('.services-grid');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const cards = Array.from(container.querySelectorAll('.service-card'));
|
||||||
|
const orderedIds = orderMap[groupName];
|
||||||
|
|
||||||
|
// Sort cards based on saved order
|
||||||
|
cards.sort((a, b) => {
|
||||||
|
const aId = a.dataset.serviceId;
|
||||||
|
const bId = b.dataset.serviceId;
|
||||||
|
const aIndex = orderedIds.indexOf(aId);
|
||||||
|
const bIndex = orderedIds.indexOf(bId);
|
||||||
|
|
||||||
|
if (aIndex === -1 && bIndex === -1) return 0;
|
||||||
|
if (aIndex === -1) return 1;
|
||||||
|
if (bIndex === -1) return -1;
|
||||||
|
return aIndex - bIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-append in sorted order
|
||||||
|
cards.forEach(card => container.appendChild(card));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize drag and drop
|
||||||
|
function initDragDrop() {
|
||||||
|
let draggedElement = null;
|
||||||
|
let draggedGroup = null;
|
||||||
|
|
||||||
|
document.addEventListener('dragstart', (e) => {
|
||||||
|
if (e.target.classList.contains('service-card')) {
|
||||||
|
draggedElement = e.target;
|
||||||
|
draggedGroup = e.target.closest('.service-group');
|
||||||
|
e.target.style.opacity = '0.5';
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData('text/html', e.target.innerHTML);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('dragend', (e) => {
|
||||||
|
if (e.target.classList.contains('service-card')) {
|
||||||
|
e.target.style.opacity = '1';
|
||||||
|
|
||||||
|
// Remove all drop indicators
|
||||||
|
document.querySelectorAll('.service-card').forEach(card => {
|
||||||
|
card.classList.remove('drag-over');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save the new order
|
||||||
|
saveCurrentOrder();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('dragover', (e) => {
|
||||||
|
if (e.preventDefault) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = e.target.closest('.service-card');
|
||||||
|
if (target && target !== draggedElement && draggedElement) {
|
||||||
|
const targetGroup = target.closest('.service-group');
|
||||||
|
|
||||||
|
// Only allow reordering within the same group
|
||||||
|
if (targetGroup === draggedGroup) {
|
||||||
|
e.dataTransfer.dropEffect = 'move';
|
||||||
|
|
||||||
|
// Remove previous indicators
|
||||||
|
document.querySelectorAll('.service-card').forEach(card => {
|
||||||
|
card.classList.remove('drag-over');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add indicator
|
||||||
|
target.classList.add('drag-over');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('drop', (e) => {
|
||||||
|
if (e.stopPropagation) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = e.target.closest('.service-card');
|
||||||
|
if (target && target !== draggedElement && draggedElement) {
|
||||||
|
const targetGroup = target.closest('.service-group');
|
||||||
|
|
||||||
|
// Only allow dropping within the same group
|
||||||
|
if (targetGroup === draggedGroup) {
|
||||||
|
const container = target.parentNode;
|
||||||
|
const targetIndex = Array.from(container.children).indexOf(target);
|
||||||
|
const draggedIndex = Array.from(container.children).indexOf(draggedElement);
|
||||||
|
|
||||||
|
if (draggedIndex < targetIndex) {
|
||||||
|
container.insertBefore(draggedElement, target.nextSibling);
|
||||||
|
} else {
|
||||||
|
container.insertBefore(draggedElement, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target?.classList.remove('drag-over');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make service cards draggable
|
||||||
|
document.querySelectorAll('.service-card').forEach(card => {
|
||||||
|
card.setAttribute('draggable', 'true');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save current order of all groups
|
||||||
|
function saveCurrentOrder() {
|
||||||
|
const orderMap = {};
|
||||||
|
|
||||||
|
document.querySelectorAll('.service-group').forEach(group => {
|
||||||
|
const groupName = group.querySelector('h2')?.textContent;
|
||||||
|
if (!groupName) return;
|
||||||
|
|
||||||
|
const cards = group.querySelectorAll('.service-card');
|
||||||
|
const order = Array.from(cards).map(card => card.dataset.serviceId);
|
||||||
|
orderMap[groupName] = order;
|
||||||
|
});
|
||||||
|
|
||||||
|
saveSavedOrder(orderMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other modules
|
||||||
|
window.dragDropModule = {
|
||||||
|
init: initDragDrop,
|
||||||
|
applySavedOrder: applySavedOrder
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user