feat(connections): support touch
This commit is contained in:
@@ -1,24 +1,17 @@
|
||||
import type { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||
import { StoreApi } from 'zustand';
|
||||
|
||||
import { getHostForElement, calcAutoPan } from '../../utils';
|
||||
import { getHostForElement, calcAutoPan, getEventPosition, isMouseEvent } from '../../utils';
|
||||
import type { OnConnect, HandleType, ReactFlowState } from '../../types';
|
||||
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
|
||||
import {
|
||||
ConnectionHandle,
|
||||
getClosestHandle,
|
||||
getConnectionPosition,
|
||||
getHandleLookup,
|
||||
isValidHandle,
|
||||
ValidConnectionFunc,
|
||||
} from './utils';
|
||||
import { ConnectionHandle, getClosestHandle, getHandleLookup, isValidHandle, ValidConnectionFunc } from './utils';
|
||||
|
||||
function resetRecentHandle(handleDomNode: Element): void {
|
||||
handleDomNode?.classList.remove('react-flow__handle-valid');
|
||||
handleDomNode?.classList.remove('react-flow__handle-connecting');
|
||||
}
|
||||
|
||||
export function handleMouseDown({
|
||||
export function handlePointerDown({
|
||||
event,
|
||||
handleId,
|
||||
nodeId,
|
||||
@@ -30,7 +23,7 @@ export function handleMouseDown({
|
||||
elementEdgeUpdaterType,
|
||||
onEdgeUpdateEnd,
|
||||
}: {
|
||||
event: ReactMouseEvent;
|
||||
event: ReactMouseEvent | ReactTouchEvent;
|
||||
handleId: string | null;
|
||||
nodeId: string;
|
||||
onConnect: OnConnect;
|
||||
@@ -39,7 +32,7 @@ export function handleMouseDown({
|
||||
setState: StoreApi<ReactFlowState>['setState'];
|
||||
isValidConnection: ValidConnectionFunc;
|
||||
elementEdgeUpdaterType?: HandleType;
|
||||
onEdgeUpdateEnd?: (evt: MouseEvent) => void;
|
||||
onEdgeUpdateEnd?: (evt: MouseEvent | TouchEvent) => void;
|
||||
}): void {
|
||||
// when react-flow is used inside a shadow root we can't use document
|
||||
const doc = getHostForElement(event.target as HTMLElement);
|
||||
@@ -48,7 +41,8 @@ export function handleMouseDown({
|
||||
let autoPanId = 0;
|
||||
let prevClosestHandle: ConnectionHandle | null;
|
||||
|
||||
const clickedElement = doc?.elementFromPoint(event.clientX, event.clientY);
|
||||
const { clientX, clientY } = isMouseEvent(event) ? event : event.touches[0]!;
|
||||
const clickedElement = doc?.elementFromPoint(clientX, clientY);
|
||||
const elementIsTarget = clickedElement?.classList.contains('target');
|
||||
const elementIsSource = clickedElement?.classList.contains('source');
|
||||
|
||||
@@ -59,7 +53,7 @@ export function handleMouseDown({
|
||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementIsTarget ? 'target' : 'source';
|
||||
const containerBounds = domNode.getBoundingClientRect();
|
||||
let prevActiveHandle: Element;
|
||||
let connectionPosition = getConnectionPosition(event, containerBounds);
|
||||
let connectionPosition = getEventPosition(event, containerBounds);
|
||||
let autoPanStarted = false;
|
||||
|
||||
const handleLookup = getHandleLookup({
|
||||
@@ -89,10 +83,10 @@ export function handleMouseDown({
|
||||
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
|
||||
function onMouseMove(event: MouseEvent) {
|
||||
function onPointerMove(event: MouseEvent | TouchEvent) {
|
||||
const { transform } = getState();
|
||||
connectionPosition = getEventPosition(event, containerBounds);
|
||||
|
||||
connectionPosition = getConnectionPosition(event, containerBounds);
|
||||
prevClosestHandle = getClosestHandle(
|
||||
pointToRendererPoint(connectionPosition, transform, false, [1, 1]),
|
||||
connectionRadius,
|
||||
@@ -138,7 +132,7 @@ export function handleMouseDown({
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(event: MouseEvent) {
|
||||
function onPointerUp(event: MouseEvent | TouchEvent) {
|
||||
cancelAnimationFrame(autoPanId);
|
||||
autoPanStarted = false;
|
||||
|
||||
@@ -172,10 +166,16 @@ export function handleMouseDown({
|
||||
connectionHandleType: null,
|
||||
});
|
||||
|
||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
||||
doc.removeEventListener('mousemove', onPointerMove as EventListener);
|
||||
doc.removeEventListener('mouseup', onPointerUp as EventListener);
|
||||
|
||||
doc.removeEventListener('touchmove', onPointerMove as EventListener);
|
||||
doc.removeEventListener('touchend', onPointerUp as EventListener);
|
||||
}
|
||||
|
||||
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
||||
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
||||
doc.addEventListener('mousemove', onPointerMove as EventListener);
|
||||
doc.addEventListener('mouseup', onPointerUp as EventListener);
|
||||
|
||||
doc.addEventListener('touchmove', onPointerMove as EventListener);
|
||||
doc.addEventListener('touchend', onPointerUp as EventListener);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||
import { handleMouseDown } from './handler';
|
||||
import { devWarn, getHostForElement } from '../../utils';
|
||||
import { handlePointerDown } from './handler';
|
||||
import { devWarn, getHostForElement, isMouseEvent } from '../../utils';
|
||||
import { addEdge } from '../../utils/graph';
|
||||
import { Position } from '../../types';
|
||||
import type { HandleProps, Connection, ReactFlowState } from '../../types';
|
||||
@@ -33,6 +33,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
children,
|
||||
className,
|
||||
onMouseDown,
|
||||
onTouchStart,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
@@ -66,9 +67,11 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
onConnect?.(edgeParams);
|
||||
};
|
||||
|
||||
const onMouseDownHandler = (event: ReactMouseEvent<HTMLDivElement>) => {
|
||||
if (event.button === 0) {
|
||||
handleMouseDown({
|
||||
const onPointerDown = (event: ReactMouseEvent<HTMLDivElement> | ReactTouchEvent<HTMLDivElement>) => {
|
||||
const isMouse = isMouseEvent(event);
|
||||
|
||||
if ((isMouse && event.button === 0) || 'touches' in event) {
|
||||
handlePointerDown({
|
||||
event,
|
||||
handleId,
|
||||
nodeId,
|
||||
@@ -79,7 +82,12 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
isValidConnection,
|
||||
});
|
||||
}
|
||||
onMouseDown?.(event);
|
||||
|
||||
if (isMouse) {
|
||||
onMouseDown?.(event);
|
||||
} else {
|
||||
onTouchStart?.(event);
|
||||
}
|
||||
};
|
||||
|
||||
const onClick = (event: ReactMouseEvent) => {
|
||||
@@ -136,7 +144,8 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
connectionStartHandle?.type === type,
|
||||
},
|
||||
])}
|
||||
onMouseDown={onMouseDownHandler}
|
||||
onMouseDown={onPointerDown}
|
||||
onTouchStart={onPointerDown}
|
||||
onClick={connectOnClick ? onClick : undefined}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
|
||||
import { ConnectionMode } from '../../types';
|
||||
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
|
||||
import { internalsSymbol } from '../../utils';
|
||||
@@ -134,10 +132,3 @@ export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHand
|
||||
return res;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function getConnectionPosition(event: MouseEvent | ReactMouseEvent, bounds: DOMRect): XYPosition {
|
||||
return {
|
||||
x: event.clientX - bounds.left,
|
||||
y: event.clientY - bounds.top,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user