Merge pull request #3700 from xyflow/refactor/tsdoc
Refactor: Cleanup types + TSDoc integration
This commit is contained in:
@@ -4,12 +4,12 @@ import type { PanelPosition, XYPosition } from '@xyflow/system';
|
||||
|
||||
import type { Node } from '../../types';
|
||||
|
||||
export type GetMiniMapNodeAttribute<NodeData = any> = (node: Node<NodeData>) => string;
|
||||
export type GetMiniMapNodeAttribute<NodeType extends Node = Node> = (node: NodeType) => string;
|
||||
|
||||
export type MiniMapProps<NodeData = any> = Omit<HTMLAttributes<SVGSVGElement>, 'onClick'> & {
|
||||
nodeColor?: string | GetMiniMapNodeAttribute<NodeData>;
|
||||
nodeStrokeColor?: string | GetMiniMapNodeAttribute<NodeData>;
|
||||
nodeClassName?: string | GetMiniMapNodeAttribute<NodeData>;
|
||||
export type MiniMapProps<NodeType extends Node = Node> = Omit<HTMLAttributes<SVGSVGElement>, 'onClick'> & {
|
||||
nodeColor?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||
nodeStrokeColor?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||
nodeClassName?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||
nodeBorderRadius?: number;
|
||||
nodeStrokeWidth?: number;
|
||||
nodeComponent?: ComponentType<MiniMapNodeProps>;
|
||||
@@ -18,7 +18,7 @@ export type MiniMapProps<NodeData = any> = Omit<HTMLAttributes<SVGSVGElement>, '
|
||||
maskStrokeWidth?: number;
|
||||
position?: PanelPosition;
|
||||
onClick?: (event: MouseEvent, position: XYPosition) => void;
|
||||
onNodeClick?: (event: MouseEvent, node: Node<NodeData>) => void;
|
||||
onNodeClick?: (event: MouseEvent, node: NodeType) => void;
|
||||
pannable?: boolean;
|
||||
zoomable?: boolean;
|
||||
ariaLabel?: string | null;
|
||||
@@ -27,8 +27,8 @@ export type MiniMapProps<NodeData = any> = Omit<HTMLAttributes<SVGSVGElement>, '
|
||||
offsetScale?: number;
|
||||
};
|
||||
|
||||
export type MiniMapNodes = Pick<
|
||||
MiniMapProps,
|
||||
export type MiniMapNodes<NodeType extends Node = Node> = Pick<
|
||||
MiniMapProps<NodeType>,
|
||||
'nodeColor' | 'nodeStrokeColor' | 'nodeClassName' | 'nodeBorderRadius' | 'nodeStrokeWidth' | 'nodeComponent'
|
||||
> & {
|
||||
onClick?: (event: MouseEvent, nodeId: string) => void;
|
||||
|
||||
@@ -9,6 +9,12 @@ function getMediaQuery() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for receiving the current color mode class 'dark' or 'light'.
|
||||
*
|
||||
* @internal
|
||||
* @param colorMode - The color mode to use ('dark', 'light' or 'system')
|
||||
*/
|
||||
export default function useColorModeClass(colorMode: ColorMode): ColorModeClass {
|
||||
const [colorModeClass, setColorModeClass] = useState<ColorModeClass | null>(
|
||||
colorMode === 'system' ? null : colorMode
|
||||
|
||||
@@ -13,6 +13,11 @@ type UseDragParams = {
|
||||
isSelectable?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for calling XYDrag helper from @xyflow/system.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }: UseDragParams) {
|
||||
const store = useStoreApi();
|
||||
const [dragging, setDragging] = useState<boolean>(false);
|
||||
|
||||
@@ -5,6 +5,12 @@ import type { Edge, ReactFlowState } from '../types';
|
||||
|
||||
const edgesSelector = (state: ReactFlowState) => state.edges;
|
||||
|
||||
/**
|
||||
* Hook for getting the current edges from the store.
|
||||
*
|
||||
* @public
|
||||
* @returns An array of edges
|
||||
*/
|
||||
function useEdges<EdgeData>(): Edge<EdgeData>[] {
|
||||
const edges = useStore(edgesSelector, shallow);
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ const selected = (item: Node | Edge) => item.selected;
|
||||
|
||||
const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false };
|
||||
|
||||
/**
|
||||
* Hook for handling global key events.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export default ({
|
||||
deleteKeyCode,
|
||||
multiSelectionKeyCode,
|
||||
|
||||
@@ -17,8 +17,8 @@ type useHandleConnectionsParams = {
|
||||
*
|
||||
* @public
|
||||
* @param param.type - handle type 'source' or 'target'
|
||||
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
|
||||
* @param param.nodeId - node id - if not provided, the node id from the NodeIdContext is used
|
||||
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
|
||||
* @param param.onConnect - gets called when a connection is established
|
||||
* @param param.onDisconnect - gets called when a connection is removed
|
||||
* @returns an array with connections
|
||||
|
||||
@@ -12,11 +12,19 @@ export type UseKeyPressOptions = {
|
||||
|
||||
const defaultDoc = typeof document !== 'undefined' ? document : null;
|
||||
|
||||
// the keycode can be a string 'a' or an array of strings ['a', 'a+d']
|
||||
// a string means a single key 'a' or a combination when '+' is used 'a+d'
|
||||
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
|
||||
// user can use the single key 'a' or the combination 'd' + 's'
|
||||
/**
|
||||
* Hook for handling key events.
|
||||
*
|
||||
* @public
|
||||
* @param param.keyCode - The key code (string or array of strings) to use
|
||||
* @param param.options - Options
|
||||
* @returns boolean
|
||||
*/
|
||||
export default (
|
||||
// the keycode can be a string 'a' or an array of strings ['a', 'a+d']
|
||||
// a string means a single key 'a' or a combination when '+' is used 'a+d'
|
||||
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
|
||||
// user can use the single key 'a' or the combination 'd' + 's'
|
||||
keyCode: KeyCode | null = null,
|
||||
options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true }
|
||||
): boolean => {
|
||||
|
||||
@@ -5,8 +5,14 @@ import type { Node, ReactFlowState } from '../types';
|
||||
|
||||
const nodesSelector = (state: ReactFlowState) => state.nodes;
|
||||
|
||||
function useNodes<NodeData>(): Node<NodeData>[] {
|
||||
const nodes = useStore(nodesSelector, shallow);
|
||||
/**
|
||||
* Hook for getting the current nodes from the store.
|
||||
*
|
||||
* @public
|
||||
* @returns An array of nodes
|
||||
*/
|
||||
function useNodes<NodeType extends Node = Node>(): NodeType[] {
|
||||
const nodes = useStore(nodesSelector, shallow) as NodeType[];
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,14 @@ import { shallow } from 'zustand/shallow';
|
||||
import { useStore } from '../hooks/useStore';
|
||||
import type { Node } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for receiving data of one or multiple nodes
|
||||
*
|
||||
* @public
|
||||
* @param nodeId - The id (or ids) of the node to get the data from
|
||||
* @param guard - Optional guard function to narrow down the node type
|
||||
* @returns An array od data objects
|
||||
*/
|
||||
export function useNodesData<NodeType extends Node = Node>(nodeId: string): NodeType['data'] | null;
|
||||
export function useNodesData<NodeType extends Node = Node>(nodeIds: string[]): NodeType['data'][];
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
|
||||
@@ -7,18 +7,16 @@ import type { Node, NodeChange, Edge, EdgeChange } from '../types';
|
||||
type ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[];
|
||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||
|
||||
// returns a hook that can be used liked this:
|
||||
// const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
function createUseItemsState(
|
||||
applyChanges: ApplyChanges<Node, NodeChange>
|
||||
): <NodeData = any>(
|
||||
initialItems: Node<NodeData>[]
|
||||
) => [Node<NodeData>[], Dispatch<SetStateAction<Node<NodeData>[]>>, OnChange<NodeChange>];
|
||||
): <NodeType extends Node = Node>(
|
||||
initialItems: NodeType[]
|
||||
) => [NodeType[], Dispatch<SetStateAction<NodeType[]>>, OnChange<NodeChange>];
|
||||
function createUseItemsState(
|
||||
applyChanges: ApplyChanges<Edge, EdgeChange>
|
||||
): <EdgeData = any>(
|
||||
initialItems: Edge<EdgeData>[]
|
||||
) => [Edge<EdgeData>[], Dispatch<SetStateAction<Edge<EdgeData>[]>>, OnChange<EdgeChange>];
|
||||
): <EdgeType extends Edge = Edge>(
|
||||
initialItems: EdgeType[]
|
||||
) => [EdgeType[], Dispatch<SetStateAction<EdgeType[]>>, OnChange<EdgeChange>];
|
||||
function createUseItemsState(
|
||||
applyChanges: ApplyChanges<any, any>
|
||||
): (initialItems: any[]) => [any[], Dispatch<SetStateAction<any[]>>, OnChange<any>] {
|
||||
@@ -31,5 +29,20 @@ function createUseItemsState(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
|
||||
*
|
||||
* @public
|
||||
* @param initialNodes
|
||||
* @returns an array [nodes, setNodes, onNodesChange]
|
||||
*/
|
||||
export const useNodesState = createUseItemsState(applyNodeChanges);
|
||||
|
||||
/**
|
||||
* Hook for managing the state of edges - should only be used for prototyping / simple use cases.
|
||||
*
|
||||
* @public
|
||||
* @param initialEdges
|
||||
* @returns an array [edges, setEdges, onEdgesChange]
|
||||
*/
|
||||
export const useEdgesState = createUseItemsState(applyEdgeChanges);
|
||||
|
||||
@@ -21,6 +21,13 @@ const defaultOptions = {
|
||||
includeHiddenNodes: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook which returns true when all nodes are initialized.
|
||||
*
|
||||
* @public
|
||||
* @param options.includeHiddenNodes - defaults to false
|
||||
* @returns boolean indicating whether all nodes are initialized
|
||||
*/
|
||||
function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
|
||||
const initialized = useStore(selector(options));
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ import { useEffect, useRef } from 'react';
|
||||
import useReactFlow from './useReactFlow';
|
||||
import type { OnInit } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for calling onInit handler.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useOnInitHandler(onInit: OnInit | undefined) {
|
||||
const rfInstance = useReactFlow();
|
||||
const isInitialized = useRef<boolean>(false);
|
||||
|
||||
@@ -7,6 +7,12 @@ export type UseOnSelectionChangeOptions = {
|
||||
onChange: OnSelectionChangeFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for registering an onSelectionChange handler.
|
||||
*
|
||||
* @public
|
||||
* @params params.onChange - The handler to register
|
||||
*/
|
||||
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -9,6 +9,14 @@ export type UseOnViewportChangeOptions = {
|
||||
onEnd?: OnViewportChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for registering an onViewportChange handler.
|
||||
*
|
||||
* @public
|
||||
* @param params.onStart - gets called when the viewport starts changing
|
||||
* @param params.onChange - gets called when the viewport changes
|
||||
* @param params.onEnd - gets called when the viewport stops changing
|
||||
*/
|
||||
function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
getElementsToRemove,
|
||||
getIncomersBase,
|
||||
getOutgoersBase,
|
||||
getOverlappingArea,
|
||||
isRectObject,
|
||||
nodeToRect,
|
||||
type Rect,
|
||||
} from '@xyflow/system';
|
||||
import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system';
|
||||
|
||||
import useViewportHelper from './useViewportHelper';
|
||||
import { useStoreApi } from './useStore';
|
||||
@@ -26,32 +18,40 @@ import type {
|
||||
} from '../types';
|
||||
import { isNode } from '../utils';
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
||||
/**
|
||||
* Hook for accessing the ReactFlow instance.
|
||||
*
|
||||
* @public
|
||||
* @returns ReactFlowInstance
|
||||
*/
|
||||
export default function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
|
||||
NodeType,
|
||||
EdgeType
|
||||
> {
|
||||
const viewportHelper = useViewportHelper();
|
||||
const store = useStoreApi();
|
||||
|
||||
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
||||
return store.getState().nodes.map((n) => ({ ...n }));
|
||||
const getNodes = useCallback<Instance.GetNodes<NodeType>>(() => {
|
||||
return store.getState().nodes.map((n) => ({ ...n })) as NodeType[];
|
||||
}, []);
|
||||
|
||||
const getNode = useCallback<Instance.GetNode<NodeData>>((id) => {
|
||||
return store.getState().nodeLookup.get(id);
|
||||
const getNode = useCallback<Instance.GetNode<NodeType>>((id) => {
|
||||
return store.getState().nodeLookup.get(id) as NodeType;
|
||||
}, []);
|
||||
|
||||
const getEdges = useCallback<Instance.GetEdges<EdgeData>>(() => {
|
||||
const getEdges = useCallback<Instance.GetEdges<EdgeType>>(() => {
|
||||
const { edges = [] } = store.getState();
|
||||
return edges.map((e) => ({ ...e }));
|
||||
return edges.map((e) => ({ ...e })) as EdgeType[];
|
||||
}, []);
|
||||
|
||||
const getEdge = useCallback<Instance.GetEdge<EdgeData>>((id) => {
|
||||
const getEdge = useCallback<Instance.GetEdge<EdgeType>>((id) => {
|
||||
const { edges = [] } = store.getState();
|
||||
return edges.find((e) => e.id === id);
|
||||
return edges.find((e) => e.id === id) as EdgeType;
|
||||
}, []);
|
||||
|
||||
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
|
||||
const setNodes = useCallback<Instance.SetNodes<NodeType>>((payload) => {
|
||||
const { nodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
||||
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
||||
const nextNodes = typeof payload === 'function' ? payload(nodes as NodeType[]) : payload;
|
||||
|
||||
if (hasDefaultNodes) {
|
||||
setNodes(nextNodes);
|
||||
@@ -59,14 +59,14 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
const changes =
|
||||
nextNodes.length === 0
|
||||
? nodes.map((node) => ({ type: 'remove', id: node.id } as NodeRemoveChange))
|
||||
: nextNodes.map((node) => ({ item: node, type: 'reset' } as NodeResetChange<NodeData>));
|
||||
: nextNodes.map((node) => ({ item: node, type: 'reset' } as NodeResetChange<NodeType>));
|
||||
onNodesChange(changes);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const setEdges = useCallback<Instance.SetEdges<EdgeData>>((payload) => {
|
||||
const setEdges = useCallback<Instance.SetEdges<EdgeType>>((payload) => {
|
||||
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
||||
const nextEdges = typeof payload === 'function' ? payload(edges) : payload;
|
||||
const nextEdges = typeof payload === 'function' ? payload(edges as EdgeType[]) : payload;
|
||||
|
||||
if (hasDefaultEdges) {
|
||||
setEdges(nextEdges);
|
||||
@@ -74,12 +74,12 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
const changes =
|
||||
nextEdges.length === 0
|
||||
? edges.map((edge) => ({ type: 'remove', id: edge.id } as EdgeRemoveChange))
|
||||
: nextEdges.map((edge) => ({ item: edge, type: 'reset' } as EdgeResetChange<EdgeData>));
|
||||
: nextEdges.map((edge) => ({ item: edge, type: 'reset' } as EdgeResetChange<EdgeType>));
|
||||
onEdgesChange(changes);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
|
||||
const addNodes = useCallback<Instance.AddNodes<NodeType>>((payload) => {
|
||||
const nodes = Array.isArray(payload) ? payload : [payload];
|
||||
const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
|
||||
|
||||
@@ -87,29 +87,29 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
const nextNodes = [...currentNodes, ...nodes];
|
||||
setNodes(nextNodes);
|
||||
} else if (onNodesChange) {
|
||||
const changes = nodes.map((node) => ({ item: node, type: 'add' } as NodeAddChange<NodeData>));
|
||||
const changes = nodes.map((node) => ({ item: node, type: 'add' } as NodeAddChange<NodeType>));
|
||||
onNodesChange(changes);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addEdges = useCallback<Instance.AddEdges<EdgeData>>((payload) => {
|
||||
const addEdges = useCallback<Instance.AddEdges<EdgeType>>((payload) => {
|
||||
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
||||
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
||||
|
||||
if (hasDefaultEdges) {
|
||||
setEdges([...edges, ...nextEdges]);
|
||||
} else if (onEdgesChange) {
|
||||
const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' } as EdgeAddChange<EdgeData>));
|
||||
const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' } as EdgeAddChange<EdgeType>));
|
||||
onEdgesChange(changes);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
||||
const toObject = useCallback<Instance.ToObject<NodeType, EdgeType>>(() => {
|
||||
const { nodes = [], edges = [], transform } = store.getState();
|
||||
const [x, y, zoom] = transform;
|
||||
return {
|
||||
nodes: nodes.map((n) => ({ ...n })),
|
||||
edges: edges.map((e) => ({ ...e })),
|
||||
nodes: nodes.map((n) => ({ ...n })) as NodeType[],
|
||||
edges: edges.map((e) => ({ ...e })) as EdgeType[],
|
||||
viewport: {
|
||||
x,
|
||||
y,
|
||||
@@ -181,11 +181,9 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
}, []);
|
||||
|
||||
const getNodeRect = useCallback(
|
||||
(
|
||||
nodeOrRect: Node<NodeData> | { id: Node['id'] } | Rect
|
||||
): [Rect | null, Node<NodeData> | null | undefined, boolean] => {
|
||||
(nodeOrRect: NodeType | { id: Node['id'] } | Rect): [Rect | null, NodeType | null | undefined, boolean] => {
|
||||
const isRect = isRectObject(nodeOrRect);
|
||||
const node = isRect ? null : store.getState().nodeLookup.get(nodeOrRect.id);
|
||||
const node = isRect ? null : (store.getState().nodeLookup.get(nodeOrRect.id) as NodeType);
|
||||
|
||||
if (!isRect && !node) {
|
||||
[null, null, isRect];
|
||||
@@ -198,7 +196,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
[]
|
||||
);
|
||||
|
||||
const getIntersectingNodes = useCallback<Instance.GetIntersectingNodes<NodeData>>(
|
||||
const getIntersectingNodes = useCallback<Instance.GetIntersectingNodes<NodeType>>(
|
||||
(nodeOrRect, partially = true, nodes) => {
|
||||
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
||||
|
||||
@@ -216,12 +214,12 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
const partiallyVisible = partially && overlappingArea > 0;
|
||||
|
||||
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
||||
});
|
||||
}) as NodeType[];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const isNodeIntersecting = useCallback<Instance.IsNodeIntersecting<NodeData>>(
|
||||
const isNodeIntersecting = useCallback<Instance.IsNodeIntersecting<NodeType>>(
|
||||
(nodeOrRect, area, partially = true) => {
|
||||
const [nodeRect] = getNodeRect(nodeOrRect);
|
||||
|
||||
@@ -237,48 +235,13 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
[]
|
||||
);
|
||||
|
||||
const getConnectedEdges = useCallback<Instance.getConnectedEdges>((node) => {
|
||||
const { edges } = store.getState();
|
||||
|
||||
const nodeIds = new Set();
|
||||
if (typeof node === 'string') {
|
||||
nodeIds.add(node);
|
||||
} else if (node.length >= 1) {
|
||||
node.forEach((n) => {
|
||||
nodeIds.add(n.id);
|
||||
});
|
||||
}
|
||||
|
||||
return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
|
||||
}, []);
|
||||
|
||||
const getIncomers = useCallback<Instance.getIncomers>((node) => {
|
||||
const { nodes, edges } = store.getState();
|
||||
|
||||
if (typeof node === 'string') {
|
||||
return getIncomersBase({ id: node }, nodes, edges);
|
||||
}
|
||||
|
||||
return getIncomersBase(node, nodes, edges);
|
||||
}, []);
|
||||
|
||||
const getOutgoers = useCallback<Instance.getOutgoers>((node) => {
|
||||
const { nodes, edges } = store.getState();
|
||||
|
||||
if (typeof node == 'string') {
|
||||
return getOutgoersBase({ id: node }, nodes, edges);
|
||||
}
|
||||
|
||||
return getOutgoersBase(node, nodes, edges);
|
||||
}, []);
|
||||
|
||||
const updateNode = useCallback<Instance.UpdateNode>(
|
||||
const updateNode = useCallback<Instance.UpdateNode<NodeType>>(
|
||||
(id, nodeUpdate, options = { replace: true }) => {
|
||||
setNodes((prevNodes) =>
|
||||
prevNodes.map((node) => {
|
||||
if (node.id === id) {
|
||||
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate;
|
||||
return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
|
||||
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as NodeType) : nodeUpdate;
|
||||
return options.replace && isNode(nextNode) ? (nextNode as NodeType) : { ...node, ...nextNode };
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -288,7 +251,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
[setNodes]
|
||||
);
|
||||
|
||||
const updateNodeData = useCallback<Instance.UpdateNodeData>(
|
||||
const updateNodeData = useCallback<Instance.UpdateNodeData<NodeType>>(
|
||||
(id, dataUpdate, options = { replace: false }) => {
|
||||
updateNode(
|
||||
id,
|
||||
@@ -317,9 +280,6 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
deleteElements,
|
||||
getIntersectingNodes,
|
||||
isNodeIntersecting,
|
||||
getConnectedEdges,
|
||||
getIncomers,
|
||||
getOutgoers,
|
||||
updateNode,
|
||||
updateNodeData,
|
||||
};
|
||||
@@ -337,8 +297,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
deleteElements,
|
||||
getIntersectingNodes,
|
||||
isNodeIntersecting,
|
||||
getConnectedEdges,
|
||||
getIncomers,
|
||||
getOutgoers,
|
||||
updateNode,
|
||||
updateNodeData,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@ import { errorMessages, getDimensions } from '@xyflow/system';
|
||||
|
||||
import { useStoreApi } from '../hooks/useStore';
|
||||
|
||||
/**
|
||||
* Hook for handling resize events.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -10,6 +10,14 @@ const zustandErrorMessage = errorMessages['error001']();
|
||||
|
||||
type ExtractState = StoreApi<ReactFlowState> extends { getState: () => infer T } ? T : never;
|
||||
|
||||
/**
|
||||
* Hook for accessing the internal store. Should only be used in rare cases.
|
||||
*
|
||||
* @public
|
||||
* @param selector
|
||||
* @param equalityFn
|
||||
* @returns The selected state slice
|
||||
*/
|
||||
function useStore<StateSlice = ExtractState>(
|
||||
selector: (state: ReactFlowState) => StateSlice,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
|
||||
@@ -3,6 +3,12 @@ import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system';
|
||||
|
||||
import { useStoreApi } from '../hooks/useStore';
|
||||
|
||||
/**
|
||||
* Hook for updating node internals.
|
||||
*
|
||||
* @public
|
||||
* @returns function for updating node internals
|
||||
*/
|
||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ import { useStoreApi } from '../hooks/useStore';
|
||||
const selectedAndDraggable = (nodesDraggable: boolean) => (n: Node) =>
|
||||
n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
|
||||
|
||||
/**
|
||||
* Hook for updating node positions.
|
||||
*
|
||||
* @internal
|
||||
* @returns function for updating node positions
|
||||
*/
|
||||
function useUpdateNodePositions() {
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ const viewportSelector = (state: ReactFlowState) => ({
|
||||
zoom: state.transform[2],
|
||||
});
|
||||
|
||||
/**
|
||||
* Hook for getting the current viewport from the store.
|
||||
*
|
||||
* @public
|
||||
* @returns The current viewport
|
||||
*/
|
||||
function useViewport(): Viewport {
|
||||
const viewport = useStore(viewportSelector, shallow);
|
||||
|
||||
|
||||
@@ -12,6 +12,12 @@ import type { ViewportHelperFunctions, ReactFlowState } from '../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => !!s.panZoom;
|
||||
|
||||
/**
|
||||
* Hook for getting viewport helper functions.
|
||||
*
|
||||
* @internal
|
||||
* @returns viewport helper functions
|
||||
*/
|
||||
const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
const store = useStoreApi();
|
||||
const panZoomInitialized = useStore(selector);
|
||||
|
||||
@@ -6,6 +6,12 @@ import type { ReactFlowState } from '../types';
|
||||
|
||||
const selector = (state: ReactFlowState) => state.panZoom?.syncViewport;
|
||||
|
||||
/**
|
||||
* Hook for syncing the viewport with the panzoom instance.
|
||||
*
|
||||
* @internal
|
||||
* @param viewport
|
||||
*/
|
||||
export default function useViewportSync(viewport?: Viewport) {
|
||||
const syncViewport = useStore(selector);
|
||||
const store = useStoreApi();
|
||||
|
||||
@@ -5,6 +5,13 @@ import { isEdgeVisible } from '@xyflow/system';
|
||||
import { useStore } from './useStore';
|
||||
import { type ReactFlowState } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for getting the visible edge ids from the store.
|
||||
*
|
||||
* @internal
|
||||
* @param onlyRenderVisible
|
||||
* @returns array with visible edge ids
|
||||
*/
|
||||
function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
|
||||
const edgeIds = useStore(
|
||||
useCallback(
|
||||
|
||||
@@ -13,6 +13,13 @@ const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
||||
: Array.from(s.nodeLookup.keys());
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for getting the visible node ids from the store.
|
||||
*
|
||||
* @internal
|
||||
* @param onlyRenderVisible
|
||||
* @returns array with visible node ids
|
||||
*/
|
||||
function useVisibleNodeIds(onlyRenderVisible: boolean) {
|
||||
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
||||
|
||||
|
||||
@@ -30,16 +30,20 @@ export type NodeRemoveChange = {
|
||||
type: 'remove';
|
||||
};
|
||||
|
||||
export type NodeAddChange<NodeData = any> = {
|
||||
item: Node<NodeData>;
|
||||
export type NodeAddChange<NodeType extends Node = Node> = {
|
||||
item: NodeType;
|
||||
type: 'add';
|
||||
};
|
||||
|
||||
export type NodeResetChange<NodeData = any> = {
|
||||
item: Node<NodeData>;
|
||||
export type NodeResetChange<NodeType extends Node = Node> = {
|
||||
item: NodeType;
|
||||
type: 'reset';
|
||||
};
|
||||
|
||||
/**
|
||||
* Union type of all possible node changes.
|
||||
* @public
|
||||
*/
|
||||
export type NodeChange =
|
||||
| NodeDimensionChange
|
||||
| NodePositionChange
|
||||
@@ -50,12 +54,12 @@ export type NodeChange =
|
||||
|
||||
export type EdgeSelectionChange = NodeSelectionChange;
|
||||
export type EdgeRemoveChange = NodeRemoveChange;
|
||||
export type EdgeAddChange<EdgeData = any> = {
|
||||
item: Edge<EdgeData>;
|
||||
export type EdgeAddChange<EdgeType extends Edge = Edge> = {
|
||||
item: EdgeType;
|
||||
type: 'add';
|
||||
};
|
||||
export type EdgeResetChange<EdgeData = any> = {
|
||||
item: Edge<EdgeData>;
|
||||
export type EdgeResetChange<EdgeType extends Edge = Edge> = {
|
||||
item: EdgeType;
|
||||
type: 'reset';
|
||||
};
|
||||
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeResetChange;
|
||||
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
OnError,
|
||||
IsValidConnection,
|
||||
ColorMode,
|
||||
SnapGrid,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type {
|
||||
@@ -44,6 +45,10 @@ import type {
|
||||
EdgeMouseHandler,
|
||||
} from '.';
|
||||
|
||||
/**
|
||||
* ReactFlow component props.
|
||||
* @public
|
||||
*/
|
||||
export type ReactFlowProps = Omit<HTMLAttributes<HTMLDivElement>, 'onError'> & {
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
@@ -110,7 +115,7 @@ export type ReactFlowProps = Omit<HTMLAttributes<HTMLDivElement>, 'onError'> & {
|
||||
multiSelectionKeyCode?: KeyCode | null;
|
||||
zoomActivationKeyCode?: KeyCode | null;
|
||||
snapToGrid?: boolean;
|
||||
snapGrid?: [number, number];
|
||||
snapGrid?: SnapGrid;
|
||||
onlyRenderVisibleElements?: boolean;
|
||||
nodesDraggable?: boolean;
|
||||
nodesConnectable?: boolean;
|
||||
|
||||
@@ -13,7 +13,6 @@ import type {
|
||||
HandleElement,
|
||||
ConnectionStatus,
|
||||
EdgePosition,
|
||||
Optional,
|
||||
StepPathOptions,
|
||||
OnError,
|
||||
} from '@xyflow/system';
|
||||
@@ -31,12 +30,13 @@ export type EdgeLabelOptions = {
|
||||
|
||||
export type EdgeUpdatable = boolean | HandleType;
|
||||
|
||||
export type DefaultEdge<EdgeData = any> = EdgeBase<EdgeData> & {
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
updatable?: EdgeUpdatable;
|
||||
focusable?: boolean;
|
||||
} & EdgeLabelOptions;
|
||||
export type DefaultEdge<EdgeData = any> = EdgeBase<EdgeData> &
|
||||
EdgeLabelOptions & {
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
updatable?: EdgeUpdatable;
|
||||
focusable?: boolean;
|
||||
};
|
||||
|
||||
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
|
||||
type: 'smoothstep';
|
||||
@@ -53,6 +53,10 @@ type StepEdgeType<T> = DefaultEdge<T> & {
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Edge type is mainly used for the `edges` that get passed to the ReactFlow component
|
||||
* @public
|
||||
*/
|
||||
export type Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeType<T> | StepEdgeType<T>;
|
||||
|
||||
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
|
||||
@@ -88,14 +92,18 @@ export type EdgeTextProps = HTMLAttributes<SVGElement> &
|
||||
y: number;
|
||||
};
|
||||
|
||||
// props that get passed to a custom edge
|
||||
/**
|
||||
* Custom edge component props
|
||||
* @public
|
||||
*/
|
||||
export type EdgeProps<T = any> = Pick<
|
||||
Edge<T>,
|
||||
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
|
||||
> &
|
||||
Pick<EdgeWrapperProps, 'sourceHandleId' | 'targetHandleId'> &
|
||||
EdgePosition &
|
||||
EdgeLabelOptions & {
|
||||
sourceHandleId?: string | null;
|
||||
targetHandleId?: string | null;
|
||||
markerStart?: string;
|
||||
markerEnd?: string;
|
||||
// @TODO: how can we get better types for pathOptions?
|
||||
@@ -103,31 +111,69 @@ export type EdgeProps<T = any> = Pick<
|
||||
interactionWidth?: number;
|
||||
};
|
||||
|
||||
export type BaseEdgeProps = Pick<EdgeProps, 'style' | 'markerStart' | 'markerEnd' | 'interactionWidth'> &
|
||||
/**
|
||||
* BaseEdge component props
|
||||
* @public
|
||||
*/
|
||||
export type BaseEdgeProps = EdgeLabelOptions & {
|
||||
id?: string;
|
||||
interactionWidth?: number;
|
||||
labelX?: number;
|
||||
labelY?: number;
|
||||
markerStart?: string;
|
||||
markerEnd?: string;
|
||||
path: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper type for edge components that get exported by the library
|
||||
* @public
|
||||
*/
|
||||
export type EdgeComponentProps = EdgePosition &
|
||||
EdgeLabelOptions & {
|
||||
id?: string;
|
||||
labelX?: number;
|
||||
labelY?: number;
|
||||
path: string;
|
||||
id?: EdgeProps['id'];
|
||||
markerStart?: EdgeProps['markerStart'];
|
||||
markerEnd?: EdgeProps['markerEnd'];
|
||||
interactionWidth?: EdgeProps['interactionWidth'];
|
||||
style?: EdgeProps['style'];
|
||||
sourceHandleId?: EdgeProps['sourceHandleId'];
|
||||
targetHandleId?: EdgeProps['targetHandleId'];
|
||||
};
|
||||
|
||||
export type EdgeComponentProps<T = any> = Optional<Omit<EdgeProps<T>, 'source' | 'target'>, 'id'>;
|
||||
|
||||
export type StraightEdgeProps<T = any> = Omit<EdgeComponentProps<T>, 'sourcePosition' | 'targetPosition'>;
|
||||
|
||||
export type SmoothStepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: SmoothStepPathOptions;
|
||||
export type EdgeComponentWithPathOptions<PathOptions> = EdgeComponentProps & {
|
||||
pathOptions?: PathOptions;
|
||||
};
|
||||
|
||||
export type BezierEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: BezierPathOptions;
|
||||
};
|
||||
/**
|
||||
* BezierEdge component props
|
||||
* @public
|
||||
*/
|
||||
export type BezierEdgeProps = EdgeComponentWithPathOptions<BezierPathOptions>;
|
||||
|
||||
export type StepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
/**
|
||||
* SmoothStepEdge component props
|
||||
* @public
|
||||
*/
|
||||
export type SmoothStepEdgeProps = EdgeComponentWithPathOptions<SmoothStepPathOptions>;
|
||||
|
||||
export type SimpleBezierEdgeProps<T = any> = EdgeComponentProps<T>;
|
||||
/**
|
||||
* StepEdge component props
|
||||
* @public
|
||||
*/
|
||||
export type StepEdgeProps = EdgeComponentWithPathOptions<StepPathOptions>;
|
||||
|
||||
/**
|
||||
* StraightEdge component props
|
||||
* @public
|
||||
*/
|
||||
export type StraightEdgeProps = Omit<EdgeComponentProps, 'sourcePosition' | 'targetPosition'>;
|
||||
|
||||
/**
|
||||
* SimpleBezier component props
|
||||
* @public
|
||||
*/
|
||||
export type SimpleBezierEdgeProps = EdgeComponentProps;
|
||||
|
||||
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { ComponentType } from 'react';
|
||||
import {
|
||||
FitViewParamsBase,
|
||||
FitViewOptionsBase,
|
||||
NodeProps,
|
||||
ZoomInOut,
|
||||
ZoomTo,
|
||||
SetViewport,
|
||||
@@ -12,9 +10,11 @@ import {
|
||||
SetCenter,
|
||||
FitBounds,
|
||||
XYPosition,
|
||||
NodeProps,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { NodeChange, EdgeChange, Node, Edge, EdgeProps, ReactFlowInstance } from '.';
|
||||
import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.';
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
export type OnNodesChange = (changes: NodeChange[]) => void;
|
||||
export type OnEdgesChange = (changes: EdgeChange[]) => void;
|
||||
@@ -41,7 +41,9 @@ export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
|
||||
export type FitViewParams = FitViewParamsBase<Node>;
|
||||
export type FitViewOptions = FitViewOptionsBase<Node>;
|
||||
export type FitView = (fitViewOptions?: FitViewOptions) => boolean;
|
||||
export type OnInit<NodeData = any, EdgeData = any> = (reactFlowInstance: ReactFlowInstance<NodeData, EdgeData>) => void;
|
||||
export type OnInit<NodeType extends Node = Node, EdgeType extends Edge = Edge> = (
|
||||
reactFlowInstance: ReactFlowInstance<NodeType, EdgeType>
|
||||
) => void;
|
||||
|
||||
export type ViewportHelperFunctions = {
|
||||
zoomIn: ZoomInOut;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import type { Rect, Viewport } from '@xyflow/system';
|
||||
import type { Node, Edge, ViewportHelperFunctions } from '.';
|
||||
|
||||
export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
|
||||
nodes: Node<NodeData>[];
|
||||
edges: Edge<EdgeData>[];
|
||||
export type ReactFlowJsonObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
|
||||
nodes: NodeType[];
|
||||
edges: EdgeType[];
|
||||
viewport: Viewport;
|
||||
};
|
||||
|
||||
@@ -15,30 +15,33 @@ export type DeleteElementsOptions = {
|
||||
};
|
||||
|
||||
export namespace Instance {
|
||||
export type GetNodes<NodeData> = () => Node<NodeData>[];
|
||||
export type SetNodes<NodeData> = (
|
||||
payload: Node<NodeData>[] | ((nodes: Node<NodeData>[]) => Node<NodeData>[])
|
||||
export type GetNodes<NodeType extends Node = Node> = () => NodeType[];
|
||||
export type SetNodes<NodeType extends Node = Node> = (
|
||||
payload: NodeType[] | ((nodes: NodeType[]) => NodeType[])
|
||||
) => void;
|
||||
export type AddNodes<NodeData> = (payload: Node<NodeData>[] | Node<NodeData>) => void;
|
||||
export type GetNode<NodeData> = (id: string) => Node<NodeData> | undefined;
|
||||
export type GetEdges<EdgeData> = () => Edge<EdgeData>[];
|
||||
export type SetEdges<EdgeData> = (
|
||||
payload: Edge<EdgeData>[] | ((edges: Edge<EdgeData>[]) => Edge<EdgeData>[])
|
||||
export type AddNodes<NodeType extends Node = Node> = (payload: NodeType[] | NodeType) => void;
|
||||
export type GetNode<NodeType extends Node = Node> = (id: string) => NodeType | undefined;
|
||||
export type GetEdges<EdgeType extends Edge = Edge> = () => EdgeType[];
|
||||
export type SetEdges<EdgeType extends Edge = Edge> = (
|
||||
payload: EdgeType[] | ((edges: EdgeType[]) => EdgeType[])
|
||||
) => void;
|
||||
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
|
||||
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
|
||||
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
|
||||
export type GetEdge<EdgeType extends Edge = Edge> = (id: string) => EdgeType | undefined;
|
||||
export type AddEdges<EdgeType extends Edge = Edge> = (payload: EdgeType[] | EdgeType) => void;
|
||||
export type ToObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = () => ReactFlowJsonObject<
|
||||
NodeType,
|
||||
EdgeType
|
||||
>;
|
||||
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => {
|
||||
deletedNodes: Node[];
|
||||
deletedEdges: Edge[];
|
||||
};
|
||||
export type GetIntersectingNodes<NodeData> = (
|
||||
node: Node<NodeData> | { id: Node['id'] } | Rect,
|
||||
export type GetIntersectingNodes<NodeType extends Node = Node> = (
|
||||
node: NodeType | { id: Node['id'] } | Rect,
|
||||
partially?: boolean,
|
||||
nodes?: Node<NodeData>[]
|
||||
) => Node<NodeData>[];
|
||||
export type IsNodeIntersecting<NodeData> = (
|
||||
node: Node<NodeData> | { id: Node['id'] } | Rect,
|
||||
nodes?: NodeType[]
|
||||
) => NodeType[];
|
||||
export type IsNodeIntersecting<NodeType extends Node = Node> = (
|
||||
node: NodeType | { id: Node['id'] } | Rect,
|
||||
area: Rect,
|
||||
partially?: boolean
|
||||
) => boolean;
|
||||
@@ -46,32 +49,32 @@ export namespace Instance {
|
||||
export type getIncomers = (node: string | Node | { id: Node['id'] }) => Node[];
|
||||
export type getOutgoers = (node: string | Node | { id: Node['id'] }) => Node[];
|
||||
|
||||
export type UpdateNode = (
|
||||
export type UpdateNode<NodeType extends Node = Node> = (
|
||||
id: string,
|
||||
dataUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
||||
nodeUpdate: Partial<NodeType> | ((node: NodeType) => Partial<NodeType>),
|
||||
options?: { replace: boolean }
|
||||
) => void;
|
||||
export type UpdateNodeData = (
|
||||
export type UpdateNodeData<NodeType extends Node = Node> = (
|
||||
id: string,
|
||||
dataUpdate: object | ((node: Node) => object),
|
||||
dataUpdate: object | ((node: NodeType) => object),
|
||||
options?: { replace: boolean }
|
||||
) => void;
|
||||
}
|
||||
|
||||
export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
|
||||
getNodes: Instance.GetNodes<NodeData>;
|
||||
setNodes: Instance.SetNodes<NodeData>;
|
||||
addNodes: Instance.AddNodes<NodeData>;
|
||||
getNode: Instance.GetNode<NodeData>;
|
||||
getEdges: Instance.GetEdges<EdgeData>;
|
||||
setEdges: Instance.SetEdges<EdgeData>;
|
||||
addEdges: Instance.AddEdges<EdgeData>;
|
||||
getEdge: Instance.GetEdge<EdgeData>;
|
||||
toObject: Instance.ToObject<NodeData, EdgeData>;
|
||||
export type ReactFlowInstance<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
|
||||
getNodes: Instance.GetNodes<NodeType>;
|
||||
setNodes: Instance.SetNodes<NodeType>;
|
||||
addNodes: Instance.AddNodes<NodeType>;
|
||||
getNode: Instance.GetNode<NodeType>;
|
||||
getEdges: Instance.GetEdges<EdgeType>;
|
||||
setEdges: Instance.SetEdges<EdgeType>;
|
||||
addEdges: Instance.AddEdges<EdgeType>;
|
||||
getEdge: Instance.GetEdge<EdgeType>;
|
||||
toObject: Instance.ToObject<NodeType, EdgeType>;
|
||||
deleteElements: Instance.DeleteElements;
|
||||
getIntersectingNodes: Instance.GetIntersectingNodes<NodeData>;
|
||||
isNodeIntersecting: Instance.IsNodeIntersecting<NodeData>;
|
||||
updateNode: Instance.UpdateNode;
|
||||
updateNodeData: Instance.UpdateNodeData;
|
||||
getIntersectingNodes: Instance.GetIntersectingNodes<NodeType>;
|
||||
isNodeIntersecting: Instance.IsNodeIntersecting<NodeType>;
|
||||
updateNode: Instance.UpdateNode<NodeType>;
|
||||
updateNodeData: Instance.UpdateNodeData<NodeType>;
|
||||
viewportInitialized: boolean;
|
||||
} & Omit<ViewportHelperFunctions, 'initialized'>;
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/system';
|
||||
|
||||
import { NodeTypes } from './general';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
/**
|
||||
* The node data structure that gets used for the nodes prop.
|
||||
* @public
|
||||
*/
|
||||
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase<
|
||||
NodeData,
|
||||
NodeType
|
||||
@@ -10,6 +15,7 @@ export type Node<NodeData = any, NodeType extends string | undefined = string |
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
resizing?: boolean;
|
||||
focusable?: boolean;
|
||||
};
|
||||
|
||||
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
||||
|
||||
@@ -142,12 +142,52 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
return updatedElements;
|
||||
}
|
||||
|
||||
export function applyNodeChanges<NodeData = any>(changes: NodeChange[], nodes: Node<NodeData>[]): Node<NodeData>[] {
|
||||
return applyChanges(changes, nodes) as Node<NodeData>[];
|
||||
/**
|
||||
* Drop in function that applies node changes to an array of nodes.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param nodes - Array of nodes to apply the changes to
|
||||
* @returns Array of updated nodes
|
||||
* @example
|
||||
* const onNodesChange = useCallback(
|
||||
(changes) => {
|
||||
setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
|
||||
},
|
||||
[setNodes],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
|
||||
);
|
||||
*/
|
||||
export function applyNodeChanges<NodeType extends Node = Node>(changes: NodeChange[], nodes: NodeType[]): NodeType[] {
|
||||
return applyChanges(changes, nodes) as NodeType[];
|
||||
}
|
||||
|
||||
export function applyEdgeChanges<EdgeData = any>(changes: EdgeChange[], edges: Edge<EdgeData>[]): Edge<EdgeData>[] {
|
||||
return applyChanges(changes, edges) as Edge<EdgeData>[];
|
||||
/**
|
||||
* Drop in function that applies edge changes to an array of edges.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param edges - Array of edge to apply the changes to
|
||||
* @returns Array of updated edges
|
||||
* @example
|
||||
* const onEdgesChange = useCallback(
|
||||
(changes) => {
|
||||
setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
|
||||
},
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFLow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
|
||||
);
|
||||
*/
|
||||
export function applyEdgeChanges<EdgeType extends Edge = Edge>(changes: EdgeChange[], edges: EdgeType[]): EdgeType[] {
|
||||
return applyChanges(changes, edges) as EdgeType[];
|
||||
}
|
||||
|
||||
export const createSelectionChange = (id: string, selected: boolean): NodeSelectionChange | EdgeSelectionChange => ({
|
||||
|
||||
@@ -10,10 +10,68 @@ import {
|
||||
|
||||
import type { Edge, Node } from '../types';
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as a Node
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Node
|
||||
*/
|
||||
export const isNode = isNodeBase<Node>;
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as an Edge
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Edge
|
||||
*/
|
||||
export const isEdge = isEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.source === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getOutgoers = getOutgoersBase<Node, Edge>;
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.target === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getIncomers = getIncomersBase<Node, Edge>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
*/
|
||||
export const addEdge = addEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* A handy utility to update an existing Edge with new properties
|
||||
* @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
|
||||
*/
|
||||
export const updateEdge = updateEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* Get all connecting edges for a given set of nodes
|
||||
* @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
|
||||
*/
|
||||
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
|
||||
|
||||
@@ -9,6 +9,12 @@ function getMediaQuery() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for receiving the current color mode class 'dark' or 'light'.
|
||||
*
|
||||
* @internal
|
||||
* @param colorMode - The color mode to use ('dark', 'light' or 'system')
|
||||
*/
|
||||
export function useColorModeClass(colorMode: ColorMode = 'light'): Readable<ColorModeClass> {
|
||||
const colorModeClass = readable<ColorModeClass>('light', (set) => {
|
||||
if (colorMode !== 'system') {
|
||||
|
||||
@@ -3,6 +3,12 @@ import type { Readable } from 'svelte/store';
|
||||
import { useStore } from '$lib/store';
|
||||
import type { ConnectionProps } from '$lib/store/derived-connection-props';
|
||||
|
||||
/**
|
||||
* Hook for receiving the current connection.
|
||||
*
|
||||
* @public
|
||||
* @returns current connection as a readable store
|
||||
*/
|
||||
export function useConnection(): Readable<ConnectionProps> {
|
||||
const { connection } = useStore();
|
||||
|
||||
|
||||
@@ -11,6 +11,15 @@ export type useHandleConnectionsParams = {
|
||||
|
||||
const initialConnections: Connection[] = [];
|
||||
|
||||
/**
|
||||
* Hook to check if a <Handle /> is connected to another <Handle /> and get the connections.
|
||||
*
|
||||
* @public
|
||||
* @param param.nodeId
|
||||
* @param param.type - handle type 'source' or 'target'
|
||||
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
|
||||
* @returns an array with connections
|
||||
*/
|
||||
export function useHandleConnections({ nodeId, type, id = null }: useHandleConnectionsParams) {
|
||||
const { edges, connectionLookup } = useStore();
|
||||
let prevConnections: Map<string, Connection> | undefined = undefined;
|
||||
|
||||
@@ -21,6 +21,14 @@ function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for receiving data of one or multiple nodes
|
||||
*
|
||||
* @public
|
||||
* @param nodeId - The id (or ids) of the node to get the data from
|
||||
* @param guard - Optional guard function to narrow down the node type
|
||||
* @returns A readable store with an array of data objects
|
||||
*/
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
nodeId: string
|
||||
): Readable<NodeType['data'] | null>;
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
/**
|
||||
* Hook for getting the current nodes from the store.
|
||||
*
|
||||
* @public
|
||||
* @returns store with an array of nodes
|
||||
*/
|
||||
export function useNodes() {
|
||||
const { nodes } = useStore();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for getting the current edges from the store.
|
||||
*
|
||||
* @public
|
||||
* @returns store with an array of edges
|
||||
*/
|
||||
export function useEdges() {
|
||||
const { edges } = useStore();
|
||||
return edges;
|
||||
|
||||
@@ -20,6 +20,12 @@ import { useStore } from '$lib/store';
|
||||
import type { Edge, FitViewOptions, Node } from '$lib/types';
|
||||
import { isNode } from '$lib/utils';
|
||||
|
||||
/**
|
||||
* Hook for accessing the ReactFlow instance.
|
||||
*
|
||||
* @public
|
||||
* @returns helper functions
|
||||
*/
|
||||
export function useSvelteFlow(): {
|
||||
zoomIn: ZoomInOut;
|
||||
zoomOut: ZoomInOut;
|
||||
|
||||
@@ -3,6 +3,12 @@ import type { UpdateNodeInternals } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
/**
|
||||
* Hook for updating node internals.
|
||||
*
|
||||
* @public
|
||||
* @returns function for updating node internals
|
||||
*/
|
||||
export function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const { domNode, updateNodeDimensions } = useStore();
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import type {
|
||||
DefaultEdgeOptionsBase,
|
||||
EdgePosition,
|
||||
SmoothStepPathOptions,
|
||||
Optional,
|
||||
StepPathOptions
|
||||
} from '@xyflow/system';
|
||||
|
||||
@@ -34,12 +33,18 @@ type StepEdgeType<T> = DefaultEdge<T> & {
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Edge type is mainly used for the `edges` that get passed to the SvelteFlow component.
|
||||
*/
|
||||
export type Edge<T = any> =
|
||||
| DefaultEdge<T>
|
||||
| SmoothStepEdgeType<T>
|
||||
| BezierEdgeType<T>
|
||||
| StepEdgeType<T>;
|
||||
|
||||
/**
|
||||
* Custom edge component props.
|
||||
*/
|
||||
export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' | 'type'> &
|
||||
EdgePosition & {
|
||||
markerStart?: string;
|
||||
@@ -48,30 +53,48 @@ export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' |
|
||||
targetHandleId?: string | null;
|
||||
};
|
||||
|
||||
export type EdgeComponentProps<T = any> = Optional<
|
||||
Omit<
|
||||
EdgeProps<T>,
|
||||
'source' | 'target' | 'sourceHandleId' | 'targetHandleId' | 'animated' | 'selected' | 'data'
|
||||
>,
|
||||
'id'
|
||||
>;
|
||||
|
||||
export type BezierEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: BezierPathOptions;
|
||||
/**
|
||||
* Helper type for edge components that get exported by the library.
|
||||
*/
|
||||
export type EdgeComponentProps = EdgePosition & {
|
||||
id?: EdgeProps['id'];
|
||||
hidden?: EdgeProps['hidden'];
|
||||
deletable?: EdgeProps['deletable'];
|
||||
selectable?: EdgeProps['selectable'];
|
||||
markerStart?: EdgeProps['markerStart'];
|
||||
markerEnd?: EdgeProps['markerEnd'];
|
||||
zIndex?: EdgeProps['zIndex'];
|
||||
ariaLabel?: EdgeProps['ariaLabel'];
|
||||
interactionWidth?: EdgeProps['interactionWidth'];
|
||||
label?: EdgeProps['label'];
|
||||
labelStyle?: EdgeProps['labelStyle'];
|
||||
style?: EdgeProps['style'];
|
||||
class?: EdgeProps['class'];
|
||||
};
|
||||
|
||||
export type SmoothStepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: SmoothStepPathOptions;
|
||||
export type EdgeComponentWithPathOptions<PathOptions> = EdgeComponentProps & {
|
||||
pathOptions?: PathOptions;
|
||||
};
|
||||
|
||||
export type StepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
/**
|
||||
* BezierEdge component props
|
||||
*/
|
||||
export type BezierEdgeProps = EdgeComponentWithPathOptions<BezierPathOptions>;
|
||||
|
||||
export type StraightEdgeProps<T = any> = Omit<
|
||||
EdgeComponentProps<T>,
|
||||
'sourcePosition' | 'targetPosition'
|
||||
>;
|
||||
/**
|
||||
* SmoothStepEdge component props
|
||||
*/
|
||||
export type SmoothStepEdgeProps = EdgeComponentWithPathOptions<SmoothStepPathOptions>;
|
||||
|
||||
/**
|
||||
* StepEdge component props
|
||||
*/
|
||||
export type StepEdgeProps = EdgeComponentWithPathOptions<StepPathOptions>;
|
||||
|
||||
/**
|
||||
* StraightEdge component props
|
||||
*/
|
||||
export type StraightEdgeProps = Omit<EdgeComponentProps, 'sourcePosition' | 'targetPosition'>;
|
||||
|
||||
export type EdgeTypes = Record<string, ComponentType<SvelteComponent<EdgeProps>>>;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import type { ComponentType, SvelteComponent } from 'svelte';
|
||||
import type { NodeBase, NodeProps } from '@xyflow/system';
|
||||
|
||||
// @todo: currently the helper function only like Node from '@reactflow/core'
|
||||
// we need a base node type or helpes that accept Node like types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
/**
|
||||
* The node data structure that gets used for the nodes prop.
|
||||
* @public
|
||||
*/
|
||||
export type Node<
|
||||
NodeData = any,
|
||||
NodeType extends string | undefined = string | undefined
|
||||
|
||||
@@ -10,10 +10,68 @@ import {
|
||||
|
||||
import type { Edge, Node } from '$lib/types';
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as a Node
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Node
|
||||
*/
|
||||
export const isNode = isNodeBase<Node>;
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as an Edge
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Edge
|
||||
*/
|
||||
export const isEdge = isEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.source === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getOutgoers = getOutgoersBase<Node, Edge>;
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.target === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getIncomers = getIncomersBase<Node, Edge>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
*/
|
||||
export const addEdge = addEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* A handy utility to update an existing Edge with new properties
|
||||
* @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
|
||||
*/
|
||||
export const updateEdge = updateEdgeBase<Edge>;
|
||||
|
||||
/**
|
||||
* Get all connecting edges for a given set of nodes
|
||||
* @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
|
||||
*/
|
||||
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
|
||||
|
||||
@@ -26,7 +26,6 @@ export type NodeBase<T = any, U extends string | undefined = string | undefined>
|
||||
extent?: 'parent' | CoordinateExtent;
|
||||
expandParent?: boolean;
|
||||
ariaLabel?: string;
|
||||
focusable?: boolean;
|
||||
origin?: NodeOrigin;
|
||||
handles?: NodeHandle[];
|
||||
computed?: {
|
||||
|
||||
@@ -69,6 +69,29 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurva
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bezier path from source to target handle
|
||||
* @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)
|
||||
* @param params.targetX - The x position of the target handle
|
||||
* @param params.targetY - The y position of the target handle
|
||||
* @param params.targetPosition - The position of the target handle (default: Position.Top)
|
||||
* @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
|
||||
* 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,
|
||||
});
|
||||
*/
|
||||
export function getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
|
||||
@@ -90,6 +90,14 @@ 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.
|
||||
* @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
|
||||
*/
|
||||
export const addEdgeBase = <EdgeType extends EdgeBase>(
|
||||
edgeParams: EdgeType | Connection,
|
||||
edges: EdgeType[]
|
||||
@@ -129,6 +137,14 @@ export type UpdateEdgeOptions = {
|
||||
shouldReplaceId?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* A handy utility to update an existing Edge with new properties
|
||||
* @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
|
||||
*/
|
||||
export const updateEdgeBase = <EdgeType extends EdgeBase>(
|
||||
oldEdge: EdgeType,
|
||||
newConnection: Connection,
|
||||
|
||||
@@ -190,6 +190,28 @@ function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): str
|
||||
return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a smooth step path from source to target handle
|
||||
* @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)
|
||||
* @param params.targetX - The x position of the target handle
|
||||
* @param params.targetY - The y position of the target handle
|
||||
* @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
|
||||
* 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,
|
||||
});
|
||||
*/
|
||||
export function getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
|
||||
@@ -7,6 +7,26 @@ export type GetStraightPathParams = {
|
||||
targetY: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a straight path from source to target handle
|
||||
* @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
|
||||
* 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,
|
||||
});
|
||||
*/
|
||||
export function getStraightPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
|
||||
@@ -8,8 +8,8 @@ import type {
|
||||
NodeOrigin,
|
||||
SnapGrid,
|
||||
Transform,
|
||||
Viewport,
|
||||
} from '../types';
|
||||
import { type Viewport } from '../types';
|
||||
import { getNodePositionWithOrigin } from './graph';
|
||||
|
||||
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max);
|
||||
@@ -19,8 +19,14 @@ export const clampPosition = (position: XYPosition = { x: 0, y: 0 }, extent: Coo
|
||||
y: clamp(position.y, extent[0][1], extent[1][1]),
|
||||
});
|
||||
|
||||
// returns a number between 0 and 1 that represents the velocity of the movement
|
||||
// when the mouse is close to the edge of the canvas
|
||||
/**
|
||||
* Calculates the velocity of panning when the mouse is close to the edge of the canvas
|
||||
* @internal
|
||||
* @param value - One dimensional poition of the mouse (x or y)
|
||||
* @param min - Minimal position on canvas before panning starts
|
||||
* @param max - Maximal position on canvas before panning starts
|
||||
* @returns - A number between 0 and 1 that represents the velocity of panning
|
||||
*/
|
||||
const calcAutoPanVelocity = (value: number, min: number, max: number): number => {
|
||||
if (value < min) {
|
||||
return clamp(Math.abs(value - min), 1, 50) / 50;
|
||||
@@ -127,12 +133,12 @@ export const getPositionWithOrigin = ({
|
||||
};
|
||||
};
|
||||
|
||||
export function snapPosition(position: XYPosition, snapGrid: SnapGrid = [1, 1]): XYPosition {
|
||||
export const snapPosition = (position: XYPosition, snapGrid: SnapGrid = [1, 1]): XYPosition => {
|
||||
return {
|
||||
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
|
||||
y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const pointToRendererPoint = (
|
||||
{ x, y }: XYPosition,
|
||||
@@ -155,6 +161,22 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a viewport that encloses the given bounds with optional padding.
|
||||
* @public
|
||||
* @remarks You can determine bounds of nodes with {@link getNodesBounds} and {@link getBoundsOfRects}
|
||||
* @param bounds - Bounds to fit inside viewport
|
||||
* @param width - Width of the viewport
|
||||
* @param height - Height of the viewport
|
||||
* @param minZoom - Minimum zoom level of the resulting viewport
|
||||
* @param maxZoom - Maximum zoom level of the resulting viewport
|
||||
* @param padding - Optional padding around the bounds
|
||||
* @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);
|
||||
*/
|
||||
export const getViewportForBounds = (
|
||||
bounds: Rect,
|
||||
width: number,
|
||||
|
||||
@@ -25,12 +25,34 @@ import {
|
||||
} from '../types';
|
||||
import { errorMessages } from '../constants';
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as an Edge
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Edge
|
||||
*/
|
||||
export const isEdgeBase = <EdgeType extends EdgeBase = EdgeBase>(element: any): element is EdgeType =>
|
||||
'id' in element && 'source' in element && 'target' in element;
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as a Node
|
||||
* @public
|
||||
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
|
||||
* @param element - The element to test
|
||||
* @returns A boolean indicating whether the element is an Node
|
||||
*/
|
||||
export const isNodeBase = <NodeType extends NodeBase = NodeBase>(element: any): element is NodeType =>
|
||||
'id' in element && !('source' in element) && !('target' in element);
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.source === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getOutgoersBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
node: NodeType | { id: string },
|
||||
nodes: NodeType[],
|
||||
@@ -50,6 +72,14 @@ export const getOutgoersBase = <NodeType extends NodeBase = NodeBase, EdgeType e
|
||||
return nodes.filter((n) => outgoerIds.has(n.id));
|
||||
};
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.target === node.id
|
||||
* @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
|
||||
*/
|
||||
export const getIncomersBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
node: NodeType | { id: string },
|
||||
nodes: NodeType[],
|
||||
@@ -102,6 +132,14 @@ export const getNodePositionWithOrigin = (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines a bounding box that contains all given nodes in an array
|
||||
* @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 nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
|
||||
* @returns Bounding box enclosing all nodes
|
||||
*/
|
||||
export const getNodesBounds = (nodes: NodeBase[], nodeOrigin: NodeOrigin = [0, 0]): Rect => {
|
||||
if (nodes.length === 0) {
|
||||
return { x: 0, y: 0, width: 0, height: 0 };
|
||||
@@ -167,6 +205,12 @@ export const getNodesInside = <NodeType extends NodeBase>(
|
||||
return visibleNodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all connecting edges for a given set of nodes
|
||||
* @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
|
||||
*/
|
||||
export const getConnectedEdgesBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||
nodes: NodeType[],
|
||||
edges: EdgeType[]
|
||||
@@ -281,9 +325,15 @@ export function calcNextPosition<NodeType extends NodeBase>(
|
||||
};
|
||||
}
|
||||
|
||||
// helper function to get arrays of nodes and edges that can be deleted
|
||||
// you can pass in a list of nodes and edges that should be deleted
|
||||
// and the function only returns elements that are deletable and also handles connected nodes and child nodes
|
||||
/**
|
||||
* Pass in nodes & edges to delete, get arrays of nodes and edges that actually can be deleted
|
||||
* @internal
|
||||
* @param param.nodesToRemove - The nodes to remove
|
||||
* @param param.edgesToRemove - The edges to remove
|
||||
* @param param.nodes - All nodes
|
||||
* @param param.edges - All edges
|
||||
* @returns matchingNodes: nodes that can be deleted, matchingEdges: edges that can be deleted
|
||||
*/
|
||||
export function getElementsToRemove<NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>({
|
||||
nodesToRemove,
|
||||
edgesToRemove,
|
||||
|
||||
Reference in New Issue
Block a user