The snapping was too strict - it would only snap when the widget was almost perfectly positioned over a cell. Users had to be very precise, making it difficult to snap widgets into cells.
// Before
threshold: 30 // Only 30 pixels - too strict
// After
threshold: 500 // Much larger area - very forgiving
The threshold is now 500 pixels, which means the widget can be anywhere reasonably near a cell and it will consider snapping.
The algorithm now prioritizes overlap over distance:
Old Algorithm:
New Algorithm:
minOverlap: 0.15)const SNAP_CONFIG = {
threshold: 500, // Consider cells within 500px
minOverlap: 0.15, // Widget must overlap cell by at least 15%
magneticSnap: true, // Real-time visual feedback
resizeOnSnap: true, // Auto-resize to fit cell
};
Snap Selection Logic:
Before:
After:
You can tune the behavior by adjusting these values in WidgetRenderer.tsx:
const SNAP_CONFIG = {
// Larger threshold = consider more cells (current: 500px is very forgiving)
threshold: 500,
// Higher minOverlap = need more widget on cell to snap
// 0.15 = 15% overlap required
// 0.25 = 25% overlap required (stricter)
// 0.05 = 5% overlap required (more forgiving)
minOverlap: 0.15,
// Disable magnetic snapping for only-on-drop behavior
magneticSnap: true,
// Disable auto-resize if you want to preserve widget size
resizeOnSnap: true,
};
Widget overlaps cell by 10%
→ No snap (below 15% threshold)
→ Widget moves freely
Widget overlaps cell by 40%
→ Cell highlights
→ On release, snaps to cell
→ Widget resizes to fit cell
Widget overlaps cell-1 by 20%
Widget overlaps cell-2 by 60%
→ cell-2 highlights (higher overlap)
→ Snaps to cell-2
Free Movement: Drag widget near (but not on) a cell
Partial Overlap: Drag widget so 20% overlaps a cell
Multiple Cells: Drag widget over corner between two cells
Full Overlap: Drag widget completely over a cell
You'll now see more detailed logging:
[Snap Debug] Checking cell: {
cellId: "cell-1",
distance: "245.3",
overlap: "0.342", // 34.2% overlap
withinThreshold: true,
meetsMinOverlap: true // Above 15% minimum
}
[Snap Debug] Final best cell: {
cellId: "cell-1",
overlap: "0.342" // Chosen due to highest overlap
}
/src/components/WidgetRenderer.tsx:
threshold from 30 to 500minOverlap config (0.15 = 15%)Result: Snapping now feels natural and forgiving. You have to actually position the widget on the cell for it to snap, rather than having it snap too eagerly when just passing nearby.