import { memo, useContext, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent } from 'react'; import cc from 'classcat'; import shallow from 'zustand/shallow'; import { useStore, useStoreApi } from '../../hooks/useStore'; import NodeIdContext from '../../contexts/NodeIdContext'; import { checkElementBelowIsValid, handleMouseDown } from './handler'; import { getHostForElement } from '../../utils'; import { addEdge } from '../../utils/graph'; import { Position } from '../../types'; import type { HandleProps, Connection, ReactFlowState } from '../../types'; const alwaysValid = () => true; export type HandleComponentProps = HandleProps & Omit, 'id'>; const selector = (s: ReactFlowState) => ({ connectionStartHandle: s.connectionStartHandle, connectOnClick: s.connectOnClick, noPanClassName: s.noPanClassName, }); const Handle = forwardRef( ( { type = 'source', position = Position.Top, isValidConnection = alwaysValid, isConnectable = true, id, onConnect, children, className, onMouseDown, ...rest }, ref ) => { const store = useStoreApi(); const nodeId = useContext(NodeIdContext) as string; const { connectionStartHandle, connectOnClick, noPanClassName } = useStore(selector, shallow); const handleId = id || null; const isTarget = type === 'target'; const onConnectExtended = (params: Connection) => { const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState(); const edgeParams = { ...defaultEdgeOptions, ...params, }; if (hasDefaultEdges) { const { edges } = store.getState(); store.setState({ edges: addEdge(edgeParams, edges) }); } onConnectAction?.(edgeParams); onConnect?.(edgeParams); }; const onMouseDownHandler = (event: ReactMouseEvent) => { if (event.button === 0) { handleMouseDown({ event, handleId, nodeId, onConnect: onConnectExtended, isTarget, getState: store.getState, setState: store.setState, isValidConnection, }); } onMouseDown?.(event); }; const onClick = (event: ReactMouseEvent) => { const { onClickConnectStart, onClickConnectEnd, connectionMode } = store.getState(); if (!connectionStartHandle) { onClickConnectStart?.(event, { nodeId, handleId, handleType: type }); store.setState({ connectionStartHandle: { nodeId, type, handleId } }); return; } const doc = getHostForElement(event.target as HTMLElement); const { connection, isValid } = checkElementBelowIsValid( event as unknown as MouseEvent, connectionMode, connectionStartHandle.type === 'target', connectionStartHandle.nodeId, connectionStartHandle.handleId || null, isValidConnection, doc ); if (isValid) { onConnectExtended(connection); } onClickConnectEnd?.(event as unknown as MouseEvent); store.setState({ connectionStartHandle: null }); }; return (
{children}
); } ); Handle.displayName = 'Handle'; export default memo(Handle);