@@ -1970,14 +1984,14 @@ function setWidgetSpan(wId, span) {
wrapper.classList.add(`lg:col-span-${span}`);
wrapper.setAttribute('data-col-span', span);
- // Knoppen styling bijwerken binnen deze widget
+ // Knoppen styling bijwerken binnen het instellingen dropdown menu
const buttons = wrapper.querySelectorAll('.widget-span-btn');
buttons.forEach(btn => {
const bSpan = parseInt(btn.getAttribute('data-span'), 10);
if (bSpan === span) {
- btn.className = "widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition bg-indigo-600 text-white shadow-2xs";
+ btn.className = "widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer bg-indigo-600 border-indigo-600 text-white shadow-2xs";
} else {
- btn.className = "widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition text-slate-500 hover:text-slate-800 dark:hover:text-slate-200";
+ btn.className = "widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700";
}
});
@@ -1987,6 +2001,9 @@ function setWidgetSpan(wId, span) {
item.col_span = span;
}
+ // Sluit het instellingen menu
+ document.getElementById(`widgetSettingsMenu_${wId}`)?.classList.add('hidden');
+
// Trigger resize voor SVG en diagrammen
window.dispatchEvent(new Event('resize'));
@@ -2002,6 +2019,22 @@ function setWidgetSpan(wId, span) {
showWidgetToast(`Formaat aangepast naar ${spanDescriptions[span] || span + ' kolommen'}`, 'success');
}
+// --- Widget Instellingen Popover Menu (Tandwieltje) ---
+function toggleWidgetSettingsMenu(wId, event) {
+ if (event) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ const menu = document.getElementById(`widgetSettingsMenu_${wId}`);
+ if (!menu) return;
+ const isCurrentlyHidden = menu.classList.contains('hidden');
+ // Sluit alle open menu's eerst
+ document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
+ if (isCurrentlyHidden) {
+ menu.classList.remove('hidden');
+ }
+}
+
// --- Widget Inklappen / Uitklappen ---
function toggleWidgetCollapse(wId) {
const body = document.getElementById(`widget_body_${wId}`);
@@ -2023,6 +2056,7 @@ function toggleWidgetCollapse(wId) {
// --- Widget Verbergen van Scherm met Undo Toast ---
function hideWidgetOnScreen(wId) {
+ document.getElementById(`widgetSettingsMenu_${wId}`)?.classList.add('hidden');
const wrapper = document.getElementById(`widgetWrapper_${wId}`) || document.querySelector(`.fociz-widget-wrapper[data-widget-id="${wId}"]`);
const item = focizWidgets.find(x => x.id === wId);
const title = item ? item.title : 'Widget';
@@ -2056,9 +2090,9 @@ function undoHideWidget(wId) {
}, 400);
}
-// --- Direct On-Screen Drag & Drop Engine (HTML5 Native) ---
+// --- Direct On-Screen Drag & Drop Engine (Live Shift & Reflow) ---
let draggedWidgetEl = null;
-let dropPlaceholderEl = null;
+let dragInitialIndex = -1;
function initPlannerWidgets() {
const grid = document.getElementById('focizWidgetGrid');
@@ -2077,13 +2111,6 @@ function initPlannerWidgets() {
} catch(e) {}
});
- if (!dropPlaceholderEl) {
- dropPlaceholderEl = document.createElement('div');
- dropPlaceholderEl.id = 'widgetDropPlaceholder';
- dropPlaceholderEl.className = 'col-span-1 min-w-0 border-2 border-dashed border-indigo-400/80 dark:border-indigo-500/80 bg-indigo-50/50 dark:bg-indigo-950/40 rounded-2xl flex items-center justify-center p-8 text-xs font-bold text-indigo-600 dark:text-indigo-400 pointer-events-none transition-all duration-150';
- dropPlaceholderEl.innerHTML = '
Laat los om hier te plaatsen
';
- }
-
// Luister naar drag handles
document.querySelectorAll('.widget-drag-handle').forEach(handle => {
handle.setAttribute('draggable', 'true');
@@ -2092,82 +2119,118 @@ function initPlannerWidgets() {
const wrapper = handle.closest('.fociz-widget-wrapper');
if (!wrapper) return;
draggedWidgetEl = wrapper;
+
+ // Bewaar beginpositie om te controleren op wijzigingen
+ const allWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
+ dragInitialIndex = allWrappers.indexOf(draggedWidgetEl);
+
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', wrapper.getAttribute('data-widget-id'));
- // Match column span van dragged widget
- const curSpan = wrapper.getAttribute('data-col-span') || '6';
- dropPlaceholderEl.className = `lg:col-span-${curSpan} col-span-1 min-w-0 border-2 border-dashed border-indigo-400/80 dark:border-indigo-500/80 bg-indigo-50/60 dark:bg-indigo-950/50 rounded-2xl flex items-center justify-center p-8 text-xs font-bold text-indigo-600 dark:text-indigo-400 pointer-events-none transition-all`;
+ // 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/50');
+ 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/50');
- }
- if (dropPlaceholderEl && dropPlaceholderEl.parentNode) {
- dropPlaceholderEl.remove();
+ draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
}
+ saveCurrentGridOrderIfChanged();
draggedWidgetEl = null;
};
});
- // Dragover op grid
+ // 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 !== dropPlaceholderEl) {
+ if (target && target !== draggedWidgetEl && target.parentElement === grid) {
const rect = target.getBoundingClientRect();
- const isAfter = (e.clientY - rect.top) > (rect.height / 2) || (e.clientX - rect.left) > (rect.width / 2);
+ 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.nextSibling !== dropPlaceholderEl) {
- target.after(dropPlaceholderEl);
+ if (target.nextElementSibling !== draggedWidgetEl) {
+ target.after(draggedWidgetEl);
}
} else {
- if (target.previousSibling !== dropPlaceholderEl) {
- target.before(dropPlaceholderEl);
+ if (target.previousElementSibling !== draggedWidgetEl) {
+ target.before(draggedWidgetEl);
}
}
}
};
grid.ondrop = (e) => {
- if (!draggedWidgetEl) return;
e.preventDefault();
-
- if (dropPlaceholderEl && dropPlaceholderEl.parentNode) {
- dropPlaceholderEl.replaceWith(draggedWidgetEl);
- }
-
if (draggedWidgetEl) {
- draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/50');
+ draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
}
-
- // Volgorde bijwerken
- const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
- currentWrappers.forEach((el, index) => {
- const wId = el.getAttribute('data-widget-id');
- const item = focizWidgets.find(x => x.id === wId);
- if (item) {
- item.order = index + 1;
- }
- });
-
- persistWidgetLayoutAuto();
- showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
-
+ saveCurrentGridOrderIfChanged();
draggedWidgetEl = null;
};
}
+function saveCurrentGridOrderIfChanged() {
+ const grid = document.getElementById('focizWidgetGrid');
+ if (!grid) return;
+ const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
+ const currentIndex = currentWrappers.indexOf(draggedWidgetEl);
+
+ let hasOrderChanged = false;
+ currentWrappers.forEach((el, index) => {
+ const wId = el.getAttribute('data-widget-id');
+ const item = focizWidgets.find(x => x.id === wId);
+ if (item) {
+ if (item.order !== index + 1) {
+ item.order = index + 1;
+ hasOrderChanged = true;
+ }
+ }
+ });
+
+ if (hasOrderChanged || (dragInitialIndex !== -1 && currentIndex !== dragInitialIndex)) {
+ persistWidgetLayoutAuto();
+ showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
+ }
+}
+
+// Sluit popup menu's als er buiten wordt geklikt
+document.addEventListener('click', (e) => {
+ if (!e.target.closest('.widget-settings-dropdown') && !e.target.closest('.widget-settings-btn')) {
+ document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
+ }
+});
+
// --- Jira Queue & Tickets Widget Engine (v2.0) ---
const plannerJiraAssigned = = json_encode($jiraAssignedIssues, JSON_UNESCAPED_UNICODE) ?>;
const plannerJiraAll = = json_encode($jiraAllIssues, JSON_UNESCAPED_UNICODE) ?>;