Merge branch 'next' into refactor/infer-node-types
This commit is contained in:
@@ -101,12 +101,12 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
);
|
||||
|
||||
const markerStartUrl = useMemo(
|
||||
() => (edge.markerStart ? `url(#${getMarkerId(edge.markerStart, rfId)})` : undefined),
|
||||
() => (edge.markerStart ? `url('#${getMarkerId(edge.markerStart, rfId)}')` : undefined),
|
||||
[edge.markerStart, rfId]
|
||||
);
|
||||
|
||||
const markerEndUrl = useMemo(
|
||||
() => (edge.markerEnd ? `url(#${getMarkerId(edge.markerEnd, rfId)})` : undefined),
|
||||
() => (edge.markerEnd ? `url('#${getMarkerId(edge.markerEnd, rfId)}')` : undefined),
|
||||
[edge.markerEnd, rfId]
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { Provider } from '../../contexts/NodeIdContext';
|
||||
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
|
||||
import { useDrag } from '../../hooks/useDrag';
|
||||
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
|
||||
import { useMoveSelectedNodes } from '../../hooks/useMoveSelectedNodes';
|
||||
import { handleNodeClick } from '../Nodes/utils';
|
||||
import { arrowKeyDiffs, builtinNodeTypes } from './utils';
|
||||
import type { Node, NodeWrapperProps } from '../../types';
|
||||
@@ -79,16 +79,33 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
const prevTargetPosition = useRef(node.targetPosition);
|
||||
const prevType = useRef(nodeType);
|
||||
|
||||
const updatePositions = useUpdateNodePositions();
|
||||
const width = node.width ?? undefined;
|
||||
const height = node.height ?? undefined;
|
||||
const computedWidth = node.computed?.width;
|
||||
const computedHeight = node.computed?.height;
|
||||
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
||||
const hasHandleBounds = !!node[internalsSymbol]?.handleBounds;
|
||||
|
||||
const moveSelectedNodes = useMoveSelectedNodes();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (nodeRef.current) {
|
||||
resizeObserver?.unobserve(nodeRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (nodeRef.current && !node.hidden) {
|
||||
const currNode = nodeRef.current;
|
||||
resizeObserver?.observe(currNode);
|
||||
|
||||
return () => resizeObserver?.unobserve(currNode);
|
||||
if (!initialized || !hasHandleBounds) {
|
||||
resizeObserver?.unobserve(currNode);
|
||||
resizeObserver?.observe(currNode);
|
||||
}
|
||||
}
|
||||
}, [node.hidden]);
|
||||
}, [node.hidden, initialized, hasHandleBounds]);
|
||||
|
||||
useEffect(() => {
|
||||
// when the user programmatically changes the source or handle position, we re-initialize the node
|
||||
@@ -123,11 +140,6 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
return null;
|
||||
}
|
||||
|
||||
const width = node.width ?? undefined;
|
||||
const height = node.height ?? undefined;
|
||||
const computedWidth = node.computed?.width;
|
||||
const computedHeight = node.computed?.height;
|
||||
|
||||
const positionAbsoluteOrigin = getPositionWithOrigin({
|
||||
x: positionAbsoluteX,
|
||||
y: positionAbsoluteY,
|
||||
@@ -135,7 +147,6 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
height: computedHeight ?? height ?? 0,
|
||||
origin: node.origin || nodeOrigin,
|
||||
});
|
||||
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
||||
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
||||
|
||||
const onMouseEnterHandler = onMouseEnter ? (event: MouseEvent) => onMouseEnter(event, { ...node }) : undefined;
|
||||
@@ -188,10 +199,9 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
.toLowerCase()}. New position, x: ${~~positionAbsoluteX}, y: ${~~positionAbsoluteY}`,
|
||||
});
|
||||
|
||||
updatePositions({
|
||||
x: arrowKeyDiffs[event.key].x,
|
||||
y: arrowKeyDiffs[event.key].y,
|
||||
isShiftPressed: event.shiftKey,
|
||||
moveSelectedNodes({
|
||||
direction: arrowKeyDiffs[event.key],
|
||||
factor: event.shiftKey ? 4 : 1,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getNodesBounds } from '@xyflow/system';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { useDrag } from '../../hooks/useDrag';
|
||||
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
|
||||
import { useMoveSelectedNodes } from '../../hooks/useMoveSelectedNodes';
|
||||
import { arrowKeyDiffs } from '../NodeWrapper/utils';
|
||||
import type { Node, ReactFlowState } from '../../types';
|
||||
|
||||
@@ -39,7 +39,7 @@ export function NodesSelection<NodeType extends Node>({
|
||||
}: NodesSelectionProps<NodeType>) {
|
||||
const store = useStoreApi();
|
||||
const { width, height, transformString, userSelectionActive } = useStore(selector, shallow);
|
||||
const updatePositions = useUpdateNodePositions();
|
||||
const moveSelectedNodes = useMoveSelectedNodes();
|
||||
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -68,10 +68,9 @@ export function NodesSelection<NodeType extends Node>({
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
||||
updatePositions({
|
||||
x: arrowKeyDiffs[event.key].x,
|
||||
y: arrowKeyDiffs[event.key].y,
|
||||
isShiftPressed: event.shiftKey,
|
||||
moveSelectedNodes({
|
||||
direction: arrowKeyDiffs[event.key],
|
||||
factor: event.shiftKey ? 4 : 1,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ export function ReactFlowProvider({
|
||||
children,
|
||||
initialNodes,
|
||||
initialEdges,
|
||||
defaultNodes,
|
||||
defaultEdges,
|
||||
initialWidth,
|
||||
initialHeight,
|
||||
fitView,
|
||||
@@ -17,16 +19,19 @@ export function ReactFlowProvider({
|
||||
children: ReactNode;
|
||||
initialNodes?: Node[];
|
||||
initialEdges?: Edge[];
|
||||
defaultNodes?: Node[];
|
||||
defaultEdges?: Edge[];
|
||||
initialWidth?: number;
|
||||
initialHeight?: number;
|
||||
fitView?: boolean;
|
||||
}) {
|
||||
const storeRef = useRef<UseBoundStoreWithEqualityFn<StoreApi<ReactFlowState>> | null>(null);
|
||||
|
||||
if (!storeRef.current) {
|
||||
storeRef.current = createRFStore({
|
||||
nodes: initialNodes,
|
||||
edges: initialEdges,
|
||||
defaultNodes,
|
||||
defaultEdges,
|
||||
width: initialWidth,
|
||||
height: initialHeight,
|
||||
fitView,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { infiniteExtent, type CoordinateExtent } from '@xyflow/system';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import type { Node, Edge, ReactFlowState, ReactFlowProps, FitViewOptions } from '../../types';
|
||||
import { initNodeOrigin } from '../../container/ReactFlow';
|
||||
import { defaultNodeOrigin } from '../../container/ReactFlow/init-values';
|
||||
|
||||
// these fields exist in the global store and we need to keep them up to date
|
||||
const reactFlowFieldsToTrack = [
|
||||
@@ -81,50 +81,53 @@ const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'] as const;
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodes: s.setNodes,
|
||||
setEdges: s.setEdges,
|
||||
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
||||
setMinZoom: s.setMinZoom,
|
||||
setMaxZoom: s.setMaxZoom,
|
||||
setTranslateExtent: s.setTranslateExtent,
|
||||
setNodeExtent: s.setNodeExtent,
|
||||
reset: s.reset,
|
||||
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
||||
});
|
||||
|
||||
const initPrevValues = {
|
||||
// these are values that are also passed directly to other components
|
||||
// than the StoreUpdater. We can reduce the number of setStore calls
|
||||
// by setting the same values here as prev fields.
|
||||
translateExtent: infiniteExtent,
|
||||
nodeOrigin: defaultNodeOrigin,
|
||||
minZoom: 0.5,
|
||||
maxZoom: 2,
|
||||
elementsSelectable: true,
|
||||
noPanClassName: 'nopan',
|
||||
rfId: '1',
|
||||
};
|
||||
|
||||
export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
||||
props: StoreUpdaterProps<NodeType, EdgeType>
|
||||
) {
|
||||
const {
|
||||
setNodes,
|
||||
setEdges,
|
||||
setDefaultNodesAndEdges,
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
setTranslateExtent,
|
||||
setNodeExtent,
|
||||
reset,
|
||||
setDefaultNodesAndEdges,
|
||||
} = useStore(selector, shallow);
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
const edgesWithDefaults = props.defaultEdges?.map((e) => ({ ...e, ...props.defaultEdgeOptions }));
|
||||
setDefaultNodesAndEdges(props.defaultNodes, edgesWithDefaults);
|
||||
setDefaultNodesAndEdges(props.defaultNodes, props.defaultEdges);
|
||||
|
||||
return () => {
|
||||
// when we reset the store we also need to reset the previous fields
|
||||
previousFields.current = initPrevValues;
|
||||
reset();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const previousFields = useRef<Partial<StoreUpdaterProps<NodeType, EdgeType>>>({
|
||||
// these are values that are also passed directly to other components
|
||||
// than the StoreUpdater. We can reduce the number of setStore calls
|
||||
// by setting the same values here as prev fields.
|
||||
translateExtent: infiniteExtent,
|
||||
nodeOrigin: initNodeOrigin,
|
||||
minZoom: 0.5,
|
||||
maxZoom: 2,
|
||||
elementsSelectable: true,
|
||||
noPanClassName: 'nopan',
|
||||
rfId: '1',
|
||||
});
|
||||
const previousFields = useRef<Partial<StoreUpdaterProps<NodeType, EdgeType>>>(initPrevValues);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
Reference in New Issue
Block a user