Merge pull request #4432 from xyflow/fix/connectionprops
refactor(useConnection): return internal node, add node generic
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
'@xyflow/system': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
refactor(useConnection): return internal node, add node generic
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import { FC } from 'react';
|
import { getBezierPath, ConnectionLineComponentProps, InternalNode } from '@xyflow/react';
|
||||||
import { getBezierPath, ConnectionLineComponentProps, Node } from '@xyflow/react';
|
|
||||||
|
|
||||||
import { getEdgeParams } from './utils';
|
import { getEdgeParams } from './utils';
|
||||||
|
|
||||||
const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({ toX, toY, fromPosition, toPosition, fromNode }) => {
|
function FloatingConnectionLine({ toX, toY, fromPosition, toPosition, fromNode }: ConnectionLineComponentProps) {
|
||||||
if (!fromNode) {
|
if (!fromNode) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -13,7 +12,7 @@ const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({ toX, toY, fr
|
|||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
position: { x: toX, y: toY },
|
position: { x: toX, y: toY },
|
||||||
} as Node;
|
} as InternalNode;
|
||||||
|
|
||||||
const { sx, sy } = getEdgeParams(fromNode, targetNode);
|
const { sx, sy } = getEdgeParams(fromNode, targetNode);
|
||||||
|
|
||||||
@@ -32,6 +31,6 @@ const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({ toX, toY, fr
|
|||||||
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default FloatingConnectionLine;
|
export default FloatingConnectionLine;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { FC, CSSProperties } from 'react';
|
import { CSSProperties } from 'react';
|
||||||
import { EdgeProps, useStore, getBezierPath } from '@xyflow/react';
|
import { EdgeProps, useStore, getBezierPath } from '@xyflow/react';
|
||||||
|
|
||||||
import { getEdgeParams } from './utils';
|
import { getEdgeParams } from './utils';
|
||||||
|
|
||||||
const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
|
function FloatingEdge({ id, source, target, style }: EdgeProps) {
|
||||||
const { sourceNode, targetNode } = useStore((s) => {
|
const { sourceNode, targetNode } = useStore((s) => {
|
||||||
const sourceNode = s.nodeLookup.get(source);
|
const sourceNode = s.nodeLookup.get(source);
|
||||||
const targetNode = s.nodeLookup.get(target);
|
const targetNode = s.nodeLookup.get(target);
|
||||||
@@ -31,6 +31,6 @@ const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
|
|||||||
<path id={id} className="react-flow__edge-path" d={path} style={style as CSSProperties} />
|
<path id={id} className="react-flow__edge-path" d={path} style={style as CSSProperties} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default FloatingEdge;
|
export default FloatingEdge;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const edgeTypes: EdgeTypes = {
|
|||||||
floating: FloatingEdge,
|
floating: FloatingEdge,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FloatingEdges = () => {
|
function FloatingEdges() {
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
@@ -49,6 +49,6 @@ const FloatingEdges = () => {
|
|||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default FloatingEdges;
|
export default FloatingEdges;
|
||||||
|
|||||||
@@ -2,21 +2,19 @@ import { Position, XYPosition, Node, Edge, InternalNode } from '@xyflow/react';
|
|||||||
|
|
||||||
// this helper function returns the intersection point
|
// this helper function returns the intersection point
|
||||||
// of the line between the center of the intersectionNode and the target node
|
// of the line between the center of the intersectionNode and the target node
|
||||||
function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
|
function getNodeIntersection(intersectionNode: InternalNode, targetNode: InternalNode): XYPosition {
|
||||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
const { internals: intersectionInternals } = intersectionNode;
|
||||||
|
|
||||||
const { position: intersectionNodePosition } = intersectionNode;
|
|
||||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured ?? {
|
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured ?? {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
};
|
};
|
||||||
const targetPosition = targetNode.position;
|
const targetPosition = targetNode.internals.positionAbsolute;
|
||||||
|
|
||||||
const w = (intersectionNodeWidth ?? 0) / 2;
|
const w = (intersectionNodeWidth ?? 0) / 2;
|
||||||
const h = (intersectionNodeHeight ?? 0) / 2;
|
const h = (intersectionNodeHeight ?? 0) / 2;
|
||||||
|
|
||||||
const x2 = intersectionNodePosition.x + w;
|
const x2 = intersectionInternals.positionAbsolute.x + w;
|
||||||
const y2 = intersectionNodePosition.y + h;
|
const y2 = intersectionInternals.positionAbsolute.y + h;
|
||||||
const x1 = targetPosition.x + w;
|
const x1 = targetPosition.x + w;
|
||||||
const y1 = targetPosition.y + h;
|
const y1 = targetPosition.y + h;
|
||||||
|
|
||||||
@@ -92,7 +90,13 @@ export function createElements(): NodesAndEdges {
|
|||||||
const x = 250 * Math.cos(radians) + center.x;
|
const x = 250 * Math.cos(radians) + center.x;
|
||||||
const y = 250 * Math.sin(radians) + center.y;
|
const y = 250 * Math.sin(radians) + center.y;
|
||||||
|
|
||||||
nodes.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });
|
const isChild = i === 1;
|
||||||
|
nodes.push({
|
||||||
|
id: `${i}`,
|
||||||
|
data: { label: 'Source' },
|
||||||
|
position: isChild ? { x: 0, y: 0 } : { x, y },
|
||||||
|
parentId: isChild ? '0' : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
edges.push({
|
edges.push({
|
||||||
id: `edge-${i}`,
|
id: `edge-${i}`,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const initialEdges: Edge[] = [
|
|||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const UseZoomPanHelperFlow = () => {
|
const UseConnectionFlow = () => {
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
|
|
||||||
const WrappedFlow = () => (
|
const WrappedFlow = () => (
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<UseZoomPanHelperFlow />
|
<UseConnectionFlow />
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore } from './useStore';
|
|
||||||
import type { ReactFlowStore } from '../types/store';
|
|
||||||
import { ConnectionState, pointToRendererPoint } from '@xyflow/system';
|
import { ConnectionState, pointToRendererPoint } from '@xyflow/system';
|
||||||
|
|
||||||
const selector = (s: ReactFlowStore): ConnectionState => {
|
import { useStore } from './useStore';
|
||||||
|
import type { InternalNode, Node, ReactFlowStore } from '../types';
|
||||||
|
|
||||||
|
const selector = (s: ReactFlowStore) => {
|
||||||
return s.connection.inProgress
|
return s.connection.inProgress
|
||||||
? { ...s.connection, to: pointToRendererPoint(s.connection.to, s.transform) }
|
? { ...s.connection, to: pointToRendererPoint(s.connection.to, s.transform) }
|
||||||
: { ...s.connection };
|
: { ...s.connection };
|
||||||
@@ -15,6 +15,6 @@ const selector = (s: ReactFlowStore): ConnectionState => {
|
|||||||
* @public
|
* @public
|
||||||
* @returns ConnectionState
|
* @returns ConnectionState
|
||||||
*/
|
*/
|
||||||
export function useConnection(): ConnectionState {
|
export function useConnection<NodeType extends Node = Node>(): ConnectionState<InternalNode<NodeType>> {
|
||||||
return useStore(selector, shallow);
|
return useStore(selector, shallow) as ConnectionState<InternalNode<NodeType>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type {
|
|||||||
OnError,
|
OnError,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { EdgeTypes, Node } from '.';
|
import { EdgeTypes, InternalNode, Node } from '.';
|
||||||
|
|
||||||
export type EdgeLabelOptions = {
|
export type EdgeLabelOptions = {
|
||||||
label?: string | ReactNode;
|
label?: string | ReactNode;
|
||||||
@@ -190,10 +190,10 @@ export type SimpleBezierEdgeProps = EdgeComponentProps;
|
|||||||
|
|
||||||
export type OnReconnect<EdgeType extends Edge = Edge> = (oldEdge: EdgeType, newConnection: Connection) => void;
|
export type OnReconnect<EdgeType extends Edge = Edge> = (oldEdge: EdgeType, newConnection: Connection) => void;
|
||||||
|
|
||||||
export type ConnectionLineComponentProps = {
|
export type ConnectionLineComponentProps<NodeType extends Node = Node> = {
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
fromNode: Node;
|
fromNode: InternalNode<NodeType>;
|
||||||
fromHandle: Handle;
|
fromHandle: Handle;
|
||||||
fromX: number;
|
fromX: number;
|
||||||
fromY: number;
|
fromY: number;
|
||||||
@@ -202,8 +202,10 @@ export type ConnectionLineComponentProps = {
|
|||||||
fromPosition: Position;
|
fromPosition: Position;
|
||||||
toPosition: Position;
|
toPosition: Position;
|
||||||
connectionStatus: 'valid' | 'invalid' | null;
|
connectionStatus: 'valid' | 'invalid' | null;
|
||||||
toNode: Node | null;
|
toNode: InternalNode<NodeType> | null;
|
||||||
toHandle: Handle | null;
|
toHandle: Handle | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConnectionLineComponent = ComponentType<ConnectionLineComponentProps>;
|
export type ConnectionLineComponent<NodeType extends Node = Node> = ComponentType<
|
||||||
|
ConnectionLineComponentProps<NodeType>
|
||||||
|
>;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
|
|||||||
userSelectionActive: boolean;
|
userSelectionActive: boolean;
|
||||||
userSelectionRect: SelectionRect | null;
|
userSelectionRect: SelectionRect | null;
|
||||||
|
|
||||||
connection: ConnectionState;
|
connection: ConnectionState<InternalNode<NodeType>>;
|
||||||
connectionMode: ConnectionMode;
|
connectionMode: ConnectionMode;
|
||||||
connectionClickStartHandle: (Pick<Handle, 'nodeId' | 'id'> & Required<Pick<Handle, 'type'>>) | null;
|
connectionClickStartHandle: (Pick<Handle, 'nodeId' | 'id'> & Required<Pick<Handle, 'type'>>) | null;
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ export type ReactFlowActions<NodeType extends Node, EdgeType extends Edge> = {
|
|||||||
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
|
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
|
||||||
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
|
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
|
||||||
cancelConnection: () => void;
|
cancelConnection: () => void;
|
||||||
updateConnection: UpdateConnection;
|
updateConnection: UpdateConnection<InternalNode<NodeType>>;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
triggerNodeChanges: (changes: NodeChange<NodeType>[]) => void;
|
triggerNodeChanges: (changes: NodeChange<NodeType>[]) => void;
|
||||||
triggerEdgeChanges: (changes: EdgeChange<EdgeType>[]) => void;
|
triggerEdgeChanges: (changes: EdgeChange<EdgeType>[]) => void;
|
||||||
|
|||||||
@@ -148,36 +148,34 @@ export const initialConnection: NoConnection = {
|
|||||||
export type NoConnection = {
|
export type NoConnection = {
|
||||||
inProgress: false;
|
inProgress: false;
|
||||||
isValid: null;
|
isValid: null;
|
||||||
|
|
||||||
from: null;
|
from: null;
|
||||||
fromHandle: null;
|
fromHandle: null;
|
||||||
fromPosition: null;
|
fromPosition: null;
|
||||||
fromNode: null;
|
fromNode: null;
|
||||||
|
|
||||||
to: null;
|
to: null;
|
||||||
toHandle: null;
|
toHandle: null;
|
||||||
toPosition: null;
|
toPosition: null;
|
||||||
toNode: null;
|
toNode: null;
|
||||||
};
|
};
|
||||||
|
export type ConnectionInProgress<NodeType extends InternalNodeBase = InternalNodeBase> = {
|
||||||
export type ConnectionInProgress = {
|
|
||||||
inProgress: true;
|
inProgress: true;
|
||||||
isValid: boolean | null;
|
isValid: boolean | null;
|
||||||
|
|
||||||
from: XYPosition;
|
from: XYPosition;
|
||||||
fromHandle: Handle;
|
fromHandle: Handle;
|
||||||
fromPosition: Position;
|
fromPosition: Position;
|
||||||
fromNode: NodeBase;
|
fromNode: NodeType;
|
||||||
|
|
||||||
to: XYPosition;
|
to: XYPosition;
|
||||||
toHandle: Handle | null;
|
toHandle: Handle | null;
|
||||||
toPosition: Position;
|
toPosition: Position;
|
||||||
toNode: NodeBase | null;
|
toNode: NodeType | null;
|
||||||
};
|
};
|
||||||
|
export type ConnectionState<NodeType extends InternalNodeBase = InternalNodeBase> =
|
||||||
|
| ConnectionInProgress<NodeType>
|
||||||
|
| NoConnection;
|
||||||
|
|
||||||
export type ConnectionState = ConnectionInProgress | NoConnection;
|
export type UpdateConnection<NodeType extends InternalNodeBase = InternalNodeBase> = (
|
||||||
|
params: ConnectionState<NodeType>
|
||||||
export type UpdateConnection = (params: ConnectionState) => void;
|
) => void;
|
||||||
|
|
||||||
export type ColorModeClass = 'light' | 'dark';
|
export type ColorModeClass = 'light' | 'dark';
|
||||||
export type ColorMode = ColorModeClass | 'system';
|
export type ColorMode = ColorModeClass | 'system';
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function onPointerDown(
|
|||||||
from,
|
from,
|
||||||
fromHandle,
|
fromHandle,
|
||||||
fromPosition: fromHandle.position,
|
fromPosition: fromHandle.position,
|
||||||
fromNode: fromNodeInternal.internals.userNode,
|
fromNode: fromNodeInternal,
|
||||||
|
|
||||||
to: position,
|
to: position,
|
||||||
toHandle: null,
|
toHandle: null,
|
||||||
@@ -163,7 +163,7 @@ function onPointerDown(
|
|||||||
: position,
|
: position,
|
||||||
toHandle: result.toHandle,
|
toHandle: result.toHandle,
|
||||||
toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
|
toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
|
||||||
toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId)!.internals.userNode : null,
|
toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId)! : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// we don't want to trigger an update when the connection
|
// we don't want to trigger an update when the connection
|
||||||
|
|||||||
Reference in New Issue
Block a user