import React, { useEffect, useRef, useState, memo, ComponentType } from 'react'; import { DraggableCore } from 'react-draggable'; import cx from 'classnames'; import { ResizeObserver } from 'resize-observer'; import { Provider } from '../../contexts/NodeIdContext'; import store from '../../store'; import { Node, XYPosition, Transform, ElementId, NodeComponentProps, WrapNodeProps } from '../../types'; const onStart = ( evt: MouseEvent, onClick: (node: Node) => void, id: ElementId, type: string, data: any, setOffset: (pos: XYPosition) => void, transform: Transform, position: XYPosition ): false | void => { const scaledClient: XYPosition = { x: evt.clientX * (1 / transform[2]), y: evt.clientY * (1 / transform[2]), }; const offsetX = scaledClient.x - position.x - transform[0]; const offsetY = scaledClient.y - position.y - transform[1]; const node = { id, type, position, data }; store.dispatch.setSelectedElements({ id, type } as Node); setOffset({ x: offsetX, y: offsetY }); onClick(node); }; const onDrag = ( evt: MouseEvent, setDragging: (isDragging: boolean) => void, id: ElementId, offset: XYPosition, transform: Transform ): void => { const scaledClient = { x: evt.clientX * (1 / transform[2]), y: evt.clientY * (1 / transform[2]), }; setDragging(true); store.dispatch.updateNodePos({ id, pos: { x: scaledClient.x - transform[0] - offset.x, y: scaledClient.y - transform[1] - offset.y, }, }); }; const onStop = ( onNodeDragStop: (params: Node) => void, isDragging: boolean, setDragging: (isDragging: boolean) => void, id: ElementId, type: string, position: XYPosition, data: any ): void => { if (isDragging) { setDragging(false); onNodeDragStop({ id, type, position, data, } as Node); } }; export default (NodeComponent: ComponentType) => { const NodeWrapper = memo( ({ id, type, data, transform, xPos, yPos, selected, onClick, onNodeDragStop, style, isInteractive, sourcePosition, targetPosition, }: WrapNodeProps) => { const nodeElement = useRef(null); const [offset, setOffset] = useState({ x: 0, y: 0 }); const [isDragging, setDragging] = useState(false); const position = { x: xPos, y: yPos }; const nodeClasses = cx('react-flow__node', { selected }); const nodeStyle = { zIndex: selected ? 10 : 3, transform: `translate(${xPos}px,${yPos}px)`, }; const updateNode = (): void => { if (!nodeElement.current) { return; } store.dispatch.updateNodeDimensions({ id, nodeElement: nodeElement.current }); }; useEffect(() => { if (nodeElement.current) { updateNode(); const resizeObserver = new ResizeObserver(entries => { for (let _ of entries) { updateNode(); } }); resizeObserver.observe(nodeElement.current); return () => { if (resizeObserver && nodeElement.current) { resizeObserver.unobserve(nodeElement.current); } }; } return; }, []); return ( onStart(evt as MouseEvent, onClick, id, type, data, setOffset, transform, position)} onDrag={evt => onDrag(evt as MouseEvent, setDragging, id, offset, transform)} onStop={() => onStop(onNodeDragStop, isDragging, setDragging, id, type, position, data)} scale={transform[2]} disabled={!isInteractive} cancel=".nodrag" >
); } ); NodeWrapper.displayName = 'NodeWrapper'; return NodeWrapper; };