diff --git a/.changeset/popular-masks-peel.md b/.changeset/popular-masks-peel.md new file mode 100644 index 00000000..fb0c3181 --- /dev/null +++ b/.changeset/popular-masks-peel.md @@ -0,0 +1,6 @@ +--- +'@xyflow/react': patch +'@xyflow/system': patch +--- + +refactor(useConnection): return internal node, add node generic diff --git a/examples/react/src/examples/FloatingEdges/FloatingConnectionLine.tsx b/examples/react/src/examples/FloatingEdges/FloatingConnectionLine.tsx index 3bac9d41..5f49d8a4 100644 --- a/examples/react/src/examples/FloatingEdges/FloatingConnectionLine.tsx +++ b/examples/react/src/examples/FloatingEdges/FloatingConnectionLine.tsx @@ -1,9 +1,8 @@ -import { FC } from 'react'; -import { getBezierPath, ConnectionLineComponentProps, Node } from '@xyflow/react'; +import { getBezierPath, ConnectionLineComponentProps, InternalNode } from '@xyflow/react'; import { getEdgeParams } from './utils'; -const FloatingConnectionLine: FC = ({ toX, toY, fromPosition, toPosition, fromNode }) => { +function FloatingConnectionLine({ toX, toY, fromPosition, toPosition, fromNode }: ConnectionLineComponentProps) { if (!fromNode) { return null; } @@ -13,7 +12,7 @@ const FloatingConnectionLine: FC = ({ toX, toY, fr width: 1, height: 1, position: { x: toX, y: toY }, - } as Node; + } as InternalNode; const { sx, sy } = getEdgeParams(fromNode, targetNode); @@ -32,6 +31,6 @@ const FloatingConnectionLine: FC = ({ toX, toY, fr ); -}; +} export default FloatingConnectionLine; diff --git a/examples/react/src/examples/FloatingEdges/FloatingEdge.tsx b/examples/react/src/examples/FloatingEdges/FloatingEdge.tsx index 0856d012..b2b65d4b 100644 --- a/examples/react/src/examples/FloatingEdges/FloatingEdge.tsx +++ b/examples/react/src/examples/FloatingEdges/FloatingEdge.tsx @@ -1,9 +1,9 @@ -import { FC, CSSProperties } from 'react'; +import { CSSProperties } from 'react'; import { EdgeProps, useStore, getBezierPath } from '@xyflow/react'; import { getEdgeParams } from './utils'; -const FloatingEdge: FC = ({ id, source, target, style }) => { +function FloatingEdge({ id, source, target, style }: EdgeProps) { const { sourceNode, targetNode } = useStore((s) => { const sourceNode = s.nodeLookup.get(source); const targetNode = s.nodeLookup.get(target); @@ -31,6 +31,6 @@ const FloatingEdge: FC = ({ id, source, target, style }) => { ); -}; +} export default FloatingEdge; diff --git a/examples/react/src/examples/FloatingEdges/index.tsx b/examples/react/src/examples/FloatingEdges/index.tsx index 032f25f6..57bf921f 100644 --- a/examples/react/src/examples/FloatingEdges/index.tsx +++ b/examples/react/src/examples/FloatingEdges/index.tsx @@ -25,7 +25,7 @@ const edgeTypes: EdgeTypes = { floating: FloatingEdge, }; -const FloatingEdges = () => { +function FloatingEdges() { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); @@ -49,6 +49,6 @@ const FloatingEdges = () => { ); -}; +} export default FloatingEdges; diff --git a/examples/react/src/examples/FloatingEdges/utils.ts b/examples/react/src/examples/FloatingEdges/utils.ts index 08593c5c..39f0fad0 100644 --- a/examples/react/src/examples/FloatingEdges/utils.ts +++ b/examples/react/src/examples/FloatingEdges/utils.ts @@ -2,21 +2,19 @@ import { Position, XYPosition, Node, Edge, InternalNode } from '@xyflow/react'; // this helper function returns the intersection point // of the line between the center of the intersectionNode and the target node -function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition { - // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a - - const { position: intersectionNodePosition } = intersectionNode; +function getNodeIntersection(intersectionNode: InternalNode, targetNode: InternalNode): XYPosition { + const { internals: intersectionInternals } = intersectionNode; const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured ?? { width: 0, height: 0, }; - const targetPosition = targetNode.position; + const targetPosition = targetNode.internals.positionAbsolute; const w = (intersectionNodeWidth ?? 0) / 2; const h = (intersectionNodeHeight ?? 0) / 2; - const x2 = intersectionNodePosition.x + w; - const y2 = intersectionNodePosition.y + h; + const x2 = intersectionInternals.positionAbsolute.x + w; + const y2 = intersectionInternals.positionAbsolute.y + h; const x1 = targetPosition.x + w; const y1 = targetPosition.y + h; @@ -92,7 +90,13 @@ export function createElements(): NodesAndEdges { const x = 250 * Math.cos(radians) + center.x; 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({ id: `edge-${i}`, diff --git a/examples/react/src/examples/UseConnection/index.tsx b/examples/react/src/examples/UseConnection/index.tsx index cc90f11f..38109b45 100644 --- a/examples/react/src/examples/UseConnection/index.tsx +++ b/examples/react/src/examples/UseConnection/index.tsx @@ -40,7 +40,7 @@ const initialEdges: Edge[] = [ { id: 'e1-3', source: '1', target: '3' }, ]; -const UseZoomPanHelperFlow = () => { +const UseConnectionFlow = () => { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); @@ -68,7 +68,7 @@ const UseZoomPanHelperFlow = () => { const WrappedFlow = () => ( - + ); diff --git a/packages/react/src/hooks/useConnection.ts b/packages/react/src/hooks/useConnection.ts index 8a4b377b..46784c4f 100644 --- a/packages/react/src/hooks/useConnection.ts +++ b/packages/react/src/hooks/useConnection.ts @@ -1,10 +1,10 @@ import { shallow } from 'zustand/shallow'; - -import { useStore } from './useStore'; -import type { ReactFlowStore } from '../types/store'; 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 ? { ...s.connection, to: pointToRendererPoint(s.connection.to, s.transform) } : { ...s.connection }; @@ -15,6 +15,6 @@ const selector = (s: ReactFlowStore): ConnectionState => { * @public * @returns ConnectionState */ -export function useConnection(): ConnectionState { - return useStore(selector, shallow); +export function useConnection(): ConnectionState> { + return useStore(selector, shallow) as ConnectionState>; } diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 640366d7..e765b389 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -16,7 +16,7 @@ import type { OnError, } from '@xyflow/system'; -import { EdgeTypes, Node } from '.'; +import { EdgeTypes, InternalNode, Node } from '.'; export type EdgeLabelOptions = { label?: string | ReactNode; @@ -190,10 +190,10 @@ export type SimpleBezierEdgeProps = EdgeComponentProps; export type OnReconnect = (oldEdge: EdgeType, newConnection: Connection) => void; -export type ConnectionLineComponentProps = { +export type ConnectionLineComponentProps = { connectionLineStyle?: CSSProperties; connectionLineType: ConnectionLineType; - fromNode: Node; + fromNode: InternalNode; fromHandle: Handle; fromX: number; fromY: number; @@ -202,8 +202,10 @@ export type ConnectionLineComponentProps = { fromPosition: Position; toPosition: Position; connectionStatus: 'valid' | 'invalid' | null; - toNode: Node | null; + toNode: InternalNode | null; toHandle: Handle | null; }; -export type ConnectionLineComponent = ComponentType; +export type ConnectionLineComponent = ComponentType< + ConnectionLineComponentProps +>; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 66bf3c44..8ddd2ff8 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -78,7 +78,7 @@ export type ReactFlowStore>; connectionMode: ConnectionMode; connectionClickStartHandle: (Pick & Required>) | null; @@ -163,7 +163,7 @@ export type ReactFlowActions = { setTranslateExtent: (translateExtent: CoordinateExtent) => void; setNodeExtent: (nodeExtent: CoordinateExtent) => void; cancelConnection: () => void; - updateConnection: UpdateConnection; + updateConnection: UpdateConnection>; reset: () => void; triggerNodeChanges: (changes: NodeChange[]) => void; triggerEdgeChanges: (changes: EdgeChange[]) => void; diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts index f712e1ea..1af0b930 100644 --- a/packages/system/src/types/general.ts +++ b/packages/system/src/types/general.ts @@ -148,36 +148,34 @@ export const initialConnection: NoConnection = { export type NoConnection = { inProgress: false; isValid: null; - from: null; fromHandle: null; fromPosition: null; fromNode: null; - to: null; toHandle: null; toPosition: null; toNode: null; }; - -export type ConnectionInProgress = { +export type ConnectionInProgress = { inProgress: true; isValid: boolean | null; - from: XYPosition; fromHandle: Handle; fromPosition: Position; - fromNode: NodeBase; - + fromNode: NodeType; to: XYPosition; toHandle: Handle | null; toPosition: Position; - toNode: NodeBase | null; + toNode: NodeType | null; }; +export type ConnectionState = + | ConnectionInProgress + | NoConnection; -export type ConnectionState = ConnectionInProgress | NoConnection; - -export type UpdateConnection = (params: ConnectionState) => void; +export type UpdateConnection = ( + params: ConnectionState +) => void; export type ColorModeClass = 'light' | 'dark'; export type ColorMode = ColorModeClass | 'system'; diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index 13f627fa..77b6da50 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -104,7 +104,7 @@ function onPointerDown( from, fromHandle, fromPosition: fromHandle.position, - fromNode: fromNodeInternal.internals.userNode, + fromNode: fromNodeInternal, to: position, toHandle: null, @@ -163,7 +163,7 @@ function onPointerDown( : position, toHandle: result.toHandle, 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