From 0a2c96fe3e1d51c8f98390c9794724739dd31843 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 23 Jan 2023 17:58:15 +0100 Subject: [PATCH 1/3] feat(connections): support touch --- .../core/src/components/Edges/wrapEdge.tsx | 6 +-- .../core/src/components/Handle/handler.ts | 44 +++++++++---------- packages/core/src/components/Handle/index.tsx | 25 +++++++---- packages/core/src/components/Handle/utils.ts | 9 ---- packages/core/src/container/Pane/index.tsx | 14 ++---- packages/core/src/hooks/useDrag/index.ts | 12 ++--- packages/core/src/types/component-props.ts | 2 +- packages/core/src/types/edges.ts | 2 +- packages/core/src/types/general.ts | 11 +++-- packages/core/src/utils/index.ts | 24 +++++++++- 10 files changed, 82 insertions(+), 67 deletions(-) diff --git a/packages/core/src/components/Edges/wrapEdge.tsx b/packages/core/src/components/Edges/wrapEdge.tsx index 4ac3b139..36c49615 100644 --- a/packages/core/src/components/Edges/wrapEdge.tsx +++ b/packages/core/src/components/Edges/wrapEdge.tsx @@ -4,7 +4,7 @@ import cc from 'classcat'; import { useStoreApi } from '../../hooks/useStore'; import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; -import { handleMouseDown } from '../Handle/handler'; +import { handlePointerDown } from '../Handle/handler'; import { EdgeAnchor } from './EdgeAnchor'; import { getMarkerId } from '../../utils/graph'; import { getMouseHandler } from './utils'; @@ -99,14 +99,14 @@ export default (EdgeComponent: ComponentType) => { setUpdating(true); onEdgeUpdateStart?.(event, edge, handleType); - const _onEdgeUpdateEnd = (evt: MouseEvent) => { + const _onEdgeUpdateEnd = (evt: MouseEvent | TouchEvent) => { setUpdating(false); onEdgeUpdateEnd?.(evt, edge, handleType); }; const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection); - handleMouseDown({ + handlePointerDown({ event, handleId, nodeId, diff --git a/packages/core/src/components/Handle/handler.ts b/packages/core/src/components/Handle/handler.ts index 40be2090..766f48ba 100644 --- a/packages/core/src/components/Handle/handler.ts +++ b/packages/core/src/components/Handle/handler.ts @@ -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['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); } diff --git a/packages/core/src/components/Handle/index.tsx b/packages/core/src/components/Handle/index.tsx index 19a78314..89212300 100644 --- a/packages/core/src/components/Handle/index.tsx +++ b/packages/core/src/components/Handle/index.tsx @@ -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( children, className, onMouseDown, + onTouchStart, ...rest }, ref @@ -66,9 +67,11 @@ const Handle = forwardRef( onConnect?.(edgeParams); }; - const onMouseDownHandler = (event: ReactMouseEvent) => { - if (event.button === 0) { - handleMouseDown({ + const onPointerDown = (event: ReactMouseEvent | ReactTouchEvent) => { + const isMouse = isMouseEvent(event); + + if ((isMouse && event.button === 0) || 'touches' in event) { + handlePointerDown({ event, handleId, nodeId, @@ -79,7 +82,12 @@ const Handle = forwardRef( isValidConnection, }); } - onMouseDown?.(event); + + if (isMouse) { + onMouseDown?.(event); + } else { + onTouchStart?.(event); + } }; const onClick = (event: ReactMouseEvent) => { @@ -136,7 +144,8 @@ const Handle = forwardRef( connectionStartHandle?.type === type, }, ])} - onMouseDown={onMouseDownHandler} + onMouseDown={onPointerDown} + onTouchStart={onPointerDown} onClick={connectOnClick ? onClick : undefined} ref={ref} {...rest} diff --git a/packages/core/src/components/Handle/utils.ts b/packages/core/src/components/Handle/utils.ts index 69d6f68c..d6cf9e8b 100644 --- a/packages/core/src/components/Handle/utils.ts +++ b/packages/core/src/components/Handle/utils.ts @@ -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, - }; -} diff --git a/packages/core/src/container/Pane/index.tsx b/packages/core/src/container/Pane/index.tsx index e92f1d79..bb9e3615 100644 --- a/packages/core/src/container/Pane/index.tsx +++ b/packages/core/src/container/Pane/index.tsx @@ -11,8 +11,9 @@ import { containerStyle } from '../../styles'; import { useStore, useStoreApi } from '../../hooks/useStore'; import { getSelectionChanges } from '../../utils/changes'; import { getConnectedEdges, getNodesInside } from '../../utils/graph'; +import { getEventPosition } from '../../utils'; import { SelectionMode } from '../../types'; -import type { ReactFlowProps, XYPosition, ReactFlowState, NodeChange, EdgeChange } from '../../types'; +import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange } from '../../types'; type PaneProps = { isSelecting: boolean; @@ -33,13 +34,6 @@ type PaneProps = { > >; -function getMousePosition(event: ReactMouseEvent, containerBounds: DOMRect): XYPosition { - return { - x: event.clientX - containerBounds.left, - y: event.clientY - containerBounds.top, - }; -} - const wrapHandler = ( handler: React.MouseEventHandler | undefined, containerRef: React.MutableRefObject @@ -118,7 +112,7 @@ const Pane = memo( return; } - const { x, y } = getMousePosition(event, containerBounds.current); + const { x, y } = getEventPosition(event, containerBounds.current); resetSelectedElements(); @@ -145,7 +139,7 @@ const Pane = memo( store.setState({ userSelectionActive: true, nodesSelectionActive: false }); - const mousePos = getMousePosition(event, containerBounds.current); + const mousePos = getEventPosition(event, containerBounds.current); const startX = userSelectionRect.startX ?? 0; const startY = userSelectionRect.startY ?? 0; diff --git a/packages/core/src/hooks/useDrag/index.ts b/packages/core/src/hooks/useDrag/index.ts index c9d0ab3d..868b6387 100644 --- a/packages/core/src/hooks/useDrag/index.ts +++ b/packages/core/src/hooks/useDrag/index.ts @@ -7,7 +7,7 @@ import { useStoreApi } from '../../hooks/useStore'; import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils'; import { handleNodeClick } from '../../components/Nodes/utils'; import useGetPointerPosition from '../useGetPointerPosition'; -import { calcAutoPan } from '../../utils'; +import { calcAutoPan, getEventPosition } from '../../utils'; import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '../../types'; export type UseDragData = { dx: number; dy: number }; @@ -168,10 +168,7 @@ function useDrag({ } containerBounds.current = domNode?.getBoundingClientRect() || null; - mousePosition.current = { - x: event.sourceEvent.clientX - (containerBounds.current?.left ?? 0), - y: event.sourceEvent.clientY - (containerBounds.current?.top ?? 0), - }; + mousePosition.current = getEventPosition(event.sourceEvent, containerBounds.current!); }) .on('drag', (event: UseDragEvent) => { const pointerPos = getPointerPosition(event); @@ -188,10 +185,7 @@ function useDrag({ dragItems.current ) { dragEvent.current = event.sourceEvent as MouseEvent; - mousePosition.current = { - x: event.sourceEvent.clientX - (containerBounds.current?.left ?? 0), - y: event.sourceEvent.clientY - (containerBounds.current?.top ?? 0), - }; + mousePosition.current = getEventPosition(event.sourceEvent, containerBounds.current!); updateNodes(pointerPos); } diff --git a/packages/core/src/types/component-props.ts b/packages/core/src/types/component-props.ts index 90b7124f..bafb8d5e 100644 --- a/packages/core/src/types/component-props.ts +++ b/packages/core/src/types/component-props.ts @@ -61,7 +61,7 @@ export type ReactFlowProps = HTMLAttributes & { onEdgeMouseLeave?: EdgeMouseHandler; onEdgeDoubleClick?: EdgeMouseHandler; onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void; - onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void; + onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void; onNodesChange?: OnNodesChange; onEdgesChange?: OnEdgesChange; onNodesDelete?: OnNodesDelete; diff --git a/packages/core/src/types/edges.ts b/packages/core/src/types/edges.ts index 92e10637..c2202788 100644 --- a/packages/core/src/types/edges.ts +++ b/packages/core/src/types/edges.ts @@ -85,7 +85,7 @@ export type WrapEdgeProps = Omit, 'sourceHandle' | 'targetHandl onMouseLeave?: EdgeMouseHandler; edgeUpdaterRadius?: number; onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void; - onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void; + onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void; rfId?: string; isFocusable: boolean; pathOptions?: BezierPathOptions | SmoothStepPathOptions; diff --git a/packages/core/src/types/general.ts b/packages/core/src/types/general.ts index 6289c9c1..ba0de157 100644 --- a/packages/core/src/types/general.ts +++ b/packages/core/src/types/general.ts @@ -1,5 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent } from 'react'; +import type { + MouseEvent as ReactMouseEvent, + TouchEvent as ReactTouchEvent, + ComponentType, + MemoExoticComponent, +} from 'react'; import type { D3DragEvent, Selection as D3Selection, SubjectPosition, ZoomBehavior } from 'd3'; import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils'; @@ -77,8 +82,8 @@ export type OnConnectStartParams = { handleType: HandleType | null; }; -export type OnConnectStart = (event: ReactMouseEvent, params: OnConnectStartParams) => void; -export type OnConnectEnd = (event: MouseEvent) => void; +export type OnConnectStart = (event: ReactMouseEvent | ReactTouchEvent, params: OnConnectStartParams) => void; +export type OnConnectEnd = (event: MouseEvent | TouchEvent) => void; export type Viewport = { x: number; diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 30aa3789..3051f25a 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,4 +1,8 @@ -import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; +import type { + KeyboardEvent as ReactKeyboardEvent, + MouseEvent as ReactMouseEvent, + TouchEvent as ReactTouchEvent, +} from 'react'; import type { Dimensions, Node, XYPosition, CoordinateExtent, Box, Rect } from '../types'; @@ -111,3 +115,21 @@ export function isInputDOMNode(event: KeyboardEvent | ReactKeyboardEvent): boole !!target?.closest('.nokey') ); } + +export const isMouseEvent = ( + event: MouseEvent | ReactMouseEvent | TouchEvent | ReactTouchEvent +): event is MouseEvent | ReactMouseEvent => 'button' in event; + +export const getEventPosition = ( + event: MouseEvent | ReactMouseEvent | TouchEvent | ReactTouchEvent, + bounds: DOMRect +) => { + const isMouse = isMouseEvent(event); + const evtX = isMouse ? event.clientX : event.touches?.[0].clientX; + const evtY = isMouse ? event.clientY : event.touches?.[0].clientY; + + return { + x: evtX - bounds.left, + y: evtY - bounds.top, + }; +}; From f3806c616548ee9071e43f4b17a29e97dcba7e85 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 24 Jan 2023 14:17:55 +0100 Subject: [PATCH 2/3] refactor(connection): cleanup --- examples/vite-app/package.json | 2 +- .../core/src/components/Handle/handler.ts | 54 ++++++++++--------- packages/core/src/components/Handle/index.tsx | 6 +-- packages/core/src/components/Handle/utils.ts | 20 +++++++ packages/core/src/store/index.ts | 1 + packages/core/src/utils/index.ts | 12 ++--- 6 files changed, 61 insertions(+), 34 deletions(-) diff --git a/examples/vite-app/package.json b/examples/vite-app/package.json index 55592a64..2fecdc49 100644 --- a/examples/vite-app/package.json +++ b/examples/vite-app/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --port 3000 --open", + "dev": "vite --port 3000 --open --host", "serve": "vite serve --port 3000", "build": "vite build", "test:dev": "cypress open", diff --git a/packages/core/src/components/Handle/handler.ts b/packages/core/src/components/Handle/handler.ts index 766f48ba..180b6027 100644 --- a/packages/core/src/components/Handle/handler.ts +++ b/packages/core/src/components/Handle/handler.ts @@ -1,15 +1,18 @@ import type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react'; import { StoreApi } from 'zustand'; -import { getHostForElement, calcAutoPan, getEventPosition, isMouseEvent } from '../../utils'; +import { getHostForElement, calcAutoPan, getEventPosition } from '../../utils'; import type { OnConnect, HandleType, ReactFlowState } from '../../types'; import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph'; -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'); -} +import { + ConnectionHandle, + getClosestHandle, + getHandleLookup, + getHandleType, + isValidHandle, + resetRecentHandle, + ValidConnectionFunc, +} from './utils'; export function handlePointerDown({ event, @@ -20,7 +23,7 @@ export function handlePointerDown({ getState, setState, isValidConnection, - elementEdgeUpdaterType, + edgeUpdaterType, onEdgeUpdateEnd, }: { event: ReactMouseEvent | ReactTouchEvent; @@ -31,27 +34,34 @@ export function handlePointerDown({ getState: StoreApi['getState']; setState: StoreApi['setState']; isValidConnection: ValidConnectionFunc; - elementEdgeUpdaterType?: HandleType; + edgeUpdaterType?: HandleType; 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); - const { connectionMode, domNode, autoPanOnConnect, connectionRadius, onConnectStart, onConnectEnd, panBy, getNodes } = - getState(); + const { + connectionMode, + domNode, + autoPanOnConnect, + connectionRadius, + onConnectStart, + onConnectEnd, + panBy, + getNodes, + cancelConnection, + } = getState(); let autoPanId = 0; let prevClosestHandle: ConnectionHandle | null; - 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'); + const { x, y } = getEventPosition(event); + const clickedHandle = doc?.elementFromPoint(x, y); + const handleType = getHandleType(edgeUpdaterType, clickedHandle); + const containerBounds = domNode?.getBoundingClientRect(); - if (!domNode || (!elementIsTarget && !elementIsSource && !elementEdgeUpdaterType)) { + if (!containerBounds || !handleType) { return; } - const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementIsTarget ? 'target' : 'source'; - const containerBounds = domNode.getBoundingClientRect(); let prevActiveHandle: Element; let connectionPosition = getEventPosition(event, containerBounds); let autoPanStarted = false; @@ -154,17 +164,13 @@ export function handlePointerDown({ onConnectEnd?.(event); - if (elementEdgeUpdaterType) { + if (edgeUpdaterType) { onEdgeUpdateEnd?.(event); } resetRecentHandle(prevActiveHandle); - setState({ - connectionNodeId: null, - connectionHandleId: null, - connectionHandleType: null, - }); + cancelConnection(); doc.removeEventListener('mousemove', onPointerMove as EventListener); doc.removeEventListener('mouseup', onPointerUp as EventListener); diff --git a/packages/core/src/components/Handle/index.tsx b/packages/core/src/components/Handle/index.tsx index 89212300..aa887d2f 100644 --- a/packages/core/src/components/Handle/index.tsx +++ b/packages/core/src/components/Handle/index.tsx @@ -68,9 +68,9 @@ const Handle = forwardRef( }; const onPointerDown = (event: ReactMouseEvent | ReactTouchEvent) => { - const isMouse = isMouseEvent(event); + const isMouseTriggered = isMouseEvent(event); - if ((isMouse && event.button === 0) || 'touches' in event) { + if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) { handlePointerDown({ event, handleId, @@ -83,7 +83,7 @@ const Handle = forwardRef( }); } - if (isMouse) { + if (isMouseTriggered) { onMouseDown?.(event); } else { onTouchStart?.(event); diff --git a/packages/core/src/components/Handle/utils.ts b/packages/core/src/components/Handle/utils.ts index d6cf9e8b..74c0ef5a 100644 --- a/packages/core/src/components/Handle/utils.ts +++ b/packages/core/src/components/Handle/utils.ts @@ -132,3 +132,23 @@ export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHand return res; }, []); } + +export function getHandleType( + edgeUpdaterType: HandleType | undefined, + handleDomNode: Element | null +): HandleType | null { + if (edgeUpdaterType) { + return edgeUpdaterType; + } else if (handleDomNode?.classList.contains('target')) { + return 'target'; + } else if (handleDomNode?.classList.contains('source')) { + return 'source'; + } + + return null; +} + +export function resetRecentHandle(handleDomNode: Element): void { + handleDomNode?.classList.remove('react-flow__handle-valid'); + handleDomNode?.classList.remove('react-flow__handle-connecting'); +} diff --git a/packages/core/src/store/index.ts b/packages/core/src/store/index.ts index 0a79a4b1..635cad96 100644 --- a/packages/core/src/store/index.ts +++ b/packages/core/src/store/index.ts @@ -273,6 +273,7 @@ const createRFStore = () => set({ connectionNodeId: initialState.connectionNodeId, connectionHandleId: initialState.connectionHandleId, + connectionHandleType: initialState.connectionHandleType, }), reset: () => set({ ...initialState }), })); diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 3051f25a..5bfd52c2 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -122,14 +122,14 @@ export const isMouseEvent = ( export const getEventPosition = ( event: MouseEvent | ReactMouseEvent | TouchEvent | ReactTouchEvent, - bounds: DOMRect + bounds?: DOMRect ) => { - const isMouse = isMouseEvent(event); - const evtX = isMouse ? event.clientX : event.touches?.[0].clientX; - const evtY = isMouse ? event.clientY : event.touches?.[0].clientY; + const isMouseTriggered = isMouseEvent(event); + const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX; + const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY; return { - x: evtX - bounds.left, - y: evtY - bounds.top, + x: evtX - (bounds?.left ?? 0), + y: evtY - (bounds?.top ?? 0), }; }; From 85003b01add71ea852bd5b0d2f1e7496050a6b52 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 24 Jan 2023 14:22:31 +0100 Subject: [PATCH 3/3] chore(changeset): add --- .changeset/hungry-eagles-turn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hungry-eagles-turn.md diff --git a/.changeset/hungry-eagles-turn.md b/.changeset/hungry-eagles-turn.md new file mode 100644 index 00000000..44941fba --- /dev/null +++ b/.changeset/hungry-eagles-turn.md @@ -0,0 +1,5 @@ +--- +'@reactflow/core': patch +--- + +Connecting nodes: Enable connections on touch devices