refactor(react): use array for nodeInternals, rename to nodes

This commit is contained in:
moklick
2023-06-12 16:56:02 +02:00
parent cefdfd4e70
commit c6501aff62
33 changed files with 146 additions and 197 deletions
+46 -43
View File
@@ -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 => {
+1 -1
View File
@@ -7,7 +7,7 @@ const initialState: ReactFlowStore = {
width: 0,
height: 0,
transform: [0, 0, 1],
nodeInternals: new Map(),
nodes: [],
edges: [],
onNodesChange: null,
onEdgesChange: null,
+32 -50
View File
@@ -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);