From b3da204bd0f843887a2b7d2c1db2332f6336c142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gjermund=20H=C3=B8s=C3=B8ien=20Wiggen?= Date: Tue, 9 Jun 2026 13:48:07 +0200 Subject: [PATCH] fix: resolve widget overlaps after resize When resizing a widget, any widget that gets overlapped is automatically pushed down to clear the collision. Multi-pass overlap detection ensures cascading widgets are all resolved. Positions persisted to API. Co-Authored-By: Claude Opus 4.8 --- web/src/app/dashboards/[id]/page.tsx | 46 ++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/web/src/app/dashboards/[id]/page.tsx b/web/src/app/dashboards/[id]/page.tsx index 120c643..134d571 100644 --- a/web/src/app/dashboards/[id]/page.tsx +++ b/web/src/app/dashboards/[id]/page.tsx @@ -194,9 +194,49 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); setResizingId(null); - const updated = widgets.find((w) => w.id === widgetId); - if (updated) { - await updateWidget(id, widgetId, { position: updated.position }); + + // Resolve overlaps: push overlapping widgets out of the way + let resolved = widgets.map((w) => (w.id === widgetId ? { ...w, position: { ...w.position } } : w)); + const movedIds = new Set(); + + // Keep pushing until no overlaps + for (let pass = 0; pass < 10; pass++) { + let hasOverlap = false; + for (let i = 0; i < resolved.length; i++) { + for (let j = i + 1; j < resolved.length; j++) { + const a = resolved[i].position; + const b = resolved[j].position; + const ax2 = a.x + a.w; + const ay2 = a.y + a.h; + const bx2 = b.x + b.w; + const by2 = b.y + b.h; + + if (ax2 > b.x && a.x < bx2 && ay2 > b.y && a.y < by2) { + hasOverlap = true; + // Fix the resized widget in place, push the OTHER widget down + const toMove = widgetId === resolved[i].id ? j : i; + const movedW = resolved[toMove]; + const fixedW = resolved[widgetId === resolved[i].id ? i : j]; + const newY = fixedW.position.y + fixedW.position.h; + resolved[toMove] = { + ...movedW, + position: { ...movedW.position, y: newY }, + }; + movedIds.add(movedW.id); + } + } + } + if (!hasOverlap) break; + } + + setWidgets(resolved); + + // Persist positions + for (const w of resolved) { + const original = widgets.find((o) => o.id === w.id); + if (original && (original.position.y !== w.position.y || original.position.h !== w.position.h)) { + await updateWidget(id, w.id, { position: w.position }); + } } };