diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index e87f388e..da2f507e 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -49,7 +49,7 @@ export default (NodeComponent: ComponentType) => { resizeObserver, dragHandle, zIndex, - isParentNode, + isParent, }: WrapNodeProps) => { const { addSelectedElements, @@ -85,7 +85,7 @@ export default (NodeComponent: ComponentType) => { onMouseEnter, onMouseMove, onMouseLeave, - isParentNode, + isParent, zIndex, ] ); @@ -228,7 +228,7 @@ export default (NodeComponent: ComponentType) => { { selected: isSelected, selectable: isSelectable, - parent: isParentNode, + parent: isParent, }, ]); diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index a34fdc46..7876c1ec 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -106,8 +106,8 @@ const NodeRenderer = (props: NodeRendererProps) => { isConnectable={isConnectable} resizeObserver={resizeObserver} dragHandle={node.dragHandle} - zIndex={internals?.treeLevel || 0} - isParentNode={!!internals?.isParentNode} + zIndex={internals?.z || 0} + isParent={!!internals?.isParent} /> ); })} diff --git a/src/hooks/useVisibleEdges.ts b/src/hooks/useVisibleEdges.ts index 33befa1e..837150f1 100644 --- a/src/hooks/useVisibleEdges.ts +++ b/src/hooks/useVisibleEdges.ts @@ -4,21 +4,18 @@ import { useStore } from '../store'; import { isEdgeVisible } from '../container/EdgeRenderer/utils'; import { ReactFlowState, NodeInternals, Edge } from '../types'; -function groupEdgesByTreeLevel(edges: Edge[], nodeInternals: NodeInternals) { +function groupEdgesByZLevel(edges: Edge[], nodeInternals: NodeInternals) { let maxLevel = -1; const levelLookup = edges.reduce>((tree, edge) => { - const treeLevel = Math.max( - nodeInternals.get(edge.source)?.treeLevel || 0, - nodeInternals.get(edge.target)?.treeLevel || 0 - ); - if (tree[treeLevel]) { - tree[treeLevel].push(edge); + const z = Math.max(nodeInternals.get(edge.source)?.z || 0, nodeInternals.get(edge.target)?.z || 0); + if (tree[z]) { + tree[z].push(edge); } else { - tree[treeLevel] = [edge]; + tree[z] = [edge]; } - maxLevel = treeLevel > maxLevel ? treeLevel : maxLevel; + maxLevel = z > maxLevel ? z : maxLevel; return tree; }, {}); @@ -69,7 +66,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, nodeInternals: NodeInternal ) ); - return groupEdgesByTreeLevel(edges, nodeInternals); + return groupEdgesByZLevel(edges, nodeInternals); } export default useVisibleEdges; diff --git a/src/store/utils.ts b/src/store/utils.ts index 923620af..9333a004 100644 --- a/src/store/utils.ts +++ b/src/store/utils.ts @@ -1,48 +1,50 @@ import { ElementId, Node, NodeInternals, NodeInternalsItem, XYPosition } from '../types'; -type XYPosAndTreeLevel = XYPosition & { treeLevel: number }; +type XYZPosition = XYPosition & { z: number }; +type ParentNodes = Record; -function addPositions(a: XYPosAndTreeLevel, b: XYPosAndTreeLevel): XYPosAndTreeLevel { - return { - x: (a.x ?? 0) + (b.x ?? 0), - y: (a.y ?? 0) + (b.y ?? 0), - treeLevel: a.treeLevel + (b.treeLevel || 1), - }; -} - -function getAbsolutePosAndTreeLevel( +function calculateXYZPosition( node: NodeInternalsItem, nodeInternals: NodeInternals, - result: XYPosAndTreeLevel -): XYPosAndTreeLevel { - const parentNode = node.parentNode ? nodeInternals.get(node.parentNode) : false; - - if (!parentNode) { + parentNodes: ParentNodes, + result: XYZPosition +): XYZPosition { + if (!node.parentNode) { return result; } + const parentNode = nodeInternals.get(node.parentNode)!; - return getAbsolutePosAndTreeLevel( - parentNode, - nodeInternals, - addPositions(result, { - x: parentNode.position?.x || 0, - y: parentNode.position?.y || 0, - treeLevel: parentNode.treeLevel || 0, - }) - ); + // +1 for each recursion level + let zAddition = 1; + + // +2 if it's a parent node, so that groups/parents are always on top + if (parentNodes[node.parentNode!]) { + zAddition = 2; + } + + if (parentNode.z) { + zAddition += parentNode.z; + } + + return calculateXYZPosition(parentNode, nodeInternals, parentNodes, { + x: (result.x ?? 0) + (parentNode.position?.x ?? 0), + y: (result.y ?? 0) + (parentNode.position?.y ?? 0), + z: (result.z ?? 0) + zAddition, + }); } export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals): NodeInternals { const nextNodeInternals = new Map(); - const parentNodes: Record = {}; + const parentNodes: ParentNodes = {}; nodes.forEach((node) => { const internals: NodeInternalsItem = { ...nodeInternals.get(node.id), + id: node.id, width: node.width || null, height: node.height || null, position: node.position, positionAbsolute: node.position, - treeLevel: node.isDragging || node.isSelected ? 1000 : node.zIndex || 0, + z: node.isDragging || node.isSelected ? 1000 : node.zIndex || 0, }; if (node.parentNode) { internals.parentNode = node.parentNode; @@ -55,25 +57,30 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals) const updatedInternals: NodeInternalsItem = nextNodeInternals.get(node.id)!; if (node.parentNode || parentNodes[node.id]) { - if (node.parentNode) { - const parentNodeInternal = nextNodeInternals.get(node.parentNode); - if (parentNodeInternal) { - parentNodeInternal.isParentNode = true; + let startingZ = updatedInternals.z; + + if (!startingZ) { + if (parentNodes[node.id] && node.parentNode) { + startingZ = 2; + } else if (node.parentNode) { + startingZ = 1; } } - const positionAbsoluteAndTreeLevel = getAbsolutePosAndTreeLevel(node, nextNodeInternals, { + const { x, y, z } = calculateXYZPosition(node, nextNodeInternals, parentNodes, { ...node.position, - treeLevel: updatedInternals.treeLevel || 1, + z: startingZ as number, }); - const { treeLevel, x, y } = positionAbsoluteAndTreeLevel; - updatedInternals.positionAbsolute = { x, y, }; - updatedInternals.treeLevel = treeLevel; + updatedInternals.z = z; + + if (parentNodes[node.id]) { + updatedInternals.isParent = true; + } } }); diff --git a/src/types/index.ts b/src/types/index.ts index 2e794286..5b99da4d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -303,7 +303,7 @@ export interface WrapNodeProps { resizeObserver: ResizeObserver | null; dragHandle?: string; zIndex: number; - isParentNode: boolean; + isParent: boolean; } export type FitViewParams = { @@ -460,14 +460,15 @@ export type OnNodesChange = (nodes: NodeChange[]) => void; export type OnEdgesChange = (nodes: EdgeChange[]) => void; export type NodeInternalsItem = { + id?: string; width?: number | null; height?: number | null; parentNode?: ElementId; position?: XYPosition; positionAbsolute?: XYPosition; handleBounds?: NodeHandleBounds; - treeLevel?: number; - isParentNode?: boolean; + z?: number; + isParent?: boolean; }; export type NodeInternals = Map;