Skip to content

Commit

Permalink
refactor(z-level): implement new stacking logic
Browse files Browse the repository at this point in the history
  • Loading branch information
moklick committed Nov 9, 2021
1 parent df25cb3 commit 050bb31
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 53 deletions.
6 changes: 3 additions & 3 deletions src/components/Nodes/wrapNode.tsx
Expand Up @@ -49,7 +49,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
resizeObserver,
dragHandle,
zIndex,
isParentNode,
isParent,
}: WrapNodeProps) => {
const {
addSelectedElements,
Expand Down Expand Up @@ -85,7 +85,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
onMouseEnter,
onMouseMove,
onMouseLeave,
isParentNode,
isParent,
zIndex,
]
);
Expand Down Expand Up @@ -228,7 +228,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
{
selected: isSelected,
selectable: isSelectable,
parent: isParentNode,
parent: isParent,
},
]);

Expand Down
4 changes: 2 additions & 2 deletions src/container/NodeRenderer/index.tsx
Expand Up @@ -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}
/>
);
})}
Expand Down
17 changes: 7 additions & 10 deletions src/hooks/useVisibleEdges.ts
Expand Up @@ -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<Record<string, Edge[]>>((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;
}, {});
Expand Down Expand Up @@ -69,7 +66,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, nodeInternals: NodeInternal
)
);

return groupEdgesByTreeLevel(edges, nodeInternals);
return groupEdgesByZLevel(edges, nodeInternals);
}

export default useVisibleEdges;
77 changes: 42 additions & 35 deletions 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<ElementId, boolean>;

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)!;

// +1 for each recursion level
let zAddition = 1;

return getAbsolutePosAndTreeLevel(
parentNode,
nodeInternals,
addPositions(result, {
x: parentNode.position?.x || 0,
y: parentNode.position?.y || 0,
treeLevel: parentNode.treeLevel || 0,
})
);
// +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<ElementId, NodeInternalsItem>();
const parentNodes: Record<ElementId, boolean> = {};
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;
Expand All @@ -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;
}
}
});

Expand Down
7 changes: 4 additions & 3 deletions src/types/index.ts
Expand Up @@ -303,7 +303,7 @@ export interface WrapNodeProps<T = any> {
resizeObserver: ResizeObserver | null;
dragHandle?: string;
zIndex: number;
isParentNode: boolean;
isParent: boolean;
}

export type FitViewParams = {
Expand Down Expand Up @@ -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<ElementId, NodeInternalsItem>;
Expand Down

0 comments on commit 050bb31

Please sign in to comment.