feat(autoPan): add props, handle scale

This commit is contained in:
moklick
2023-01-17 16:26:04 +01:00
parent 86f7a9a43d
commit f2a4260ac9
7 changed files with 27 additions and 6 deletions
@@ -1,7 +1,7 @@
import type { MouseEvent as ReactMouseEvent } from 'react'; import type { MouseEvent as ReactMouseEvent } from 'react';
import { StoreApi } from 'zustand'; import { StoreApi } from 'zustand';
import { clamp, getHostForElement, getVelocity } from '../../utils'; import { getHostForElement, getVelocity } from '../../utils';
import { ConnectionMode } from '../../types'; import { ConnectionMode } from '../../types';
import type { OnConnect, Connection, HandleType, ReactFlowState, XYPosition } from '../../types'; import type { OnConnect, Connection, HandleType, ReactFlowState, XYPosition } from '../../types';
@@ -100,13 +100,13 @@ export function handleMouseDown({
}): void { }): void {
// when react-flow is used inside a shadow root we can't use document // when react-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement); const doc = getHostForElement(event.target as HTMLElement);
const { onConnectStart, connectionMode, domNode } = getState(); const { onConnectStart, connectionMode, domNode, autoPanOnConnect } = getState();
let connectionPosition: XYPosition | null = null; let connectionPosition: XYPosition | null = null;
let requestAnimationFrameId = 0; let requestAnimationFrameId = 0;
// when the user is moving the mouse close to the edge of the canvas while connecting we move the canvas // when the user is moving the mouse close to the edge of the canvas while connecting we move the canvas
const updateViewport = (): void => { const updateViewport = (): void => {
if (!connectionPosition || !containerBounds) { if (!connectionPosition || !containerBounds || !autoPanOnConnect) {
return; return;
} }
@@ -132,6 +132,7 @@ export function handleMouseDown({
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source'; const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
const containerBounds = domNode.getBoundingClientRect(); const containerBounds = domNode.getBoundingClientRect();
let recentHoveredHandle: Element; let recentHoveredHandle: Element;
connectionPosition = { connectionPosition = {
x: event.clientX - containerBounds.left, x: event.clientX - containerBounds.left,
y: event.clientY - containerBounds.top, y: event.clientY - containerBounds.top,
@@ -45,6 +45,8 @@ type StoreUpdaterProps = Pick<
| 'noPanClassName' | 'noPanClassName'
| 'nodeOrigin' | 'nodeOrigin'
| 'elevateNodesOnSelect' | 'elevateNodesOnSelect'
| 'autoPanOnConnect'
| 'autoPanOnNodeDrag'
> & { rfId: string }; > & { rfId: string };
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
@@ -119,6 +121,8 @@ const StoreUpdater = ({
noPanClassName, noPanClassName,
nodeOrigin, nodeOrigin,
rfId, rfId,
autoPanOnConnect,
autoPanOnNodeDrag,
}: StoreUpdaterProps) => { }: StoreUpdaterProps) => {
const { const {
setNodes, setNodes,
@@ -172,6 +176,8 @@ const StoreUpdater = ({
useDirectStoreUpdater('noPanClassName', noPanClassName, store.setState); useDirectStoreUpdater('noPanClassName', noPanClassName, store.setState);
useDirectStoreUpdater('nodeOrigin', nodeOrigin, store.setState); useDirectStoreUpdater('nodeOrigin', nodeOrigin, store.setState);
useDirectStoreUpdater('rfId', rfId, store.setState); useDirectStoreUpdater('rfId', rfId, store.setState);
useDirectStoreUpdater('autoPanOnConnect', autoPanOnConnect, store.setState);
useDirectStoreUpdater('autoPanOnNodeDrag', autoPanOnNodeDrag, store.setState);
useStoreUpdater<Node[]>(nodes, setNodes); useStoreUpdater<Node[]>(nodes, setNodes);
useStoreUpdater<Edge[]>(edges, setEdges); useStoreUpdater<Edge[]>(edges, setEdges);
@@ -160,6 +160,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
elevateNodesOnSelect = true, elevateNodesOnSelect = true,
elevateEdgesOnSelect = false, elevateEdgesOnSelect = false,
disableKeyboardA11y = false, disableKeyboardA11y = false,
autoPanOnConnect = true,
autoPanOnNodeDrag = true,
style, style,
id, id,
...rest ...rest
@@ -287,6 +289,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
noPanClassName={noPanClassName} noPanClassName={noPanClassName}
nodeOrigin={nodeOrigin} nodeOrigin={nodeOrigin}
rfId={rfId} rfId={rfId}
autoPanOnConnect={autoPanOnConnect}
autoPanOnNodeDrag={autoPanOnNodeDrag}
/> />
<SelectionListener onSelectionChange={onSelectionChange} /> <SelectionListener onSelectionChange={onSelectionChange} />
{children} {children}
+7 -3
View File
@@ -114,8 +114,10 @@ function useDrag({
const yMovement = getVelocity(centerPosition.current.y, 35, containerBounds.current.height - 35) * 20; const yMovement = getVelocity(centerPosition.current.y, 35, containerBounds.current.height - 35) * 20;
if (xMovement !== 0 || yMovement !== 0) { if (xMovement !== 0 || yMovement !== 0) {
lastPos.current.x = (lastPos.current.x ?? 0) + xMovement * -1; const scale = store.getState().transform[2];
lastPos.current.y = (lastPos.current.y ?? 0) + yMovement * -1;
lastPos.current.x = (lastPos.current.x ?? 0) - xMovement / scale;
lastPos.current.y = (lastPos.current.y ?? 0) - yMovement / scale;
updateNodes(lastPos.current as XYPosition); updateNodes(lastPos.current as XYPosition);
@@ -175,8 +177,9 @@ function useDrag({
}) })
.on('drag', (event: UseDragEvent) => { .on('drag', (event: UseDragEvent) => {
const pointerPos = getPointerPosition(event); const pointerPos = getPointerPosition(event);
const { autoPanOnNodeDrag } = store.getState();
if (!animationFrameStarted.current) { if (!animationFrameStarted.current && autoPanOnNodeDrag) {
animationFrameStarted.current = true; animationFrameStarted.current = true;
updateViewport(); updateViewport();
} }
@@ -199,6 +202,7 @@ function useDrag({
setDragging(false); setDragging(false);
animationFrameStarted.current = false; animationFrameStarted.current = false;
cancelAnimationFrame(requestAnimationFrameId.current); cancelAnimationFrame(requestAnimationFrameId.current);
if (dragItems.current) { if (dragItems.current) {
const { updateNodePositions, nodeInternals, onNodeDragStop, onSelectionDragStop } = store.getState(); const { updateNodePositions, nodeInternals, onNodeDragStop, onSelectionDragStop } = store.getState();
const onStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop); const onStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop);
+2
View File
@@ -56,6 +56,8 @@ const initialState: ReactFlowStore = {
connectOnClick: true, connectOnClick: true,
ariaLiveMessage: '', ariaLiveMessage: '',
autoPanOnConnect: true,
autoPanOnNodeDrag: true,
}; };
export default initialState; export default initialState;
@@ -139,6 +139,8 @@ export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
elevateNodesOnSelect?: boolean; elevateNodesOnSelect?: boolean;
elevateEdgesOnSelect?: boolean; elevateEdgesOnSelect?: boolean;
disableKeyboardA11y?: boolean; disableKeyboardA11y?: boolean;
autoPanOnNodeDrag?: boolean;
autoPanOnConnect?: boolean;
}; };
export type ReactFlowRefType = HTMLDivElement; export type ReactFlowRefType = HTMLDivElement;
+2
View File
@@ -210,6 +210,8 @@ export type ReactFlowStore = {
onSelectionChange?: OnSelectionChangeFunc; onSelectionChange?: OnSelectionChangeFunc;
ariaLiveMessage: string; ariaLiveMessage: string;
autoPanOnConnect: boolean;
autoPanOnNodeDrag: boolean;
}; };
export type ReactFlowActions = { export type ReactFlowActions = {