mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-05 22:57:40 +03:00
Fix fit-to-screen drag: support non-adjacent and horizontal swaps
Previous approach sorted react-grid-layout's post-drag positions to infer order, which broke for non-adjacent and horizontal moves because rgl pushes items down instead of swapping them. New approach: - onDrag records which item is being dragged (draggedItemRef) - onDragStop uses the dragged item's final x/y to compute the target slot in our own grid, then performs a clean swap in the ordered name array - Layout is always fully regenerated from our order array, ignoring rgl's position arithmetic entirely https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
This commit is contained in:
parent
46334ec8be
commit
3d1de5bf69
@ -467,24 +467,61 @@ export default function DraggableGridLayout({
|
|||||||
}, [fitToScreen, fitGridParams, cameras, includeBirdseye, birdseyeConfig]);
|
}, [fitToScreen, fitGridParams, cameras, includeBirdseye, birdseyeConfig]);
|
||||||
|
|
||||||
const [fitLayoutOverride, setFitLayoutOverride] = useState<Layout | undefined>();
|
const [fitLayoutOverride, setFitLayoutOverride] = useState<Layout | undefined>();
|
||||||
|
const draggedItemRef = useRef<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFitLayoutOverride(undefined);
|
setFitLayoutOverride(undefined);
|
||||||
}, [fitGridParams, cameras, includeBirdseye]);
|
}, [fitGridParams, cameras, includeBirdseye]);
|
||||||
|
|
||||||
|
const handleFitDrag = useCallback(
|
||||||
|
(_layout: Layout, _oldItem: LayoutItem, layoutItem: LayoutItem) => {
|
||||||
|
draggedItemRef.current = layoutItem.i;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const handleFitDragStop = useCallback(
|
const handleFitDragStop = useCallback(
|
||||||
(newLayout: Layout) => {
|
(newLayout: Layout, _oldItem: LayoutItem, layoutItem: LayoutItem) => {
|
||||||
if (!fitToScreen || !fitGridParams) return;
|
if (!fitToScreen || !fitGridParams) return;
|
||||||
|
|
||||||
const w = fitGridParams.gridUnitsPerCam;
|
const w = fitGridParams.gridUnitsPerCam;
|
||||||
const colsPerRow = fitGridParams.colsPerRow;
|
const colsPerRow = fitGridParams.colsPerRow;
|
||||||
|
const draggedId = draggedItemRef.current ?? layoutItem.i;
|
||||||
|
draggedItemRef.current = null;
|
||||||
|
|
||||||
const sorted = [...newLayout].sort((a, b) => {
|
const currentOrder = fitLayoutOverride ?? fitLayout ?? [];
|
||||||
if (a.y !== b.y) return a.y - b.y;
|
const orderedNames = [...currentOrder]
|
||||||
return a.x - b.x;
|
.sort((a, b) => {
|
||||||
});
|
if (a.y !== b.y) return a.y - b.y;
|
||||||
|
return a.x - b.x;
|
||||||
|
})
|
||||||
|
.map((item) => item.i);
|
||||||
|
|
||||||
const normalized = sorted.map((item, index) => ({
|
const dropCenterX = layoutItem.x + w / 2;
|
||||||
i: item.i,
|
const dropCenterY = layoutItem.y + w / 2;
|
||||||
|
const targetCol = Math.min(Math.floor(dropCenterX / w), colsPerRow - 1);
|
||||||
|
const targetRow = Math.floor(dropCenterY / w);
|
||||||
|
const totalRows = Math.ceil(orderedNames.length / colsPerRow);
|
||||||
|
const clampedRow = Math.min(targetRow, totalRows - 1);
|
||||||
|
const targetIndex = Math.min(
|
||||||
|
clampedRow * colsPerRow + targetCol,
|
||||||
|
orderedNames.length - 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceIndex = orderedNames.indexOf(draggedId);
|
||||||
|
if (sourceIndex === -1 || sourceIndex === targetIndex) {
|
||||||
|
setFitLayoutOverride((prev) => prev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrder = [...orderedNames];
|
||||||
|
[newOrder[sourceIndex], newOrder[targetIndex]] = [
|
||||||
|
newOrder[targetIndex],
|
||||||
|
newOrder[sourceIndex],
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalized = newOrder.map((name, index) => ({
|
||||||
|
i: name,
|
||||||
x: (index % colsPerRow) * w,
|
x: (index % colsPerRow) * w,
|
||||||
y: Math.floor(index / colsPerRow) * w,
|
y: Math.floor(index / colsPerRow) * w,
|
||||||
w,
|
w,
|
||||||
@ -493,7 +530,7 @@ export default function DraggableGridLayout({
|
|||||||
|
|
||||||
setFitLayoutOverride(normalized);
|
setFitLayoutOverride(normalized);
|
||||||
},
|
},
|
||||||
[fitToScreen, fitGridParams],
|
[fitToScreen, fitGridParams, fitLayoutOverride, fitLayout],
|
||||||
);
|
);
|
||||||
|
|
||||||
const activeGridLayout = useMemo(() => {
|
const activeGridLayout = useMemo(() => {
|
||||||
@ -874,6 +911,7 @@ export default function DraggableGridLayout({
|
|||||||
dragConfig={{
|
dragConfig={{
|
||||||
enabled: isEditMode,
|
enabled: isEditMode,
|
||||||
}}
|
}}
|
||||||
|
onDrag={fitToScreen ? handleFitDrag : undefined}
|
||||||
onDragStop={fitToScreen ? handleFitDragStop : handleLayoutChange}
|
onDragStop={fitToScreen ? handleFitDragStop : handleLayoutChange}
|
||||||
onResize={handleResize}
|
onResize={handleResize}
|
||||||
onResizeStart={() => setShowCircles(false)}
|
onResizeStart={() => setShowCircles(false)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user