refactor(react): use array for nodeInternals, rename to nodes
This commit is contained in:
@@ -4,8 +4,8 @@ import { useStore, getStraightPath, EdgeProps } from '@xyflow/react';
|
|||||||
import { getEdgeParams } from './utils.js';
|
import { getEdgeParams } from './utils.js';
|
||||||
|
|
||||||
function FloatingEdge({ id, source, target, markerEnd, style }: EdgeProps) {
|
function FloatingEdge({ id, source, target, markerEnd, style }: EdgeProps) {
|
||||||
const sourceNode = useStore(useCallback((store) => store.nodeInternals.get(source), [source]));
|
const sourceNode = useStore(useCallback((store) => store.nodes.find((n) => n.id === source), [source]));
|
||||||
const targetNode = useStore(useCallback((store) => store.nodeInternals.get(target), [target]));
|
const targetNode = useStore(useCallback((store) => store.nodes.find((n) => n.id === target), [target]));
|
||||||
|
|
||||||
if (!sourceNode || !targetNode) {
|
if (!sourceNode || !targetNode) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const CustomEdge: FC<EdgeProps> = ({
|
|||||||
data,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
const isConnectedNodeDragging = useStore((s) =>
|
const isConnectedNodeDragging = useStore((s) =>
|
||||||
s.getNodes().find((n) => n.dragging && (target === n.id || source === n.id))
|
s.nodes.find((n) => n.dragging && (target === n.id || source === n.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
const [edgePath, labelX, labelY] = getBezierPath({
|
const [edgePath, labelX, labelY] = getBezierPath({
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const CustomEdge: FC<EdgeProps> = ({
|
|||||||
data,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
const isConnectedNodeDragging = useStore((s) =>
|
const isConnectedNodeDragging = useStore((s) =>
|
||||||
s.getNodes().find((n) => n.dragging && (target === n.id || source === n.id))
|
s.nodes.find((n) => n.dragging && (target === n.id || source === n.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
const [edgePath, labelX, labelY] = getBezierPath({
|
const [edgePath, labelX, labelY] = getBezierPath({
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { FC, useMemo, CSSProperties } from 'react';
|
import { FC, CSSProperties } from 'react';
|
||||||
import { EdgeProps, useStore, getBezierPath, ReactFlowState } from '@xyflow/react';
|
import { EdgeProps, useStore, getBezierPath } from '@xyflow/react';
|
||||||
|
|
||||||
import { getEdgeParams } from './utils';
|
import { getEdgeParams } from './utils';
|
||||||
|
|
||||||
const nodeSelector = (s: ReactFlowState) => s.nodeInternals;
|
|
||||||
|
|
||||||
const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
|
const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
|
||||||
const nodeInternals = useStore(nodeSelector);
|
const { sourceNode, targetNode } = useStore((s) => {
|
||||||
|
const sourceNode = s.nodes.find((n) => n.id === source);
|
||||||
|
const targetNode = s.nodes.find((n) => n.id === target);
|
||||||
|
|
||||||
const sourceNode = useMemo(() => nodeInternals.get(source), [source, nodeInternals]);
|
return { sourceNode, targetNode };
|
||||||
const targetNode = useMemo(() => nodeInternals.get(target), [target, nodeInternals]);
|
});
|
||||||
|
|
||||||
if (!sourceNode || !targetNode) {
|
if (!sourceNode || !targetNode) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { NodeToolbar, ReactFlowState, useStore } from '@xyflow/react';
|
import { NodeToolbar, ReactFlowState, useStore } from '@xyflow/react';
|
||||||
|
|
||||||
const selectedNodesSelector = (state: ReactFlowState) =>
|
const selectedNodesSelector = (state: ReactFlowState) =>
|
||||||
state
|
state.nodes.filter((node) => node.selected).map((node) => node.id);
|
||||||
.getNodes()
|
|
||||||
.filter((node) => node.selected)
|
|
||||||
.map((node) => node.id);
|
|
||||||
|
|
||||||
export default function SelectedNodesToolbar() {
|
export default function SelectedNodesToolbar() {
|
||||||
const selectedNodeIds = useStore(selectedNodesSelector);
|
const selectedNodeIds = useStore(selectedNodesSelector);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import styles from './provider.module.css';
|
|||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
const { setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const nodeInfos = useStore((store) =>
|
const nodeInfos = useStore((store) =>
|
||||||
store.getNodes().map((n) => `Node ${n.id} - x: ${n.position.x.toFixed(2)}, y: ${n.position.y.toFixed(2)}`)
|
store.nodes.map((n) => `Node ${n.id} - x: ${n.position.x.toFixed(2)}, y: ${n.position.y.toFixed(2)}`)
|
||||||
);
|
);
|
||||||
const transform = useStore((store) => store.transform);
|
const transform = useStore((store) => store.transform);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const defaultWidth = 200;
|
|||||||
const defaultHeight = 150;
|
const defaultHeight = 150;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => {
|
const selector = (s: ReactFlowState) => {
|
||||||
const nodes = s.getNodes();
|
|
||||||
const viewBB: Rect = {
|
const viewBB: Rect = {
|
||||||
x: -s.transform[0] / s.transform[2],
|
x: -s.transform[0] / s.transform[2],
|
||||||
y: -s.transform[1] / s.transform[2],
|
y: -s.transform[1] / s.transform[2],
|
||||||
@@ -26,7 +25,7 @@ const selector = (s: ReactFlowState) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
viewBB,
|
viewBB,
|
||||||
boundingRect: nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes, s.nodeOrigin), viewBB) : viewBB,
|
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(s.nodes, s.nodeOrigin), viewBB) : viewBB,
|
||||||
rfId: s.rfId,
|
rfId: s.rfId,
|
||||||
nodeOrigin: s.nodeOrigin,
|
nodeOrigin: s.nodeOrigin,
|
||||||
panZoom: s.panZoom,
|
panZoom: s.panZoom,
|
||||||
@@ -118,7 +117,7 @@ function MiniMap({
|
|||||||
|
|
||||||
const onSvgNodeClick = onNodeClick
|
const onSvgNodeClick = onNodeClick
|
||||||
? useCallback((event: MouseEvent, nodeId: string) => {
|
? useCallback((event: MouseEvent, nodeId: string) => {
|
||||||
const node = store.getState().nodeInternals.get(nodeId)!;
|
const node = store.getState().nodes.find((n) => n.id === nodeId)!;
|
||||||
onNodeClick(event, node);
|
onNodeClick(event, node);
|
||||||
}, [])
|
}, [])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import type { MiniMapNodes, GetMiniMapNodeAttribute } from './types';
|
|||||||
declare const window: any;
|
declare const window: any;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
||||||
const selectorNodes = (s: ReactFlowState) => s.getNodes().filter((node) => !node.hidden && node.width && node.height);
|
const selectorNodes = (s: ReactFlowState) => s.nodes.filter((node) => !node.hidden && node.width && node.height);
|
||||||
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
||||||
|
|
||||||
function MiniMapNodes({
|
function MiniMapNodes({
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ function ResizeControl({
|
|||||||
|
|
||||||
const dragHandler = drag<HTMLDivElement, unknown>()
|
const dragHandler = drag<HTMLDivElement, unknown>()
|
||||||
.on('start', (event: ResizeDragEvent) => {
|
.on('start', (event: ResizeDragEvent) => {
|
||||||
const { nodeInternals, transform, snapGrid, snapToGrid } = store.getState();
|
const { nodes, transform, snapGrid, snapToGrid } = store.getState();
|
||||||
const node = nodeInternals.get(id);
|
const node = nodes.find((n) => n.id === id);
|
||||||
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
||||||
|
|
||||||
prevValues.current = {
|
prevValues.current = {
|
||||||
@@ -86,9 +86,9 @@ function ResizeControl({
|
|||||||
onResizeStart?.(event, { ...prevValues.current });
|
onResizeStart?.(event, { ...prevValues.current });
|
||||||
})
|
})
|
||||||
.on('drag', (event: ResizeDragEvent) => {
|
.on('drag', (event: ResizeDragEvent) => {
|
||||||
const { nodeInternals, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState();
|
const { nodes, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState();
|
||||||
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
||||||
const node = nodeInternals.get(id);
|
const node = nodes.find((n) => n.id === id);
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
const changes: NodeChange[] = [];
|
const changes: NodeChange[] = [];
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const nodesEqualityFn = (a: Node[], b: Node[]) => {
|
|||||||
const storeSelector = (state: ReactFlowState) => ({
|
const storeSelector = (state: ReactFlowState) => ({
|
||||||
transform: state.transform,
|
transform: state.transform,
|
||||||
nodeOrigin: state.nodeOrigin,
|
nodeOrigin: state.nodeOrigin,
|
||||||
selectedNodesCount: state.getNodes().filter((node) => node.selected).length,
|
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
|
||||||
});
|
});
|
||||||
|
|
||||||
function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number, align: Align): string {
|
function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number, align: Align): string {
|
||||||
@@ -87,7 +87,7 @@ function NodeToolbar({
|
|||||||
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
|
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
|
||||||
|
|
||||||
return nodeIds.reduce<Node[]>((acc, id) => {
|
return nodeIds.reduce<Node[]>((acc, id) => {
|
||||||
const node = state.nodeInternals.get(id);
|
const node = state.nodes.find((n) => n.id === id);
|
||||||
if (node) {
|
if (node) {
|
||||||
acc.push(node);
|
acc.push(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const ConnectionLine = ({
|
|||||||
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
|
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowStore) => ({
|
(s: ReactFlowStore) => ({
|
||||||
fromNode: s.nodeInternals.get(nodeId),
|
fromNode: s.nodes.find((n) => n.id === nodeId),
|
||||||
handleId: s.connectionStartHandle?.handleId,
|
handleId: s.connectionStartHandle?.handleId,
|
||||||
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
||||||
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
const [updating, setUpdating] = useState<boolean>(false);
|
const [updating, setUpdating] = useState<boolean>(false);
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const edgePosition = useStore((state) => {
|
const edgePosition = useStore((state) => {
|
||||||
const sourceNode = state.nodeInternals.get(source);
|
const sourceNode = state.nodes.find((n) => n.id === source);
|
||||||
const targetNode = state.nodeInternals.get(target);
|
const targetNode = state.nodes.find((n) => n.id === target);
|
||||||
|
|
||||||
if (!sourceNode || !targetNode) {
|
if (!sourceNode || !targetNode) {
|
||||||
return null;
|
return null;
|
||||||
@@ -118,7 +118,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
onConnectStart,
|
onConnectStart,
|
||||||
onConnectEnd,
|
onConnectEnd,
|
||||||
cancelConnection,
|
cancelConnection,
|
||||||
getNodes,
|
nodes,
|
||||||
panBy,
|
panBy,
|
||||||
updateConnection,
|
updateConnection,
|
||||||
} = store.getState();
|
} = store.getState();
|
||||||
@@ -128,7 +128,6 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
|
|
||||||
const isTarget = isSourceHandle;
|
const isTarget = isSourceHandle;
|
||||||
const edge = edges.find((e) => e.id === id)!;
|
const edge = edges.find((e) => e.id === id)!;
|
||||||
const nodes = getNodes();
|
|
||||||
|
|
||||||
setUpdating(true);
|
setUpdating(true);
|
||||||
onEdgeUpdateStart?.(event, edge, handleType);
|
onEdgeUpdateStart?.(event, edge, handleType);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
connectionMode: currentStore.connectionMode,
|
connectionMode: currentStore.connectionMode,
|
||||||
connectionRadius: currentStore.connectionRadius,
|
connectionRadius: currentStore.connectionRadius,
|
||||||
domNode: currentStore.domNode,
|
domNode: currentStore.domNode,
|
||||||
nodes: currentStore.getNodes(),
|
nodes: currentStore.nodes,
|
||||||
lib: currentStore.lib,
|
lib: currentStore.lib,
|
||||||
isTarget,
|
isTarget,
|
||||||
handleId,
|
handleId,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function getMouseHandler(
|
|||||||
return handler === undefined
|
return handler === undefined
|
||||||
? handler
|
? handler
|
||||||
: (event: MouseEvent) => {
|
: (event: MouseEvent) => {
|
||||||
const node = getState().nodeInternals.get(id)!;
|
const node = getState().nodes.find((n) => n.id === id)!;
|
||||||
handler(event, { ...node });
|
handler(event, { ...node });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -34,8 +34,8 @@ export function handleNodeClick({
|
|||||||
unselect?: boolean;
|
unselect?: boolean;
|
||||||
nodeRef?: RefObject<HTMLDivElement>;
|
nodeRef?: RefObject<HTMLDivElement>;
|
||||||
}) {
|
}) {
|
||||||
const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodeInternals } = store.getState();
|
const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodes } = store.getState();
|
||||||
const node = nodeInternals.get(id)!;
|
const node = nodes.find((n) => n.id === id)!;
|
||||||
|
|
||||||
store.setState({ nodesSelectionActive: false });
|
store.setState({ nodesSelectionActive: false });
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
const node = store.getState().nodeInternals.get(id)!;
|
const node = store.getState().nodes.find((n) => n.id === id)!;
|
||||||
onClick(event, { ...node });
|
onClick(event, { ...node });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export type NodesSelectionProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => {
|
const selector = (s: ReactFlowState) => {
|
||||||
const selectedNodes = s.getNodes().filter((n) => n.selected);
|
const selectedNodes = s.nodes.filter((n) => n.selected);
|
||||||
const { width, height, x, y } = getRectOfNodes(selectedNodes, s.nodeOrigin);
|
const { width, height, x, y } = getRectOfNodes(selectedNodes, s.nodeOrigin);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -57,10 +57,7 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
|
|||||||
|
|
||||||
const onContextMenu = onSelectionContextMenu
|
const onContextMenu = onSelectionContextMenu
|
||||||
? (event: MouseEvent) => {
|
? (event: MouseEvent) => {
|
||||||
const selectedNodes = store
|
const selectedNodes = store.getState().nodes.filter((n) => n.selected);
|
||||||
.getState()
|
|
||||||
.getNodes()
|
|
||||||
.filter((n) => n.selected);
|
|
||||||
onSelectionContextMenu(event, selectedNodes);
|
onSelectionContextMenu(event, selectedNodes);
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ type SelectionListenerProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
selectedNodes: s.getNodes().filter((n) => n.selected),
|
selectedNodes: s.nodes.filter((n) => n.selected),
|
||||||
selectedEdges: s.edges.filter((e) => e.selected),
|
selectedEdges: s.edges.filter((e) => e.selected),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -129,8 +129,7 @@ const Pane = memo(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = (event: ReactMouseEvent): void => {
|
const onMouseMove = (event: ReactMouseEvent): void => {
|
||||||
const { userSelectionRect, edges, transform, onNodesChange, onEdgesChange, nodeOrigin, getNodes } =
|
const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
|
||||||
store.getState();
|
|
||||||
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,7 +148,6 @@ const Pane = memo(
|
|||||||
height: Math.abs(mousePos.y - startY),
|
height: Math.abs(mousePos.y - startY),
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodes = getNodes();
|
|
||||||
const selectedNodes = getNodesInside<Node>(
|
const selectedNodes = getNodesInside<Node>(
|
||||||
nodes,
|
nodes,
|
||||||
nextUserSelectRect,
|
nextUserSelectRect,
|
||||||
|
|||||||
@@ -22,14 +22,7 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
|
|||||||
if (nodeRef?.current) {
|
if (nodeRef?.current) {
|
||||||
xyDrag.current = XYDrag({
|
xyDrag.current = XYDrag({
|
||||||
domNode: nodeRef.current,
|
domNode: nodeRef.current,
|
||||||
getStoreItems: () => {
|
getStoreItems: () => store.getState(),
|
||||||
const currentStore = store.getState();
|
|
||||||
|
|
||||||
return {
|
|
||||||
nodes: currentStore.getNodes(),
|
|
||||||
...store.getState(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onNodeClick: () => {
|
onNodeClick: () => {
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
handleNodeClick({
|
handleNodeClick({
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import type { KeyCode } from '@xyflow/system';
|
|||||||
import { useStoreApi } from '../hooks/useStore';
|
import { useStoreApi } from '../hooks/useStore';
|
||||||
import useKeyPress from './useKeyPress';
|
import useKeyPress from './useKeyPress';
|
||||||
import useReactFlow from './useReactFlow';
|
import useReactFlow from './useReactFlow';
|
||||||
|
import { Edge, Node } from '../types';
|
||||||
|
|
||||||
|
const getSelected = (item: Node | Edge) => item.selected;
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
@@ -20,10 +23,8 @@ export default ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deleteKeyPressed) {
|
if (deleteKeyPressed) {
|
||||||
const { edges, getNodes } = store.getState();
|
const { edges, nodes } = store.getState();
|
||||||
const selectedNodes = getNodes().filter((node) => node.selected);
|
deleteElements({ nodes: nodes.filter(getSelected), edges: edges.filter(getSelected) });
|
||||||
const selectedEdges = edges.filter((edge) => edge.selected);
|
|
||||||
deleteElements({ nodes: selectedNodes, edges: selectedEdges });
|
|
||||||
store.setState({ nodesSelectionActive: false });
|
store.setState({ nodesSelectionActive: false });
|
||||||
}
|
}
|
||||||
}, [deleteKeyPressed]);
|
}, [deleteKeyPressed]);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { shallow } from 'zustand/shallow';
|
|||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import type { Node, ReactFlowState } from '../types';
|
import type { Node, ReactFlowState } from '../types';
|
||||||
|
|
||||||
const nodesSelector = (state: ReactFlowState) => state.getNodes();
|
const nodesSelector = (state: ReactFlowState) => state.nodes;
|
||||||
|
|
||||||
function useNodes<NodeData>(): Node<NodeData>[] {
|
function useNodes<NodeData>(): Node<NodeData>[] {
|
||||||
const nodes = useStore(nodesSelector, shallow);
|
const nodes = useStore(nodesSelector, shallow);
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ export type UseNodesInitializedOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => {
|
const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => {
|
||||||
if (s.nodeInternals.size === 0) {
|
if (s.nodes.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s.nodes
|
||||||
.getNodes()
|
|
||||||
.filter((n) => (options.includeHiddenNodes ? true : !n.hidden))
|
.filter((n) => (options.includeHiddenNodes ? true : !n.hidden))
|
||||||
.every((n) => n[internalsSymbol]?.handleBounds !== undefined);
|
.every((n) => n[internalsSymbol]?.handleBounds !== undefined);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,14 +23,11 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
||||||
return store
|
return store.getState().nodes.map((n) => ({ ...n }));
|
||||||
.getState()
|
|
||||||
.getNodes()
|
|
||||||
.map((n) => ({ ...n }));
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getNode = useCallback<Instance.GetNode<NodeData>>((id) => {
|
const getNode = useCallback<Instance.GetNode<NodeData>>((id) => {
|
||||||
return store.getState().nodeInternals.get(id);
|
return store.getState().nodes.find((n) => n.id === id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getEdges = useCallback<Instance.GetEdges<EdgeData>>(() => {
|
const getEdges = useCallback<Instance.GetEdges<EdgeData>>(() => {
|
||||||
@@ -44,8 +41,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
|
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
|
||||||
const { getNodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
const { nodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
||||||
const nodes = getNodes();
|
|
||||||
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
@@ -76,10 +72,9 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
|
|
||||||
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
|
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
|
||||||
const nodes = Array.isArray(payload) ? payload : [payload];
|
const nodes = Array.isArray(payload) ? payload : [payload];
|
||||||
const { getNodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
const currentNodes = getNodes();
|
|
||||||
const nextNodes = [...currentNodes, ...nodes];
|
const nextNodes = [...currentNodes, ...nodes];
|
||||||
setNodes(nextNodes);
|
setNodes(nextNodes);
|
||||||
} else if (onNodesChange) {
|
} else if (onNodesChange) {
|
||||||
@@ -101,10 +96,10 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
||||||
const { getNodes, edges = [], transform } = store.getState();
|
const { nodes = [], edges = [], transform } = store.getState();
|
||||||
const [x, y, zoom] = transform;
|
const [x, y, zoom] = transform;
|
||||||
return {
|
return {
|
||||||
nodes: getNodes().map((n) => ({ ...n })),
|
nodes: nodes.map((n) => ({ ...n })),
|
||||||
edges: edges.map((e) => ({ ...e })),
|
edges: edges.map((e) => ({ ...e })),
|
||||||
viewport: {
|
viewport: {
|
||||||
x,
|
x,
|
||||||
@@ -116,8 +111,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
|
|
||||||
const deleteElements = useCallback<Instance.DeleteElements>(({ nodes: nodesDeleted, edges: edgesDeleted }) => {
|
const deleteElements = useCallback<Instance.DeleteElements>(({ nodes: nodesDeleted, edges: edgesDeleted }) => {
|
||||||
const {
|
const {
|
||||||
nodeInternals,
|
nodes,
|
||||||
getNodes,
|
|
||||||
edges,
|
edges,
|
||||||
hasDefaultNodes,
|
hasDefaultNodes,
|
||||||
hasDefaultEdges,
|
hasDefaultEdges,
|
||||||
@@ -129,7 +123,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
const { matchingNodes, matchingEdges } = getElementsToRemove<Node, Edge>({
|
const { matchingNodes, matchingEdges } = getElementsToRemove<Node, Edge>({
|
||||||
nodesToRemove: nodesDeleted || [],
|
nodesToRemove: nodesDeleted || [],
|
||||||
edgesToRemove: edgesDeleted || [],
|
edgesToRemove: edgesDeleted || [],
|
||||||
nodes: getNodes(),
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,12 +136,8 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
matchingNodes.forEach((node) => {
|
|
||||||
nodeInternals.delete(node.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
store.setState({
|
store.setState({
|
||||||
nodeInternals: new Map(nodeInternals),
|
nodes: nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +171,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
nodeOrRect: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect
|
nodeOrRect: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect
|
||||||
): [Rect | null, Node<NodeData> | null | undefined, boolean] => {
|
): [Rect | null, Node<NodeData> | null | undefined, boolean] => {
|
||||||
const isRect = isRectObject(nodeOrRect);
|
const isRect = isRectObject(nodeOrRect);
|
||||||
const node = isRect ? null : store.getState().nodeInternals.get(nodeOrRect.id);
|
const node = isRect ? null : store.getState().nodes.find((n) => n.id === nodeOrRect.id);
|
||||||
|
|
||||||
if (!isRect && !node) {
|
if (!isRect && !node) {
|
||||||
[null, null, isRect];
|
[null, null, isRect];
|
||||||
@@ -202,7 +192,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (nodes || store.getState().getNodes()).filter((n) => {
|
return (nodes || store.getState().nodes).filter((n) => {
|
||||||
if (!isRect && (n.id === node!.id || !n.positionAbsolute)) {
|
if (!isRect && (n.id === node!.id || !n.positionAbsolute)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ function useUpdateNodePositions() {
|
|||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
|
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
|
||||||
const { nodeExtent, updateNodePositions, getNodes, snapToGrid, snapGrid, onError, nodesDraggable } =
|
const { nodeExtent, updateNodePositions, nodes, snapToGrid, snapGrid, onError, nodesDraggable } = store.getState();
|
||||||
store.getState();
|
|
||||||
const nodes = getNodes();
|
|
||||||
const selectedNodes = nodes.filter(
|
const selectedNodes = nodes.filter(
|
||||||
(n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'))
|
(n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
return { x, y, zoom };
|
return { x, y, zoom };
|
||||||
},
|
},
|
||||||
fitView: (options) => {
|
fitView: (options) => {
|
||||||
const { getNodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
||||||
|
|
||||||
return panZoom
|
return panZoom
|
||||||
? fitView(
|
? fitView(
|
||||||
{
|
{
|
||||||
nodes: getNodes(),
|
nodes,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
|
|||||||
const visibleEdges =
|
const visibleEdges =
|
||||||
onlyRenderVisible && s.width && s.height
|
onlyRenderVisible && s.width && s.height
|
||||||
? s.edges.filter((e) => {
|
? s.edges.filter((e) => {
|
||||||
const sourceNode = s.nodeInternals.get(e.source);
|
const sourceNode = s.nodes.find((n) => n.id === e.source);
|
||||||
const targetNode = s.nodeInternals.get(e.target);
|
const targetNode = s.nodes.find((n) => n.id === e.target);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
sourceNode &&
|
sourceNode &&
|
||||||
@@ -29,7 +29,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
|
|||||||
})
|
})
|
||||||
: s.edges;
|
: s.edges;
|
||||||
|
|
||||||
return groupEdgesByZLevel(visibleEdges, s.nodeInternals, elevateEdgesOnSelect);
|
return groupEdgesByZLevel(visibleEdges, s.nodes, elevateEdgesOnSelect);
|
||||||
},
|
},
|
||||||
[onlyRenderVisible, elevateEdgesOnSelect]
|
[onlyRenderVisible, elevateEdgesOnSelect]
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ function useVisibleNodes(onlyRenderVisible: boolean) {
|
|||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowState) =>
|
(s: ReactFlowState) =>
|
||||||
onlyRenderVisible
|
onlyRenderVisible
|
||||||
? getNodesInside<Node>(s.getNodes(), { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
? getNodesInside<Node>(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
||||||
: s.getNodes(),
|
: s.nodes,
|
||||||
[onlyRenderVisible]
|
[onlyRenderVisible]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ const createRFStore = () =>
|
|||||||
createStore<ReactFlowState>((set, get) => ({
|
createStore<ReactFlowState>((set, get) => ({
|
||||||
...initialState,
|
...initialState,
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeInternals, nodeOrigin, elevateNodesOnSelect } = get();
|
const { nodes: storeNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
||||||
set({ nodeInternals: createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect) });
|
set({ nodes: createNodeInternals(nodes, storeNodes, nodeOrigin, elevateNodesOnSelect) });
|
||||||
},
|
},
|
||||||
getNodes: () => {
|
getNodes: () => {
|
||||||
return Array.from(get().nodeInternals.values());
|
return get().nodes;
|
||||||
},
|
},
|
||||||
setEdges: (edges: Edge[]) => {
|
setEdges: (edges: Edge[]) => {
|
||||||
const { defaultEdgeOptions = {} } = get();
|
const { defaultEdgeOptions = {} } = get();
|
||||||
@@ -40,17 +40,17 @@ const createRFStore = () =>
|
|||||||
const hasDefaultNodes = typeof nodes !== 'undefined';
|
const hasDefaultNodes = typeof nodes !== 'undefined';
|
||||||
const hasDefaultEdges = typeof edges !== 'undefined';
|
const hasDefaultEdges = typeof edges !== 'undefined';
|
||||||
|
|
||||||
const nodeInternals = hasDefaultNodes
|
const nextNodes = hasDefaultNodes
|
||||||
? createNodeInternals(nodes, new Map(), get().nodeOrigin, get().elevateNodesOnSelect)
|
? createNodeInternals(nodes, [], get().nodeOrigin, get().elevateNodesOnSelect)
|
||||||
: new Map();
|
: [];
|
||||||
const nextEdges = hasDefaultEdges ? edges : [];
|
const nextEdges = hasDefaultEdges ? edges : [];
|
||||||
|
|
||||||
set({ nodeInternals, edges: nextEdges, hasDefaultNodes, hasDefaultEdges });
|
set({ nodes: nextNodes, edges: nextEdges, hasDefaultNodes, hasDefaultEdges });
|
||||||
},
|
},
|
||||||
updateNodeDimensions: (updates) => {
|
updateNodeDimensions: (updates) => {
|
||||||
const {
|
const {
|
||||||
onNodesChange,
|
onNodesChange,
|
||||||
nodeInternals,
|
nodes,
|
||||||
fitViewOnInit,
|
fitViewOnInit,
|
||||||
fitViewOnInitDone,
|
fitViewOnInitDone,
|
||||||
fitViewOnInitOptions,
|
fitViewOnInitOptions,
|
||||||
@@ -70,11 +70,12 @@ const createRFStore = () =>
|
|||||||
|
|
||||||
const style = window.getComputedStyle(viewportNode);
|
const style = window.getComputedStyle(viewportNode);
|
||||||
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
||||||
|
const changes: NodeDimensionChange[] = [];
|
||||||
|
|
||||||
const changes: NodeDimensionChange[] = updates.reduce<NodeDimensionChange[]>((res, update) => {
|
const nextNodes = nodes.map((node) => {
|
||||||
const node = nodeInternals.get(update.id);
|
const update = updates.find((change) => change.id === node.id);
|
||||||
|
|
||||||
if (node) {
|
if (update) {
|
||||||
const dimensions = getDimensions(update.nodeElement);
|
const dimensions = getDimensions(update.nodeElement);
|
||||||
const doUpdate = !!(
|
const doUpdate = !!(
|
||||||
dimensions.width &&
|
dimensions.width &&
|
||||||
@@ -83,8 +84,15 @@ const createRFStore = () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (doUpdate) {
|
if (doUpdate) {
|
||||||
nodeInternals.set(node.id, {
|
changes.push({
|
||||||
|
id: node.id,
|
||||||
|
type: 'dimensions',
|
||||||
|
dimensions,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
...node,
|
...node,
|
||||||
|
...dimensions,
|
||||||
[internalsSymbol]: {
|
[internalsSymbol]: {
|
||||||
...node[internalsSymbol],
|
...node[internalsSymbol],
|
||||||
handleBounds: {
|
handleBounds: {
|
||||||
@@ -92,21 +100,14 @@ const createRFStore = () =>
|
|||||||
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin),
|
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 =
|
const nextFitViewOnInitDone =
|
||||||
fitViewOnInitDone ||
|
fitViewOnInitDone ||
|
||||||
@@ -115,7 +116,7 @@ const createRFStore = () =>
|
|||||||
!!panZoom &&
|
!!panZoom &&
|
||||||
fitView(
|
fitView(
|
||||||
{
|
{
|
||||||
nodes: Array.from(nodeInternals.values()),
|
nodes: nextNodes,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
panZoom,
|
panZoom,
|
||||||
@@ -125,7 +126,7 @@ const createRFStore = () =>
|
|||||||
},
|
},
|
||||||
fitViewOnInitOptions
|
fitViewOnInitOptions
|
||||||
));
|
));
|
||||||
set({ nodeInternals: new Map(nodeInternals), fitViewOnInitDone: nextFitViewOnInitDone });
|
set({ nodes: nextNodes, fitViewOnInitDone: nextFitViewOnInitDone });
|
||||||
|
|
||||||
if (changes?.length > 0) {
|
if (changes?.length > 0) {
|
||||||
onNodesChange?.(changes);
|
onNodesChange?.(changes);
|
||||||
@@ -153,13 +154,13 @@ const createRFStore = () =>
|
|||||||
},
|
},
|
||||||
|
|
||||||
triggerNodeChanges: (changes) => {
|
triggerNodeChanges: (changes) => {
|
||||||
const { onNodesChange, nodeInternals, hasDefaultNodes, nodeOrigin, getNodes, elevateNodesOnSelect } = get();
|
const { onNodesChange, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
||||||
|
|
||||||
if (changes?.length) {
|
if (changes?.length) {
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
const nodes = applyNodeChanges(changes, getNodes());
|
const updatedNodes = applyNodeChanges(changes, nodes);
|
||||||
const nextNodeInternals = createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect);
|
const nextNodes = createNodeInternals(updatedNodes, nodes, nodeOrigin, elevateNodesOnSelect);
|
||||||
set({ nodeInternals: nextNodeInternals });
|
set({ nodes: nextNodes });
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange?.(changes);
|
onNodesChange?.(changes);
|
||||||
@@ -167,14 +168,14 @@ const createRFStore = () =>
|
|||||||
},
|
},
|
||||||
|
|
||||||
addSelectedNodes: (selectedNodeIds) => {
|
addSelectedNodes: (selectedNodeIds) => {
|
||||||
const { multiSelectionActive, edges, getNodes } = get();
|
const { multiSelectionActive, edges, nodes } = get();
|
||||||
let changedNodes: NodeSelectionChange[];
|
let changedNodes: NodeSelectionChange[];
|
||||||
let changedEdges: EdgeSelectionChange[] | null = null;
|
let changedEdges: EdgeSelectionChange[] | null = null;
|
||||||
|
|
||||||
if (multiSelectionActive) {
|
if (multiSelectionActive) {
|
||||||
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[];
|
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[];
|
||||||
} else {
|
} else {
|
||||||
changedNodes = getSelectionChanges(getNodes(), selectedNodeIds);
|
changedNodes = getSelectionChanges(nodes, selectedNodeIds);
|
||||||
changedEdges = getSelectionChanges(edges, []);
|
changedEdges = getSelectionChanges(edges, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ const createRFStore = () =>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
addSelectedEdges: (selectedEdgeIds) => {
|
addSelectedEdges: (selectedEdgeIds) => {
|
||||||
const { multiSelectionActive, edges, getNodes } = get();
|
const { multiSelectionActive, edges, nodes } = get();
|
||||||
let changedEdges: EdgeSelectionChange[];
|
let changedEdges: EdgeSelectionChange[];
|
||||||
let changedNodes: NodeSelectionChange[] | null = null;
|
let changedNodes: NodeSelectionChange[] | null = null;
|
||||||
|
|
||||||
@@ -194,7 +195,7 @@ const createRFStore = () =>
|
|||||||
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[];
|
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[];
|
||||||
} else {
|
} else {
|
||||||
changedEdges = getSelectionChanges(edges, selectedEdgeIds);
|
changedEdges = getSelectionChanges(edges, selectedEdgeIds);
|
||||||
changedNodes = getSelectionChanges(getNodes(), []);
|
changedNodes = getSelectionChanges(nodes, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodesAndEdgesSelections({
|
updateNodesAndEdgesSelections({
|
||||||
@@ -205,8 +206,8 @@ const createRFStore = () =>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
|
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
|
||||||
const { edges: storeEdges, getNodes } = get();
|
const { edges: storeEdges, nodes: storeNodes } = get();
|
||||||
const nodesToUnselect = nodes ? nodes : getNodes();
|
const nodesToUnselect = nodes ? nodes : storeNodes;
|
||||||
const edgesToUnselect = edges ? edges : storeEdges;
|
const edgesToUnselect = edges ? edges : storeEdges;
|
||||||
|
|
||||||
const changedNodes = nodesToUnselect.map((n) => {
|
const changedNodes = nodesToUnselect.map((n) => {
|
||||||
@@ -242,8 +243,7 @@ const createRFStore = () =>
|
|||||||
set({ translateExtent });
|
set({ translateExtent });
|
||||||
},
|
},
|
||||||
resetSelectedElements: () => {
|
resetSelectedElements: () => {
|
||||||
const { edges, getNodes } = get();
|
const { edges, nodes } = get();
|
||||||
const nodes = getNodes();
|
|
||||||
|
|
||||||
const nodesToUnselect = nodes
|
const nodesToUnselect = nodes
|
||||||
.filter((e) => e.selected)
|
.filter((e) => e.selected)
|
||||||
@@ -260,15 +260,18 @@ const createRFStore = () =>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
setNodeExtent: (nodeExtent) => {
|
setNodeExtent: (nodeExtent) => {
|
||||||
const { nodeInternals } = get();
|
const { nodes } = get();
|
||||||
|
|
||||||
nodeInternals.forEach((node) => {
|
|
||||||
node.positionAbsolute = clampPosition(node.position, nodeExtent);
|
|
||||||
});
|
|
||||||
|
|
||||||
set({
|
set({
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
nodeInternals: new Map(nodeInternals),
|
nodes: nodes.map((node) => {
|
||||||
|
const positionAbsolute = clampPosition(node.position, nodeExtent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
positionAbsolute,
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
panBy: (delta): boolean => {
|
panBy: (delta): boolean => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const initialState: ReactFlowStore = {
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
transform: [0, 0, 1],
|
transform: [0, 0, 1],
|
||||||
nodeInternals: new Map(),
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
onNodesChange: null,
|
onNodesChange: null,
|
||||||
onEdgesChange: null,
|
onEdgesChange: null,
|
||||||
|
|||||||
@@ -7,25 +7,21 @@ import {
|
|||||||
type NodeOrigin,
|
type NodeOrigin,
|
||||||
} from '@xyflow/system';
|
} 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>;
|
type ParentNodes = Record<string, boolean>;
|
||||||
|
|
||||||
function calculateXYZPosition(
|
function calculateXYZPosition(node: Node, nodes: Node[], result: XYZPosition, nodeOrigin: NodeOrigin): XYZPosition {
|
||||||
node: Node,
|
|
||||||
nodeInternals: NodeInternals,
|
|
||||||
result: XYZPosition,
|
|
||||||
nodeOrigin: NodeOrigin
|
|
||||||
): XYZPosition {
|
|
||||||
if (!node.parentNode) {
|
if (!node.parentNode) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
const parentNode = nodeInternals.get(node.parentNode)!;
|
|
||||||
|
const parentNode = nodes.find((n) => n.id === node.parentNode)!;
|
||||||
const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin);
|
const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin);
|
||||||
|
|
||||||
return calculateXYZPosition(
|
return calculateXYZPosition(
|
||||||
parentNode,
|
parentNode,
|
||||||
nodeInternals,
|
nodes,
|
||||||
{
|
{
|
||||||
x: (result.x ?? 0) + parentNodePosition.x,
|
x: (result.x ?? 0) + parentNodePosition.x,
|
||||||
y: (result.y ?? 0) + parentNodePosition.y,
|
y: (result.y ?? 0) + parentNodePosition.y,
|
||||||
@@ -35,21 +31,17 @@ function calculateXYZPosition(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateAbsoluteNodePositions(
|
export function updateAbsoluteNodePositions(nodes: Node[], nodeOrigin: NodeOrigin, parentNodes?: ParentNodes) {
|
||||||
nodeInternals: NodeInternals,
|
nodes.forEach((node) => {
|
||||||
nodeOrigin: NodeOrigin,
|
if (node.parentNode && !nodes.find((n) => n.id === node.parentNode)) {
|
||||||
parentNodes?: ParentNodes
|
|
||||||
) {
|
|
||||||
nodeInternals.forEach((node) => {
|
|
||||||
if (node.parentNode && !nodeInternals.has(node.parentNode)) {
|
|
||||||
throw new Error(`Parent node ${node.parentNode} not found`);
|
throw new Error(`Parent node ${node.parentNode} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.parentNode || parentNodes?.[node.id]) {
|
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(
|
const { x, y, z } = calculateXYZPosition(
|
||||||
node,
|
node,
|
||||||
nodeInternals,
|
nodes,
|
||||||
{
|
{
|
||||||
...node.position,
|
...node.position,
|
||||||
z: node[internalsSymbol]?.z ?? 0,
|
z: node[internalsSymbol]?.z ?? 0,
|
||||||
@@ -73,19 +65,19 @@ export function updateAbsoluteNodePositions(
|
|||||||
|
|
||||||
export function createNodeInternals(
|
export function createNodeInternals(
|
||||||
nodes: Node[],
|
nodes: Node[],
|
||||||
nodeInternals: NodeInternals,
|
storeNodes: Node[],
|
||||||
nodeOrigin: NodeOrigin,
|
nodeOrigin: NodeOrigin,
|
||||||
elevateNodesOnSelect: boolean
|
elevateNodesOnSelect: boolean
|
||||||
): NodeInternals {
|
): Node[] {
|
||||||
const nextNodeInternals = new Map<string, Node>();
|
const nextNodes: Node[] = [];
|
||||||
const parentNodes: ParentNodes = {};
|
const parentNodes: ParentNodes = {};
|
||||||
const selectedNodeZ: number = elevateNodesOnSelect ? 1000 : 0;
|
const selectedNodeZ: number = elevateNodesOnSelect ? 1000 : 0;
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0);
|
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,
|
width: currInternals?.width,
|
||||||
height: currInternals?.height,
|
height: currInternals?.height,
|
||||||
...node,
|
...node,
|
||||||
@@ -96,11 +88,11 @@ export function createNodeInternals(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
internals.parentNode = node.parentNode;
|
updatedNode.parentNode = node.parentNode;
|
||||||
parentNodes[node.parentNode] = true;
|
parentNodes[node.parentNode] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(internals, internalsSymbol, {
|
Object.defineProperty(updatedNode, internalsSymbol, {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
value: {
|
value: {
|
||||||
handleBounds: currInternals?.[internalsSymbol]?.handleBounds,
|
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) {
|
export function handleControlledSelectionChange<NodeOrEdge extends Node | Edge>(
|
||||||
nodeChanges.forEach((change) => {
|
changes: NodeSelectionChange[] | EdgeSelectionChange[],
|
||||||
const node = nodeInternals.get(change.id);
|
items: NodeOrEdge[]
|
||||||
if (node) {
|
): NodeOrEdge[] {
|
||||||
nodeInternals.set(node.id, {
|
return items.map((item) => {
|
||||||
...node,
|
const change = changes.find((change) => change.id === item.id);
|
||||||
[internalsSymbol]: node[internalsSymbol],
|
|
||||||
selected: change.selected,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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) {
|
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) {
|
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 (changedNodes?.length) {
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
set({ nodeInternals: handleControlledNodeSelectionChange(changedNodes, nodeInternals) });
|
set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange?.(changedNodes);
|
onNodesChange?.(changedNodes);
|
||||||
@@ -161,7 +143,7 @@ export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get,
|
|||||||
|
|
||||||
if (changedEdges?.length) {
|
if (changedEdges?.length) {
|
||||||
if (hasDefaultEdges) {
|
if (hasDefaultEdges) {
|
||||||
set({ edges: handleControlledEdgeSelectionChange(changedEdges, edges) });
|
set({ edges: handleControlledSelectionChange(changedEdges, edges) });
|
||||||
}
|
}
|
||||||
|
|
||||||
onEdgesChange?.(changedEdges);
|
onEdgesChange?.(changedEdges);
|
||||||
|
|||||||
@@ -41,5 +41,3 @@ export type WrapNodeProps<NodeData = any> = Pick<
|
|||||||
rfId: string;
|
rfId: string;
|
||||||
disableKeyboardA11y: boolean;
|
disableKeyboardA11y: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeInternals = Map<string, Node>;
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import type {
|
|||||||
NodeChange,
|
NodeChange,
|
||||||
OnNodesChange,
|
OnNodesChange,
|
||||||
OnEdgesChange,
|
OnEdgesChange,
|
||||||
NodeInternals,
|
|
||||||
DefaultEdgeOptions,
|
DefaultEdgeOptions,
|
||||||
FitViewOptions,
|
FitViewOptions,
|
||||||
OnNodesDelete,
|
OnNodesDelete,
|
||||||
@@ -46,7 +45,7 @@ export type ReactFlowStore = {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
transform: Transform;
|
transform: Transform;
|
||||||
nodeInternals: NodeInternals;
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
onNodesChange: OnNodesChange | null;
|
onNodesChange: OnNodesChange | null;
|
||||||
onEdgesChange: OnEdgesChange | null;
|
onEdgesChange: OnEdgesChange | null;
|
||||||
|
|||||||
@@ -33,13 +33,11 @@ export type GroupedEdges<EdgeType extends BaseEdge> = {
|
|||||||
|
|
||||||
export function groupEdgesByZLevel<EdgeType extends BaseEdge>(
|
export function groupEdgesByZLevel<EdgeType extends BaseEdge>(
|
||||||
edges: EdgeType[],
|
edges: EdgeType[],
|
||||||
nodes: Map<string, BaseNode> | BaseNode[],
|
nodes: BaseNode[],
|
||||||
elevateEdgesOnSelect = false
|
elevateEdgesOnSelect = false
|
||||||
): GroupedEdges<EdgeType>[] {
|
): GroupedEdges<EdgeType>[] {
|
||||||
let maxLevel = -1;
|
let maxLevel = -1;
|
||||||
|
|
||||||
const isNodeInternals = 'get' in nodes;
|
|
||||||
|
|
||||||
const levelLookup = edges.reduce<Record<string, EdgeType[]>>((tree, edge) => {
|
const levelLookup = edges.reduce<Record<string, EdgeType[]>>((tree, edge) => {
|
||||||
const hasZIndex = isNumeric(edge.zIndex);
|
const hasZIndex = isNumeric(edge.zIndex);
|
||||||
let z = hasZIndex ? edge.zIndex! : 0;
|
let z = hasZIndex ? edge.zIndex! : 0;
|
||||||
@@ -48,10 +46,8 @@ export function groupEdgesByZLevel<EdgeType extends BaseEdge>(
|
|||||||
z = hasZIndex
|
z = hasZIndex
|
||||||
? edge.zIndex!
|
? edge.zIndex!
|
||||||
: Math.max(
|
: Math.max(
|
||||||
(isNodeInternals ? nodes.get(edge.source) : nodes.find((n) => n.id === edge.source))?.[internalsSymbol]
|
nodes.find((n) => n.id === edge.source)?.[internalsSymbol]?.z || 0,
|
||||||
?.z || 0,
|
nodes.find((n) => n.id === edge.target)?.[internalsSymbol]?.z || 0
|
||||||
(isNodeInternals ? nodes.get(edge.target) : nodes.find((n) => n.id === edge.target))?.[internalsSymbol]
|
|
||||||
?.z || 0
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user