refactor(react): use array for nodeInternals, rename to nodes
This commit is contained in:
@@ -26,11 +26,11 @@ const createRFStore = () =>
|
||||
createStore<ReactFlowState>((set, get) => ({
|
||||
...initialState,
|
||||
setNodes: (nodes: Node[]) => {
|
||||
const { nodeInternals, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
set({ nodeInternals: createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect) });
|
||||
const { nodes: storeNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
set({ nodes: createNodeInternals(nodes, storeNodes, nodeOrigin, elevateNodesOnSelect) });
|
||||
},
|
||||
getNodes: () => {
|
||||
return Array.from(get().nodeInternals.values());
|
||||
return get().nodes;
|
||||
},
|
||||
setEdges: (edges: Edge[]) => {
|
||||
const { defaultEdgeOptions = {} } = get();
|
||||
@@ -40,17 +40,17 @@ const createRFStore = () =>
|
||||
const hasDefaultNodes = typeof nodes !== 'undefined';
|
||||
const hasDefaultEdges = typeof edges !== 'undefined';
|
||||
|
||||
const nodeInternals = hasDefaultNodes
|
||||
? createNodeInternals(nodes, new Map(), get().nodeOrigin, get().elevateNodesOnSelect)
|
||||
: new Map();
|
||||
const nextNodes = hasDefaultNodes
|
||||
? createNodeInternals(nodes, [], get().nodeOrigin, get().elevateNodesOnSelect)
|
||||
: [];
|
||||
const nextEdges = hasDefaultEdges ? edges : [];
|
||||
|
||||
set({ nodeInternals, edges: nextEdges, hasDefaultNodes, hasDefaultEdges });
|
||||
set({ nodes: nextNodes, edges: nextEdges, hasDefaultNodes, hasDefaultEdges });
|
||||
},
|
||||
updateNodeDimensions: (updates) => {
|
||||
const {
|
||||
onNodesChange,
|
||||
nodeInternals,
|
||||
nodes,
|
||||
fitViewOnInit,
|
||||
fitViewOnInitDone,
|
||||
fitViewOnInitOptions,
|
||||
@@ -70,11 +70,12 @@ const createRFStore = () =>
|
||||
|
||||
const style = window.getComputedStyle(viewportNode);
|
||||
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
||||
const changes: NodeDimensionChange[] = [];
|
||||
|
||||
const changes: NodeDimensionChange[] = updates.reduce<NodeDimensionChange[]>((res, update) => {
|
||||
const node = nodeInternals.get(update.id);
|
||||
const nextNodes = nodes.map((node) => {
|
||||
const update = updates.find((change) => change.id === node.id);
|
||||
|
||||
if (node) {
|
||||
if (update) {
|
||||
const dimensions = getDimensions(update.nodeElement);
|
||||
const doUpdate = !!(
|
||||
dimensions.width &&
|
||||
@@ -83,8 +84,15 @@ const createRFStore = () =>
|
||||
);
|
||||
|
||||
if (doUpdate) {
|
||||
nodeInternals.set(node.id, {
|
||||
changes.push({
|
||||
id: node.id,
|
||||
type: 'dimensions',
|
||||
dimensions,
|
||||
});
|
||||
|
||||
return {
|
||||
...node,
|
||||
...dimensions,
|
||||
[internalsSymbol]: {
|
||||
...node[internalsSymbol],
|
||||
handleBounds: {
|
||||
@@ -92,21 +100,14 @@ const createRFStore = () =>
|
||||
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin),
|
||||
},
|
||||
},
|
||||
...dimensions,
|
||||
});
|
||||
|
||||
res.push({
|
||||
id: node.id,
|
||||
type: 'dimensions',
|
||||
dimensions,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}, []);
|
||||
return node;
|
||||
});
|
||||
|
||||
updateAbsoluteNodePositions(nodeInternals, nodeOrigin);
|
||||
updateAbsoluteNodePositions(nextNodes, nodeOrigin);
|
||||
|
||||
const nextFitViewOnInitDone =
|
||||
fitViewOnInitDone ||
|
||||
@@ -115,7 +116,7 @@ const createRFStore = () =>
|
||||
!!panZoom &&
|
||||
fitView(
|
||||
{
|
||||
nodes: Array.from(nodeInternals.values()),
|
||||
nodes: nextNodes,
|
||||
width,
|
||||
height,
|
||||
panZoom,
|
||||
@@ -125,7 +126,7 @@ const createRFStore = () =>
|
||||
},
|
||||
fitViewOnInitOptions
|
||||
));
|
||||
set({ nodeInternals: new Map(nodeInternals), fitViewOnInitDone: nextFitViewOnInitDone });
|
||||
set({ nodes: nextNodes, fitViewOnInitDone: nextFitViewOnInitDone });
|
||||
|
||||
if (changes?.length > 0) {
|
||||
onNodesChange?.(changes);
|
||||
@@ -153,13 +154,13 @@ const createRFStore = () =>
|
||||
},
|
||||
|
||||
triggerNodeChanges: (changes) => {
|
||||
const { onNodesChange, nodeInternals, hasDefaultNodes, nodeOrigin, getNodes, elevateNodesOnSelect } = get();
|
||||
const { onNodesChange, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
|
||||
if (changes?.length) {
|
||||
if (hasDefaultNodes) {
|
||||
const nodes = applyNodeChanges(changes, getNodes());
|
||||
const nextNodeInternals = createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect);
|
||||
set({ nodeInternals: nextNodeInternals });
|
||||
const updatedNodes = applyNodeChanges(changes, nodes);
|
||||
const nextNodes = createNodeInternals(updatedNodes, nodes, nodeOrigin, elevateNodesOnSelect);
|
||||
set({ nodes: nextNodes });
|
||||
}
|
||||
|
||||
onNodesChange?.(changes);
|
||||
@@ -167,14 +168,14 @@ const createRFStore = () =>
|
||||
},
|
||||
|
||||
addSelectedNodes: (selectedNodeIds) => {
|
||||
const { multiSelectionActive, edges, getNodes } = get();
|
||||
const { multiSelectionActive, edges, nodes } = get();
|
||||
let changedNodes: NodeSelectionChange[];
|
||||
let changedEdges: EdgeSelectionChange[] | null = null;
|
||||
|
||||
if (multiSelectionActive) {
|
||||
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[];
|
||||
} else {
|
||||
changedNodes = getSelectionChanges(getNodes(), selectedNodeIds);
|
||||
changedNodes = getSelectionChanges(nodes, selectedNodeIds);
|
||||
changedEdges = getSelectionChanges(edges, []);
|
||||
}
|
||||
|
||||
@@ -186,7 +187,7 @@ const createRFStore = () =>
|
||||
});
|
||||
},
|
||||
addSelectedEdges: (selectedEdgeIds) => {
|
||||
const { multiSelectionActive, edges, getNodes } = get();
|
||||
const { multiSelectionActive, edges, nodes } = get();
|
||||
let changedEdges: EdgeSelectionChange[];
|
||||
let changedNodes: NodeSelectionChange[] | null = null;
|
||||
|
||||
@@ -194,7 +195,7 @@ const createRFStore = () =>
|
||||
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[];
|
||||
} else {
|
||||
changedEdges = getSelectionChanges(edges, selectedEdgeIds);
|
||||
changedNodes = getSelectionChanges(getNodes(), []);
|
||||
changedNodes = getSelectionChanges(nodes, []);
|
||||
}
|
||||
|
||||
updateNodesAndEdgesSelections({
|
||||
@@ -205,8 +206,8 @@ const createRFStore = () =>
|
||||
});
|
||||
},
|
||||
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
|
||||
const { edges: storeEdges, getNodes } = get();
|
||||
const nodesToUnselect = nodes ? nodes : getNodes();
|
||||
const { edges: storeEdges, nodes: storeNodes } = get();
|
||||
const nodesToUnselect = nodes ? nodes : storeNodes;
|
||||
const edgesToUnselect = edges ? edges : storeEdges;
|
||||
|
||||
const changedNodes = nodesToUnselect.map((n) => {
|
||||
@@ -242,8 +243,7 @@ const createRFStore = () =>
|
||||
set({ translateExtent });
|
||||
},
|
||||
resetSelectedElements: () => {
|
||||
const { edges, getNodes } = get();
|
||||
const nodes = getNodes();
|
||||
const { edges, nodes } = get();
|
||||
|
||||
const nodesToUnselect = nodes
|
||||
.filter((e) => e.selected)
|
||||
@@ -260,15 +260,18 @@ const createRFStore = () =>
|
||||
});
|
||||
},
|
||||
setNodeExtent: (nodeExtent) => {
|
||||
const { nodeInternals } = get();
|
||||
|
||||
nodeInternals.forEach((node) => {
|
||||
node.positionAbsolute = clampPosition(node.position, nodeExtent);
|
||||
});
|
||||
const { nodes } = get();
|
||||
|
||||
set({
|
||||
nodeExtent,
|
||||
nodeInternals: new Map(nodeInternals),
|
||||
nodes: nodes.map((node) => {
|
||||
const positionAbsolute = clampPosition(node.position, nodeExtent);
|
||||
|
||||
return {
|
||||
...node,
|
||||
positionAbsolute,
|
||||
};
|
||||
}),
|
||||
});
|
||||
},
|
||||
panBy: (delta): boolean => {
|
||||
|
||||
@@ -7,7 +7,7 @@ const initialState: ReactFlowStore = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
transform: [0, 0, 1],
|
||||
nodeInternals: new Map(),
|
||||
nodes: [],
|
||||
edges: [],
|
||||
onNodesChange: null,
|
||||
onEdgesChange: null,
|
||||
|
||||
@@ -7,25 +7,21 @@ import {
|
||||
type NodeOrigin,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { Edge, EdgeSelectionChange, Node, NodeInternals, NodeSelectionChange, ReactFlowState } from '../types';
|
||||
import type { Edge, EdgeSelectionChange, Node, NodeSelectionChange, ReactFlowState } from '../types';
|
||||
|
||||
type ParentNodes = Record<string, boolean>;
|
||||
|
||||
function calculateXYZPosition(
|
||||
node: Node,
|
||||
nodeInternals: NodeInternals,
|
||||
result: XYZPosition,
|
||||
nodeOrigin: NodeOrigin
|
||||
): XYZPosition {
|
||||
function calculateXYZPosition(node: Node, nodes: Node[], result: XYZPosition, nodeOrigin: NodeOrigin): XYZPosition {
|
||||
if (!node.parentNode) {
|
||||
return result;
|
||||
}
|
||||
const parentNode = nodeInternals.get(node.parentNode)!;
|
||||
|
||||
const parentNode = nodes.find((n) => n.id === node.parentNode)!;
|
||||
const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin);
|
||||
|
||||
return calculateXYZPosition(
|
||||
parentNode,
|
||||
nodeInternals,
|
||||
nodes,
|
||||
{
|
||||
x: (result.x ?? 0) + parentNodePosition.x,
|
||||
y: (result.y ?? 0) + parentNodePosition.y,
|
||||
@@ -35,21 +31,17 @@ function calculateXYZPosition(
|
||||
);
|
||||
}
|
||||
|
||||
export function updateAbsoluteNodePositions(
|
||||
nodeInternals: NodeInternals,
|
||||
nodeOrigin: NodeOrigin,
|
||||
parentNodes?: ParentNodes
|
||||
) {
|
||||
nodeInternals.forEach((node) => {
|
||||
if (node.parentNode && !nodeInternals.has(node.parentNode)) {
|
||||
export function updateAbsoluteNodePositions(nodes: Node[], nodeOrigin: NodeOrigin, parentNodes?: ParentNodes) {
|
||||
nodes.forEach((node) => {
|
||||
if (node.parentNode && !nodes.find((n) => n.id === node.parentNode)) {
|
||||
throw new Error(`Parent node ${node.parentNode} not found`);
|
||||
}
|
||||
|
||||
if (node.parentNode || parentNodes?.[node.id]) {
|
||||
const parentNode = node.parentNode ? nodeInternals.get(node.parentNode) : null;
|
||||
const parentNode = node.parentNode ? nodes.find((n) => n.id === node.parentNode) : null;
|
||||
const { x, y, z } = calculateXYZPosition(
|
||||
node,
|
||||
nodeInternals,
|
||||
nodes,
|
||||
{
|
||||
...node.position,
|
||||
z: node[internalsSymbol]?.z ?? 0,
|
||||
@@ -73,19 +65,19 @@ export function updateAbsoluteNodePositions(
|
||||
|
||||
export function createNodeInternals(
|
||||
nodes: Node[],
|
||||
nodeInternals: NodeInternals,
|
||||
storeNodes: Node[],
|
||||
nodeOrigin: NodeOrigin,
|
||||
elevateNodesOnSelect: boolean
|
||||
): NodeInternals {
|
||||
const nextNodeInternals = new Map<string, Node>();
|
||||
): Node[] {
|
||||
const nextNodes: Node[] = [];
|
||||
const parentNodes: ParentNodes = {};
|
||||
const selectedNodeZ: number = elevateNodesOnSelect ? 1000 : 0;
|
||||
|
||||
nodes.forEach((node) => {
|
||||
const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0);
|
||||
const currInternals = nodeInternals.get(node.id);
|
||||
const currInternals = storeNodes.find((n) => n.id === node.id);
|
||||
|
||||
const internals: Node = {
|
||||
const updatedNode: Node = {
|
||||
width: currInternals?.width,
|
||||
height: currInternals?.height,
|
||||
...node,
|
||||
@@ -96,11 +88,11 @@ export function createNodeInternals(
|
||||
};
|
||||
|
||||
if (node.parentNode) {
|
||||
internals.parentNode = node.parentNode;
|
||||
updatedNode.parentNode = node.parentNode;
|
||||
parentNodes[node.parentNode] = true;
|
||||
}
|
||||
|
||||
Object.defineProperty(internals, internalsSymbol, {
|
||||
Object.defineProperty(updatedNode, internalsSymbol, {
|
||||
enumerable: false,
|
||||
value: {
|
||||
handleBounds: currInternals?.[internalsSymbol]?.handleBounds,
|
||||
@@ -108,36 +100,26 @@ export function createNodeInternals(
|
||||
},
|
||||
});
|
||||
|
||||
nextNodeInternals.set(node.id, internals);
|
||||
nextNodes.push(updatedNode);
|
||||
});
|
||||
|
||||
updateAbsoluteNodePositions(nextNodeInternals, nodeOrigin, parentNodes);
|
||||
updateAbsoluteNodePositions(nodes, nodeOrigin, parentNodes);
|
||||
|
||||
return nextNodeInternals;
|
||||
return nextNodes;
|
||||
}
|
||||
|
||||
export function handleControlledNodeSelectionChange(nodeChanges: NodeSelectionChange[], nodeInternals: NodeInternals) {
|
||||
nodeChanges.forEach((change) => {
|
||||
const node = nodeInternals.get(change.id);
|
||||
if (node) {
|
||||
nodeInternals.set(node.id, {
|
||||
...node,
|
||||
[internalsSymbol]: node[internalsSymbol],
|
||||
selected: change.selected,
|
||||
});
|
||||
}
|
||||
});
|
||||
export function handleControlledSelectionChange<NodeOrEdge extends Node | Edge>(
|
||||
changes: NodeSelectionChange[] | EdgeSelectionChange[],
|
||||
items: NodeOrEdge[]
|
||||
): NodeOrEdge[] {
|
||||
return items.map((item) => {
|
||||
const change = changes.find((change) => change.id === item.id);
|
||||
|
||||
return new Map(nodeInternals);
|
||||
}
|
||||
|
||||
export function handleControlledEdgeSelectionChange(edgeChanges: EdgeSelectionChange[], edges: Edge[]) {
|
||||
return edges.map((e) => {
|
||||
const change = edgeChanges.find((change) => change.id === e.id);
|
||||
if (change) {
|
||||
e.selected = change.selected;
|
||||
item.selected = change.selected;
|
||||
}
|
||||
return e;
|
||||
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,11 +131,11 @@ type UpdateNodesAndEdgesParams = {
|
||||
};
|
||||
|
||||
export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }: UpdateNodesAndEdgesParams) {
|
||||
const { nodeInternals, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
|
||||
const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
|
||||
|
||||
if (changedNodes?.length) {
|
||||
if (hasDefaultNodes) {
|
||||
set({ nodeInternals: handleControlledNodeSelectionChange(changedNodes, nodeInternals) });
|
||||
set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
|
||||
}
|
||||
|
||||
onNodesChange?.(changedNodes);
|
||||
@@ -161,7 +143,7 @@ export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get,
|
||||
|
||||
if (changedEdges?.length) {
|
||||
if (hasDefaultEdges) {
|
||||
set({ edges: handleControlledEdgeSelectionChange(changedEdges, edges) });
|
||||
set({ edges: handleControlledSelectionChange(changedEdges, edges) });
|
||||
}
|
||||
|
||||
onEdgesChange?.(changedEdges);
|
||||
|
||||
Reference in New Issue
Block a user