refactor(z-level): implement new stacking logic
This commit is contained in:
@@ -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,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>;
|
||||||
|
|||||||
Reference in New Issue
Block a user