@@ -42,7 +42,10 @@ export type NodeReplaceChange<NodeType extends NodeBase = NodeBase> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Union type of all possible node changes.
|
||||
* The [`onNodesChange`](/api-reference/react-flow#on-nodes-change) callback takes
|
||||
*an array of `NodeChange` objects that you should use to update your flow's state.
|
||||
*The `NodeChange` type is a union of six different object types that represent that
|
||||
*various ways an node can change in a flow.
|
||||
* @public
|
||||
*/
|
||||
export type NodeChange<NodeType extends NodeBase = NodeBase> =
|
||||
@@ -67,6 +70,14 @@ export type EdgeReplaceChange<EdgeType extends EdgeBase = EdgeBase> = {
|
||||
type: 'replace';
|
||||
};
|
||||
|
||||
/**
|
||||
* The [`onEdgesChange`](/api-reference/react-flow#on-edges-change) callback takes
|
||||
*an array of `EdgeChange` objects that you should use to update your flow's state.
|
||||
*The `EdgeChange` type is a union of four different object types that represent that
|
||||
*various ways an edge can change in a flow.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type EdgeChange<EdgeType extends EdgeBase = EdgeBase> =
|
||||
| EdgeSelectionChange
|
||||
| EdgeRemoveChange
|
||||
|
||||
@@ -12,11 +12,13 @@ export type EdgeBase<
|
||||
source: string;
|
||||
/** Id of target node */
|
||||
target: string;
|
||||
/** Id of source handle
|
||||
/**
|
||||
* Id of source handle
|
||||
* only needed if there are multiple handles per node
|
||||
*/
|
||||
sourceHandle?: string | null;
|
||||
/** Id of target handle
|
||||
/**
|
||||
* Id of target handle
|
||||
* only needed if there are multiple handles per node
|
||||
*/
|
||||
targetHandle?: string | null;
|
||||
@@ -27,11 +29,13 @@ export type EdgeBase<
|
||||
/** Arbitrary data passed to an edge */
|
||||
data?: EdgeData;
|
||||
selected?: boolean;
|
||||
/** Set the marker on the beginning of an edge
|
||||
/**
|
||||
* Set the marker on the beginning of an edge
|
||||
* @example 'arrow', 'arrowclosed' or custom marker
|
||||
*/
|
||||
markerStart?: EdgeMarkerType;
|
||||
/** Set the marker on the end of an edge
|
||||
/**
|
||||
* Set the marker on the end of an edge
|
||||
* @example 'arrow', 'arrowclosed' or custom marker
|
||||
*/
|
||||
markerEnd?: EdgeMarkerType;
|
||||
@@ -54,11 +58,24 @@ export type BezierPathOptions = {
|
||||
curvature?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type DefaultEdgeOptionsBase<EdgeType extends EdgeBase> = Omit<
|
||||
EdgeType,
|
||||
'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'selected'
|
||||
>;
|
||||
|
||||
/**
|
||||
* If you set the `connectionLineType` prop on your [`<ReactFlow />`](/api-reference/react-flow#connection-connectionLineType)
|
||||
*component, it will dictate the style of connection line rendered when creating
|
||||
*new edges.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @remarks If you choose to render a custom connection line component, this value will be
|
||||
*passed to your component as part of its [`ConnectionLineComponentProps`](/api-reference/types/connection-line-component-props).
|
||||
*/
|
||||
export enum ConnectionLineType {
|
||||
Bezier = 'default',
|
||||
Straight = 'straight',
|
||||
@@ -67,6 +84,13 @@ export enum ConnectionLineType {
|
||||
SimpleBezier = 'simplebezier',
|
||||
}
|
||||
|
||||
/**
|
||||
* Edges can optionally have markers at the start and end of an edge. The `EdgeMarker`
|
||||
*type is used to configure those markers! Check the docs for [`MarkerType`](/api-reference/types/marker-type)
|
||||
*for details on what types of edge marker are available.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type EdgeMarker = {
|
||||
type: MarkerType;
|
||||
color?: string;
|
||||
@@ -79,6 +103,12 @@ export type EdgeMarker = {
|
||||
|
||||
export type EdgeMarkerType = string | EdgeMarker;
|
||||
|
||||
/**
|
||||
* Edges may optionally have a marker on either end. The MarkerType type enumerates
|
||||
* the options available to you when configuring a given marker.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum MarkerType {
|
||||
Arrow = 'arrow',
|
||||
ArrowClosed = 'arrowclosed',
|
||||
@@ -88,6 +118,9 @@ export type MarkerProps = EdgeMarker & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type EdgePosition = {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
|
||||
@@ -26,6 +26,13 @@ export type SetViewport = (viewport: Viewport, options?: ViewportHelperFunctionO
|
||||
export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => Promise<boolean>;
|
||||
export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge)
|
||||
* between two nodes. The [`addEdge`](/api-reference/utils/add-edge) util can be used to upgrade
|
||||
* a `Connection` to an [`Edge`](/api-reference/types/edge).
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type Connection = {
|
||||
source: string;
|
||||
target: string;
|
||||
@@ -33,15 +40,28 @@ export type Connection = {
|
||||
targetHandle: string | null;
|
||||
};
|
||||
|
||||
// TODO: remove in next version
|
||||
/**
|
||||
* The `HandleConnection` type is an extention of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`.
|
||||
*/
|
||||
export type HandleConnection = Connection & {
|
||||
edgeId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `NodeConnection` type is an extention of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`.
|
||||
*
|
||||
*/
|
||||
export type NodeConnection = Connection & {
|
||||
edgeId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `ConnectionMode` is used to set the mode of connection between nodes.
|
||||
* The `Strict` mode is the default one and only allows source to target edges.
|
||||
* `Loose` mode allows source to source and target to target edges as well.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum ConnectionMode {
|
||||
Strict = 'strict',
|
||||
Loose = 'loose',
|
||||
@@ -59,6 +79,9 @@ export type OnConnectEnd = (event: MouseEvent | TouchEvent, connectionState: Fin
|
||||
|
||||
export type IsValidConnection = (edge: EdgeBase | Connection) => boolean;
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type FitViewParamsBase<NodeType extends NodeBase> = {
|
||||
nodes: Map<string, InternalNodeBase<NodeType>>;
|
||||
width: number;
|
||||
@@ -68,6 +91,9 @@ export type FitViewParamsBase<NodeType extends NodeBase> = {
|
||||
maxZoom: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
||||
padding?: number;
|
||||
includeHiddenNodes?: boolean;
|
||||
@@ -77,6 +103,17 @@ export type FitViewOptionsBase<NodeType extends NodeBase = NodeBase> = {
|
||||
nodes?: (NodeType | { id: string })[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Internally, React Flow maintains a coordinate system that is independent of the
|
||||
* rest of the page. The `Viewport` type tells you where in that system your flow
|
||||
* is currently being display at and how zoomed in or out it is.
|
||||
*
|
||||
* @public
|
||||
* @remarks A `Transform` has the same properties as the viewport, but they represent
|
||||
* different things. Make sure you don't get them muddled up or things will start
|
||||
* to look weird!
|
||||
*
|
||||
*/
|
||||
export type Viewport = {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -87,12 +124,23 @@ export type KeyCode = string | Array<string>;
|
||||
|
||||
export type SnapGrid = [number, number];
|
||||
|
||||
/**
|
||||
* This enum is used to set the different modes of panning the viewport when the
|
||||
* user scrolls. The `Free` mode allows the user to pan in any direction by scrolling
|
||||
* with a device like a trackpad. The `Vertical` and `Horizontal` modes restrict
|
||||
* scroll panning to only the vertical or horizontal axis, respectively.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum PanOnScrollMode {
|
||||
Free = 'free',
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal',
|
||||
}
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type ViewportHelperFunctionOptions = {
|
||||
duration?: number;
|
||||
};
|
||||
@@ -113,6 +161,14 @@ export type D3ZoomHandler = (this: Element, event: any, d: unknown) => void;
|
||||
|
||||
export type UpdateNodeInternals = (nodeId: string | string[]) => void;
|
||||
|
||||
/**
|
||||
* This type is mostly used to help position things on top of the flow viewport. For
|
||||
* example both the [`<MiniMap />`](/api-reference/components/minimap) and
|
||||
* [`<Controls />`](/api-reference/components/controls) components take a `position`
|
||||
* prop of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
|
||||
export type ProOptions = {
|
||||
@@ -174,6 +230,13 @@ export type ConnectionInProgress<NodeType extends InternalNodeBase = InternalNod
|
||||
toPosition: Position;
|
||||
toNode: NodeType | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `ConnectionState` type bundles all information about an ongoing connection.
|
||||
* It is returned by the [`useConnection`](/api-reference/hooks/use-connection) hook.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ConnectionState<NodeType extends InternalNodeBase = InternalNodeBase> =
|
||||
| ConnectionInProgress<NodeType>
|
||||
| NoConnection;
|
||||
|
||||
@@ -14,11 +14,13 @@ export type Handle = {
|
||||
};
|
||||
|
||||
export type HandleProps = {
|
||||
/** Type of the handle
|
||||
/**
|
||||
* Type of the handle
|
||||
* @example HandleType.Source, HandleType.Target
|
||||
*/
|
||||
type: HandleType;
|
||||
/** Position of the handle
|
||||
/**
|
||||
* Position of the handle
|
||||
* @example Position.TopLeft, Position.TopRight,
|
||||
* Position.BottomLeft, Position.BottomRight
|
||||
*/
|
||||
@@ -29,11 +31,13 @@ export type HandleProps = {
|
||||
isConnectableStart?: boolean;
|
||||
/** Should you be able to connect to this handle */
|
||||
isConnectableEnd?: boolean;
|
||||
/** Callback if connection is valid
|
||||
/**
|
||||
* Callback if connection is valid
|
||||
* @remarks connection becomes an edge if isValidConnection returns true
|
||||
*/
|
||||
isValidConnection?: IsValidConnection;
|
||||
/** Id of the handle
|
||||
/**
|
||||
* Id of the handle
|
||||
* @remarks optional if there is only one handle of this type
|
||||
*/
|
||||
id?: string | null;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Optional } from '../utils/types';
|
||||
/**
|
||||
* Framework independent node data structure.
|
||||
*
|
||||
* @inline
|
||||
* @typeParam NodeData - type of the node data
|
||||
* @typeParam NodeType - type of the node
|
||||
*/
|
||||
@@ -13,7 +14,8 @@ export type NodeBase<
|
||||
> = {
|
||||
/** Unique id of a node */
|
||||
id: string;
|
||||
/** Position of a node on the pane
|
||||
/**
|
||||
* Position of a node on the pane
|
||||
* @example { x: 0, y: 0 }
|
||||
*/
|
||||
position: XYPosition;
|
||||
@@ -21,11 +23,13 @@ export type NodeBase<
|
||||
data: NodeData;
|
||||
/** Type of node defined in nodeTypes */
|
||||
type?: NodeType;
|
||||
/** Only relevant for default, source, target nodeType. controls source position
|
||||
/**
|
||||
* Only relevant for default, source, target nodeType. controls source position
|
||||
* @example 'right', 'left', 'top', 'bottom'
|
||||
*/
|
||||
sourcePosition?: Position;
|
||||
/** Only relevant for default, source, target nodeType. controls target position
|
||||
/**
|
||||
* Only relevant for default, source, target nodeType. controls target position
|
||||
* @example 'right', 'left', 'top', 'bottom'
|
||||
*/
|
||||
targetPosition?: Position;
|
||||
@@ -45,13 +49,15 @@ export type NodeBase<
|
||||
/** Parent node id, used for creating sub-flows */
|
||||
parentId?: string;
|
||||
zIndex?: number;
|
||||
/** Boundary a node can be moved in
|
||||
/**
|
||||
* Boundary a node can be moved in
|
||||
* @example 'parent' or [[0, 0], [100, 100]]
|
||||
*/
|
||||
extent?: 'parent' | CoordinateExtent;
|
||||
expandParent?: boolean;
|
||||
ariaLabel?: string;
|
||||
/** Origin of the node relative to it's position
|
||||
/**
|
||||
* Origin of the node relative to it's position
|
||||
* @example
|
||||
* [0.5, 0.5] // centers the node
|
||||
* [0, 0] // top left
|
||||
@@ -73,8 +79,10 @@ export type InternalNodeBase<NodeType extends NodeBase = NodeBase> = NodeType &
|
||||
internals: {
|
||||
positionAbsolute: XYPosition;
|
||||
z: number;
|
||||
/** Holds a reference to the original node object provided by the user.
|
||||
* Used as an optimization to avoid certain operations. */
|
||||
/**
|
||||
* Holds a reference to the original node object provided by the user.
|
||||
* Used as an optimization to avoid certain operations.
|
||||
*/
|
||||
userNode: NodeType;
|
||||
handleBounds?: NodeHandleBounds;
|
||||
bounds?: NodeBounds;
|
||||
@@ -82,7 +90,7 @@ export type InternalNodeBase<NodeType extends NodeBase = NodeBase> = NodeType &
|
||||
};
|
||||
|
||||
/**
|
||||
* The node data structure that gets used for the nodes prop.
|
||||
* The node data structure that gets used for the custom nodes props.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
@@ -134,10 +142,22 @@ export type NodeDragItem = {
|
||||
expandParent?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The origin of a Node determines how it is placed relative to its own coordinates.
|
||||
* `[0, 0]` places it at the top left corner, `[0.5, 0.5]` right in the center and
|
||||
* `[1, 1]` at the bottom right of its position.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type NodeOrigin = [number, number];
|
||||
|
||||
export type OnSelectionDrag = (event: MouseEvent, nodes: NodeBase[]) => void;
|
||||
|
||||
/**
|
||||
* Type for the handles of a node
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type NodeHandle = Omit<Optional<Handle, 'width' | 'height'>, 'nodeId'>;
|
||||
|
||||
export type Align = 'center' | 'start' | 'end';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/**
|
||||
* While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a
|
||||
* component in the corners of a container, the `Position` enum is less precise and used
|
||||
* primarily in relation to edges and handles.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum Position {
|
||||
Left = 'left',
|
||||
Top = 'top',
|
||||
@@ -12,6 +19,11 @@ export const oppositePosition = {
|
||||
[Position.Bottom]: Position.Top,
|
||||
};
|
||||
|
||||
/**
|
||||
* All positions are stored in an object with x and y coordinates.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type XYPosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -33,4 +45,14 @@ export type Box = XYPosition & {
|
||||
|
||||
export type Transform = [number, number, number];
|
||||
|
||||
/**
|
||||
* A coordinate extent represents two points in a coordinate system: one in the top
|
||||
* left corner and one in the bottom right corner. It is used to represent the
|
||||
* bounds of nodes in the flow or the bounds of the viewport.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @remarks Props that expect a `CoordinateExtent` usually default to `[[-∞, -∞], [+∞, +∞]]`
|
||||
* to represent an unbounded extent.
|
||||
*/
|
||||
export type CoordinateExtent = [[number, number], [number, number]];
|
||||
|
||||
@@ -61,9 +61,11 @@ export const getEventPosition = (event: MouseEvent | TouchEvent, bounds?: DOMRec
|
||||
};
|
||||
};
|
||||
|
||||
// The handle bounds are calculated relative to the node element.
|
||||
// We store them in the internals object of the node in order to avoid
|
||||
// unnecessary recalculations.
|
||||
/*
|
||||
* The handle bounds are calculated relative to the node element.
|
||||
* We store them in the internals object of the node in order to avoid
|
||||
* unnecessary recalculations.
|
||||
*/
|
||||
export const getHandleBounds = (
|
||||
type: 'source' | 'target',
|
||||
nodeElement: HTMLDivElement,
|
||||
|
||||
@@ -38,8 +38,10 @@ export function getBezierEdgeCenter({
|
||||
targetControlX: number;
|
||||
targetControlY: number;
|
||||
}): [number, number, number, number] {
|
||||
// cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
||||
// https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
||||
/*
|
||||
* cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
||||
* https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
||||
*/
|
||||
const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
|
||||
const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
|
||||
const offsetX = Math.abs(centerX - sourceX);
|
||||
@@ -70,7 +72,9 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurva
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bezier path from source to target handle
|
||||
* The `getBezierPath` util returns everything you need to render a bezier edge
|
||||
*between two nodes.
|
||||
* @public
|
||||
* @param params.sourceX - The x position of the source handle
|
||||
* @param params.sourceY - The y position of the source handle
|
||||
* @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
|
||||
@@ -80,17 +84,22 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurva
|
||||
* @param params.curvature - The curvature of the bezier edge
|
||||
* @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
|
||||
* @example
|
||||
* ```js
|
||||
* const source = { x: 0, y: 20 };
|
||||
const target = { x: 150, y: 100 };
|
||||
|
||||
const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
|
||||
sourceX: source.x,
|
||||
sourceY: source.y,
|
||||
sourcePosition: Position.Right,
|
||||
targetX: target.x,
|
||||
targetY: target.y,
|
||||
targetPosition: Position.Left,
|
||||
});
|
||||
* const target = { x: 150, y: 100 };
|
||||
*
|
||||
* const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
|
||||
* sourceX: source.x,
|
||||
* sourceY: source.y,
|
||||
* sourcePosition: Position.Right,
|
||||
* targetX: target.x,
|
||||
* targetY: target.y,
|
||||
* targetPosition: Position.Left,
|
||||
*});
|
||||
*```
|
||||
*
|
||||
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to
|
||||
*work with multiple edge paths at once.
|
||||
*/
|
||||
export function getBezierPath({
|
||||
sourceX,
|
||||
|
||||
@@ -90,12 +90,16 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* This util is a convenience function to add a new Edge to an array of edges
|
||||
* @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
|
||||
* This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
|
||||
* @public
|
||||
* @param edgeParams - Either an Edge or a Connection you want to add
|
||||
* @param edges - The array of all current edges
|
||||
* @returns A new array of edges with the new edge added
|
||||
*
|
||||
* @remarks If an edge with the same `target` and `source` already exists (and the same
|
||||
*`targetHandle` and `sourceHandle` if those are set), then this util won't add
|
||||
*a new edge even if the `id` property is different.
|
||||
*
|
||||
*/
|
||||
export const addEdge = <EdgeType extends EdgeBase>(
|
||||
edgeParams: EdgeType | Connection,
|
||||
@@ -137,12 +141,21 @@ export type ReconnectEdgeOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A handy utility to reconnect an existing edge with new properties
|
||||
* A handy utility to update an existing [`Edge`](/api-reference/types/edge) with new properties.
|
||||
*This searches your edge array for an edge with a matching `id` and updates its
|
||||
*properties with the connection you provide.
|
||||
* @public
|
||||
* @param oldEdge - The edge you want to update
|
||||
* @param newConnection - The new connection you want to update the edge with
|
||||
* @param edges - The array of all current edges
|
||||
* @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
|
||||
* @returns the updated edges array
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
*const onReconnect = useCallback(
|
||||
* (oldEdge: Edge, newConnection: Connection) => setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),[]);
|
||||
*```
|
||||
*/
|
||||
export const reconnectEdge = <EdgeType extends EdgeBase>(
|
||||
oldEdge: EdgeType,
|
||||
|
||||
@@ -38,8 +38,10 @@ const getDirection = ({
|
||||
|
||||
const distance = (a: XYPosition, b: XYPosition) => Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
|
||||
|
||||
// ith this function we try to mimic a orthogonal edge routing behaviour
|
||||
// It's not as good as a real orthogonal edge routing but it's faster and good enough as a default for step and smooth step edges
|
||||
/*
|
||||
* ith this function we try to mimic a orthogonal edge routing behaviour
|
||||
* It's not as good as a real orthogonal edge routing but it's faster and good enough as a default for step and smooth step edges
|
||||
*/
|
||||
function getPoints({
|
||||
source,
|
||||
sourcePosition = Position.Bottom,
|
||||
@@ -83,16 +85,20 @@ function getPoints({
|
||||
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
||||
centerX = center.x ?? defaultCenterX;
|
||||
centerY = center.y ?? defaultCenterY;
|
||||
// --->
|
||||
// |
|
||||
// >---
|
||||
/*
|
||||
* --->
|
||||
* |
|
||||
* >---
|
||||
*/
|
||||
const verticalSplit: XYPosition[] = [
|
||||
{ x: centerX, y: sourceGapped.y },
|
||||
{ x: centerX, y: targetGapped.y },
|
||||
];
|
||||
// |
|
||||
// ---
|
||||
// |
|
||||
/*
|
||||
* |
|
||||
* ---
|
||||
* |
|
||||
*/
|
||||
const horizontalSplit: XYPosition[] = [
|
||||
{ x: sourceGapped.x, y: centerY },
|
||||
{ x: targetGapped.x, y: centerY },
|
||||
@@ -191,7 +197,10 @@ function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): str
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a smooth step path from source to target handle
|
||||
* The `getSmoothStepPath` util returns everything you need to render a stepped path
|
||||
*between two nodes. The `borderRadius` property can be used to choose how rounded
|
||||
*the corners of those steps are.
|
||||
* @public
|
||||
* @param params.sourceX - The x position of the source handle
|
||||
* @param params.sourceY - The y position of the source handle
|
||||
* @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
|
||||
@@ -200,17 +209,20 @@ function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): str
|
||||
* @param params.targetPosition - The position of the target handle (default: Position.Top)
|
||||
* @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
|
||||
* @example
|
||||
* ```js
|
||||
* const source = { x: 0, y: 20 };
|
||||
const target = { x: 150, y: 100 };
|
||||
|
||||
const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
|
||||
sourceX: source.x,
|
||||
sourceY: source.y,
|
||||
sourcePosition: Position.Right,
|
||||
targetX: target.x,
|
||||
targetY: target.y,
|
||||
targetPosition: Position.Left,
|
||||
});
|
||||
* const target = { x: 150, y: 100 };
|
||||
*
|
||||
* const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
|
||||
* sourceX: source.x,
|
||||
* sourceY: source.y,
|
||||
* sourcePosition: Position.Right,
|
||||
* targetX: target.x,
|
||||
* targetY: target.y,
|
||||
* targetPosition: Position.Left,
|
||||
* });
|
||||
* ```
|
||||
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
|
||||
*/
|
||||
export function getSmoothStepPath({
|
||||
sourceX,
|
||||
|
||||
@@ -8,24 +8,28 @@ export type GetStraightPathParams = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a straight path from source to target handle
|
||||
* Calculates the straight line path between two points.
|
||||
* @public
|
||||
* @param params.sourceX - The x position of the source handle
|
||||
* @param params.sourceY - The y position of the source handle
|
||||
* @param params.targetX - The x position of the target handle
|
||||
* @param params.targetY - The y position of the target handle
|
||||
* @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
|
||||
* @example
|
||||
* ```js
|
||||
* const source = { x: 0, y: 20 };
|
||||
const target = { x: 150, y: 100 };
|
||||
|
||||
const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
|
||||
sourceX: source.x,
|
||||
sourceY: source.y,
|
||||
sourcePosition: Position.Right,
|
||||
targetX: target.x,
|
||||
targetY: target.y,
|
||||
targetPosition: Position.Left,
|
||||
});
|
||||
* const target = { x: 150, y: 100 };
|
||||
*
|
||||
* const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
|
||||
* sourceX: source.x,
|
||||
* sourceY: source.y,
|
||||
* sourcePosition: Position.Right,
|
||||
* targetX: target.x,
|
||||
* targetY: target.y,
|
||||
* targetPosition: Position.Left,
|
||||
* });
|
||||
* ```
|
||||
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
|
||||
*/
|
||||
export function getStraightPath({
|
||||
sourceX,
|
||||
|
||||
@@ -186,8 +186,8 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
|
||||
* @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}
|
||||
* @example
|
||||
* const { x, y, zoom } = getViewportForBounds(
|
||||
{ x: 0, y: 0, width: 100, height: 100},
|
||||
1200, 800, 0.5, 2);
|
||||
*{ x: 0, y: 0, width: 100, height: 100},
|
||||
*1200, 800, 0.5, 2);
|
||||
*/
|
||||
export const getViewportForBounds = (
|
||||
bounds: Rect,
|
||||
|
||||
@@ -54,12 +54,27 @@ export const isInternalNodeBase = <NodeType extends InternalNodeBase = InternalN
|
||||
): element is NodeType => 'id' in element && 'internals' in element && !('source' in element) && !('target' in element);
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.source === node.id
|
||||
* This util is used to tell you what nodes, if any, are connected to the given node
|
||||
* as the _target_ of an edge.
|
||||
* @public
|
||||
* @param node - The node to get the connected nodes from
|
||||
* @param nodes - The array of all nodes
|
||||
* @param edges - The array of all edges
|
||||
* @returns An array of nodes that are connected over eges where the source is the given node
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
*import { getOutgoers } from '@xyflow/react';
|
||||
*
|
||||
*const nodes = [];
|
||||
*const edges = [];
|
||||
*
|
||||
*const outgoers = getOutgoers(
|
||||
* { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
|
||||
* nodes,
|
||||
* edges,
|
||||
*);
|
||||
*```
|
||||
*/
|
||||
export const getOutgoers = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
node: NodeType | { id: string },
|
||||
@@ -81,12 +96,27 @@ export const getOutgoers = <NodeType extends NodeBase = NodeBase, EdgeType exten
|
||||
};
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.target === node.id
|
||||
* This util is used to tell you what nodes, if any, are connected to the given node
|
||||
* as the _source_ of an edge.
|
||||
* @public
|
||||
* @param node - The node to get the connected nodes from
|
||||
* @param nodes - The array of all nodes
|
||||
* @param edges - The array of all edges
|
||||
* @returns An array of nodes that are connected over eges where the target is the given node
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
*import { getIncomers } from '@xyflow/react';
|
||||
*
|
||||
*const nodes = [];
|
||||
*const edges = [];
|
||||
*
|
||||
*const incomers = getIncomers(
|
||||
* { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
|
||||
* nodes,
|
||||
* edges,
|
||||
*);
|
||||
*```
|
||||
*/
|
||||
export const getIncomers = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
node: NodeType | { id: string },
|
||||
@@ -124,12 +154,40 @@ export type GetNodesBoundsParams<NodeType extends NodeBase = NodeBase> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function for determining a bounding box that contains all given nodes in an array.
|
||||
* Returns the bounding box that contains all the given nodes in an array. This can
|
||||
* be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds)
|
||||
* to calculate the correct transform to fit the given nodes in a viewport.
|
||||
* @public
|
||||
* @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport.
|
||||
* @param nodes - Nodes to calculate the bounds for
|
||||
* @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
|
||||
* @returns Bounding box enclosing all nodes
|
||||
*
|
||||
* @remarks This function was previously called `getRectOfNodes`
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
*import { getNodesBounds } from '@xyflow/react';
|
||||
*
|
||||
*const nodes = [
|
||||
* {
|
||||
* id: 'a',
|
||||
* position: { x: 0, y: 0 },
|
||||
* data: { label: 'a' },
|
||||
* width: 50,
|
||||
* height: 25,
|
||||
* },
|
||||
* {
|
||||
* id: 'b',
|
||||
* position: { x: 100, y: 100 },
|
||||
* data: { label: 'b' },
|
||||
* width: 50,
|
||||
* height: 25,
|
||||
* },
|
||||
*];
|
||||
*
|
||||
*const bounds = getNodesBounds(nodes);
|
||||
*```
|
||||
*/
|
||||
export const getNodesBounds = <NodeType extends NodeBase = NodeBase>(
|
||||
nodes: (NodeType | InternalNodeBase<NodeType> | string)[],
|
||||
@@ -238,10 +296,30 @@ export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all connecting edges for a given set of nodes
|
||||
* This utility filters an array of edges, keeping only those where either the source or target
|
||||
* node is present in the given array of nodes.
|
||||
* @public
|
||||
* @param nodes - Nodes you want to get the connected edges for
|
||||
* @param edges - All edges
|
||||
* @returns Array of edges that connect any of the given nodes with each other
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
*import { getConnectedEdges } from '@xyflow/react';
|
||||
*
|
||||
*const nodes = [
|
||||
* { id: 'a', position: { x: 0, y: 0 } },
|
||||
* { id: 'b', position: { x: 100, y: 0 } },
|
||||
*];
|
||||
*
|
||||
*const edges = [
|
||||
* { id: 'a->c', source: 'a', target: 'c' },
|
||||
* { id: 'c->d', source: 'c', target: 'd' },
|
||||
*];
|
||||
*
|
||||
*const connectedEdges = getConnectedEdges(nodes, edges);
|
||||
* // => [{ id: 'a->c', source: 'a', target: 'c' }]
|
||||
*```
|
||||
*/
|
||||
export const getConnectedEdges = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
nodes: NodeType[],
|
||||
|
||||
@@ -15,8 +15,10 @@ export function getNodeToolbarTransform(
|
||||
alignmentOffset = 1;
|
||||
}
|
||||
|
||||
// position === Position.Top
|
||||
// we set the x any y position of the toolbar based on the nodes position
|
||||
/*
|
||||
* position === Position.Top
|
||||
* we set the x any y position of the toolbar based on the nodes position
|
||||
*/
|
||||
let pos = [
|
||||
(nodeRect.x + nodeRect.width * alignmentOffset) * viewport.zoom + viewport.x,
|
||||
nodeRect.y * viewport.zoom + viewport.y - offset,
|
||||
|
||||
@@ -276,8 +276,10 @@ export function handleExpandParent(
|
||||
},
|
||||
});
|
||||
|
||||
// We move all child nodes in the oppsite direction
|
||||
// so the x,y changes of the parent do not move the children
|
||||
/*
|
||||
* We move all child nodes in the oppsite direction
|
||||
* so the x,y changes of the parent do not move the children
|
||||
*/
|
||||
parentLookup.get(parentId)?.forEach((childNode) => {
|
||||
if (!children.some((child) => child.id === childNode.id)) {
|
||||
changes.push({
|
||||
@@ -472,9 +474,11 @@ function addConnectionToLookup(
|
||||
nodeId: string,
|
||||
handleId: string | null
|
||||
) {
|
||||
// We add the connection to the connectionLookup at the following keys
|
||||
// 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId
|
||||
// If the key already exists, we add the connection to the existing map
|
||||
/*
|
||||
* We add the connection to the connectionLookup at the following keys
|
||||
* 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId
|
||||
* If the key already exists, we add the connection to the existing map
|
||||
*/
|
||||
let key = nodeId;
|
||||
const nodeMap = connectionLookup.get(key) || new Map();
|
||||
connectionLookup.set(key, nodeMap.set(connectionKey, connection));
|
||||
|
||||
@@ -140,8 +140,10 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
|
||||
for (const [id, dragItem] of dragItems) {
|
||||
if (!nodeLookup.has(id)) {
|
||||
// if the node is not in the nodeLookup anymore, it was probably deleted while dragging
|
||||
// and we don't need to update it anymore
|
||||
/*
|
||||
* if the node is not in the nodeLookup anymore, it was probably deleted while dragging
|
||||
* and we don't need to update it anymore
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -150,8 +152,10 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
nextPosition = snapPosition(nextPosition, snapGrid);
|
||||
}
|
||||
|
||||
// if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node
|
||||
// based on its position so that the node stays at it's position relative to the selection.
|
||||
/*
|
||||
* if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node
|
||||
* based on its position so that the node stays at it's position relative to the selection.
|
||||
*/
|
||||
let adjustedNodeExtent: CoordinateExtent = [
|
||||
[nodeExtent[0][0], nodeExtent[0][1]],
|
||||
[nodeExtent[1][0], nodeExtent[1][1]],
|
||||
|
||||
@@ -74,9 +74,11 @@ export function getDragItems<NodeType extends NodeBase>(
|
||||
return dragItems;
|
||||
}
|
||||
|
||||
// returns two params:
|
||||
// 1. the dragged node (or the first of the list, if we are dragging a node selection)
|
||||
// 2. array of selected nodes (for multi selections)
|
||||
/*
|
||||
* returns two params:
|
||||
* 1. the dragged node (or the first of the list, if we are dragging a node selection)
|
||||
* 2. array of selected nodes (for multi selections)
|
||||
*/
|
||||
export function getEventHandlerParams<NodeType extends InternalNodeBase>({
|
||||
nodeId,
|
||||
dragItems,
|
||||
@@ -112,10 +114,10 @@ export function getEventHandlerParams<NodeType extends InternalNodeBase>({
|
||||
!node
|
||||
? nodesFromDragItems[0]
|
||||
: {
|
||||
...node,
|
||||
position: dragItems.get(nodeId)?.position || node.position,
|
||||
dragging,
|
||||
},
|
||||
...node,
|
||||
position: dragItems.get(nodeId)?.position || node.position,
|
||||
dragging,
|
||||
},
|
||||
nodesFromDragItems,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -165,8 +165,10 @@ function onPointerDown(
|
||||
toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId)! : null,
|
||||
};
|
||||
|
||||
// we don't want to trigger an update when the connection
|
||||
// is snapped to the same handle as before
|
||||
/*
|
||||
* we don't want to trigger an update when the connection
|
||||
* is snapped to the same handle as before
|
||||
*/
|
||||
if (
|
||||
isValid &&
|
||||
closestHandle &&
|
||||
@@ -190,8 +192,10 @@ function onPointerDown(
|
||||
onConnect?.(connection);
|
||||
}
|
||||
|
||||
// it's important to get a fresh reference from the store here
|
||||
// in order to get the latest state of onConnectEnd
|
||||
/*
|
||||
* it's important to get a fresh reference from the store here
|
||||
* in order to get the latest state of onConnectEnd
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { inProgress, ...connectionState } = previousConnection;
|
||||
const finalConnectionState = {
|
||||
@@ -248,8 +252,10 @@ function isValidHandle(
|
||||
|
||||
const { x, y } = getEventPosition(event);
|
||||
const handleBelow = doc.elementFromPoint(x, y);
|
||||
// we always want to prioritize the handle below the mouse cursor over the closest distance handle,
|
||||
// because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
|
||||
/*
|
||||
* we always want to prioritize the handle below the mouse cursor over the closest distance handle,
|
||||
* because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
|
||||
*/
|
||||
const handleToCheck = handleBelow?.classList.contains(`${lib}-flow__handle`) ? handleBelow : handleDomNode;
|
||||
|
||||
const result: Result = {
|
||||
|
||||
@@ -19,8 +19,10 @@ function getNodesWithinDistance(position: XYPosition, nodeLookup: NodeLookup, di
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// this distance is used for the area around the user pointer
|
||||
// while doing a connection for finding the closest nodes
|
||||
/*
|
||||
* this distance is used for the area around the user pointer
|
||||
* while doing a connection for finding the closest nodes
|
||||
*/
|
||||
const ADDITIONAL_DISTANCE = 250;
|
||||
|
||||
export function getClosestHandle(
|
||||
|
||||
@@ -114,22 +114,22 @@ export function XYPanZoom({
|
||||
|
||||
const wheelHandler = isPanOnScroll
|
||||
? createPanOnScrollHandler({
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom: d3ZoomInstance,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
})
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom: d3ZoomInstance,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
})
|
||||
: createZoomOnScrollHandler({
|
||||
noWheelClassName,
|
||||
preventScrolling,
|
||||
d3ZoomHandler,
|
||||
});
|
||||
noWheelClassName,
|
||||
preventScrolling,
|
||||
d3ZoomHandler,
|
||||
});
|
||||
|
||||
d3Selection.on('wheel.zoom', wheelHandler, { passive: false });
|
||||
|
||||
@@ -178,9 +178,11 @@ export function XYPanZoom({
|
||||
});
|
||||
d3ZoomInstance.filter(filter);
|
||||
|
||||
// We cannot add zoomOnDoubleClick to the filter above because
|
||||
// double tapping on touch screens circumvents the filter and
|
||||
// dblclick.zoom is fired on the selection directly
|
||||
/*
|
||||
* We cannot add zoomOnDoubleClick to the filter above because
|
||||
* double tapping on touch screens circumvents the filter and
|
||||
* dblclick.zoom is fired on the selection directly
|
||||
*/
|
||||
if (zoomOnDoubleClick) {
|
||||
d3Selection.on('dblclick.zoom', d3DblClickZoomHandler);
|
||||
} else {
|
||||
|
||||
@@ -90,8 +90,10 @@ export function createPanOnScrollHandler({
|
||||
return;
|
||||
}
|
||||
|
||||
// increase scroll speed in firefox
|
||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||
/*
|
||||
* increase scroll speed in firefox
|
||||
* firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||
*/
|
||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
||||
let deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
|
||||
let deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
|
||||
@@ -114,9 +116,11 @@ export function createPanOnScrollHandler({
|
||||
|
||||
clearTimeout(zoomPanValues.panScrollTimeout);
|
||||
|
||||
// for pan on scroll we need to handle the event calls on our own
|
||||
// we can't use the start, zoom and end events from d3-zoom
|
||||
// because start and move gets called on every scroll event and not once at the beginning
|
||||
/*
|
||||
* for pan on scroll we need to handle the event calls on our own
|
||||
* we can't use the start, zoom and end events from d3-zoom
|
||||
* because start and move gets called on every scroll event and not once at the beginning
|
||||
*/
|
||||
if (!zoomPanValues.isPanScrolling) {
|
||||
zoomPanValues.isPanScrolling = true;
|
||||
|
||||
|
||||
@@ -154,8 +154,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
|
||||
parentExtent = parentNode && node.extent === 'parent' ? nodeToParentExtent(parentNode) : undefined;
|
||||
}
|
||||
|
||||
// Collect all child nodes to correct their relative positions when top/left changes
|
||||
// Determine largest minimal extent the parent node is allowed to resize to
|
||||
/*
|
||||
* Collect all child nodes to correct their relative positions when top/left changes
|
||||
* Determine largest minimal extent the parent node is allowed to resize to
|
||||
*/
|
||||
childNodes = [];
|
||||
childExtent = undefined;
|
||||
|
||||
@@ -230,8 +232,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
|
||||
prevValues.x = change.x;
|
||||
prevValues.y = change.y;
|
||||
|
||||
// when top/left changes, correct the relative positions of child nodes
|
||||
// so that they stay in the same position
|
||||
/*
|
||||
* when top/left changes, correct the relative positions of child nodes
|
||||
* so that they stay in the same position
|
||||
*/
|
||||
if (childNodes.length > 0) {
|
||||
const xChange = x - prevX;
|
||||
const yChange = y - prevY;
|
||||
|
||||
@@ -11,10 +11,25 @@ export type ResizeParamsWithDirection = ResizeParams & {
|
||||
direction: number[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to determine the control line position of the NodeResizer
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ControlLinePosition = 'top' | 'bottom' | 'left' | 'right';
|
||||
|
||||
/**
|
||||
* Used to determine the control position of the NodeResizer
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ControlPosition = ControlLinePosition | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
||||
|
||||
/**
|
||||
* Used to determine the variant of the resize control
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum ResizeControlVariant {
|
||||
Line = 'line',
|
||||
Handle = 'handle',
|
||||
|
||||
Reference in New Issue
Block a user