feat(FN-3941): separate graph pan from selection-gated drag

Separates graph panning from selection-gated drag by updating `useNodeDrag` and `useGraphInteraction` hooks to handle the two interaction modes independently, with `GraphTaskNode` now gating drag on selection state. Tests were added across the graph interaction, node drag, persistence, and integrati

Fusion-Task-Id: FN-3941
This commit is contained in:
Fusion
2026-05-10 11:34:17 -07:00
committed by gsxdsm
parent b046d70730
commit e2bd5000cf
12 changed files with 124 additions and 17 deletions

View File

@@ -8,6 +8,7 @@ interface UseNodeDragOptions {
taskId: string;
position: GraphPosition;
scale: number;
canDrag: boolean;
onPositionChange: (taskId: string, position: GraphPosition) => void;
onDragStateChange?: (isDragging: boolean) => void;
onDragEnd?: () => void;
@@ -19,7 +20,7 @@ interface PendingState {
startPosition: GraphPosition;
}
export function useNodeDrag({ taskId, position, scale, onPositionChange, onDragStateChange, onDragEnd }: UseNodeDragOptions) {
export function useNodeDrag({ taskId, position, scale, canDrag, onPositionChange, onDragStateChange, onDragEnd }: UseNodeDragOptions) {
const [isDragging, setIsDragging] = useState(false);
const pendingRef = useRef<PendingState | null>(null);
const positionRef = useRef(position);
@@ -38,7 +39,7 @@ export function useNodeDrag({ taskId, position, scale, onPositionChange, onDragS
}, [onDragEnd, onDragStateChange]);
const onPointerDown = useCallback((event: ReactPointerEvent<HTMLElement>) => {
if (!event.isPrimary) return;
if (!event.isPrimary || !canDrag) return;
event.stopPropagation();
const currentTarget = event.currentTarget;
if (typeof currentTarget.setPointerCapture === "function") {
@@ -49,7 +50,7 @@ export function useNodeDrag({ taskId, position, scale, onPositionChange, onDragS
startPointer: { x: event.clientX, y: event.clientY },
startPosition: positionRef.current,
};
}, []);
}, [canDrag]);
const onPointerMove = useCallback((event: ReactPointerEvent<HTMLElement>) => {
const pending = pendingRef.current;