From 450cb90e67b752ec2d9c94dea3e087b130437a5d Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 20 Oct 2021 19:26:22 +0200 Subject: [PATCH 1/2] refactor(nested-nodes): zIndex --- src/components/Nodes/wrapNode.tsx | 3 +- src/container/NodeRenderer/index.tsx | 206 ++++++++++++++------------- src/types/index.ts | 1 + 3 files changed, 107 insertions(+), 103 deletions(-) diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 6da0f95c..a0b30cd9 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -48,6 +48,7 @@ export default (NodeComponent: ComponentType) => { isDragging, resizeObserver, dragHandle, + zIndex, }: WrapNodeProps) => { const { addSelectedElements, @@ -63,7 +64,7 @@ export default (NodeComponent: ComponentType) => { const nodeStyle: CSSProperties = useMemo( () => ({ - zIndex: isSelected ? 10 : 3, + zIndex: isSelected ? zIndex + 1 : zIndex, transform: `translate(${xPos}px,${yPos}px)`, pointerEvents: isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave ? 'all' : 'none', diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index f362f63a..16e3e037 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -39,111 +39,112 @@ interface NodesProps extends NodeRendererProps { nodesDraggable: boolean; nodesConnectable: boolean; elementsSelectable: boolean; + recursionDepth: number; } -const Nodes = memo( - ({ - nodes = [], - isDraggable, - resizeObserver, - scale, - snapToGrid, - snapGrid, - nodesDraggable, - nodesConnectable, - elementsSelectable, - ...props - }: NodesProps) => { +function Nodes({ + nodes = [], + isDraggable, + resizeObserver, + scale, + snapToGrid, + snapGrid, + nodesDraggable, + nodesConnectable, + elementsSelectable, + recursionDepth, + ...props +}: NodesProps): any { + return nodes.map((node) => { + const nodeType = node.type || 'default'; + + if (!props.nodeTypes[nodeType]) { + console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`); + } + + const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType; + const isNodeDraggable = + typeof isDraggable !== 'undefined' + ? isDraggable + : !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); + 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'; + let childRect; + + if (node.childNodes) { + childRect = getRectOfNodes(node.childNodes); + node.position = node.isDragging + ? node.position + : { x: Math.round(childRect.x) - 10, y: Math.round(childRect.y) - 10 }; + node.style = { + ...node.style, + width: Math.round(childRect.width) + 20, + height: Math.round(childRect.height) + 20, + boxSizing: 'border-box', + }; + } + return ( - <> - {nodes.map((node) => { - const nodeType = node.type || 'default'; - - if (!props.nodeTypes[nodeType]) { - console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`); - } - - const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType; - const isNodeDraggable = - typeof isDraggable !== 'undefined' - ? isDraggable - : !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); - 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'; - - if (node.childNodes) { - const childRect = getRectOfNodes(node.childNodes); - node.position = node.isDragging - ? node.position - : { x: Math.round(childRect.x) - 10, y: Math.round(childRect.y) - 10 }; - node.style = { - ...node.style, - width: Math.round(childRect.width) + 20, - height: Math.round(childRect.height) + 20, - boxSizing: 'border-box', - }; - } - - return ( - - - {node.childNodes && ( - - )} - - ); - })} - + + + {node.childNodes && ( + + )} + ); - } -); + }); +} + +const MemoizedNodes = memo(Nodes); const NodeRenderer = (props: NodeRendererProps) => { const { @@ -191,7 +192,7 @@ const NodeRenderer = (props: NodeRendererProps) => { return (
- { resizeObserver={resizeObserver} elementsSelectable={elementsSelectable} scale={transform[2]} + recursionDepth={0} {...props} />
diff --git a/src/types/index.ts b/src/types/index.ts index 817657dd..b3133c0a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -300,6 +300,7 @@ export interface WrapNodeProps { isDragging?: boolean; resizeObserver: ResizeObserver | null; dragHandle?: string; + zIndex: number; } export type FitViewParams = { From 8f23321bd02418db80200d42ec4e464d172633ec Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 21 Oct 2021 09:05:56 +0200 Subject: [PATCH 2/2] refactor(hooks): add useVisibleNodes and useVisibleEdges --- src/container/EdgeRenderer/index.tsx | 36 ++--------------------- src/container/NodeRenderer/index.tsx | 17 +++-------- src/hooks/useVisibleEdges.ts | 44 ++++++++++++++++++++++++++++ src/hooks/useVisibleNodes.ts | 22 ++++++++++++++ 4 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 src/hooks/useVisibleEdges.ts create mode 100644 src/hooks/useVisibleNodes.ts diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 4b707b98..0b83ce99 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -4,7 +4,7 @@ import shallow from 'zustand/shallow'; import { useStore } from '../../store'; import ConnectionLine from '../../components/ConnectionLine/index'; import MarkerDefinitions from './MarkerDefinitions'; -import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from './utils'; +import { getEdgePositions, getHandle, getSourceTargetNodes } from './utils'; import { Position, Edge, @@ -16,6 +16,7 @@ import { ReactFlowState, NodeHandleBounds, } from '../../types'; +import useVisibleEdges from '../../hooks/useVisibleEdges'; interface EdgeRendererProps { edgeTypes: any; @@ -234,38 +235,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => { nodes, } = useStore(selector, shallow); - const edges = useStore( - useCallback( - (s: ReactFlowState) => { - if (!props.onlyRenderVisibleElements) { - return s.edges; - } - - return s.edges.filter((e) => { - const { sourceNode, targetNode } = getSourceTargetNodes(e, s.nodes); - - return ( - sourceNode?.width && - sourceNode?.height && - targetNode?.width && - targetNode?.height && - isEdgeVisible({ - sourcePos: sourceNode.position, - targetPos: targetNode.position, - sourceWidth: sourceNode.width, - sourceHeight: sourceNode.height, - targetWidth: targetNode.width, - targetHeight: targetNode.height, - width: s.width, - height: s.height, - transform: s.transform, - }) - ); - }); - }, - [props.onlyRenderVisibleElements] - ) - ); + const edges = useVisibleEdges(props.onlyRenderVisibleElements); if (!width) { return null; diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 16e3e037..2f8be03f 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -1,9 +1,10 @@ -import React, { memo, useMemo, ComponentType, MouseEvent, useCallback, Fragment } from 'react'; +import React, { memo, useMemo, ComponentType, MouseEvent, Fragment } from 'react'; import shallow from 'zustand/shallow'; import { useStore } from '../../store'; import { Node, NodeTypesType, ReactFlowState, WrapNodeProps, SnapGrid } from '../../types'; -import { getNodesInside, getRectOfNodes } from '../../utils/graph'; +import { getRectOfNodes } from '../../utils/graph'; +import useVisibleNodes from '../../hooks/useVisibleNodes'; interface NodeRendererProps { nodeTypes: NodeTypesType; selectNodesOnDrag: boolean; @@ -156,17 +157,7 @@ const NodeRenderer = (props: NodeRendererProps) => { snapGrid, snapToGrid, } = useStore(selector, shallow); - - const nodes = useStore( - useCallback( - (s: ReactFlowState) => { - return props.onlyRenderVisibleElements - ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true) - : s.nodes; - }, - [props.onlyRenderVisibleElements] - ) - ); + const nodes = useVisibleNodes(props.onlyRenderVisibleElements); const transformStyle = useMemo( () => ({ diff --git a/src/hooks/useVisibleEdges.ts b/src/hooks/useVisibleEdges.ts new file mode 100644 index 00000000..74406400 --- /dev/null +++ b/src/hooks/useVisibleEdges.ts @@ -0,0 +1,44 @@ +import { useCallback } from 'react'; + +import { useStore } from '../store'; +import { isEdgeVisible, getSourceTargetNodes } from '../container/EdgeRenderer/utils'; +import { ReactFlowState } from '../types'; + +function useVisibleEdges(onlyRenderVisible: boolean) { + const edges = useStore( + useCallback( + (s: ReactFlowState) => { + if (!onlyRenderVisible) { + return s.edges; + } + + return s.edges.filter((e) => { + const { sourceNode, targetNode } = getSourceTargetNodes(e, s.nodes); + + return ( + sourceNode?.width && + sourceNode?.height && + targetNode?.width && + targetNode?.height && + isEdgeVisible({ + sourcePos: sourceNode.position, + targetPos: targetNode.position, + sourceWidth: sourceNode.width, + sourceHeight: sourceNode.height, + targetWidth: targetNode.width, + targetHeight: targetNode.height, + width: s.width, + height: s.height, + transform: s.transform, + }) + ); + }); + }, + [onlyRenderVisible] + ) + ); + + return edges; +} + +export default useVisibleEdges; diff --git a/src/hooks/useVisibleNodes.ts b/src/hooks/useVisibleNodes.ts new file mode 100644 index 00000000..d6c9bd96 --- /dev/null +++ b/src/hooks/useVisibleNodes.ts @@ -0,0 +1,22 @@ +import { useCallback } from 'react'; + +import { useStore } from '../store'; +import { getNodesInside } from '../utils/graph'; +import { ReactFlowState } from '../types'; + +function useVisibleNodes(onlyRenderVisible: boolean) { + const nodes = useStore( + useCallback( + (s: ReactFlowState) => { + return onlyRenderVisible + ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true) + : s.nodes; + }, + [onlyRenderVisible] + ) + ); + + return nodes; +} + +export default useVisibleNodes;