diff --git a/package-lock.json b/package-lock.json index 76a7da8a..0aee762f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-flow-renderer", - "version": "10.0.0-next.25", + "version": "10.0.0-next.26", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "react-flow-renderer", - "version": "10.0.0-next.25", + "version": "10.0.0-next.26", "license": "MIT", "dependencies": { "@babel/runtime": "^7.16.3", @@ -16,7 +16,7 @@ "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "react-draggable": "^4.4.4", - "zustand": "^3.6.6" + "zustand": "^3.6.7" }, "devDependencies": { "@babel/core": "^7.16.0", @@ -30,7 +30,6 @@ "@svgr/rollup": "^6.1.1", "@types/redux": "^3.6.31", "@types/resize-observer-browser": "^0.1.6", - "@welldone-software/why-did-you-render": "^6.2.1", "autoprefixer": "^10.3.6", "babel-preset-react-app": "^10.0.0", "cypress": "^9.1.0", @@ -42,7 +41,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "release-it": "^14.11.8", - "rollup": "^2.60.2", + "rollup": "^2.61.0", "rollup-plugin-bundle-size": "^1.0.3", "rollup-plugin-livereload": "^2.0.5", "rollup-plugin-postcss": "^4.0.2", @@ -2906,18 +2905,6 @@ "@types/node": "*" } }, - "node_modules/@welldone-software/why-did-you-render": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz", - "integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==", - "dev": true, - "dependencies": { - "lodash": "^4" - }, - "peerDependencies": { - "react": "^16 || ^17" - } - }, "node_modules/@yarn-tool/resolve-package": { "version": "1.0.39", "resolved": "https://registry.npmjs.org/@yarn-tool/resolve-package/-/resolve-package-1.0.39.tgz", @@ -9321,9 +9308,9 @@ } }, "node_modules/rollup": { - "version": "2.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz", - "integrity": "sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw==", + "version": "2.61.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.61.0.tgz", + "integrity": "sha512-teQ+T1mUYbyvGyUavCodiyA9hD4DxwYZJwr/qehZGhs1Z49vsmzelMVYMxGU4ZhGRKxYPupHuz5yzm/wj7VpWA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -10885,9 +10872,9 @@ } }, "node_modules/zustand": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.6.6.tgz", - "integrity": "sha512-y4755cIzJHQFEHgTQ5cHrlHdmXMxm5N3DU05Q27yT6rK4lKs2336t5IsAz5q9/GRaoEz6o8SiCOPDhZd5BnneA==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.6.7.tgz", + "integrity": "sha512-bYWKXMfoJwZyliFgidcQ3tJC/F2f2hHq1+lhTk8dVPX2k28i4VnpnKjBGNxAd2QNliV0m455Vm+gDL8LShrV3g==", "engines": { "node": ">=12.7.0" }, @@ -12988,15 +12975,6 @@ "@types/node": "*" } }, - "@welldone-software/why-did-you-render": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz", - "integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==", - "dev": true, - "requires": { - "lodash": "^4" - } - }, "@yarn-tool/resolve-package": { "version": "1.0.39", "resolved": "https://registry.npmjs.org/@yarn-tool/resolve-package/-/resolve-package-1.0.39.tgz", @@ -17763,9 +17741,9 @@ } }, "rollup": { - "version": "2.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz", - "integrity": "sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw==", + "version": "2.61.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.61.0.tgz", + "integrity": "sha512-teQ+T1mUYbyvGyUavCodiyA9hD4DxwYZJwr/qehZGhs1Z49vsmzelMVYMxGU4ZhGRKxYPupHuz5yzm/wj7VpWA==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -18957,9 +18935,9 @@ "dev": true }, "zustand": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.6.6.tgz", - "integrity": "sha512-y4755cIzJHQFEHgTQ5cHrlHdmXMxm5N3DU05Q27yT6rK4lKs2336t5IsAz5q9/GRaoEz6o8SiCOPDhZd5BnneA==" + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.6.7.tgz", + "integrity": "sha512-bYWKXMfoJwZyliFgidcQ3tJC/F2f2hHq1+lhTk8dVPX2k28i4VnpnKjBGNxAd2QNliV0m455Vm+gDL8LShrV3g==" } } } diff --git a/package.json b/package.json index 62361438..96ca697e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-flow-renderer", - "version": "10.0.0-next.25", + "version": "10.0.0-next.26", "engines": { "node": ">=12" }, @@ -38,7 +38,7 @@ "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "react-draggable": "^4.4.4", - "zustand": "^3.6.6" + "zustand": "^3.6.7" }, "devDependencies": { "@babel/core": "^7.16.0", @@ -52,7 +52,6 @@ "@svgr/rollup": "^6.1.1", "@types/redux": "^3.6.31", "@types/resize-observer-browser": "^0.1.6", - "@welldone-software/why-did-you-render": "^6.2.1", "autoprefixer": "^10.3.6", "babel-preset-react-app": "^10.0.0", "cypress": "^9.1.0", @@ -64,7 +63,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "release-it": "^14.11.8", - "rollup": "^2.60.2", + "rollup": "^2.61.0", "rollup-plugin-bundle-size": "^1.0.3", "rollup-plugin-livereload": "^2.0.5", "rollup-plugin-postcss": "^4.0.2", diff --git a/src/components/ConnectionLine/index.tsx b/src/components/ConnectionLine/index.tsx index ce164dea..43a98d5a 100644 --- a/src/components/ConnectionLine/index.tsx +++ b/src/components/ConnectionLine/index.tsx @@ -31,8 +31,7 @@ const selector = (s: ReactFlowState) => ({ nodeInternals: s.nodeInternals, trans const getSourceHandle = (handleId: string | null, sourceNode: NodeInternalsItem, connectionHandleType: HandleType) => { const handleTypeInverted = connectionHandleType === 'source' ? 'target' : 'source'; - const handleBound = - sourceNode.handleBounds?.[connectionHandleType] || sourceNode.handleBounds?.[handleTypeInverted]; + const handleBound = sourceNode.handleBounds?.[connectionHandleType] || sourceNode.handleBounds?.[handleTypeInverted]; return handleId ? handleBound?.find((d: HandleElement) => d.id === handleId) : handleBound?.[0]; }; @@ -66,8 +65,8 @@ export default ({ const sourceHandle = getSourceHandle(handleId, sourceNode.current, connectionHandleType); const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : (sourceNode.current?.width ?? 0) / 2; const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : sourceNode.current?.height ?? 0; - const sourceX = sourceNode.current.positionAbsolute!.x + sourceHandleX; - const sourceY = sourceNode.current.positionAbsolute!.y + sourceHandleY; + const sourceX = sourceNode.current.positionAbsolute.x + sourceHandleX; + const sourceY = sourceNode.current.positionAbsolute.y + sourceHandleY; const targetX = (connectionPositionX - transform[0]) / transform[2]; const targetY = (connectionPositionY - transform[1]) / transform[2]; @@ -96,34 +95,24 @@ export default ({ let dAttr: string = ''; + const pathParams = { + sourceX, + sourceY, + sourcePosition: sourceHandle?.position, + targetX, + targetY, + targetPosition, + }; + if (connectionLineType === ConnectionLineType.Bezier) { - dAttr = getBezierPath({ - sourceX, - sourceY, - sourcePosition: sourceHandle?.position, - targetX, - targetY, - targetPosition, - }); + dAttr = getBezierPath(pathParams); } else if (connectionLineType === ConnectionLineType.Step) { dAttr = getSmoothStepPath({ - sourceX, - sourceY, - sourcePosition: sourceHandle?.position, - targetX, - targetY, - targetPosition, + ...pathParams, borderRadius: 0, }); } else if (connectionLineType === ConnectionLineType.SmoothStep) { - dAttr = getSmoothStepPath({ - sourceX, - sourceY, - sourcePosition: sourceHandle?.position, - targetX, - targetY, - targetPosition, - }); + dAttr = getSmoothStepPath(pathParams); } else { dAttr = `M${sourceX},${sourceY} ${targetX},${targetY}`; } diff --git a/src/components/Handle/handler.ts b/src/components/Handle/handler.ts index 2ef44055..b1097453 100644 --- a/src/components/Handle/handler.ts +++ b/src/components/Handle/handler.ts @@ -36,8 +36,6 @@ function checkElementBelowIsValid( isValidConnection: ValidConnectionFunc, doc: Document | ShadowRoot ) { - // TODO: why does this throw an error? elementFromPoint should be available for ShadowRoot too - // @ts-ignore const elementBelow = doc.elementFromPoint(event.clientX, event.clientY); const elementBelowIsTarget = elementBelow?.classList.contains('target') || false; const elementBelowIsSource = elementBelow?.classList.contains('source') || false; @@ -112,7 +110,6 @@ export function onMouseDown( return; } - // @ts-ignore const elementBelow = doc.elementFromPoint(event.clientX, event.clientY); const elementBelowIsTarget = elementBelow?.classList.contains('target'); const elementBelowIsSource = elementBelow?.classList.contains('source'); diff --git a/src/components/Handle/index.tsx b/src/components/Handle/index.tsx index e3b0111c..bdf32606 100644 --- a/src/components/Handle/index.tsx +++ b/src/components/Handle/index.tsx @@ -5,7 +5,6 @@ import shallow from 'zustand/shallow'; import { useStore } from '../../store'; import NodeIdContext from '../../contexts/NodeIdContext'; import { HandleProps, Connection, ReactFlowState, Position } from '../../types'; - import { onMouseDown, SetSourceIdFunc, SetPosition } from './handler'; const alwaysValid = () => true; diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index 115320ee..3373776a 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -1,11 +1,12 @@ /** * The nodes selection rectangle gets displayed when a user - * made a selectio with on or several nodes + * made a selection with on or several nodes */ import React, { memo, useMemo, useCallback, useRef, MouseEvent } from 'react'; import { DraggableCore, DraggableData } from 'react-draggable'; import cc from 'classcat'; +import shallow from 'zustand/shallow'; import { useStore } from '../../store'; import { Node, ReactFlowState } from '../../types'; @@ -38,8 +39,10 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, }: NodesSelectionProps) { - const { transform, userSelectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = - useStore(selector); + const { transform, userSelectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = useStore( + selector, + shallow + ); const [tX, tY, tScale] = transform; const nodeRef = useRef(null); diff --git a/src/components/SelectionListener/index.tsx b/src/components/SelectionListener/index.tsx index c8b59795..dcd05e7d 100644 --- a/src/components/SelectionListener/index.tsx +++ b/src/components/SelectionListener/index.tsx @@ -9,7 +9,7 @@ interface SelectionListenerProps { } // @TODO: work with nodeInternals instead of converting it to an array -const selectedElementsSelector = (s: ReactFlowState) => ({ +const selector = (s: ReactFlowState) => ({ selectedNodes: Array.from(s.nodeInternals) .filter(([_, n]) => n.selected) .map(([_, node]) => node), @@ -17,9 +17,9 @@ const selectedElementsSelector = (s: ReactFlowState) => ({ }); // This is just a helper component for calling the onSelectionChange listener. - +// @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component? export default ({ onSelectionChange }: SelectionListenerProps) => { - const { selectedNodes, selectedEdges } = useStore(selectedElementsSelector, shallow); + const { selectedNodes, selectedEdges } = useStore(selector, shallow); useEffect(() => { onSelectionChange({ nodes: selectedNodes, edges: selectedEdges }); diff --git a/src/container/EdgeRenderer/utils.ts b/src/container/EdgeRenderer/utils.ts index 01292e6b..fd3e86f6 100644 --- a/src/container/EdgeRenderer/utils.ts +++ b/src/container/EdgeRenderer/utils.ts @@ -172,13 +172,13 @@ export function getNodeData(nodeInternals: NodeInternals, nodeId: string): [Rect !node.handleBounds || !node.width || !node.height || - typeof node.positionAbsolute?.x === 'undefined' || - typeof node.positionAbsolute?.y === 'undefined'; + typeof node.positionAbsolute.x === 'undefined' || + typeof node.positionAbsolute.y === 'undefined'; return [ { - x: node?.positionAbsolute?.x || 0, - y: node?.positionAbsolute?.y || 0, + x: node?.positionAbsolute.x || 0, + y: node?.positionAbsolute.y || 0, width: node?.width || 0, height: node?.height || 0, }, diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index 02f10bd7..4231d71a 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -63,7 +63,6 @@ const FlowRenderer = ({ noPanClassName, }: FlowRendererProps) => { const { setNodesSelectionActive, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow); - const selectionKeyPressed = useKeyPress(selectionKeyCode); useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode }); @@ -77,19 +76,8 @@ const FlowRenderer = ({ [onPaneClick] ); - const onContextMenu = useCallback( - (event: MouseEvent) => { - onPaneContextMenu?.(event); - }, - [onPaneContextMenu] - ); - - const onWheel = useCallback( - (event: WheelEvent) => { - onPaneScroll?.(event); - }, - [onPaneScroll] - ); + const onContextMenu = useCallback((event: MouseEvent) => onPaneContextMenu?.(event), [onPaneContextMenu]); + const onWheel = useCallback((event: WheelEvent) => onPaneScroll?.(event), [onPaneScroll]); return ( ({ }); const NodeRenderer = (props: NodeRendererProps) => { - const { - scale, - nodesDraggable, - nodesConnectable, - elementsSelectable, - updateNodeDimensions, - snapGrid, - snapToGrid, - nodeInternals, - } = useStore(selector, shallow); + const { scale, nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions, snapGrid, snapToGrid } = + useStore(selector, shallow); const nodes = useVisibleNodes(props.onlyRenderVisibleElements); const reseizeObserverRef = useRef(); @@ -77,7 +69,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
{nodes.map((node) => { const nodeType = node.type || 'default'; - const internals = nodeInternals.get(node.id); if (!props.nodeTypes[nodeType]) { console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`); @@ -88,10 +79,7 @@ const NodeRenderer = (props: NodeRendererProps) => { const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined')); const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined')); const isInitialized = - node.width !== null && - node.height !== null && - typeof node.width !== 'undefined' && - typeof node.height !== 'undefined'; + node.width && node.height && typeof node.width !== 'undefined' && typeof node.height !== 'undefined'; return ( { sourcePosition={node.sourcePosition} targetPosition={node.targetPosition} hidden={node.hidden} - xPos={internals?.positionAbsolute?.x || 0} - yPos={internals?.positionAbsolute?.y || 0} + xPos={node.positionAbsolute.x} + yPos={node.positionAbsolute.y} dragging={!!node.dragging} - isInitialized={isInitialized} + isInitialized={!!isInitialized} snapGrid={snapGrid} snapToGrid={snapToGrid} selectNodesOnDrag={props.selectNodesOnDrag} @@ -127,8 +115,8 @@ const NodeRenderer = (props: NodeRendererProps) => { isConnectable={isConnectable} resizeObserver={resizeObserver} dragHandle={node.dragHandle} - zIndex={internals?.z || 0} - isParent={!!internals?.isParent} + zIndex={node.z} + isParent={!!node.isParent} noDragClassName={props.noDragClassName} noPanClassName={props.noPanClassName} /> diff --git a/src/hooks/useNodesEdgesState.ts b/src/hooks/useNodesEdgesState.ts index d64abdb7..d73d868d 100644 --- a/src/hooks/useNodesEdgesState.ts +++ b/src/hooks/useNodesEdgesState.ts @@ -7,7 +7,7 @@ type ApplyChanges = (changes: ChangesType[], items: ItemT type OnChange = (changes: ChangesType[]) => void; // returns a hook that can be used liked this: -// const [nodes, setNodes, onNodesChange] = useNodesState(intialNodes); +// const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); function createUseItemsState( applyChangesFunction: ApplyChanges ): (initialItems: ItemType[]) => [ItemType[], Dispatch>, OnChange] { diff --git a/src/hooks/useOnPaneReadyHandler.ts b/src/hooks/useOnPaneReadyHandler.ts index a51b6cdd..d6a48dfa 100644 --- a/src/hooks/useOnPaneReadyHandler.ts +++ b/src/hooks/useOnPaneReadyHandler.ts @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'; import { pointToRendererPoint } from '../utils/graph'; import { useStoreApi } from '../store'; -import useZoomPanHelper from './useZoomPanHelper'; +import useZoomPanHelper from '../hooks/useZoomPanHelper'; import { OnPaneReady, XYPosition, Node, Edge, FlowExportObject } from '../types'; function useOnPaneReadyHandler(onPaneReady: OnPaneReady | undefined) { diff --git a/src/hooks/useZoomPanHelper.ts b/src/hooks/useZoomPanHelper.ts index 2168fd17..0fc766da 100644 --- a/src/hooks/useZoomPanHelper.ts +++ b/src/hooks/useZoomPanHelper.ts @@ -80,11 +80,11 @@ const useZoomPanHelper = (): ZoomPanHelperFunctions => { }, setCenter: (x, y, options) => { const { width, height, maxZoom } = store.getState(); - const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom; const centerX = width / 2 - x * nextZoom; const centerY = height / 2 - y * nextZoom; const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom); + d3Zoom.transform(getTransition(d3Selection, options?.duration), transform); }, fitBounds: (bounds, options) => { diff --git a/src/index.ts b/src/index.ts index ab8ee442..4afb04d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,3 @@ -// import './wdyr'; - import ReactFlow from './container/ReactFlow'; export default ReactFlow; diff --git a/src/types/general.ts b/src/types/general.ts index d06cb632..ab1b101b 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -26,8 +26,11 @@ export type OnEdgesChange = (nodes: EdgeChange[]) => void; export type ZoomInOut = (options?: ZoomPanHelperFunctionOptions) => void; export type ZoomTo = (zoomLevel: number, options?: ZoomPanHelperFunctionOptions) => void; +export type GetZoom = () => number; +export type GetTransform = () => FlowTransform; export type SetTransform = (transform: FlowTransform, options?: ZoomPanHelperFunctionOptions) => void; export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void; +export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void; export type ReactFlowInstance = { zoomIn: ZoomInOut; @@ -125,17 +128,18 @@ export type SetCenterOptions = ZoomPanHelperFunctionOptions & { export type FitBoundsOptions = ZoomPanHelperFunctionOptions & { padding?: number; }; + export interface ZoomPanHelperFunctions { zoomIn: ZoomInOut; zoomOut: ZoomInOut; zoomTo: ZoomTo; - getZoom: () => number; + getZoom: GetZoom; setTransform: SetTransform; - getTransform: () => FlowTransform; + getTransform: GetTransform; fitView: FitView; setCenter: SetCenter; - fitBounds: (bounds: Rect, options?: FitBoundsOptions) => void; - project: (position: XYPosition) => XYPosition; + fitBounds: FitBounds; + project: Project; initialized: boolean; } diff --git a/src/utils/graph.ts b/src/utils/graph.ts index 6452fff4..0a6f836d 100644 --- a/src/utils/graph.ts +++ b/src/utils/graph.ts @@ -37,7 +37,7 @@ export const getIncomers = (node: Node, nodes: Node[], edges: Edge[]): Node[] => }; const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): string => - `reactflow__edge-${source}${sourceHandle}-${target}${targetHandle}`; + `reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`; export const getMarkerId = (marker: EdgeMarkerType | undefined): string => { if (typeof marker === 'undefined') { diff --git a/src/wdyr.ts b/src/wdyr.ts deleted file mode 100644 index 79be3097..00000000 --- a/src/wdyr.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -import React from 'react'; - -if (process.env.NODE_ENV === 'development') { - const whyDidYouRender = require('@welldone-software/why-did-you-render'); - whyDidYouRender(React, { - trackAllPureComponents: true, - }); -}