Merge pull request #2763 from wbkd/feat/touch-connect
Feat: touch connect
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@reactflow/core': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Connecting nodes: Enable connections on touch devices
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port 3000 --open",
|
"dev": "vite --port 3000 --open --host",
|
||||||
"serve": "vite serve --port 3000",
|
"serve": "vite serve --port 3000",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"test:dev": "cypress open",
|
"test:dev": "cypress open",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import cc from 'classcat';
|
|||||||
|
|
||||||
import { useStoreApi } from '../../hooks/useStore';
|
import { useStoreApi } from '../../hooks/useStore';
|
||||||
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
||||||
import { handleMouseDown } from '../Handle/handler';
|
import { handlePointerDown } from '../Handle/handler';
|
||||||
import { EdgeAnchor } from './EdgeAnchor';
|
import { EdgeAnchor } from './EdgeAnchor';
|
||||||
import { getMarkerId } from '../../utils/graph';
|
import { getMarkerId } from '../../utils/graph';
|
||||||
import { getMouseHandler } from './utils';
|
import { getMouseHandler } from './utils';
|
||||||
@@ -99,14 +99,14 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
setUpdating(true);
|
setUpdating(true);
|
||||||
onEdgeUpdateStart?.(event, edge, handleType);
|
onEdgeUpdateStart?.(event, edge, handleType);
|
||||||
|
|
||||||
const _onEdgeUpdateEnd = (evt: MouseEvent) => {
|
const _onEdgeUpdateEnd = (evt: MouseEvent | TouchEvent) => {
|
||||||
setUpdating(false);
|
setUpdating(false);
|
||||||
onEdgeUpdateEnd?.(evt, edge, handleType);
|
onEdgeUpdateEnd?.(evt, edge, handleType);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
||||||
|
|
||||||
handleMouseDown({
|
handlePointerDown({
|
||||||
event,
|
event,
|
||||||
handleId,
|
handleId,
|
||||||
nodeId,
|
nodeId,
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
import type { MouseEvent as ReactMouseEvent } from 'react';
|
import type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||||
import { StoreApi } from 'zustand';
|
import { StoreApi } from 'zustand';
|
||||||
|
|
||||||
import { getHostForElement, calcAutoPan } from '../../utils';
|
import { getHostForElement, calcAutoPan, getEventPosition } from '../../utils';
|
||||||
import type { OnConnect, HandleType, ReactFlowState } from '../../types';
|
import type { OnConnect, HandleType, ReactFlowState } from '../../types';
|
||||||
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
|
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
|
||||||
import {
|
import {
|
||||||
ConnectionHandle,
|
ConnectionHandle,
|
||||||
getClosestHandle,
|
getClosestHandle,
|
||||||
getConnectionPosition,
|
|
||||||
getHandleLookup,
|
getHandleLookup,
|
||||||
|
getHandleType,
|
||||||
isValidHandle,
|
isValidHandle,
|
||||||
|
resetRecentHandle,
|
||||||
ValidConnectionFunc,
|
ValidConnectionFunc,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
function resetRecentHandle(handleDomNode: Element): void {
|
export function handlePointerDown({
|
||||||
handleDomNode?.classList.remove('react-flow__handle-valid');
|
|
||||||
handleDomNode?.classList.remove('react-flow__handle-connecting');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleMouseDown({
|
|
||||||
event,
|
event,
|
||||||
handleId,
|
handleId,
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -27,10 +23,10 @@ export function handleMouseDown({
|
|||||||
getState,
|
getState,
|
||||||
setState,
|
setState,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
elementEdgeUpdaterType,
|
edgeUpdaterType,
|
||||||
onEdgeUpdateEnd,
|
onEdgeUpdateEnd,
|
||||||
}: {
|
}: {
|
||||||
event: ReactMouseEvent;
|
event: ReactMouseEvent | ReactTouchEvent;
|
||||||
handleId: string | null;
|
handleId: string | null;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
onConnect: OnConnect;
|
onConnect: OnConnect;
|
||||||
@@ -38,28 +34,36 @@ export function handleMouseDown({
|
|||||||
getState: StoreApi<ReactFlowState>['getState'];
|
getState: StoreApi<ReactFlowState>['getState'];
|
||||||
setState: StoreApi<ReactFlowState>['setState'];
|
setState: StoreApi<ReactFlowState>['setState'];
|
||||||
isValidConnection: ValidConnectionFunc;
|
isValidConnection: ValidConnectionFunc;
|
||||||
elementEdgeUpdaterType?: HandleType;
|
edgeUpdaterType?: HandleType;
|
||||||
onEdgeUpdateEnd?: (evt: MouseEvent) => void;
|
onEdgeUpdateEnd?: (evt: MouseEvent | TouchEvent) => void;
|
||||||
}): 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 { connectionMode, domNode, autoPanOnConnect, connectionRadius, onConnectStart, onConnectEnd, panBy, getNodes } =
|
const {
|
||||||
getState();
|
connectionMode,
|
||||||
|
domNode,
|
||||||
|
autoPanOnConnect,
|
||||||
|
connectionRadius,
|
||||||
|
onConnectStart,
|
||||||
|
onConnectEnd,
|
||||||
|
panBy,
|
||||||
|
getNodes,
|
||||||
|
cancelConnection,
|
||||||
|
} = getState();
|
||||||
let autoPanId = 0;
|
let autoPanId = 0;
|
||||||
let prevClosestHandle: ConnectionHandle | null;
|
let prevClosestHandle: ConnectionHandle | null;
|
||||||
|
|
||||||
const clickedElement = doc?.elementFromPoint(event.clientX, event.clientY);
|
const { x, y } = getEventPosition(event);
|
||||||
const elementIsTarget = clickedElement?.classList.contains('target');
|
const clickedHandle = doc?.elementFromPoint(x, y);
|
||||||
const elementIsSource = clickedElement?.classList.contains('source');
|
const handleType = getHandleType(edgeUpdaterType, clickedHandle);
|
||||||
|
const containerBounds = domNode?.getBoundingClientRect();
|
||||||
|
|
||||||
if (!domNode || (!elementIsTarget && !elementIsSource && !elementEdgeUpdaterType)) {
|
if (!containerBounds || !handleType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementIsTarget ? 'target' : 'source';
|
|
||||||
const containerBounds = domNode.getBoundingClientRect();
|
|
||||||
let prevActiveHandle: Element;
|
let prevActiveHandle: Element;
|
||||||
let connectionPosition = getConnectionPosition(event, containerBounds);
|
let connectionPosition = getEventPosition(event, containerBounds);
|
||||||
let autoPanStarted = false;
|
let autoPanStarted = false;
|
||||||
|
|
||||||
const handleLookup = getHandleLookup({
|
const handleLookup = getHandleLookup({
|
||||||
@@ -89,10 +93,10 @@ export function handleMouseDown({
|
|||||||
|
|
||||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||||
|
|
||||||
function onMouseMove(event: MouseEvent) {
|
function onPointerMove(event: MouseEvent | TouchEvent) {
|
||||||
const { transform } = getState();
|
const { transform } = getState();
|
||||||
|
connectionPosition = getEventPosition(event, containerBounds);
|
||||||
|
|
||||||
connectionPosition = getConnectionPosition(event, containerBounds);
|
|
||||||
prevClosestHandle = getClosestHandle(
|
prevClosestHandle = getClosestHandle(
|
||||||
pointToRendererPoint(connectionPosition, transform, false, [1, 1]),
|
pointToRendererPoint(connectionPosition, transform, false, [1, 1]),
|
||||||
connectionRadius,
|
connectionRadius,
|
||||||
@@ -138,7 +142,7 @@ export function handleMouseDown({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseUp(event: MouseEvent) {
|
function onPointerUp(event: MouseEvent | TouchEvent) {
|
||||||
cancelAnimationFrame(autoPanId);
|
cancelAnimationFrame(autoPanId);
|
||||||
autoPanStarted = false;
|
autoPanStarted = false;
|
||||||
|
|
||||||
@@ -160,22 +164,24 @@ export function handleMouseDown({
|
|||||||
|
|
||||||
onConnectEnd?.(event);
|
onConnectEnd?.(event);
|
||||||
|
|
||||||
if (elementEdgeUpdaterType) {
|
if (edgeUpdaterType) {
|
||||||
onEdgeUpdateEnd?.(event);
|
onEdgeUpdateEnd?.(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecentHandle(prevActiveHandle);
|
resetRecentHandle(prevActiveHandle);
|
||||||
|
|
||||||
setState({
|
cancelConnection();
|
||||||
connectionNodeId: null,
|
|
||||||
connectionHandleId: null,
|
|
||||||
connectionHandleType: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
doc.removeEventListener('mousemove', onPointerMove as EventListener);
|
||||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
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('mousemove', onPointerMove as EventListener);
|
||||||
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
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 cc from 'classcat';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||||
import { handleMouseDown } from './handler';
|
import { handlePointerDown } from './handler';
|
||||||
import { devWarn, getHostForElement } from '../../utils';
|
import { devWarn, getHostForElement, isMouseEvent } from '../../utils';
|
||||||
import { addEdge } from '../../utils/graph';
|
import { addEdge } from '../../utils/graph';
|
||||||
import { Position } from '../../types';
|
import { Position } from '../../types';
|
||||||
import type { HandleProps, Connection, ReactFlowState } from '../../types';
|
import type { HandleProps, Connection, ReactFlowState } from '../../types';
|
||||||
@@ -33,6 +33,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
onMouseDown,
|
onMouseDown,
|
||||||
|
onTouchStart,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
@@ -66,9 +67,11 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
onConnect?.(edgeParams);
|
onConnect?.(edgeParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseDownHandler = (event: ReactMouseEvent<HTMLDivElement>) => {
|
const onPointerDown = (event: ReactMouseEvent<HTMLDivElement> | ReactTouchEvent<HTMLDivElement>) => {
|
||||||
if (event.button === 0) {
|
const isMouseTriggered = isMouseEvent(event);
|
||||||
handleMouseDown({
|
|
||||||
|
if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) {
|
||||||
|
handlePointerDown({
|
||||||
event,
|
event,
|
||||||
handleId,
|
handleId,
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -79,7 +82,12 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
isValidConnection,
|
isValidConnection,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onMouseDown?.(event);
|
|
||||||
|
if (isMouseTriggered) {
|
||||||
|
onMouseDown?.(event);
|
||||||
|
} else {
|
||||||
|
onTouchStart?.(event);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClick = (event: ReactMouseEvent) => {
|
const onClick = (event: ReactMouseEvent) => {
|
||||||
@@ -136,7 +144,8 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
connectionStartHandle?.type === type,
|
connectionStartHandle?.type === type,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
onMouseDown={onMouseDownHandler}
|
onMouseDown={onPointerDown}
|
||||||
|
onTouchStart={onPointerDown}
|
||||||
onClick={connectOnClick ? onClick : undefined}
|
onClick={connectOnClick ? onClick : undefined}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
|
||||||
|
|
||||||
import { ConnectionMode } from '../../types';
|
import { ConnectionMode } from '../../types';
|
||||||
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
|
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
|
||||||
import { internalsSymbol } from '../../utils';
|
import { internalsSymbol } from '../../utils';
|
||||||
@@ -135,9 +133,22 @@ export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHand
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConnectionPosition(event: MouseEvent | ReactMouseEvent, bounds: DOMRect): XYPosition {
|
export function getHandleType(
|
||||||
return {
|
edgeUpdaterType: HandleType | undefined,
|
||||||
x: event.clientX - bounds.left,
|
handleDomNode: Element | null
|
||||||
y: event.clientY - bounds.top,
|
): 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');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import { containerStyle } from '../../styles';
|
|||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import { getSelectionChanges } from '../../utils/changes';
|
import { getSelectionChanges } from '../../utils/changes';
|
||||||
import { getConnectedEdges, getNodesInside } from '../../utils/graph';
|
import { getConnectedEdges, getNodesInside } from '../../utils/graph';
|
||||||
|
import { getEventPosition } from '../../utils';
|
||||||
import { SelectionMode } from '../../types';
|
import { SelectionMode } from '../../types';
|
||||||
import type { ReactFlowProps, XYPosition, ReactFlowState, NodeChange, EdgeChange } from '../../types';
|
import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange } from '../../types';
|
||||||
|
|
||||||
type PaneProps = {
|
type PaneProps = {
|
||||||
isSelecting: boolean;
|
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 = (
|
const wrapHandler = (
|
||||||
handler: React.MouseEventHandler | undefined,
|
handler: React.MouseEventHandler | undefined,
|
||||||
containerRef: React.MutableRefObject<HTMLDivElement | null>
|
containerRef: React.MutableRefObject<HTMLDivElement | null>
|
||||||
@@ -118,7 +112,7 @@ const Pane = memo(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { x, y } = getMousePosition(event, containerBounds.current);
|
const { x, y } = getEventPosition(event, containerBounds.current);
|
||||||
|
|
||||||
resetSelectedElements();
|
resetSelectedElements();
|
||||||
|
|
||||||
@@ -145,7 +139,7 @@ const Pane = memo(
|
|||||||
|
|
||||||
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
||||||
|
|
||||||
const mousePos = getMousePosition(event, containerBounds.current);
|
const mousePos = getEventPosition(event, containerBounds.current);
|
||||||
const startX = userSelectionRect.startX ?? 0;
|
const startX = userSelectionRect.startX ?? 0;
|
||||||
const startY = userSelectionRect.startY ?? 0;
|
const startY = userSelectionRect.startY ?? 0;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useStoreApi } from '../../hooks/useStore';
|
|||||||
import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils';
|
import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils';
|
||||||
import { handleNodeClick } from '../../components/Nodes/utils';
|
import { handleNodeClick } from '../../components/Nodes/utils';
|
||||||
import useGetPointerPosition from '../useGetPointerPosition';
|
import useGetPointerPosition from '../useGetPointerPosition';
|
||||||
import { calcAutoPan } from '../../utils';
|
import { calcAutoPan, getEventPosition } from '../../utils';
|
||||||
import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '../../types';
|
import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '../../types';
|
||||||
|
|
||||||
export type UseDragData = { dx: number; dy: number };
|
export type UseDragData = { dx: number; dy: number };
|
||||||
@@ -168,10 +168,7 @@ function useDrag({
|
|||||||
}
|
}
|
||||||
|
|
||||||
containerBounds.current = domNode?.getBoundingClientRect() || null;
|
containerBounds.current = domNode?.getBoundingClientRect() || null;
|
||||||
mousePosition.current = {
|
mousePosition.current = getEventPosition(event.sourceEvent, containerBounds.current!);
|
||||||
x: event.sourceEvent.clientX - (containerBounds.current?.left ?? 0),
|
|
||||||
y: event.sourceEvent.clientY - (containerBounds.current?.top ?? 0),
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
.on('drag', (event: UseDragEvent) => {
|
.on('drag', (event: UseDragEvent) => {
|
||||||
const pointerPos = getPointerPosition(event);
|
const pointerPos = getPointerPosition(event);
|
||||||
@@ -188,10 +185,7 @@ function useDrag({
|
|||||||
dragItems.current
|
dragItems.current
|
||||||
) {
|
) {
|
||||||
dragEvent.current = event.sourceEvent as MouseEvent;
|
dragEvent.current = event.sourceEvent as MouseEvent;
|
||||||
mousePosition.current = {
|
mousePosition.current = getEventPosition(event.sourceEvent, containerBounds.current!);
|
||||||
x: event.sourceEvent.clientX - (containerBounds.current?.left ?? 0),
|
|
||||||
y: event.sourceEvent.clientY - (containerBounds.current?.top ?? 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
updateNodes(pointerPos);
|
updateNodes(pointerPos);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ const createRFStore = () =>
|
|||||||
set({
|
set({
|
||||||
connectionNodeId: initialState.connectionNodeId,
|
connectionNodeId: initialState.connectionNodeId,
|
||||||
connectionHandleId: initialState.connectionHandleId,
|
connectionHandleId: initialState.connectionHandleId,
|
||||||
|
connectionHandleType: initialState.connectionHandleType,
|
||||||
}),
|
}),
|
||||||
reset: () => set({ ...initialState }),
|
reset: () => set({ ...initialState }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
|
|||||||
onEdgeMouseLeave?: EdgeMouseHandler;
|
onEdgeMouseLeave?: EdgeMouseHandler;
|
||||||
onEdgeDoubleClick?: EdgeMouseHandler;
|
onEdgeDoubleClick?: EdgeMouseHandler;
|
||||||
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void;
|
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;
|
onNodesChange?: OnNodesChange;
|
||||||
onEdgesChange?: OnEdgesChange;
|
onEdgesChange?: OnEdgesChange;
|
||||||
onNodesDelete?: OnNodesDelete;
|
onNodesDelete?: OnNodesDelete;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandl
|
|||||||
onMouseLeave?: EdgeMouseHandler;
|
onMouseLeave?: EdgeMouseHandler;
|
||||||
edgeUpdaterRadius?: number;
|
edgeUpdaterRadius?: number;
|
||||||
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void;
|
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;
|
rfId?: string;
|
||||||
isFocusable: boolean;
|
isFocusable: boolean;
|
||||||
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* 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 { D3DragEvent, Selection as D3Selection, SubjectPosition, ZoomBehavior } from 'd3';
|
||||||
|
|
||||||
import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
||||||
@@ -77,8 +82,8 @@ export type OnConnectStartParams = {
|
|||||||
handleType: HandleType | null;
|
handleType: HandleType | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OnConnectStart = (event: ReactMouseEvent, params: OnConnectStartParams) => void;
|
export type OnConnectStart = (event: ReactMouseEvent | ReactTouchEvent, params: OnConnectStartParams) => void;
|
||||||
export type OnConnectEnd = (event: MouseEvent) => void;
|
export type OnConnectEnd = (event: MouseEvent | TouchEvent) => void;
|
||||||
|
|
||||||
export type Viewport = {
|
export type Viewport = {
|
||||||
x: number;
|
x: number;
|
||||||
|
|||||||
@@ -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';
|
import type { Dimensions, Node, XYPosition, CoordinateExtent, Box, Rect } from '../types';
|
||||||
|
|
||||||
@@ -111,3 +115,21 @@ export function isInputDOMNode(event: KeyboardEvent | ReactKeyboardEvent): boole
|
|||||||
!!target?.closest('.nokey')
|
!!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 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 ?? 0),
|
||||||
|
y: evtY - (bounds?.top ?? 0),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user