diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index e3ba07bd..5de86c49 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -216,7 +216,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => { const width = useStoreState((state) => state.width); const height = useStoreState((state) => state.height); - const { connectionLineStyle, connectionLineType, arrowHeadColor, connectionLineComponent } = props; + const { connectionLineType, arrowHeadColor, connectionLineStyle, connectionLineComponent } = props; if (!width) { return null; diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 88272045..321f2853 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -63,20 +63,20 @@ export interface GraphViewProps { snapToGrid: boolean; snapGrid: [number, number]; onlyRenderVisibleNodes: boolean; - nodesDraggable: boolean; - nodesConnectable: boolean; - elementsSelectable: boolean; - selectNodesOnDrag: boolean; - minZoom: number; - maxZoom: number; + nodesDraggable?: boolean; + nodesConnectable?: boolean; + elementsSelectable?: boolean; + selectNodesOnDrag?: boolean; + minZoom?: number; + maxZoom?: number; defaultZoom: number; defaultPosition: [number, number]; translateExtent?: TranslateExtent; arrowHeadColor: string; markerEndId?: string; - zoomOnScroll: boolean; - zoomOnDoubleClick: boolean; - paneMoveable: boolean; + zoomOnScroll?: boolean; + zoomOnDoubleClick?: boolean; + paneMoveable?: boolean; } const GraphView = ({ @@ -114,7 +114,7 @@ const GraphView = ({ nodesDraggable, nodesConnectable, elementsSelectable, - selectNodesOnDrag, + selectNodesOnDrag = true, minZoom, maxZoom, defaultZoom, @@ -140,39 +140,19 @@ const GraphView = ({ const setOnConnectStop = useStoreActions((actions) => actions.setOnConnectStop); const setOnConnectEnd = useStoreActions((actions) => actions.setOnConnectEnd); const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid); + const setSnapToGrid = useStoreActions((actions) => actions.setSnapToGrid); const setNodesDraggable = useStoreActions((actions) => actions.setNodesDraggable); const setNodesConnectable = useStoreActions((actions) => actions.setNodesConnectable); const setElementsSelectable = useStoreActions((actions) => actions.setElementsSelectable); const setInitTransform = useStoreActions((actions) => actions.setInitTransform); - const setMinMaxZoom = useStoreActions((actions) => actions.setMinMaxZoom); + const setMinZoom = useStoreActions((actions) => actions.setMinZoom); + const setMaxZoom = useStoreActions((actions) => actions.setMaxZoom); const setTranslateExtent = useStoreActions((actions) => actions.setTranslateExtent); const fitView = useStoreActions((actions) => actions.fitView); const zoom = useStoreActions((actions) => actions.zoom); const zoomTo = useStoreActions((actions) => actions.zoomTo); const currentStore = useStore(); - const onZoomPaneClick = useCallback( - (event: React.MouseEvent) => { - onPaneClick?.(event); - unsetNodesSelection(); - }, - [onPaneClick] - ); - - const onZoomPaneContextMenu = useCallback( - (event: React.MouseEvent) => { - onPaneContextMenu?.(event); - }, - [onPaneContextMenu] - ); - - const onZoomPaneScroll = useCallback( - (event: WheelEvent) => { - onPaneScroll?.(event); - }, - [onPaneScroll] - ); - useResizeHandler(rendererNode); useGlobalKeyHandler({ onElementsRemove, deleteKeyCode }); useElementUpdater(elements); @@ -212,6 +192,28 @@ const GraphView = ({ } }, [d3Initialised, onLoad]); + const onZoomPaneClick = useCallback( + (event: React.MouseEvent) => { + onPaneClick?.(event); + unsetNodesSelection(); + }, + [onPaneClick] + ); + + const onZoomPaneContextMenu = useCallback( + (event: React.MouseEvent) => { + onPaneContextMenu?.(event); + }, + [onPaneContextMenu] + ); + + const onZoomPaneScroll = useCallback( + (event: WheelEvent) => { + onPaneScroll?.(event); + }, + [onPaneScroll] + ); + useEffect(() => { if (onConnect) { setOnConnect(onConnect); @@ -237,24 +239,46 @@ const GraphView = ({ }, [onConnectEnd]); useEffect(() => { - setSnapGrid({ snapToGrid, snapGrid }); - }, [snapToGrid, snapGrid]); + if (typeof snapToGrid !== 'undefined') { + setSnapToGrid(snapToGrid); + } + }, [snapToGrid]); useEffect(() => { - setNodesDraggable(nodesDraggable); + if (typeof snapGrid !== 'undefined') { + setSnapGrid(snapGrid); + } + }, [snapGrid]); + + useEffect(() => { + if (typeof nodesDraggable !== 'undefined') { + setNodesDraggable(nodesDraggable); + } }, [nodesDraggable]); useEffect(() => { - setNodesConnectable(nodesConnectable); + if (typeof nodesConnectable !== 'undefined') { + setNodesConnectable(nodesConnectable); + } }, [nodesConnectable]); useEffect(() => { - setElementsSelectable(elementsSelectable); + if (typeof elementsSelectable !== 'undefined') { + setElementsSelectable(elementsSelectable); + } }, [elementsSelectable]); useEffect(() => { - setMinMaxZoom({ minZoom, maxZoom }); - }, [minZoom, maxZoom]); + if (typeof minZoom !== 'undefined') { + setMinZoom(minZoom); + } + }, [minZoom]); + + useEffect(() => { + if (typeof maxZoom !== 'undefined') { + setMaxZoom(maxZoom); + } + }, [maxZoom]); useEffect(() => { if (typeof translateExtent !== 'undefined') { diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index bf99ac32..66125c1d 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -14,9 +14,9 @@ interface NodeRendererProps { onNodeContextMenu?: (event: MouseEvent, node: Node) => void; onNodeDragStart?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; - onlyRenderVisibleNodes?: boolean; - snapToGrid?: boolean; - snapGrid?: [number, number]; + onlyRenderVisibleNodes: boolean; + snapToGrid: boolean; + snapGrid: [number, number]; } function renderNode( @@ -75,7 +75,7 @@ function renderNode( ); } -const NodeRenderer = ({ onlyRenderVisibleNodes = true, ...props }: NodeRendererProps) => { +const NodeRenderer = (props: NodeRendererProps) => { const nodes = useStoreState((s) => s.nodes); const transform = useStoreState((s) => s.transform); const selectedElements = useStoreState((s) => s.selectedElements); @@ -88,7 +88,7 @@ const NodeRenderer = ({ onlyRenderVisibleNodes = true, ...props }: NodeRendererP transform: `translate(${transform[0]}px,${transform[1]}px) scale(${transform[2]})`, }; - const renderNodes = onlyRenderVisibleNodes ? getNodesInside(nodes, viewportBox, transform, true) : nodes; + const renderNodes = props.onlyRenderVisibleNodes ? getNodesInside(nodes, viewportBox, transform, true) : nodes; return (
diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index e92fa364..0593fa8f 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -36,6 +36,19 @@ import { import '../../style.css'; +const defaultNodeTypes = { + input: InputNode, + default: DefaultNode, + output: OutputNode, +}; + +const defaultEdgeTypes = { + default: BezierEdge, + straight: StraightEdge, + step: StepEdge, + smoothstep: SmoothStepEdge, +}; + export interface ReactFlowProps extends Omit, 'onLoad'> { elements: Elements; onElementClick?: (event: MouseEvent, element: Node | Edge) => void; @@ -62,40 +75,38 @@ export interface ReactFlowProps extends Omit, 'on onPaneScroll?: (event?: WheelEvent) => void; onPaneClick?: (event: MouseEvent) => void; onPaneContextMenu?: (event: MouseEvent) => void; - nodeTypes: NodeTypesType; - edgeTypes: EdgeTypesType; - connectionLineType: ConnectionLineType; + nodeTypes?: NodeTypesType; + edgeTypes?: EdgeTypesType; + connectionLineType?: ConnectionLineType; connectionLineStyle?: CSSProperties; connectionLineComponent?: ConnectionLineComponent; - deleteKeyCode: number; - selectionKeyCode: number; - snapToGrid: boolean; - snapGrid: [number, number]; - onlyRenderVisibleNodes: boolean; - nodesDraggable: boolean; - nodesConnectable: boolean; - elementsSelectable: boolean; - selectNodesOnDrag: boolean; - paneMoveable: boolean; - minZoom: number; - maxZoom: number; - defaultZoom: number; - defaultPosition: [number, number]; + deleteKeyCode?: number; + selectionKeyCode?: number; + snapToGrid?: boolean; + snapGrid?: [number, number]; + onlyRenderVisibleNodes?: boolean; + nodesDraggable?: boolean; + nodesConnectable?: boolean; + elementsSelectable?: boolean; + selectNodesOnDrag?: boolean; + paneMoveable?: boolean; + minZoom?: number; + maxZoom?: number; + defaultZoom?: number; + defaultPosition?: [number, number]; translateExtent?: TranslateExtent; - arrowHeadColor: string; + arrowHeadColor?: string; markerEndId?: string; - zoomOnScroll: boolean; - zoomOnDoubleClick: boolean; + zoomOnScroll?: boolean; + zoomOnDoubleClick?: boolean; } const ReactFlow = ({ - style, - onElementClick, elements = [], className, - children, - nodeTypes, - edgeTypes, + nodeTypes = defaultNodeTypes, + edgeTypes = defaultEdgeTypes, + onElementClick, onLoad, onMove, onMoveStart, @@ -116,38 +127,40 @@ const ReactFlow = ({ onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, - connectionLineType, + connectionLineType = ConnectionLineType.Bezier, connectionLineStyle, connectionLineComponent, - deleteKeyCode, - selectionKeyCode, - snapToGrid, - snapGrid, - onlyRenderVisibleNodes, + deleteKeyCode = 8, + selectionKeyCode = 16, + snapToGrid = false, + snapGrid = [15, 15], + onlyRenderVisibleNodes = true, + selectNodesOnDrag = true, nodesDraggable, nodesConnectable, elementsSelectable, - selectNodesOnDrag, minZoom, maxZoom, - defaultZoom, - defaultPosition, + defaultZoom = 1, + defaultPosition = [0, 0], translateExtent, - arrowHeadColor, + arrowHeadColor = '#b1b1b7', markerEndId, - zoomOnScroll, - zoomOnDoubleClick, - paneMoveable, + zoomOnScroll = true, + zoomOnDoubleClick = true, + paneMoveable = true, onPaneClick, onPaneScroll, onPaneContextMenu, + children, + ...rest }: ReactFlowProps) => { - const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), [nodeTypes]); - const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), [edgeTypes]); + const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), []); + const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), []); const reactFlowClasses = cc(['react-flow', className]); return ( -
+
; selectionKeyPressed: boolean; - zoomOnScroll: boolean; - zoomOnDoubleClick: boolean; - paneMoveable: boolean; - defaultPosition: [number, number]; - defaultZoom: number; + zoomOnScroll?: boolean; + zoomOnDoubleClick?: boolean; + paneMoveable?: boolean; + defaultPosition?: [number, number]; + defaultZoom?: number; translateExtent?: TranslateExtent; onMove?: (flowTransform?: FlowTransform) => void; onMoveStart?: (flowTransform?: FlowTransform) => void; @@ -33,12 +33,12 @@ export default ({ onMove, onMoveStart, onMoveEnd, - zoomOnScroll, - zoomOnDoubleClick, + zoomOnScroll = true, + zoomOnDoubleClick = true, selectionKeyPressed, - paneMoveable, - defaultPosition, - defaultZoom, + paneMoveable = true, + defaultPosition = [0, 0], + defaultZoom = 1, translateExtent, }: UseD3ZoomParams): void => { const prevTransform = useRef({ x: 0, y: 0, zoom: 0 }); diff --git a/src/store/index.ts b/src/store/index.ts index 1e3e743a..2d9fadb7 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -28,6 +28,7 @@ import { NodeDiffUpdate, FitViewParams, TranslateExtent, + SnapGrid, } from '../types'; type TransformXYK = { @@ -41,16 +42,6 @@ type NodeDimensionUpdate = { nodeElement: HTMLDivElement; }; -type SetMinMaxZoom = { - minZoom: number; - maxZoom: number; -}; - -type SetSnapGrid = { - snapToGrid: boolean; - snapGrid: [number, number]; -}; - type InitD3 = { zoomPane: Element; defaultPosition: [number, number]; @@ -85,7 +76,7 @@ export interface StoreModel { connectionPosition: XYPosition; snapToGrid: boolean; - snapGrid: [number, number]; + snapGrid: SnapGrid; nodesDraggable: boolean; nodesConnectable: boolean; @@ -124,11 +115,13 @@ export interface StoreModel { initD3: Action; - setMinMaxZoom: Action; + setMinZoom: Action; + setMaxZoom: Action; setTranslateExtent: Action; - setSnapGrid: Action; + setSnapToGrid: Action; + setSnapGrid: Action; setConnectionPosition: Action; @@ -187,7 +180,7 @@ export const storeModel: StoreModel = { connectionHandleType: 'source', connectionPosition: { x: 0, y: 0 }, - snapGrid: [16, 16], + snapGrid: [15, 15], snapToGrid: false, nodesDraggable: true, @@ -395,12 +388,19 @@ export const storeModel: StoreModel = { state.d3Initialised = true; }), - setMinMaxZoom: action((state, { minZoom, maxZoom }) => { + setMinZoom: action((state, minZoom) => { state.minZoom = minZoom; - state.maxZoom = maxZoom; if (state.d3Zoom) { - state.d3Zoom.scaleExtent([minZoom, maxZoom]); + state.d3Zoom.scaleExtent([minZoom, state.maxZoom]); + } + }), + + setMaxZoom: action((state, maxZoom) => { + state.minZoom = maxZoom; + + if (state.d3Zoom) { + state.d3Zoom.scaleExtent([state.minZoom, maxZoom]); } }), @@ -421,9 +421,13 @@ export const storeModel: StoreModel = { state.connectionHandleType = connectionHandleType; }), - setSnapGrid: action((state, { snapToGrid, snapGrid }) => { + setSnapToGrid: action((state, snapToGrid) => { state.snapToGrid = snapToGrid; - state.snapGrid = snapGrid; + }), + + setSnapGrid: action((state, snapGrid) => { + state.snapGrid[0] = snapGrid[0]; + state.snapGrid[1] = snapGrid[1]; }), setInteractive: action((state, isInteractive) => { diff --git a/src/types/index.ts b/src/types/index.ts index 4fd58470..83307d6d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -32,6 +32,8 @@ export interface Box extends XYPosition { y2: number; } +export type SnapGrid = [number, number]; + export interface Node { id: ElementId; position: XYPosition; @@ -214,7 +216,7 @@ export interface WrapNodeProps { isHidden?: boolean; isInitialized?: boolean; snapToGrid?: boolean; - snapGrid?: [number, number]; + snapGrid?: SnapGrid; isDragging?: boolean; }