Skip to content

Commit 04519f7

Browse files
committed
fix: Use pointerWithin collision detection for cross-section tile drag
The default closestCenter collision detection picks the parent drop zone when a tile is dragged within a section, making cross-section moves impossible. Switch to pointerWithin which targets where the pointer actually lands. Also pass containerId through DragData to skip no-op drops (tile dropped on the same section it started in).
1 parent b19d345 commit 04519f7

2 files changed

Lines changed: 16 additions & 2 deletions

File tree

packages/app/src/DBDashboardPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ const Tile = forwardRef(
363363
<TileDragHandle
364364
tileId={chart.id}
365365
tileName={chart.config.name ?? 'Tile'}
366+
containerId={chart.containerId}
366367
/>
367368
)}
368369
{(chart.config.displayType === DisplayType.Line ||

packages/app/src/components/DashboardDndContext.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DragOverlay,
66
DragStartEvent,
77
MouseSensor,
8+
pointerWithin,
89
TouchSensor,
910
useDraggable,
1011
useDroppable,
@@ -24,7 +25,12 @@ import { IconGripVertical } from '@tabler/icons-react';
2425
// --- Types ---
2526

2627
export type DragData =
27-
| { type: 'tile'; tileId: string; tileName: string }
28+
| {
29+
type: 'tile';
30+
tileId: string;
31+
tileName: string;
32+
containerId?: string;
33+
}
2834
| { type: 'section'; sectionId: string; sectionTitle: string };
2935

3036
type Props = {
@@ -78,8 +84,12 @@ export function DashboardDndProvider({
7884
if (activeData.type === 'tile') {
7985
if (overId.startsWith('drop-section-')) {
8086
const targetId = overId.replace('drop-section-', '');
87+
// Skip no-op: tile already in this section
88+
if (targetId === activeData.containerId) return;
8189
onMoveTileToSection(activeData.tileId, targetId);
8290
} else if (overId === 'drop-ungrouped') {
91+
// Skip no-op: tile already ungrouped
92+
if (!activeData.containerId) return;
8393
onMoveTileToSection(activeData.tileId, undefined);
8494
}
8595
return;
@@ -104,6 +114,7 @@ export function DashboardDndProvider({
104114
return (
105115
<DndContext
106116
sensors={sensors}
117+
collisionDetection={pointerWithin}
107118
onDragStart={handleDragStart}
108119
onDragEnd={handleDragEnd}
109120
>
@@ -156,13 +167,15 @@ export function DashboardDndProvider({
156167
export function TileDragHandle({
157168
tileId,
158169
tileName,
170+
containerId,
159171
}: {
160172
tileId: string;
161173
tileName: string;
174+
containerId?: string;
162175
}) {
163176
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
164177
id: `drag-tile-${tileId}`,
165-
data: { type: 'tile', tileId, tileName } satisfies DragData,
178+
data: { type: 'tile', tileId, tileName, containerId } satisfies DragData,
166179
});
167180

168181
return (

0 commit comments

Comments
 (0)