import { memo, useState, useMemo, useRef } from 'react'; import type { ComponentType, KeyboardEvent } from 'react'; import cc from 'classcat'; import { useStoreApi } from '../../hooks/useStore'; import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; import { handleMouseDown } from '../Handle/handler'; import { EdgeAnchor } from './EdgeAnchor'; import { getMarkerId } from '../../utils/graph'; import { getMouseHandler } from './utils'; import { elementSelectionKeys } from '../../utils'; import type { EdgeProps, WrapEdgeProps, Connection } from '../../types'; export default (EdgeComponent: ComponentType) => { const EdgeWrapper = ({ id, className, type, data, onClick, onEdgeDoubleClick, selected, animated, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, source, target, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, elementsSelectable, hidden, sourceHandleId, targetHandleId, onContextMenu, onMouseEnter, onMouseMove, onMouseLeave, edgeUpdaterRadius, onEdgeUpdate, onEdgeUpdateStart, onEdgeUpdateEnd, markerEnd, markerStart, rfId, ariaLabel, isFocusable, pathOptions, interactionWidth, }: WrapEdgeProps): JSX.Element | null => { const edgeRef = useRef(null); const [updateHover, setUpdateHover] = useState(false); const [updating, setUpdating] = useState(false); const store = useStoreApi(); const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart, rfId)})`, [markerStart, rfId]); const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd, rfId)})`, [markerEnd, rfId]); if (hidden) { return null; } const onEdgeClick = (event: React.MouseEvent): void => { const { edges, addSelectedEdges } = store.getState(); if (elementsSelectable) { store.setState({ nodesSelectionActive: false }); addSelectedEdges([id]); } if (onClick) { const edge = edges.find((e) => e.id === id)!; onClick(event, edge); } }; const onEdgeDoubleClickHandler = getMouseHandler(id, store.getState, onEdgeDoubleClick); const onEdgeContextMenu = getMouseHandler(id, store.getState, onContextMenu); const onEdgeMouseEnter = getMouseHandler(id, store.getState, onMouseEnter); const onEdgeMouseMove = getMouseHandler(id, store.getState, onMouseMove); const onEdgeMouseLeave = getMouseHandler(id, store.getState, onMouseLeave); const handleEdgeUpdater = (event: React.MouseEvent, isSourceHandle: boolean) => { const nodeId = isSourceHandle ? target : source; const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null; const handleType = isSourceHandle ? 'target' : 'source'; const isValidConnection = () => true; const isTarget = isSourceHandle; const edge = store.getState().edges.find((e) => e.id === id)!; setUpdating(true); onEdgeUpdateStart?.(event, edge, handleType); const _onEdgeUpdateEnd = (evt: MouseEvent) => { setUpdating(false); onEdgeUpdateEnd?.(evt, edge, handleType); }; const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection); handleMouseDown({ event, handleId, nodeId, onConnect: onConnectEdge, isTarget, getState: store.getState, setState: store.setState, isValidConnection, elementEdgeUpdaterType: handleType, onEdgeUpdateEnd: _onEdgeUpdateEnd, }); }; const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent): void => handleEdgeUpdater(event, true); const onEdgeUpdaterTargetMouseDown = (event: React.MouseEvent): void => handleEdgeUpdater(event, false); const onEdgeUpdaterMouseEnter = () => setUpdateHover(true); const onEdgeUpdaterMouseOut = () => setUpdateHover(false); const inactive = !elementsSelectable && !onClick; const handleEdgeUpdate = typeof onEdgeUpdate !== 'undefined'; const onKeyDown = (event: KeyboardEvent) => { if (elementSelectionKeys.includes(event.key) && elementsSelectable) { const { unselectNodesAndEdges, addSelectedEdges, edges } = store.getState(); const unselect = event.key === 'Escape'; if (unselect) { edgeRef.current?.blur(); unselectNodesAndEdges({ edges: [edges.find((e) => e.id === id)!] }); } else { addSelectedEdges([id]); } } }; return ( {!updating && ( )} {handleEdgeUpdate && ( <> )} ); }; EdgeWrapper.displayName = 'EdgeWrapper'; return memo(EdgeWrapper); };