fix: resize now uses functional setState to avoid stale closure
The onUp handler was capturing stale widgets from the render closure, overwriting the resize dimensions. Now uses setWidgets(current => ...) to read latest state and apply overlap resolution correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -190,54 +190,45 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUp = async () => {
|
const onUp = () => {
|
||||||
document.removeEventListener("mousemove", onMove);
|
document.removeEventListener("mousemove", onMove);
|
||||||
document.removeEventListener("mouseup", onUp);
|
document.removeEventListener("mouseup", onUp);
|
||||||
setResizingId(null);
|
setResizingId(null);
|
||||||
|
|
||||||
// Resolve overlaps: push overlapping widgets out of the way
|
// Resolve overlaps using latest state via functional updater
|
||||||
let resolved = widgets.map((w) => (w.id === widgetId ? { ...w, position: { ...w.position } } : w));
|
setWidgets((current) => {
|
||||||
const movedIds = new Set<string>();
|
let resolved = current.map((w) => ({ ...w, position: { ...w.position } }));
|
||||||
|
|
||||||
// Keep pushing until no overlaps
|
|
||||||
for (let pass = 0; pass < 10; pass++) {
|
for (let pass = 0; pass < 10; pass++) {
|
||||||
let hasOverlap = false;
|
let hasOverlap = false;
|
||||||
for (let i = 0; i < resolved.length; i++) {
|
for (let i = 0; i < resolved.length; i++) {
|
||||||
for (let j = i + 1; j < resolved.length; j++) {
|
for (let j = i + 1; j < resolved.length; j++) {
|
||||||
const a = resolved[i].position;
|
const a = resolved[i].position;
|
||||||
const b = resolved[j].position;
|
const b = resolved[j].position;
|
||||||
const ax2 = a.x + a.w;
|
if (a.x + a.w > b.x && a.x < b.x + b.w && a.y + a.h > b.y && a.y < b.y + b.h) {
|
||||||
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;
|
hasOverlap = true;
|
||||||
// Fix the resized widget in place, push the OTHER widget down
|
|
||||||
const toMove = widgetId === resolved[i].id ? j : i;
|
const toMove = widgetId === resolved[i].id ? j : i;
|
||||||
const movedW = resolved[toMove];
|
|
||||||
const fixedW = resolved[widgetId === resolved[i].id ? i : j];
|
const fixedW = resolved[widgetId === resolved[i].id ? i : j];
|
||||||
const newY = fixedW.position.y + fixedW.position.h;
|
|
||||||
resolved[toMove] = {
|
resolved[toMove] = {
|
||||||
...movedW,
|
...resolved[toMove],
|
||||||
position: { ...movedW.position, y: newY },
|
position: { ...resolved[toMove].position, y: fixedW.position.y + fixedW.position.h },
|
||||||
};
|
};
|
||||||
movedIds.add(movedW.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasOverlap) break;
|
if (!hasOverlap) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWidgets(resolved);
|
// Persist changed positions
|
||||||
|
|
||||||
// Persist positions
|
|
||||||
for (const w of resolved) {
|
for (const w of resolved) {
|
||||||
const original = widgets.find((o) => o.id === w.id);
|
const orig = current.find((o) => o.id === w.id);
|
||||||
if (original && (original.position.y !== w.position.y || original.position.h !== w.position.h)) {
|
if (orig && (orig.position.y !== w.position.y || orig.position.h !== w.position.h)) {
|
||||||
await updateWidget(id, w.id, { position: w.position });
|
updateWidget(id, w.id, { position: w.position });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mousemove", onMove);
|
document.addEventListener("mousemove", onMove);
|
||||||
|
|||||||
Reference in New Issue
Block a user