refactor(positions): remove unused node origins
This commit is contained in:
@@ -25,12 +25,8 @@ const selector = (s: ReactFlowState) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
viewBB,
|
viewBB,
|
||||||
boundingRect:
|
boundingRect: s.nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup), viewBB) : viewBB,
|
||||||
s.nodeLookup.size > 0
|
|
||||||
? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup, { nodeOrigin: s.nodeOrigin }), viewBB)
|
|
||||||
: viewBB,
|
|
||||||
rfId: s.rfId,
|
rfId: s.rfId,
|
||||||
nodeOrigin: s.nodeOrigin,
|
|
||||||
panZoom: s.panZoom,
|
panZoom: s.panZoom,
|
||||||
translateExtent: s.translateExtent,
|
translateExtent: s.translateExtent,
|
||||||
flowWidth: s.width,
|
flowWidth: s.width,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, CSSProperties } from 'react';
|
import { useCallback, CSSProperties } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { Rect, Position, getNodeToolbarTransform, getNodesBounds } from '@xyflow/system';
|
import { Position, getNodeToolbarTransform, getInternalNodesBounds, NodeLookup } from '@xyflow/system';
|
||||||
|
|
||||||
import { InternalNode, ReactFlowState } from '../../types';
|
import { InternalNode, ReactFlowState } from '../../types';
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
@@ -17,21 +17,24 @@ const nodeEqualityFn = (a?: InternalNode, b?: InternalNode) =>
|
|||||||
a?.selected !== b?.selected ||
|
a?.selected !== b?.selected ||
|
||||||
a?.internals.z !== b?.internals.z;
|
a?.internals.z !== b?.internals.z;
|
||||||
|
|
||||||
const nodesEqualityFn = (a: InternalNode[], b: InternalNode[]) => {
|
const nodesEqualityFn = (a: NodeLookup, b: NodeLookup) => {
|
||||||
if (a.length !== b.length) {
|
if (a.size !== b.size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !a.some((node, i) => nodeEqualityFn(node, b[i]));
|
for (const [key, node] of a) {
|
||||||
|
if (nodeEqualityFn(node, b.get(key))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const storeSelector = (state: ReactFlowState) => ({
|
const storeSelector = (state: ReactFlowState) => ({
|
||||||
viewport: {
|
x: state.transform[0],
|
||||||
x: state.transform[0],
|
y: state.transform[1],
|
||||||
y: state.transform[1],
|
zoom: state.transform[2],
|
||||||
zoom: state.transform[2],
|
|
||||||
},
|
|
||||||
nodeOrigin: state.nodeOrigin,
|
|
||||||
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
|
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,36 +52,41 @@ export function NodeToolbar({
|
|||||||
const contextNodeId = useNodeId();
|
const contextNodeId = useNodeId();
|
||||||
|
|
||||||
const nodesSelector = useCallback(
|
const nodesSelector = useCallback(
|
||||||
(state: ReactFlowState): InternalNode[] => {
|
(state: ReactFlowState): NodeLookup => {
|
||||||
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
|
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
|
||||||
|
const internalNodes = nodeIds.reduce<NodeLookup>((res, id) => {
|
||||||
return nodeIds.reduce<InternalNode[]>((acc, id) => {
|
|
||||||
const node = state.nodeLookup.get(id);
|
const node = state.nodeLookup.get(id);
|
||||||
if (node) {
|
if (node) {
|
||||||
acc.push(node);
|
res.set(node.id, node);
|
||||||
}
|
}
|
||||||
return acc;
|
|
||||||
}, []);
|
return res;
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
return internalNodes;
|
||||||
},
|
},
|
||||||
[nodeId, contextNodeId]
|
[nodeId, contextNodeId]
|
||||||
);
|
);
|
||||||
const nodes = useStore(nodesSelector, nodesEqualityFn);
|
const nodes = useStore(nodesSelector, nodesEqualityFn);
|
||||||
const { viewport, nodeOrigin, selectedNodesCount } = useStore(storeSelector, shallow);
|
const { x, y, zoom, selectedNodesCount } = useStore(storeSelector, shallow);
|
||||||
|
|
||||||
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
||||||
const isActive =
|
const isActive =
|
||||||
typeof isVisible === 'boolean' ? isVisible : nodes.length === 1 && nodes[0].selected && selectedNodesCount === 1;
|
typeof isVisible === 'boolean'
|
||||||
|
? isVisible
|
||||||
|
: nodes.size === 1 && nodes.values().next().value.selected && selectedNodesCount === 1;
|
||||||
|
|
||||||
if (!isActive || !nodes.length) {
|
if (!isActive || !nodes.size) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeRect: Rect = getNodesBounds(nodes, { nodeOrigin });
|
const nodeRect = getInternalNodesBounds(nodes);
|
||||||
const zIndex: number = Math.max(...nodes.map((node) => node.internals.z + 1));
|
const nodesArray = Array.from(nodes.values());
|
||||||
|
const zIndex = Math.max(...nodesArray.map((node) => node.internals.z + 1));
|
||||||
|
|
||||||
const wrapperStyle: CSSProperties = {
|
const wrapperStyle: CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
transform: getNodeToolbarTransform(nodeRect, viewport, position, offset, align),
|
transform: getNodeToolbarTransform(nodeRect, { x, y, zoom }, position, offset, align),
|
||||||
zIndex,
|
zIndex,
|
||||||
...style,
|
...style,
|
||||||
};
|
};
|
||||||
@@ -89,7 +97,8 @@ export function NodeToolbar({
|
|||||||
style={wrapperStyle}
|
style={wrapperStyle}
|
||||||
className={cc(['react-flow__node-toolbar', className])}
|
className={cc(['react-flow__node-toolbar', className])}
|
||||||
{...rest}
|
{...rest}
|
||||||
data-id={nodes.reduce((acc, node) => `${acc}${node.id} `, '').trim()}
|
// @todo: check if we could only do this for non-prod envs
|
||||||
|
data-id={nodesArray.reduce((acc, node) => `${acc}${node.id} `, '').trim()}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export type NodesSelectionProps<NodeType> = {
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => {
|
const selector = (s: ReactFlowState) => {
|
||||||
const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
|
const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
|
||||||
nodeOrigin: s.nodeOrigin,
|
|
||||||
filter: (node) => !!node.selected,
|
filter: (node) => !!node.selected,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export type FlowRendererProps<NodeType extends Node = Node> = Omit<
|
|||||||
| 'selectNodesOnDrag'
|
| 'selectNodesOnDrag'
|
||||||
| 'defaultMarkerColor'
|
| 'defaultMarkerColor'
|
||||||
| 'rfId'
|
| 'rfId'
|
||||||
| 'nodeOrigin'
|
|
||||||
> & {
|
> & {
|
||||||
isControlledViewport: boolean;
|
isControlledViewport: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export function Pane({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onPointerMove = (event: ReactPointerEvent): void => {
|
const onPointerMove = (event: ReactPointerEvent): void => {
|
||||||
const { userSelectionRect, edgeLookup, transform, nodeOrigin, nodeLookup, triggerNodeChanges, triggerEdgeChanges } =
|
const { userSelectionRect, edgeLookup, transform, nodeLookup, triggerNodeChanges, triggerEdgeChanges } =
|
||||||
store.getState();
|
store.getState();
|
||||||
|
|
||||||
if (!containerBounds.current || !userSelectionRect) {
|
if (!containerBounds.current || !userSelectionRect) {
|
||||||
@@ -181,8 +181,7 @@ export function Pane({
|
|||||||
nextUserSelectRect,
|
nextUserSelectRect,
|
||||||
transform,
|
transform,
|
||||||
selectionMode === SelectionMode.Partial,
|
selectionMode === SelectionMode.Partial,
|
||||||
true,
|
true
|
||||||
nodeOrigin
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedEdgeIds = new Set<string>();
|
const selectedEdgeIds = new Set<string>();
|
||||||
|
|||||||
@@ -45,22 +45,23 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
return { x, y, zoom };
|
return { x, y, zoom };
|
||||||
},
|
},
|
||||||
fitView: (options) => {
|
fitView: (options) => {
|
||||||
const { nodeLookup, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
const { nodeLookup, width, height, minZoom, maxZoom, panZoom } = store.getState();
|
||||||
|
|
||||||
return panZoom
|
if (!panZoom) {
|
||||||
? fitView(
|
return false;
|
||||||
{
|
}
|
||||||
nodeLookup,
|
|
||||||
width,
|
return fitView(
|
||||||
height,
|
{
|
||||||
nodeOrigin,
|
nodeLookup,
|
||||||
minZoom,
|
width,
|
||||||
maxZoom,
|
height,
|
||||||
panZoom,
|
minZoom,
|
||||||
},
|
maxZoom,
|
||||||
options
|
panZoom,
|
||||||
)
|
},
|
||||||
: false;
|
options
|
||||||
|
);
|
||||||
},
|
},
|
||||||
setCenter: (x, y, options) => {
|
setCenter: (x, y, options) => {
|
||||||
const { width, height, maxZoom, panZoom } = store.getState();
|
const { width, height, maxZoom, panZoom } = store.getState();
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ const createStore = ({
|
|||||||
return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
|
return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
|
||||||
},
|
},
|
||||||
fitView: (options?: FitViewOptions): boolean => {
|
fitView: (options?: FitViewOptions): boolean => {
|
||||||
const { panZoom, width, height, minZoom, maxZoom, nodeOrigin, nodeLookup } = get();
|
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
|
||||||
|
|
||||||
if (!panZoom) {
|
if (!panZoom) {
|
||||||
return false;
|
return false;
|
||||||
@@ -302,7 +302,6 @@ const createStore = ({
|
|||||||
panZoom,
|
panZoom,
|
||||||
minZoom,
|
minZoom,
|
||||||
maxZoom,
|
maxZoom,
|
||||||
nodeOrigin,
|
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ const getInitialState = ({
|
|||||||
if (fitView && width && height) {
|
if (fitView && width && height) {
|
||||||
// @todo users nodeOrigin should be used here
|
// @todo users nodeOrigin should be used here
|
||||||
const bounds = getInternalNodesBounds(nodeLookup, {
|
const bounds = getInternalNodesBounds(nodeLookup, {
|
||||||
nodeOrigin: [0, 0],
|
|
||||||
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
||||||
});
|
});
|
||||||
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||||
|
|||||||
@@ -148,8 +148,7 @@ export function createStore({
|
|||||||
height: get(store.height),
|
height: get(store.height),
|
||||||
minZoom: get(store.minZoom),
|
minZoom: get(store.minZoom),
|
||||||
maxZoom: get(store.maxZoom),
|
maxZoom: get(store.maxZoom),
|
||||||
panZoom,
|
panZoom
|
||||||
nodeOrigin: get(store.nodeOrigin)
|
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { ZoomBehavior } from 'd3-zoom';
|
|||||||
import type { Transition } from 'd3-transition';
|
import type { Transition } from 'd3-transition';
|
||||||
|
|
||||||
import type { XYPosition, Rect } from './utils';
|
import type { XYPosition, Rect } from './utils';
|
||||||
import type { InternalNodeBase, NodeBase, NodeDragItem, NodeOrigin } from './nodes';
|
import type { InternalNodeBase, NodeBase, NodeDragItem } from './nodes';
|
||||||
import type { ConnectingHandle, HandleType } from './handles';
|
import type { ConnectingHandle, HandleType } from './handles';
|
||||||
import { PanZoomInstance } from './panzoom';
|
import { PanZoomInstance } from './panzoom';
|
||||||
import { EdgeBase } from '..';
|
import { EdgeBase } from '..';
|
||||||
@@ -63,7 +63,6 @@ export type FitViewParamsBase<NodeType extends NodeBase> = {
|
|||||||
panZoom: PanZoomInstance;
|
panZoom: PanZoomInstance;
|
||||||
minZoom: number;
|
minZoom: number;
|
||||||
maxZoom: number;
|
maxZoom: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ export const getNodesBounds = (nodes: NodeBase[], params: GetNodesBoundsParams =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type GetInternalNodesBoundsParams<NodeType> = {
|
export type GetInternalNodesBoundsParams<NodeType> = {
|
||||||
nodeOrigin?: NodeOrigin;
|
|
||||||
useRelativePosition?: boolean;
|
useRelativePosition?: boolean;
|
||||||
filter?: (node: NodeType) => boolean;
|
filter?: (node: NodeType) => boolean;
|
||||||
};
|
};
|
||||||
@@ -158,9 +157,7 @@ export type GetInternalNodesBoundsParams<NodeType> = {
|
|||||||
*/
|
*/
|
||||||
export const getInternalNodesBounds = <NodeType extends InternalNodeBase | NodeDragItem>(
|
export const getInternalNodesBounds = <NodeType extends InternalNodeBase | NodeDragItem>(
|
||||||
nodeLookup: Map<string, NodeType>,
|
nodeLookup: Map<string, NodeType>,
|
||||||
params: GetInternalNodesBoundsParams<NodeType> = {
|
params: GetInternalNodesBoundsParams<NodeType> = {}
|
||||||
nodeOrigin: [0, 0],
|
|
||||||
}
|
|
||||||
): Rect => {
|
): Rect => {
|
||||||
if (nodeLookup.size === 0) {
|
if (nodeLookup.size === 0) {
|
||||||
return { x: 0, y: 0, width: 0, height: 0 };
|
return { x: 0, y: 0, width: 0, height: 0 };
|
||||||
@@ -170,7 +167,7 @@ export const getInternalNodesBounds = <NodeType extends InternalNodeBase | NodeD
|
|||||||
|
|
||||||
nodeLookup.forEach((node) => {
|
nodeLookup.forEach((node) => {
|
||||||
if (params.filter == undefined || params.filter(node)) {
|
if (params.filter == undefined || params.filter(node)) {
|
||||||
const nodeBox = nodeToBox(node as InternalNodeBase, params.nodeOrigin);
|
const nodeBox = nodeToBox(node as InternalNodeBase);
|
||||||
box = getBoundsOfBoxes(box, nodeBox);
|
box = getBoundsOfBoxes(box, nodeBox);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -184,8 +181,7 @@ export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
|
|||||||
[tx, ty, tScale]: Transform = [0, 0, 1],
|
[tx, ty, tScale]: Transform = [0, 0, 1],
|
||||||
partially = false,
|
partially = false,
|
||||||
// set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute
|
// set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute
|
||||||
excludeNonSelectableNodes = false,
|
excludeNonSelectableNodes = false
|
||||||
nodeOrigin: NodeOrigin = [0, 0]
|
|
||||||
): InternalNodeBase<NodeType>[] => {
|
): InternalNodeBase<NodeType>[] => {
|
||||||
const paneRect = {
|
const paneRect = {
|
||||||
...pointToRendererPoint(rect, [tx, ty, tScale]),
|
...pointToRendererPoint(rect, [tx, ty, tScale]),
|
||||||
@@ -204,7 +200,7 @@ export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node, nodeOrigin));
|
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node));
|
||||||
const notInitialized = width === null || height === null;
|
const notInitialized = width === null || height === null;
|
||||||
|
|
||||||
const partiallyVisible = partially && overlappingArea > 0;
|
const partiallyVisible = partially && overlappingArea > 0;
|
||||||
@@ -238,22 +234,22 @@ export const getConnectedEdges = <NodeType extends NodeBase = NodeBase, EdgeType
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function fitView<Params extends FitViewParamsBase<NodeBase>, Options extends FitViewOptionsBase<NodeBase>>(
|
export function fitView<Params extends FitViewParamsBase<NodeBase>, Options extends FitViewOptionsBase<NodeBase>>(
|
||||||
{ nodeLookup, width, height, panZoom, minZoom, maxZoom, nodeOrigin = [0, 0] }: Params,
|
{ nodeLookup, width, height, panZoom, minZoom, maxZoom }: Params,
|
||||||
options?: Options
|
options?: Options
|
||||||
) {
|
) {
|
||||||
const filteredNodes: InternalNodeBase[] = [];
|
const filteredNodes: Map<string, InternalNodeBase> = new Map();
|
||||||
const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null;
|
const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null;
|
||||||
|
|
||||||
nodeLookup.forEach((n) => {
|
nodeLookup.forEach((n) => {
|
||||||
const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden);
|
const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden);
|
||||||
|
|
||||||
if (isVisible && (!optionNodeIds || optionNodeIds.has(n.id))) {
|
if (isVisible && (!optionNodeIds || optionNodeIds.has(n.id))) {
|
||||||
filteredNodes.push(n);
|
filteredNodes.set(n.id, n);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (filteredNodes.length > 0) {
|
if (filteredNodes.size > 0) {
|
||||||
const bounds = getNodesBounds(filteredNodes, { nodeOrigin });
|
const bounds = getInternalNodesBounds(filteredNodes);
|
||||||
|
|
||||||
const viewport = getViewportForBounds(
|
const viewport = getViewportForBounds(
|
||||||
bounds,
|
bounds,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
|||||||
let nodesBox: Box = { x: 0, y: 0, x2: 0, y2: 0 };
|
let nodesBox: Box = { x: 0, y: 0, x2: 0, y2: 0 };
|
||||||
|
|
||||||
if (dragItems.size > 1 && nodeExtent) {
|
if (dragItems.size > 1 && nodeExtent) {
|
||||||
const rect = getInternalNodesBounds(dragItems, { nodeOrigin });
|
const rect = getInternalNodesBounds(dragItems);
|
||||||
nodesBox = rectToBox(rect);
|
nodesBox = rectToBox(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user