diff --git a/web/src/views/live/DraggableGridLayout.tsx b/web/src/views/live/DraggableGridLayout.tsx index 60cde0aca..cc3bf75b6 100644 --- a/web/src/views/live/DraggableGridLayout.tsx +++ b/web/src/views/live/DraggableGridLayout.tsx @@ -467,27 +467,47 @@ export default function DraggableGridLayout({ }, [fitToScreen, fitGridParams, cameras, includeBirdseye, birdseyeConfig]); const [fitLayoutOverride, setFitLayoutOverride] = useState(); - const draggedItemRef = useRef(null); + const fitDragRef = useRef<{ + draggedId: string; + mouseX: number; + mouseY: number; + } | null>(null); useEffect(() => { setFitLayoutOverride(undefined); }, [fitGridParams, cameras, includeBirdseye]); const handleFitDrag = useCallback( - (_layout: Layout, _oldItem: LayoutItem, layoutItem: LayoutItem) => { - draggedItemRef.current = layoutItem.i; + ( + _layout: Layout, + _oldItem: LayoutItem, + layoutItem: LayoutItem, + _placeholder: LayoutItem, + event: MouseEvent, + ) => { + fitDragRef.current = { + draggedId: layoutItem.i, + mouseX: event.clientX, + mouseY: event.clientY, + }; }, [], ); const handleFitDragStop = useCallback( - (newLayout: Layout, _oldItem: LayoutItem, layoutItem: LayoutItem) => { - if (!fitToScreen || !fitGridParams) return; + ( + _newLayout: Layout, + _oldItem: LayoutItem, + _layoutItem: LayoutItem, + _placeholder: LayoutItem, + event: MouseEvent, + ) => { + if (!fitToScreen || !fitGridParams || !fitDragRef.current) return; const w = fitGridParams.gridUnitsPerCam; const colsPerRow = fitGridParams.colsPerRow; - const draggedId = draggedItemRef.current ?? layoutItem.i; - draggedItemRef.current = null; + const draggedId = fitDragRef.current.draggedId; + fitDragRef.current = null; const currentOrder = fitLayoutOverride ?? fitLayout ?? []; const orderedNames = [...currentOrder] @@ -497,10 +517,23 @@ export default function DraggableGridLayout({ }) .map((item) => item.i); - const dropCenterX = layoutItem.x + w / 2; - const dropCenterY = layoutItem.y + w / 2; - const targetCol = Math.min(Math.floor(dropCenterX / w), colsPerRow - 1); - const targetRow = Math.floor(dropCenterY / w); + const gridEl = containerRef.current?.querySelector( + ".grid-layout", + ) as HTMLElement | null; + if (!gridEl) return; + + const rect = gridEl.getBoundingClientRect(); + const mouseRelX = event.clientX - rect.left; + const mouseRelY = event.clientY - rect.top; + + const cellWidthPx = rect.width / colsPerRow; + const cellHeightPx = cellHeight * w; + + const targetCol = Math.max( + 0, + Math.min(Math.floor(mouseRelX / cellWidthPx), colsPerRow - 1), + ); + const targetRow = Math.max(0, Math.floor(mouseRelY / cellHeightPx)); const totalRows = Math.ceil(orderedNames.length / colsPerRow); const clampedRow = Math.min(targetRow, totalRows - 1); const targetIndex = Math.min( @@ -510,7 +543,7 @@ export default function DraggableGridLayout({ const sourceIndex = orderedNames.indexOf(draggedId); if (sourceIndex === -1 || sourceIndex === targetIndex) { - setFitLayoutOverride((prev) => prev); + setFitLayoutOverride((prev) => (prev ? [...prev] : prev)); return; } @@ -530,7 +563,7 @@ export default function DraggableGridLayout({ setFitLayoutOverride(normalized); }, - [fitToScreen, fitGridParams, fitLayoutOverride, fitLayout], + [fitToScreen, fitGridParams, fitLayoutOverride, fitLayout, cellHeight, containerRef], ); const activeGridLayout = useMemo(() => {