Dev #42
+301
-85
@@ -564,8 +564,23 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modular Customizable Widget Grid (Fociz v2.0) -->
|
<!-- Modular Customizable Widget Grid Container (Fociz v2.0) -->
|
||||||
<div id="focizWidgetGrid" class="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
|
<div class="relative w-full" id="focizWidgetGridContainer">
|
||||||
|
<!-- Blueprint Achtergrond Raster (zichtbaar zodra een widget wordt opgepakt) -->
|
||||||
|
<div id="widgetGridBlueprint" class="absolute inset-0 hidden pointer-events-none z-0 rounded-3xl border-2 border-dashed border-indigo-400/30 dark:border-indigo-500/25 bg-indigo-500/[0.02] p-3 transition-all duration-300">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-12 h-full gap-6">
|
||||||
|
<?php for ($col = 1; $col <= 12; $col++): ?>
|
||||||
|
<div class="h-full min-h-[360px] rounded-2xl border border-dashed border-indigo-400/25 dark:border-indigo-400/15 bg-indigo-50/20 dark:bg-indigo-950/20 flex flex-col items-center justify-between py-4 select-none">
|
||||||
|
<span class="text-[10px] font-mono font-black text-indigo-500/60 dark:text-indigo-400/50 tracking-wider">KOLOM <?= $col ?></span>
|
||||||
|
<div class="w-2 h-2 rounded-full bg-indigo-400/30 dark:bg-indigo-500/30"></div>
|
||||||
|
<span class="text-[10px] font-mono font-black text-indigo-500/40 dark:text-indigo-400/30">1/12</span>
|
||||||
|
</div>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Widget Raster (Fociz v2.0) -->
|
||||||
|
<div id="focizWidgetGrid" class="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start relative z-10 min-h-[300px]">
|
||||||
<?php foreach ($userWidgetLayout as $wId => $widget):
|
<?php foreach ($userWidgetLayout as $wId => $widget):
|
||||||
if (empty($widget['enabled'])) continue;
|
if (empty($widget['enabled'])) continue;
|
||||||
$span = (int)($widget['col_span'] ?? 6);
|
$span = (int)($widget['col_span'] ?? 6);
|
||||||
@@ -583,7 +598,7 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
|||||||
<!-- Bovenste rand van de widget: Prachtig gestyled en direct geïntegreerd in de widget -->
|
<!-- Bovenste rand van de widget: Prachtig gestyled en direct geïntegreerd in de widget -->
|
||||||
<div class="widget-header-bar flex items-center justify-between px-3.5 py-1.5 bg-slate-50/90 dark:bg-[#0c0f17]/90 border border-b-0 border-slate-200/80 dark:border-slate-800/80 rounded-t-2xl select-none backdrop-blur-xs transition-colors group-hover/widget:border-indigo-400/50 dark:group-hover/widget:border-indigo-500/30">
|
<div class="widget-header-bar flex items-center justify-between px-3.5 py-1.5 bg-slate-50/90 dark:bg-[#0c0f17]/90 border border-b-0 border-slate-200/80 dark:border-slate-800/80 rounded-t-2xl select-none backdrop-blur-xs transition-colors group-hover/widget:border-indigo-400/50 dark:group-hover/widget:border-indigo-500/30">
|
||||||
<!-- Links: Drag Handle Grip + Widget Titel -->
|
<!-- Links: Drag Handle Grip + Widget Titel -->
|
||||||
<div class="widget-drag-handle flex items-center space-x-1.5 cursor-grab active:cursor-grabbing text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 py-0.5 px-1 -ml-1 rounded-lg hover:bg-slate-200/50 dark:hover:bg-slate-800/50 transition group/drag" draggable="true" data-widget-id="<?= htmlspecialchars($wId) ?>" title="Sleep om te verplaatsen">
|
<div class="widget-drag-handle flex items-center space-x-1.5 cursor-grab active:cursor-grabbing text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 py-0.5 px-1 -ml-1 rounded-lg hover:bg-slate-200/50 dark:hover:bg-slate-800/50 transition group/drag touch-none" data-widget-id="<?= htmlspecialchars($wId) ?>" title="Sleep om te verplaatsen">
|
||||||
<i data-lucide="grip-vertical" class="w-3.5 h-3.5 text-slate-400 group-hover/drag:text-indigo-500 transition-colors flex-shrink-0"></i>
|
<i data-lucide="grip-vertical" class="w-3.5 h-3.5 text-slate-400 group-hover/drag:text-indigo-500 transition-colors flex-shrink-0"></i>
|
||||||
<span class="text-xs font-bold text-slate-700 dark:text-slate-300 tracking-wide truncate max-w-[160px] sm:max-w-[240px]"><?= htmlspecialchars($widget['title']) ?></span>
|
<span class="text-xs font-bold text-slate-700 dark:text-slate-300 tracking-wide truncate max-w-[160px] sm:max-w-[240px]"><?= htmlspecialchars($widget['title']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1526,6 +1541,7 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
|||||||
</div><!-- /.fociz-widget-wrapper -->
|
</div><!-- /.fociz-widget-wrapper -->
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- /#focizWidgetGridContainer -->
|
||||||
<?php
|
<?php
|
||||||
if ($isAjaxRequest) {
|
if ($isAjaxRequest) {
|
||||||
$contentHtml = ob_get_clean();
|
$contentHtml = ob_get_clean();
|
||||||
@@ -2090,8 +2106,17 @@ function undoHideWidget(wId) {
|
|||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Direct On-Screen Drag & Drop Engine (Live Shift & Reflow) ---
|
// --- Direct On-Screen Drag & Drop Engine (Hover Ghost, Blueprint Grid & Auto-Scroll) ---
|
||||||
let draggedWidgetEl = null;
|
let activeDraggedWrapper = null;
|
||||||
|
let dragCandidate = null;
|
||||||
|
let isDraggingActive = false;
|
||||||
|
let floatingGhost = null;
|
||||||
|
let dropPlaceholder = null;
|
||||||
|
let grabOffsetX = 0;
|
||||||
|
let grabOffsetY = 0;
|
||||||
|
let currentPointerX = 0;
|
||||||
|
let currentPointerY = 0;
|
||||||
|
let autoScrollAnimId = null;
|
||||||
let dragInitialIndex = -1;
|
let dragInitialIndex = -1;
|
||||||
|
|
||||||
function initPlannerWidgets() {
|
function initPlannerWidgets() {
|
||||||
@@ -2111,100 +2136,290 @@ function initPlannerWidgets() {
|
|||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Luister naar drag handles
|
// Luister naar drag handles met Pointer Events voor soepele hovering en scroll
|
||||||
document.querySelectorAll('.widget-drag-handle').forEach(handle => {
|
document.querySelectorAll('.widget-drag-handle').forEach(handle => {
|
||||||
handle.setAttribute('draggable', 'true');
|
handle.setAttribute('draggable', 'false'); // Uitschakelen van krakkemikkige native browser drag
|
||||||
|
handle.style.touchAction = 'none';
|
||||||
|
|
||||||
|
handle.onpointerdown = (e) => {
|
||||||
|
// Alleen primaire muisklik (links)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
// Negeer als er op knoppen in de buurt geklikt wordt
|
||||||
|
if (e.target.closest('.widget-settings-btn') || e.target.closest('.widget-settings-dropdown')) return;
|
||||||
|
|
||||||
handle.ondragstart = (e) => {
|
|
||||||
const wrapper = handle.closest('.fociz-widget-wrapper');
|
const wrapper = handle.closest('.fociz-widget-wrapper');
|
||||||
if (!wrapper) return;
|
if (!wrapper) return;
|
||||||
draggedWidgetEl = wrapper;
|
|
||||||
|
|
||||||
// Bewaar beginpositie om te controleren op wijzigingen
|
dragCandidate = {
|
||||||
const allWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
|
wrapper: wrapper,
|
||||||
dragInitialIndex = allWrappers.indexOf(draggedWidgetEl);
|
handle: handle,
|
||||||
|
startX: e.clientX,
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
startY: e.clientY
|
||||||
e.dataTransfer.setData('text/plain', wrapper.getAttribute('data-widget-id'));
|
|
||||||
|
|
||||||
// Sluit eventuele open popover menu's
|
|
||||||
document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (draggedWidgetEl) {
|
|
||||||
draggedWidgetEl.classList.add('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handle.ondragend = (e) => {
|
|
||||||
if (draggedWidgetEl) {
|
|
||||||
draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
|
|
||||||
}
|
|
||||||
saveCurrentGridOrderIfChanged();
|
|
||||||
draggedWidgetEl = null;
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dragover op grid: verplaatst de widget live in het DOM zodat andere blokken direct opzij schuiven
|
|
||||||
grid.ondragover = (e) => {
|
|
||||||
if (!draggedWidgetEl) return;
|
|
||||||
e.preventDefault();
|
|
||||||
e.dataTransfer.dropEffect = 'move';
|
|
||||||
|
|
||||||
// Check of cursor onderaan het grid zweeft
|
|
||||||
const gridRect = grid.getBoundingClientRect();
|
|
||||||
if (e.clientY > gridRect.bottom - 40) {
|
|
||||||
if (grid.lastElementChild !== draggedWidgetEl) {
|
|
||||||
grid.appendChild(draggedWidgetEl);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check of cursor bovenaan het grid zweeft
|
|
||||||
if (e.clientY < gridRect.top + 40) {
|
|
||||||
if (grid.firstElementChild !== draggedWidgetEl) {
|
|
||||||
grid.prepend(draggedWidgetEl);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = e.target.closest('.fociz-widget-wrapper');
|
|
||||||
if (target && target !== draggedWidgetEl && target.parentElement === grid) {
|
|
||||||
const rect = target.getBoundingClientRect();
|
|
||||||
const midY = rect.top + rect.height / 2;
|
|
||||||
const midX = rect.left + rect.width / 2;
|
|
||||||
|
|
||||||
// Bepaal of de muis voorbij het midden is (verticaal of horizontaal in dezelfde rij)
|
|
||||||
const isAfter = (e.clientY > midY) || (Math.abs(e.clientY - midY) < rect.height * 0.35 && e.clientX > midX);
|
|
||||||
|
|
||||||
if (isAfter) {
|
|
||||||
if (target.nextElementSibling !== draggedWidgetEl) {
|
|
||||||
target.after(draggedWidgetEl);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (target.previousElementSibling !== draggedWidgetEl) {
|
|
||||||
target.before(draggedWidgetEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
grid.ondrop = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (draggedWidgetEl) {
|
|
||||||
draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
|
|
||||||
}
|
|
||||||
saveCurrentGridOrderIfChanged();
|
|
||||||
draggedWidgetEl = null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globale pointer listeners (blijven soepel actief zelfs als cursor snel beweegt)
|
||||||
|
window.addEventListener('pointermove', (e) => {
|
||||||
|
currentPointerX = e.clientX;
|
||||||
|
currentPointerY = e.clientY;
|
||||||
|
|
||||||
|
if (!isDraggingActive && dragCandidate) {
|
||||||
|
// Controleer of de gebruiker minimaal 5px heeft bewogen (voorkomt valse triggers bij simpele kliks)
|
||||||
|
const dist = Math.hypot(e.clientX - dragCandidate.startX, e.clientY - dragCandidate.startY);
|
||||||
|
if (dist > 5) {
|
||||||
|
startWidgetDragging(dragCandidate.wrapper, dragCandidate.startX, dragCandidate.startY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDraggingActive) {
|
||||||
|
updateFloatingGhostPosition();
|
||||||
|
updateDropTargetFromPointer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('pointerup', (e) => {
|
||||||
|
if (isDraggingActive) {
|
||||||
|
finishWidgetDragging();
|
||||||
|
}
|
||||||
|
dragCandidate = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('pointercancel', () => {
|
||||||
|
if (isDraggingActive) {
|
||||||
|
cancelWidgetDragging();
|
||||||
|
}
|
||||||
|
dragCandidate = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape' && isDraggingActive) {
|
||||||
|
cancelWidgetDragging();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start het oppakken & slepen van de widget
|
||||||
|
function startWidgetDragging(wrapper, startX, startY) {
|
||||||
|
const grid = document.getElementById('focizWidgetGrid');
|
||||||
|
if (!grid || !wrapper) return;
|
||||||
|
|
||||||
|
isDraggingActive = true;
|
||||||
|
activeDraggedWrapper = wrapper;
|
||||||
|
|
||||||
|
// Sluit openstaande popovers
|
||||||
|
document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
|
||||||
|
|
||||||
|
// Bepaal beginpositie
|
||||||
|
const allWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
|
||||||
|
dragInitialIndex = allWrappers.indexOf(wrapper);
|
||||||
|
|
||||||
|
// 1. Toon het 12-koloms Blueprint Achtergrond Raster
|
||||||
|
const blueprint = document.getElementById('widgetGridBlueprint');
|
||||||
|
if (blueprint) {
|
||||||
|
blueprint.classList.remove('hidden');
|
||||||
|
blueprint.classList.add('block');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Creëer de zwevende Hover Ghost kaart
|
||||||
|
const rect = wrapper.getBoundingClientRect();
|
||||||
|
grabOffsetX = startX - rect.left;
|
||||||
|
grabOffsetY = startY - rect.top;
|
||||||
|
|
||||||
|
floatingGhost = wrapper.cloneNode(true);
|
||||||
|
floatingGhost.id = 'widgetFloatingGhost';
|
||||||
|
floatingGhost.removeAttribute('data-widget-id');
|
||||||
|
floatingGhost.querySelectorAll('[id]').forEach(el => el.removeAttribute('id'));
|
||||||
|
floatingGhost.querySelectorAll('.widget-settings-dropdown').forEach(d => d.remove());
|
||||||
|
|
||||||
|
floatingGhost.style.position = 'fixed';
|
||||||
|
floatingGhost.style.left = `${rect.left}px`;
|
||||||
|
floatingGhost.style.top = `${rect.top}px`;
|
||||||
|
floatingGhost.style.width = `${rect.width}px`;
|
||||||
|
floatingGhost.style.height = `${rect.height}px`;
|
||||||
|
floatingGhost.style.pointerEvents = 'none';
|
||||||
|
floatingGhost.style.zIndex = '999999';
|
||||||
|
floatingGhost.style.transform = 'scale(1.02) rotate(1.2deg)';
|
||||||
|
floatingGhost.style.opacity = '0.94';
|
||||||
|
floatingGhost.style.transition = 'transform 0.15s ease, box-shadow 0.15s ease';
|
||||||
|
floatingGhost.style.boxShadow = '0 25px 60px -10px rgba(79, 70, 229, 0.45), 0 0 0 2px rgba(99, 102, 241, 0.7)';
|
||||||
|
floatingGhost.style.cursor = 'grabbing';
|
||||||
|
document.body.appendChild(floatingGhost);
|
||||||
|
|
||||||
|
// 3. Plaats een duidelijke Drop Slot Placeholder in het raster
|
||||||
|
const span = wrapper.getAttribute('data-col-span') || '6';
|
||||||
|
dropPlaceholder = document.createElement('div');
|
||||||
|
dropPlaceholder.id = 'widgetDropPlaceholder';
|
||||||
|
dropPlaceholder.className = `lg:col-span-${span} col-span-1 min-w-0 rounded-2xl border-2 border-dashed border-indigo-500 dark:border-indigo-400 bg-indigo-50/70 dark:bg-indigo-950/50 flex flex-col items-center justify-center p-6 transition-all duration-200 shadow-inner`;
|
||||||
|
dropPlaceholder.style.minHeight = `${Math.max(130, Math.min(rect.height, 280))}px`;
|
||||||
|
dropPlaceholder.innerHTML = `
|
||||||
|
<div class="flex items-center space-x-2 text-indigo-600 dark:text-indigo-400 font-bold text-xs animate-pulse">
|
||||||
|
<svg class="w-5 h-5 animate-bounce" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path></svg>
|
||||||
|
<span>Laat los om hier te plaatsen</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
wrapper.after(dropPlaceholder);
|
||||||
|
wrapper.style.display = 'none';
|
||||||
|
|
||||||
|
document.body.classList.add('select-none', 'cursor-grabbing');
|
||||||
|
|
||||||
|
// 4. Start automatische soepele scroll bij nadering van schermranden
|
||||||
|
startAutoScroller();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFloatingGhostPosition() {
|
||||||
|
if (!floatingGhost) return;
|
||||||
|
floatingGhost.style.left = `${currentPointerX - grabOffsetX}px`;
|
||||||
|
floatingGhost.style.top = `${currentPointerY - grabOffsetY}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDropTargetFromPointer() {
|
||||||
|
const grid = document.getElementById('focizWidgetGrid');
|
||||||
|
if (!grid || !dropPlaceholder || !activeDraggedWrapper) return;
|
||||||
|
|
||||||
|
const wrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper')).filter(w => w !== activeDraggedWrapper);
|
||||||
|
let targetFound = false;
|
||||||
|
|
||||||
|
for (const wrap of wrappers) {
|
||||||
|
const rect = wrap.getBoundingClientRect();
|
||||||
|
if (currentPointerY >= rect.top - 15 && currentPointerY <= rect.bottom + 15 &&
|
||||||
|
currentPointerX >= rect.left - 15 && currentPointerX <= rect.right + 15) {
|
||||||
|
|
||||||
|
const midY = rect.top + rect.height / 2;
|
||||||
|
const midX = rect.left + rect.width / 2;
|
||||||
|
const isAfter = (currentPointerY > midY) || (Math.abs(currentPointerY - midY) < rect.height * 0.35 && currentPointerX > midX);
|
||||||
|
|
||||||
|
if (isAfter) {
|
||||||
|
if (wrap.nextElementSibling !== dropPlaceholder) {
|
||||||
|
wrap.after(dropPlaceholder);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (wrap.previousElementSibling !== dropPlaceholder) {
|
||||||
|
wrap.before(dropPlaceholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetFound && wrappers.length > 0) {
|
||||||
|
const firstRect = wrappers[0].getBoundingClientRect();
|
||||||
|
const lastRect = wrappers[wrappers.length - 1].getBoundingClientRect();
|
||||||
|
if (currentPointerY < firstRect.top) {
|
||||||
|
if (grid.firstElementChild !== dropPlaceholder) {
|
||||||
|
grid.prepend(dropPlaceholder);
|
||||||
|
}
|
||||||
|
} else if (currentPointerY > lastRect.bottom) {
|
||||||
|
if (grid.lastElementChild !== dropPlaceholder) {
|
||||||
|
grid.appendChild(dropPlaceholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-scroller wanneer je met de widget naar de boven- of onderkant van het scherm beweegt
|
||||||
|
function startAutoScroller() {
|
||||||
|
stopAutoScroller();
|
||||||
|
function scrollStep() {
|
||||||
|
if (!isDraggingActive) return;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const threshold = 110;
|
||||||
|
|
||||||
|
if (currentPointerY < threshold && window.scrollY > 0) {
|
||||||
|
const factor = (threshold - currentPointerY) / threshold;
|
||||||
|
const speed = Math.max(3, Math.round(factor * 26));
|
||||||
|
window.scrollBy(0, -speed);
|
||||||
|
updateDropTargetFromPointer();
|
||||||
|
} else if (currentPointerY > viewportHeight - threshold) {
|
||||||
|
const factor = (currentPointerY - (viewportHeight - threshold)) / threshold;
|
||||||
|
const speed = Math.max(3, Math.round(factor * 26));
|
||||||
|
window.scrollBy(0, speed);
|
||||||
|
updateDropTargetFromPointer();
|
||||||
|
}
|
||||||
|
autoScrollAnimId = requestAnimationFrame(scrollStep);
|
||||||
|
}
|
||||||
|
autoScrollAnimId = requestAnimationFrame(scrollStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAutoScroller() {
|
||||||
|
if (autoScrollAnimId) {
|
||||||
|
cancelAnimationFrame(autoScrollAnimId);
|
||||||
|
autoScrollAnimId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Voltooi het slepen (loslaten & AJAX opslaan)
|
||||||
|
function finishWidgetDragging() {
|
||||||
|
isDraggingActive = false;
|
||||||
|
stopAutoScroller();
|
||||||
|
|
||||||
|
document.body.classList.remove('select-none', 'cursor-grabbing');
|
||||||
|
|
||||||
|
// Verberg blueprint raster
|
||||||
|
const blueprint = document.getElementById('widgetGridBlueprint');
|
||||||
|
if (blueprint) {
|
||||||
|
blueprint.classList.add('hidden');
|
||||||
|
blueprint.classList.remove('block');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verwijder zwevende ghost
|
||||||
|
if (floatingGhost) {
|
||||||
|
floatingGhost.remove();
|
||||||
|
floatingGhost = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plaats originele widget terug op de plek van de placeholder
|
||||||
|
if (dropPlaceholder && activeDraggedWrapper) {
|
||||||
|
dropPlaceholder.replaceWith(activeDraggedWrapper);
|
||||||
|
activeDraggedWrapper.style.display = '';
|
||||||
|
dropPlaceholder = null;
|
||||||
|
|
||||||
|
// Subtiel succes flitsje op de neergezette widget
|
||||||
|
activeDraggedWrapper.classList.add('ring-2', 'ring-indigo-500', 'shadow-xl');
|
||||||
|
setTimeout(() => {
|
||||||
|
activeDraggedWrapper?.classList.remove('ring-2', 'ring-indigo-500', 'shadow-xl');
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer of de volgorde gewijzigd is en sla op via AJAX
|
||||||
|
saveCurrentGridOrderIfChanged();
|
||||||
|
activeDraggedWrapper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annuleer slepen (bijv. via Escape)
|
||||||
|
function cancelWidgetDragging() {
|
||||||
|
isDraggingActive = false;
|
||||||
|
stopAutoScroller();
|
||||||
|
|
||||||
|
document.body.classList.remove('select-none', 'cursor-grabbing');
|
||||||
|
|
||||||
|
const blueprint = document.getElementById('widgetGridBlueprint');
|
||||||
|
if (blueprint) {
|
||||||
|
blueprint.classList.add('hidden');
|
||||||
|
blueprint.classList.remove('block');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floatingGhost) {
|
||||||
|
floatingGhost.remove();
|
||||||
|
floatingGhost = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropPlaceholder && activeDraggedWrapper) {
|
||||||
|
dropPlaceholder.remove();
|
||||||
|
dropPlaceholder = null;
|
||||||
|
activeDraggedWrapper.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
activeDraggedWrapper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer gewijzigde volgorde en sla asynchroon op via AJAX
|
||||||
function saveCurrentGridOrderIfChanged() {
|
function saveCurrentGridOrderIfChanged() {
|
||||||
const grid = document.getElementById('focizWidgetGrid');
|
const grid = document.getElementById('focizWidgetGrid');
|
||||||
if (!grid) return;
|
if (!grid) return;
|
||||||
const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
|
const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
|
||||||
const currentIndex = currentWrappers.indexOf(draggedWidgetEl);
|
const currentIndex = activeDraggedWrapper ? currentWrappers.indexOf(activeDraggedWrapper) : -1;
|
||||||
|
|
||||||
let hasOrderChanged = false;
|
let hasOrderChanged = false;
|
||||||
currentWrappers.forEach((el, index) => {
|
currentWrappers.forEach((el, index) => {
|
||||||
@@ -2219,6 +2434,7 @@ function saveCurrentGridOrderIfChanged() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (hasOrderChanged || (dragInitialIndex !== -1 && currentIndex !== dragInitialIndex)) {
|
if (hasOrderChanged || (dragInitialIndex !== -1 && currentIndex !== dragInitialIndex)) {
|
||||||
|
// Geruisloos opslaan via AJAX
|
||||||
persistWidgetLayoutAuto();
|
persistWidgetLayoutAuto();
|
||||||
showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
|
showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user