added parentLookup

This commit is contained in:
peterkogo
2024-04-15 17:03:03 +02:00
parent c5f394e1f7
commit ccf6578824
7 changed files with 49 additions and 31 deletions
@@ -42,7 +42,7 @@ export function NodeWrapper<NodeType extends Node>({
nodeOrigin, nodeOrigin,
onError, onError,
}: NodeWrapperProps<NodeType>) { }: NodeWrapperProps<NodeType>) {
const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => { const { node, positionAbsoluteX, positionAbsoluteY, internals } = useStore((s) => {
const node = s.nodeLookup.get(id)! as InternalNode<NodeType>; const node = s.nodeLookup.get(id)! as InternalNode<NodeType>;
const positionAbsolute = nodeExtent const positionAbsolute = nodeExtent
@@ -55,8 +55,9 @@ export function NodeWrapper<NodeType extends Node>({
// so we we need to force a re-render when some change // so we we need to force a re-render when some change
positionAbsoluteX: positionAbsolute.x, positionAbsoluteX: positionAbsolute.x,
positionAbsoluteY: positionAbsolute.y, positionAbsoluteY: positionAbsolute.y,
zIndex: node.internals.z, internals: node.internals,
isParent: node.internals.isParent, // zIndex: node.internals.z,
// isParent: node.internals.isParent,
}; };
}, shallow); }, shallow);
@@ -213,14 +214,14 @@ export function NodeWrapper<NodeType extends Node>({
{ {
selected: node.selected, selected: node.selected,
selectable: isSelectable, selectable: isSelectable,
parent: isParent, parent: internals.isParent,
draggable: isDraggable, draggable: isDraggable,
dragging, dragging,
}, },
])} ])}
ref={nodeRef} ref={nodeRef}
style={{ style={{
zIndex, zIndex: internals.z,
transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`, transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`,
pointerEvents: hasPointerEvents ? 'all' : 'none', pointerEvents: hasPointerEvents ? 'all' : 'none',
visibility: initialized ? 'visible' : 'hidden', visibility: initialized ? 'visible' : 'hidden',
@@ -254,7 +255,7 @@ export function NodeWrapper<NodeType extends Node>({
targetPosition={node.targetPosition} targetPosition={node.targetPosition}
dragging={dragging} dragging={dragging}
dragHandle={node.dragHandle} dragHandle={node.dragHandle}
zIndex={zIndex} zIndex={internals.z}
{...nodeDimensions} {...nodeDimensions}
/> />
</Provider> </Provider>
+2 -2
View File
@@ -38,14 +38,14 @@ const createRFStore = ({
(set, get) => ({ (set, get) => ({
...getInitialState({ nodes, edges, width, height, fitView, defaultNodes, defaultEdges }), ...getInitialState({ nodes, edges, width, height, fitView, defaultNodes, defaultEdges }),
setNodes: (nodes: Node[]) => { setNodes: (nodes: Node[]) => {
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get(); const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
// setNodes() is called exclusively in response to user actions: // setNodes() is called exclusively in response to user actions:
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup, // - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup. // - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
// //
// When this happens, we take the note objects passed by the user and extend them with fields // When this happens, we take the note objects passed by the user and extend them with fields
// relevant for internal React Flow operations. // relevant for internal React Flow operations.
adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect }); adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect });
set({ nodes }); set({ nodes });
}, },
+3 -1
View File
@@ -29,13 +29,14 @@ const getInitialState = ({
fitView?: boolean; fitView?: boolean;
} = {}): ReactFlowStore => { } = {}): ReactFlowStore => {
const nodeLookup = new Map(); const nodeLookup = new Map();
const parentLookup = new Map();
const connectionLookup = new Map(); const connectionLookup = new Map();
const edgeLookup = new Map(); const edgeLookup = new Map();
const storeEdges = defaultEdges ?? edges ?? []; const storeEdges = defaultEdges ?? edges ?? [];
const storeNodes = defaultNodes ?? nodes ?? []; const storeNodes = defaultNodes ?? nodes ?? [];
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges); updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
adoptUserNodes(storeNodes, nodeLookup, { adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
nodeOrigin: [0, 0], nodeOrigin: [0, 0],
elevateNodesOnSelect: false, elevateNodesOnSelect: false,
}); });
@@ -59,6 +60,7 @@ const getInitialState = ({
transform, transform,
nodes: storeNodes, nodes: storeNodes,
nodeLookup, nodeLookup,
parentLookup,
edges: storeEdges, edges: storeEdges,
edgeLookup, edgeLookup,
connectionLookup, connectionLookup,
+1
View File
@@ -54,6 +54,7 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
transform: Transform; transform: Transform;
nodes: NodeType[]; nodes: NodeType[];
nodeLookup: NodeLookup<InternalNode<NodeType>>; nodeLookup: NodeLookup<InternalNode<NodeType>>;
parentLookup: Map<string, InternalNode<NodeType>[]>;
edges: Edge[]; edges: Edge[];
edgeLookup: EdgeLookup<EdgeType>; edgeLookup: EdgeLookup<EdgeType>;
connectionLookup: ConnectionLookup; connectionLookup: ConnectionLookup;
@@ -81,7 +81,8 @@ export const getInitialStore = ({
fitView?: boolean; fitView?: boolean;
}) => { }) => {
const nodeLookup: NodeLookup = new Map(); const nodeLookup: NodeLookup = new Map();
adoptUserNodes(nodes, nodeLookup, { const parentLookup = new Map();
adoptUserNodes(nodes, nodeLookup, parentLookup, {
nodeOrigin: [0, 0], nodeOrigin: [0, 0],
elevateNodesOnSelect: false, elevateNodesOnSelect: false,
checkEquality: false checkEquality: false
@@ -104,8 +105,9 @@ export const getInitialStore = ({
return { return {
flowId: writable<string | null>(null), flowId: writable<string | null>(null),
nodes: createNodesStore(nodes, nodeLookup), nodes: createNodesStore(nodes, nodeLookup, parentLookup),
nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup), nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup),
parentLookup: readable<Map<string, InternalNode[]>>(parentLookup),
edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup), edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup),
visibleNodes: readable<InternalNode[]>([]), visibleNodes: readable<InternalNode[]>([]),
edges: createEdgesStore(edges, connectionLookup, edgeLookup), edges: createEdgesStore(edges, connectionLookup, edgeLookup),
+3 -2
View File
@@ -127,7 +127,8 @@ export type NodeStoreOptions = {
// The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes. // The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes.
export const createNodesStore = ( export const createNodesStore = (
nodes: Node[], nodes: Node[],
nodeLookup: NodeLookup<InternalNode> nodeLookup: NodeLookup<InternalNode>,
parentLookup: Map<string, InternalNode[]>
): { ): {
subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber; subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber;
update: (this: void, updater: Updater<Node[]>) => void; update: (this: void, updater: Updater<Node[]>) => void;
@@ -141,7 +142,7 @@ export const createNodesStore = (
let elevateNodesOnSelect = true; let elevateNodesOnSelect = true;
const _set = (nds: Node[]): Node[] => { const _set = (nds: Node[]): Node[] => {
adoptUserNodes(nds, nodeLookup, { adoptUserNodes(nds, nodeLookup, parentLookup, {
elevateNodesOnSelect, elevateNodesOnSelect,
defaults, defaults,
checkEquality: false checkEquality: false
+29 -18
View File
@@ -27,7 +27,7 @@ export function updateAbsolutePositions<NodeType extends NodeBase>(
elevateNodesOnSelect: true, elevateNodesOnSelect: true,
defaults: {}, defaults: {},
}, },
parentNodeIds?: Set<string> parentLookup?: Map<string, InternalNodeBase<NodeType>[]>
) { ) {
const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0;
@@ -38,7 +38,7 @@ export function updateAbsolutePositions<NodeType extends NodeBase>(
throw new Error(`Parent node ${parentId} not found`); throw new Error(`Parent node ${parentId} not found`);
} }
if (parentId || node.internals.isParent || parentNodeIds?.has(id)) { if (parentId || node.internals.isParent || parentLookup?.has(id)) {
const parentNode = parentId ? nodeLookup.get(parentId) : null; const parentNode = parentId ? nodeLookup.get(parentId) : null;
const { x, y, z } = calculateXYZPosition( const { x, y, z } = calculateXYZPosition(
node, node,
@@ -47,20 +47,21 @@ export function updateAbsolutePositions<NodeType extends NodeBase>(
...node.position, ...node.position,
z: (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0), z: (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0),
}, },
parentNode?.origin || options.nodeOrigin parentNode?.origin ?? options.nodeOrigin
); );
const currPosition = node.internals.positionAbsolute; const currPosition = node.internals.positionAbsolute;
const positionChanged = x !== currPosition.x || y !== currPosition.y; const positionChanged = x !== currPosition.x || y !== currPosition.y;
node.internals.positionAbsolute = positionChanged ? { x, y } : currPosition; node.internals = {
node.internals.z = z; ...node.internals,
positionAbsolute: positionChanged ? { x, y } : currPosition,
z,
};
if (parentNodeIds !== undefined) { if (parentLookup !== undefined) {
node.internals.isParent = !!parentNodeIds?.has(id); node.internals.isParent = !!parentLookup.has(id);
} }
nodeLookup.set(id, node);
} }
} }
} }
@@ -75,6 +76,7 @@ type UpdateNodesOptions<NodeType extends NodeBase> = {
export function adoptUserNodes<NodeType extends NodeBase>( export function adoptUserNodes<NodeType extends NodeBase>(
nodes: NodeType[], nodes: NodeType[],
nodeLookup: Map<string, InternalNodeBase<NodeType>>, nodeLookup: Map<string, InternalNodeBase<NodeType>>,
parentLookup: Map<string, InternalNodeBase<NodeType>[]>,
options: UpdateNodesOptions<NodeType> = { options: UpdateNodesOptions<NodeType> = {
nodeOrigin: [0, 0] as NodeOrigin, nodeOrigin: [0, 0] as NodeOrigin,
elevateNodesOnSelect: true, elevateNodesOnSelect: true,
@@ -84,20 +86,19 @@ export function adoptUserNodes<NodeType extends NodeBase>(
) { ) {
const tmpLookup = new Map(nodeLookup); const tmpLookup = new Map(nodeLookup);
nodeLookup.clear(); nodeLookup.clear();
parentLookup.clear();
const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0;
const parentNodeIds = new Set<string>(); // const parentNodeIds = new Set<string>();
nodes.forEach((userNode) => { nodes.forEach((userNode) => {
const currentStoreNode = tmpLookup.get(userNode.id); const currentStoreNode = tmpLookup.get(userNode.id);
if (userNode.parentId) { let internalNode = currentStoreNode!;
parentNodeIds.add(userNode.parentId);
}
if (options.checkEquality && userNode === currentStoreNode?.internals.userNode) { if (options.checkEquality && userNode === currentStoreNode?.internals.userNode) {
nodeLookup.set(userNode.id, currentStoreNode); nodeLookup.set(userNode.id, currentStoreNode);
} else { } else {
nodeLookup.set(userNode.id, { internalNode = {
...options.defaults, ...options.defaults,
...userNode, ...userNode,
measured: { measured: {
@@ -111,12 +112,22 @@ export function adoptUserNodes<NodeType extends NodeBase>(
userNode, userNode,
isParent: false, isParent: false,
}, },
}); };
nodeLookup.set(userNode.id, internalNode);
}
if (userNode.parentId) {
const childNodes = parentLookup.get(userNode.parentId);
if (childNodes) {
childNodes.push(internalNode);
} else {
parentLookup.set(userNode.parentId, [internalNode]);
}
} }
}); });
if (parentNodeIds.size > 0) { if (parentLookup.size > 0) {
updateAbsolutePositions(nodeLookup, options, parentNodeIds); updateAbsolutePositions(nodeLookup, options, parentLookup);
} }
} }