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), }; };