refactor(z-level): implement new stacking logic

This commit is contained in:
moklick
2021-11-09 12:11:47 +01:00
parent df25cb32f0
commit 050bb3130f
5 changed files with 58 additions and 53 deletions
+3 -3
View File
@@ -49,7 +49,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
resizeObserver, resizeObserver,
dragHandle, dragHandle,
zIndex, zIndex,
isParentNode, isParent,
}: WrapNodeProps) => { }: WrapNodeProps) => {
const { const {
addSelectedElements, addSelectedElements,
@@ -85,7 +85,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
onMouseEnter, onMouseEnter,
onMouseMove, onMouseMove,
onMouseLeave, onMouseLeave,
isParentNode, isParent,
zIndex, zIndex,
] ]
); );
@@ -228,7 +228,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
{ {
selected: isSelected, selected: isSelected,
selectable: isSelectable, selectable: isSelectable,
parent: isParentNode, parent: isParent,
}, },
]); ]);
+2 -2
View File
@@ -106,8 +106,8 @@ const NodeRenderer = (props: NodeRendererProps) => {
isConnectable={isConnectable} isConnectable={isConnectable}
resizeObserver={resizeObserver} resizeObserver={resizeObserver}
dragHandle={node.dragHandle} dragHandle={node.dragHandle}
zIndex={internals?.treeLevel || 0} zIndex={internals?.z || 0}
isParentNode={!!internals?.isParentNode} isParent={!!internals?.isParent}
/> />
); );
})} })}
+7 -10
View File
@@ -4,21 +4,18 @@ import { useStore } from '../store';
import { isEdgeVisible } from '../container/EdgeRenderer/utils'; import { isEdgeVisible } from '../container/EdgeRenderer/utils';
import { ReactFlowState, NodeInternals, Edge } from '../types'; import { ReactFlowState, NodeInternals, Edge } from '../types';
function groupEdgesByTreeLevel(edges: Edge[], nodeInternals: NodeInternals) { function groupEdgesByZLevel(edges: Edge[], nodeInternals: NodeInternals) {
let maxLevel = -1; let maxLevel = -1;
const levelLookup = edges.reduce<Record<string, Edge[]>>((tree, edge) => { const levelLookup = edges.reduce<Record<string, Edge[]>>((tree, edge) => {
const treeLevel = Math.max( const z = Math.max(nodeInternals.get(edge.source)?.z || 0, nodeInternals.get(edge.target)?.z || 0);
nodeInternals.get(edge.source)?.treeLevel || 0, if (tree[z]) {
nodeInternals.get(edge.target)?.treeLevel || 0 tree[z].push(edge);
);
if (tree[treeLevel]) {
tree[treeLevel].push(edge);
} else { } else {
tree[treeLevel] = [edge]; tree[z] = [edge];
} }
maxLevel = treeLevel > maxLevel ? treeLevel : maxLevel; maxLevel = z > maxLevel ? z : maxLevel;
return tree; return tree;
}, {}); }, {});
@@ -69,7 +66,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, nodeInternals: NodeInternal
) )
); );
return groupEdgesByTreeLevel(edges, nodeInternals); return groupEdgesByZLevel(edges, nodeInternals);
} }
export default useVisibleEdges; export default useVisibleEdges;
+42 -35
View File
@@ -1,48 +1,50 @@
import { ElementId, Node, NodeInternals, NodeInternalsItem, XYPosition } from '../types'; import { ElementId, Node, NodeInternals, NodeInternalsItem, XYPosition } from '../types';
type XYPosAndTreeLevel = XYPosition & { treeLevel: number }; type XYZPosition = XYPosition & { z: number };
type ParentNodes = Record<ElementId, boolean>;
function addPositions(a: XYPosAndTreeLevel, b: XYPosAndTreeLevel): XYPosAndTreeLevel { function calculateXYZPosition(
return {
x: (a.x ?? 0) + (b.x ?? 0),
y: (a.y ?? 0) + (b.y ?? 0),
treeLevel: a.treeLevel + (b.treeLevel || 1),
};
}
function getAbsolutePosAndTreeLevel(
node: NodeInternalsItem, node: NodeInternalsItem,
nodeInternals: NodeInternals, nodeInternals: NodeInternals,
result: XYPosAndTreeLevel parentNodes: ParentNodes,
): XYPosAndTreeLevel { result: XYZPosition
const parentNode = node.parentNode ? nodeInternals.get(node.parentNode) : false; ): XYZPosition {
if (!node.parentNode) {
if (!parentNode) {
return result; return result;
} }
const parentNode = nodeInternals.get(node.parentNode)!;
return getAbsolutePosAndTreeLevel( // +1 for each recursion level
parentNode, let zAddition = 1;
nodeInternals,
addPositions(result, { // +2 if it's a parent node, so that groups/parents are always on top
x: parentNode.position?.x || 0, if (parentNodes[node.parentNode!]) {
y: parentNode.position?.y || 0, zAddition = 2;
treeLevel: parentNode.treeLevel || 0, }
})
); 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 { export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals): NodeInternals {
const nextNodeInternals = new Map<ElementId, NodeInternalsItem>(); const nextNodeInternals = new Map<ElementId, NodeInternalsItem>();
const parentNodes: Record<ElementId, boolean> = {}; const parentNodes: ParentNodes = {};
nodes.forEach((node) => { nodes.forEach((node) => {
const internals: NodeInternalsItem = { const internals: NodeInternalsItem = {
...nodeInternals.get(node.id), ...nodeInternals.get(node.id),
id: node.id,
width: node.width || null, width: node.width || null,
height: node.height || null, height: node.height || null,
position: node.position, position: node.position,
positionAbsolute: 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) { if (node.parentNode) {
internals.parentNode = node.parentNode; internals.parentNode = node.parentNode;
@@ -55,25 +57,30 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals)
const updatedInternals: NodeInternalsItem = nextNodeInternals.get(node.id)!; const updatedInternals: NodeInternalsItem = nextNodeInternals.get(node.id)!;
if (node.parentNode || parentNodes[node.id]) { if (node.parentNode || parentNodes[node.id]) {
if (node.parentNode) { let startingZ = updatedInternals.z;
const parentNodeInternal = nextNodeInternals.get(node.parentNode);
if (parentNodeInternal) { if (!startingZ) {
parentNodeInternal.isParentNode = true; 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, ...node.position,
treeLevel: updatedInternals.treeLevel || 1, z: startingZ as number,
}); });
const { treeLevel, x, y } = positionAbsoluteAndTreeLevel;
updatedInternals.positionAbsolute = { updatedInternals.positionAbsolute = {
x, x,
y, y,
}; };
updatedInternals.treeLevel = treeLevel; updatedInternals.z = z;
if (parentNodes[node.id]) {
updatedInternals.isParent = true;
}
} }
}); });
+4 -3
View File
@@ -303,7 +303,7 @@ export interface WrapNodeProps<T = any> {
resizeObserver: ResizeObserver | null; resizeObserver: ResizeObserver | null;
dragHandle?: string; dragHandle?: string;
zIndex: number; zIndex: number;
isParentNode: boolean; isParent: boolean;
} }
export type FitViewParams = { export type FitViewParams = {
@@ -460,14 +460,15 @@ export type OnNodesChange = (nodes: NodeChange[]) => void;
export type OnEdgesChange = (nodes: EdgeChange[]) => void; export type OnEdgesChange = (nodes: EdgeChange[]) => void;
export type NodeInternalsItem = { export type NodeInternalsItem = {
id?: string;
width?: number | null; width?: number | null;
height?: number | null; height?: number | null;
parentNode?: ElementId; parentNode?: ElementId;
position?: XYPosition; position?: XYPosition;
positionAbsolute?: XYPosition; positionAbsolute?: XYPosition;
handleBounds?: NodeHandleBounds; handleBounds?: NodeHandleBounds;
treeLevel?: number; z?: number;
isParentNode?: boolean; isParent?: boolean;
}; };
export type NodeInternals = Map<ElementId, NodeInternalsItem>; export type NodeInternals = Map<ElementId, NodeInternalsItem>;