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';
|
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'> & {
|
export type MiniMapProps<NodeType extends Node = Node> = Omit<HTMLAttributes<SVGSVGElement>, 'onClick'> & {
|
||||||
nodeColor?: string | GetMiniMapNodeAttribute<NodeData>;
|
nodeColor?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeStrokeColor?: string | GetMiniMapNodeAttribute<NodeData>;
|
nodeStrokeColor?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeClassName?: string | GetMiniMapNodeAttribute<NodeData>;
|
nodeClassName?: string | GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeBorderRadius?: number;
|
nodeBorderRadius?: number;
|
||||||
nodeStrokeWidth?: number;
|
nodeStrokeWidth?: number;
|
||||||
nodeComponent?: ComponentType<MiniMapNodeProps>;
|
nodeComponent?: ComponentType<MiniMapNodeProps>;
|
||||||
@@ -18,7 +18,7 @@ export type MiniMapProps<NodeData = any> = Omit<HTMLAttributes<SVGSVGElement>, '
|
|||||||
maskStrokeWidth?: number;
|
maskStrokeWidth?: number;
|
||||||
position?: PanelPosition;
|
position?: PanelPosition;
|
||||||
onClick?: (event: MouseEvent, position: XYPosition) => void;
|
onClick?: (event: MouseEvent, position: XYPosition) => void;
|
||||||
onNodeClick?: (event: MouseEvent, node: Node<NodeData>) => void;
|
onNodeClick?: (event: MouseEvent, node: NodeType) => void;
|
||||||
pannable?: boolean;
|
pannable?: boolean;
|
||||||
zoomable?: boolean;
|
zoomable?: boolean;
|
||||||
ariaLabel?: string | null;
|
ariaLabel?: string | null;
|
||||||
@@ -27,8 +27,8 @@ export type MiniMapProps<NodeData = any> = Omit<HTMLAttributes<SVGSVGElement>, '
|
|||||||
offsetScale?: number;
|
offsetScale?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MiniMapNodes = Pick<
|
export type MiniMapNodes<NodeType extends Node = Node> = Pick<
|
||||||
MiniMapProps,
|
MiniMapProps<NodeType>,
|
||||||
'nodeColor' | 'nodeStrokeColor' | 'nodeClassName' | 'nodeBorderRadius' | 'nodeStrokeWidth' | 'nodeComponent'
|
'nodeColor' | 'nodeStrokeColor' | 'nodeClassName' | 'nodeBorderRadius' | 'nodeStrokeWidth' | 'nodeComponent'
|
||||||
> & {
|
> & {
|
||||||
onClick?: (event: MouseEvent, nodeId: string) => void;
|
onClick?: (event: MouseEvent, nodeId: string) => void;
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ function getMediaQuery() {
|
|||||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
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 {
|
export default function useColorModeClass(colorMode: ColorMode): ColorModeClass {
|
||||||
const [colorModeClass, setColorModeClass] = useState<ColorModeClass | null>(
|
const [colorModeClass, setColorModeClass] = useState<ColorModeClass | null>(
|
||||||
colorMode === 'system' ? null : colorMode
|
colorMode === 'system' ? null : colorMode
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ type UseDragParams = {
|
|||||||
isSelectable?: boolean;
|
isSelectable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for calling XYDrag helper from @xyflow/system.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }: UseDragParams) {
|
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }: UseDragParams) {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const [dragging, setDragging] = useState<boolean>(false);
|
const [dragging, setDragging] = useState<boolean>(false);
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import type { Edge, ReactFlowState } from '../types';
|
|||||||
|
|
||||||
const edgesSelector = (state: ReactFlowState) => state.edges;
|
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>[] {
|
function useEdges<EdgeData>(): Edge<EdgeData>[] {
|
||||||
const edges = useStore(edgesSelector, shallow);
|
const edges = useStore(edgesSelector, shallow);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ const selected = (item: Node | Edge) => item.selected;
|
|||||||
|
|
||||||
const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false };
|
const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for handling global key events.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export default ({
|
export default ({
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
multiSelectionKeyCode,
|
multiSelectionKeyCode,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ type useHandleConnectionsParams = {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @param param.type - handle type 'source' or 'target'
|
* @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.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.onConnect - gets called when a connection is established
|
||||||
* @param param.onDisconnect - gets called when a connection is removed
|
* @param param.onDisconnect - gets called when a connection is removed
|
||||||
* @returns an array with connections
|
* @returns an array with connections
|
||||||
|
|||||||
@@ -12,11 +12,19 @@ export type UseKeyPressOptions = {
|
|||||||
|
|
||||||
const defaultDoc = typeof document !== 'undefined' ? document : null;
|
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'
|
* Hook for handling key events.
|
||||||
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
|
*
|
||||||
// user can use the single key 'a' or the combination 'd' + 's'
|
* @public
|
||||||
|
* @param param.keyCode - The key code (string or array of strings) to use
|
||||||
|
* @param param.options - Options
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
export default (
|
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,
|
keyCode: KeyCode | null = null,
|
||||||
options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true }
|
options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true }
|
||||||
): boolean => {
|
): boolean => {
|
||||||
|
|||||||
@@ -5,8 +5,14 @@ import type { Node, ReactFlowState } from '../types';
|
|||||||
|
|
||||||
const nodesSelector = (state: ReactFlowState) => state.nodes;
|
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;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ import { shallow } from 'zustand/shallow';
|
|||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import type { Node } from '../types';
|
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>(nodeId: string): NodeType['data'] | null;
|
||||||
export function useNodesData<NodeType extends Node = Node>(nodeIds: string[]): NodeType['data'][];
|
export function useNodesData<NodeType extends Node = Node>(nodeIds: string[]): NodeType['data'][];
|
||||||
export function useNodesData<NodeType extends Node = Node>(
|
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 ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[];
|
||||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||||
|
|
||||||
// returns a hook that can be used liked this:
|
|
||||||
// const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
|
||||||
function createUseItemsState(
|
function createUseItemsState(
|
||||||
applyChanges: ApplyChanges<Node, NodeChange>
|
applyChanges: ApplyChanges<Node, NodeChange>
|
||||||
): <NodeData = any>(
|
): <NodeType extends Node = Node>(
|
||||||
initialItems: Node<NodeData>[]
|
initialItems: NodeType[]
|
||||||
) => [Node<NodeData>[], Dispatch<SetStateAction<Node<NodeData>[]>>, OnChange<NodeChange>];
|
) => [NodeType[], Dispatch<SetStateAction<NodeType[]>>, OnChange<NodeChange>];
|
||||||
function createUseItemsState(
|
function createUseItemsState(
|
||||||
applyChanges: ApplyChanges<Edge, EdgeChange>
|
applyChanges: ApplyChanges<Edge, EdgeChange>
|
||||||
): <EdgeData = any>(
|
): <EdgeType extends Edge = Edge>(
|
||||||
initialItems: Edge<EdgeData>[]
|
initialItems: EdgeType[]
|
||||||
) => [Edge<EdgeData>[], Dispatch<SetStateAction<Edge<EdgeData>[]>>, OnChange<EdgeChange>];
|
) => [EdgeType[], Dispatch<SetStateAction<EdgeType[]>>, OnChange<EdgeChange>];
|
||||||
function createUseItemsState(
|
function createUseItemsState(
|
||||||
applyChanges: ApplyChanges<any, any>
|
applyChanges: ApplyChanges<any, any>
|
||||||
): (initialItems: any[]) => [any[], Dispatch<SetStateAction<any[]>>, OnChange<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);
|
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);
|
export const useEdgesState = createUseItemsState(applyEdgeChanges);
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ const defaultOptions = {
|
|||||||
includeHiddenNodes: false,
|
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 {
|
function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
|
||||||
const initialized = useStore(selector(options));
|
const initialized = useStore(selector(options));
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import { useEffect, useRef } from 'react';
|
|||||||
import useReactFlow from './useReactFlow';
|
import useReactFlow from './useReactFlow';
|
||||||
import type { OnInit } from '../types';
|
import type { OnInit } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for calling onInit handler.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
function useOnInitHandler(onInit: OnInit | undefined) {
|
function useOnInitHandler(onInit: OnInit | undefined) {
|
||||||
const rfInstance = useReactFlow();
|
const rfInstance = useReactFlow();
|
||||||
const isInitialized = useRef<boolean>(false);
|
const isInitialized = useRef<boolean>(false);
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ export type UseOnSelectionChangeOptions = {
|
|||||||
onChange: OnSelectionChangeFunc;
|
onChange: OnSelectionChangeFunc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for registering an onSelectionChange handler.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @params params.onChange - The handler to register
|
||||||
|
*/
|
||||||
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ export type UseOnViewportChangeOptions = {
|
|||||||
onEnd?: OnViewportChange;
|
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) {
|
function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import {
|
import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system';
|
||||||
getElementsToRemove,
|
|
||||||
getIncomersBase,
|
|
||||||
getOutgoersBase,
|
|
||||||
getOverlappingArea,
|
|
||||||
isRectObject,
|
|
||||||
nodeToRect,
|
|
||||||
type Rect,
|
|
||||||
} from '@xyflow/system';
|
|
||||||
|
|
||||||
import useViewportHelper from './useViewportHelper';
|
import useViewportHelper from './useViewportHelper';
|
||||||
import { useStoreApi } from './useStore';
|
import { useStoreApi } from './useStore';
|
||||||
@@ -26,32 +18,40 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
import { isNode } from '../utils';
|
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 viewportHelper = useViewportHelper();
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
const getNodes = useCallback<Instance.GetNodes<NodeType>>(() => {
|
||||||
return store.getState().nodes.map((n) => ({ ...n }));
|
return store.getState().nodes.map((n) => ({ ...n })) as NodeType[];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getNode = useCallback<Instance.GetNode<NodeData>>((id) => {
|
const getNode = useCallback<Instance.GetNode<NodeType>>((id) => {
|
||||||
return store.getState().nodeLookup.get(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();
|
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();
|
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 { 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) {
|
if (hasDefaultNodes) {
|
||||||
setNodes(nextNodes);
|
setNodes(nextNodes);
|
||||||
@@ -59,14 +59,14 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
const changes =
|
const changes =
|
||||||
nextNodes.length === 0
|
nextNodes.length === 0
|
||||||
? nodes.map((node) => ({ type: 'remove', id: node.id } as NodeRemoveChange))
|
? 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);
|
onNodesChange(changes);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setEdges = useCallback<Instance.SetEdges<EdgeData>>((payload) => {
|
const setEdges = useCallback<Instance.SetEdges<EdgeType>>((payload) => {
|
||||||
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
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) {
|
if (hasDefaultEdges) {
|
||||||
setEdges(nextEdges);
|
setEdges(nextEdges);
|
||||||
@@ -74,12 +74,12 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
const changes =
|
const changes =
|
||||||
nextEdges.length === 0
|
nextEdges.length === 0
|
||||||
? edges.map((edge) => ({ type: 'remove', id: edge.id } as EdgeRemoveChange))
|
? 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);
|
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 = Array.isArray(payload) ? payload : [payload];
|
||||||
const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
|
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];
|
const nextNodes = [...currentNodes, ...nodes];
|
||||||
setNodes(nextNodes);
|
setNodes(nextNodes);
|
||||||
} else if (onNodesChange) {
|
} 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);
|
onNodesChange(changes);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addEdges = useCallback<Instance.AddEdges<EdgeData>>((payload) => {
|
const addEdges = useCallback<Instance.AddEdges<EdgeType>>((payload) => {
|
||||||
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
||||||
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
||||||
|
|
||||||
if (hasDefaultEdges) {
|
if (hasDefaultEdges) {
|
||||||
setEdges([...edges, ...nextEdges]);
|
setEdges([...edges, ...nextEdges]);
|
||||||
} else if (onEdgesChange) {
|
} 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);
|
onEdgesChange(changes);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
const toObject = useCallback<Instance.ToObject<NodeType, EdgeType>>(() => {
|
||||||
const { nodes = [], edges = [], transform } = store.getState();
|
const { nodes = [], edges = [], transform } = store.getState();
|
||||||
const [x, y, zoom] = transform;
|
const [x, y, zoom] = transform;
|
||||||
return {
|
return {
|
||||||
nodes: nodes.map((n) => ({ ...n })),
|
nodes: nodes.map((n) => ({ ...n })) as NodeType[],
|
||||||
edges: edges.map((e) => ({ ...e })),
|
edges: edges.map((e) => ({ ...e })) as EdgeType[],
|
||||||
viewport: {
|
viewport: {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -181,11 +181,9 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getNodeRect = useCallback(
|
const getNodeRect = useCallback(
|
||||||
(
|
(nodeOrRect: NodeType | { id: Node['id'] } | Rect): [Rect | null, NodeType | null | undefined, boolean] => {
|
||||||
nodeOrRect: Node<NodeData> | { id: Node['id'] } | Rect
|
|
||||||
): [Rect | null, Node<NodeData> | null | undefined, boolean] => {
|
|
||||||
const isRect = isRectObject(nodeOrRect);
|
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) {
|
if (!isRect && !node) {
|
||||||
[null, null, isRect];
|
[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) => {
|
(nodeOrRect, partially = true, nodes) => {
|
||||||
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
||||||
|
|
||||||
@@ -216,12 +214,12 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
const partiallyVisible = partially && overlappingArea > 0;
|
const partiallyVisible = partially && overlappingArea > 0;
|
||||||
|
|
||||||
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
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) => {
|
(nodeOrRect, area, partially = true) => {
|
||||||
const [nodeRect] = getNodeRect(nodeOrRect);
|
const [nodeRect] = getNodeRect(nodeOrRect);
|
||||||
|
|
||||||
@@ -237,48 +235,13 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getConnectedEdges = useCallback<Instance.getConnectedEdges>((node) => {
|
const updateNode = useCallback<Instance.UpdateNode<NodeType>>(
|
||||||
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>(
|
|
||||||
(id, nodeUpdate, options = { replace: true }) => {
|
(id, nodeUpdate, options = { replace: true }) => {
|
||||||
setNodes((prevNodes) =>
|
setNodes((prevNodes) =>
|
||||||
prevNodes.map((node) => {
|
prevNodes.map((node) => {
|
||||||
if (node.id === id) {
|
if (node.id === id) {
|
||||||
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate;
|
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as NodeType) : nodeUpdate;
|
||||||
return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
|
return options.replace && isNode(nextNode) ? (nextNode as NodeType) : { ...node, ...nextNode };
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@@ -288,7 +251,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
[setNodes]
|
[setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateNodeData = useCallback<Instance.UpdateNodeData>(
|
const updateNodeData = useCallback<Instance.UpdateNodeData<NodeType>>(
|
||||||
(id, dataUpdate, options = { replace: false }) => {
|
(id, dataUpdate, options = { replace: false }) => {
|
||||||
updateNode(
|
updateNode(
|
||||||
id,
|
id,
|
||||||
@@ -317,9 +280,6 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
deleteElements,
|
deleteElements,
|
||||||
getIntersectingNodes,
|
getIntersectingNodes,
|
||||||
isNodeIntersecting,
|
isNodeIntersecting,
|
||||||
getConnectedEdges,
|
|
||||||
getIncomers,
|
|
||||||
getOutgoers,
|
|
||||||
updateNode,
|
updateNode,
|
||||||
updateNodeData,
|
updateNodeData,
|
||||||
};
|
};
|
||||||
@@ -337,8 +297,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
deleteElements,
|
deleteElements,
|
||||||
getIntersectingNodes,
|
getIntersectingNodes,
|
||||||
isNodeIntersecting,
|
isNodeIntersecting,
|
||||||
getConnectedEdges,
|
updateNode,
|
||||||
getIncomers,
|
updateNodeData,
|
||||||
getOutgoers,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import { errorMessages, getDimensions } from '@xyflow/system';
|
|||||||
|
|
||||||
import { useStoreApi } from '../hooks/useStore';
|
import { useStoreApi } from '../hooks/useStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for handling resize events.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
|
function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ const zustandErrorMessage = errorMessages['error001']();
|
|||||||
|
|
||||||
type ExtractState = StoreApi<ReactFlowState> extends { getState: () => infer T } ? T : never;
|
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>(
|
function useStore<StateSlice = ExtractState>(
|
||||||
selector: (state: ReactFlowState) => StateSlice,
|
selector: (state: ReactFlowState) => StateSlice,
|
||||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system';
|
|||||||
|
|
||||||
import { useStoreApi } from '../hooks/useStore';
|
import { useStoreApi } from '../hooks/useStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for updating node internals.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns function for updating node internals
|
||||||
|
*/
|
||||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ import { useStoreApi } from '../hooks/useStore';
|
|||||||
const selectedAndDraggable = (nodesDraggable: boolean) => (n: Node) =>
|
const selectedAndDraggable = (nodesDraggable: boolean) => (n: Node) =>
|
||||||
n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
|
n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for updating node positions.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @returns function for updating node positions
|
||||||
|
*/
|
||||||
function useUpdateNodePositions() {
|
function useUpdateNodePositions() {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ const viewportSelector = (state: ReactFlowState) => ({
|
|||||||
zoom: state.transform[2],
|
zoom: state.transform[2],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for getting the current viewport from the store.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns The current viewport
|
||||||
|
*/
|
||||||
function useViewport(): Viewport {
|
function useViewport(): Viewport {
|
||||||
const viewport = useStore(viewportSelector, shallow);
|
const viewport = useStore(viewportSelector, shallow);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ import type { ViewportHelperFunctions, ReactFlowState } from '../types';
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => !!s.panZoom;
|
const selector = (s: ReactFlowState) => !!s.panZoom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for getting viewport helper functions.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @returns viewport helper functions
|
||||||
|
*/
|
||||||
const useViewportHelper = (): ViewportHelperFunctions => {
|
const useViewportHelper = (): ViewportHelperFunctions => {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const panZoomInitialized = useStore(selector);
|
const panZoomInitialized = useStore(selector);
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import type { ReactFlowState } from '../types';
|
|||||||
|
|
||||||
const selector = (state: ReactFlowState) => state.panZoom?.syncViewport;
|
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) {
|
export default function useViewportSync(viewport?: Viewport) {
|
||||||
const syncViewport = useStore(selector);
|
const syncViewport = useStore(selector);
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import { isEdgeVisible } from '@xyflow/system';
|
|||||||
import { useStore } from './useStore';
|
import { useStore } from './useStore';
|
||||||
import { type ReactFlowState } from '../types';
|
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[] {
|
function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
|
||||||
const edgeIds = useStore(
|
const edgeIds = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
|||||||
: Array.from(s.nodeLookup.keys());
|
: 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) {
|
function useVisibleNodeIds(onlyRenderVisible: boolean) {
|
||||||
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
||||||
|
|
||||||
|
|||||||
@@ -30,16 +30,20 @@ export type NodeRemoveChange = {
|
|||||||
type: 'remove';
|
type: 'remove';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeAddChange<NodeData = any> = {
|
export type NodeAddChange<NodeType extends Node = Node> = {
|
||||||
item: Node<NodeData>;
|
item: NodeType;
|
||||||
type: 'add';
|
type: 'add';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeResetChange<NodeData = any> = {
|
export type NodeResetChange<NodeType extends Node = Node> = {
|
||||||
item: Node<NodeData>;
|
item: NodeType;
|
||||||
type: 'reset';
|
type: 'reset';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all possible node changes.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export type NodeChange =
|
export type NodeChange =
|
||||||
| NodeDimensionChange
|
| NodeDimensionChange
|
||||||
| NodePositionChange
|
| NodePositionChange
|
||||||
@@ -50,12 +54,12 @@ export type NodeChange =
|
|||||||
|
|
||||||
export type EdgeSelectionChange = NodeSelectionChange;
|
export type EdgeSelectionChange = NodeSelectionChange;
|
||||||
export type EdgeRemoveChange = NodeRemoveChange;
|
export type EdgeRemoveChange = NodeRemoveChange;
|
||||||
export type EdgeAddChange<EdgeData = any> = {
|
export type EdgeAddChange<EdgeType extends Edge = Edge> = {
|
||||||
item: Edge<EdgeData>;
|
item: EdgeType;
|
||||||
type: 'add';
|
type: 'add';
|
||||||
};
|
};
|
||||||
export type EdgeResetChange<EdgeData = any> = {
|
export type EdgeResetChange<EdgeType extends Edge = Edge> = {
|
||||||
item: Edge<EdgeData>;
|
item: EdgeType;
|
||||||
type: 'reset';
|
type: 'reset';
|
||||||
};
|
};
|
||||||
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeResetChange;
|
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeResetChange;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type {
|
|||||||
OnError,
|
OnError,
|
||||||
IsValidConnection,
|
IsValidConnection,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
|
SnapGrid,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -44,6 +45,10 @@ import type {
|
|||||||
EdgeMouseHandler,
|
EdgeMouseHandler,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReactFlow component props.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export type ReactFlowProps = Omit<HTMLAttributes<HTMLDivElement>, 'onError'> & {
|
export type ReactFlowProps = Omit<HTMLAttributes<HTMLDivElement>, 'onError'> & {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -110,7 +115,7 @@ export type ReactFlowProps = Omit<HTMLAttributes<HTMLDivElement>, 'onError'> & {
|
|||||||
multiSelectionKeyCode?: KeyCode | null;
|
multiSelectionKeyCode?: KeyCode | null;
|
||||||
zoomActivationKeyCode?: KeyCode | null;
|
zoomActivationKeyCode?: KeyCode | null;
|
||||||
snapToGrid?: boolean;
|
snapToGrid?: boolean;
|
||||||
snapGrid?: [number, number];
|
snapGrid?: SnapGrid;
|
||||||
onlyRenderVisibleElements?: boolean;
|
onlyRenderVisibleElements?: boolean;
|
||||||
nodesDraggable?: boolean;
|
nodesDraggable?: boolean;
|
||||||
nodesConnectable?: boolean;
|
nodesConnectable?: boolean;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import type {
|
|||||||
HandleElement,
|
HandleElement,
|
||||||
ConnectionStatus,
|
ConnectionStatus,
|
||||||
EdgePosition,
|
EdgePosition,
|
||||||
Optional,
|
|
||||||
StepPathOptions,
|
StepPathOptions,
|
||||||
OnError,
|
OnError,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
@@ -31,12 +30,13 @@ export type EdgeLabelOptions = {
|
|||||||
|
|
||||||
export type EdgeUpdatable = boolean | HandleType;
|
export type EdgeUpdatable = boolean | HandleType;
|
||||||
|
|
||||||
export type DefaultEdge<EdgeData = any> = EdgeBase<EdgeData> & {
|
export type DefaultEdge<EdgeData = any> = EdgeBase<EdgeData> &
|
||||||
style?: CSSProperties;
|
EdgeLabelOptions & {
|
||||||
className?: string;
|
style?: CSSProperties;
|
||||||
updatable?: EdgeUpdatable;
|
className?: string;
|
||||||
focusable?: boolean;
|
updatable?: EdgeUpdatable;
|
||||||
} & EdgeLabelOptions;
|
focusable?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
|
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
|
||||||
type: 'smoothstep';
|
type: 'smoothstep';
|
||||||
@@ -53,6 +53,10 @@ type StepEdgeType<T> = DefaultEdge<T> & {
|
|||||||
pathOptions?: StepPathOptions;
|
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 Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeType<T> | StepEdgeType<T>;
|
||||||
|
|
||||||
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
|
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
|
||||||
@@ -88,14 +92,18 @@ export type EdgeTextProps = HTMLAttributes<SVGElement> &
|
|||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// props that get passed to a custom edge
|
/**
|
||||||
|
* Custom edge component props
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export type EdgeProps<T = any> = Pick<
|
export type EdgeProps<T = any> = Pick<
|
||||||
Edge<T>,
|
Edge<T>,
|
||||||
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
|
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
|
||||||
> &
|
> &
|
||||||
Pick<EdgeWrapperProps, 'sourceHandleId' | 'targetHandleId'> &
|
|
||||||
EdgePosition &
|
EdgePosition &
|
||||||
EdgeLabelOptions & {
|
EdgeLabelOptions & {
|
||||||
|
sourceHandleId?: string | null;
|
||||||
|
targetHandleId?: string | null;
|
||||||
markerStart?: string;
|
markerStart?: string;
|
||||||
markerEnd?: string;
|
markerEnd?: string;
|
||||||
// @TODO: how can we get better types for pathOptions?
|
// @TODO: how can we get better types for pathOptions?
|
||||||
@@ -103,31 +111,69 @@ export type EdgeProps<T = any> = Pick<
|
|||||||
interactionWidth?: number;
|
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 & {
|
EdgeLabelOptions & {
|
||||||
id?: string;
|
id?: EdgeProps['id'];
|
||||||
labelX?: number;
|
markerStart?: EdgeProps['markerStart'];
|
||||||
labelY?: number;
|
markerEnd?: EdgeProps['markerEnd'];
|
||||||
path: string;
|
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 EdgeComponentWithPathOptions<PathOptions> = EdgeComponentProps & {
|
||||||
|
pathOptions?: PathOptions;
|
||||||
export type StraightEdgeProps<T = any> = Omit<EdgeComponentProps<T>, 'sourcePosition' | 'targetPosition'>;
|
|
||||||
|
|
||||||
export type SmoothStepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
|
||||||
pathOptions?: SmoothStepPathOptions;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import type { ComponentType } from 'react';
|
|
||||||
import {
|
import {
|
||||||
FitViewParamsBase,
|
FitViewParamsBase,
|
||||||
FitViewOptionsBase,
|
FitViewOptionsBase,
|
||||||
NodeProps,
|
|
||||||
ZoomInOut,
|
ZoomInOut,
|
||||||
ZoomTo,
|
ZoomTo,
|
||||||
SetViewport,
|
SetViewport,
|
||||||
@@ -12,9 +10,11 @@ import {
|
|||||||
SetCenter,
|
SetCenter,
|
||||||
FitBounds,
|
FitBounds,
|
||||||
XYPosition,
|
XYPosition,
|
||||||
|
NodeProps,
|
||||||
} from '@xyflow/system';
|
} 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 OnNodesChange = (changes: NodeChange[]) => void;
|
||||||
export type OnEdgesChange = (changes: EdgeChange[]) => void;
|
export type OnEdgesChange = (changes: EdgeChange[]) => void;
|
||||||
@@ -41,7 +41,9 @@ export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
|
|||||||
export type FitViewParams = FitViewParamsBase<Node>;
|
export type FitViewParams = FitViewParamsBase<Node>;
|
||||||
export type FitViewOptions = FitViewOptionsBase<Node>;
|
export type FitViewOptions = FitViewOptionsBase<Node>;
|
||||||
export type FitView = (fitViewOptions?: FitViewOptions) => boolean;
|
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 = {
|
export type ViewportHelperFunctions = {
|
||||||
zoomIn: ZoomInOut;
|
zoomIn: ZoomInOut;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
import type { Rect, Viewport } from '@xyflow/system';
|
import type { Rect, Viewport } from '@xyflow/system';
|
||||||
import type { Node, Edge, ViewportHelperFunctions } from '.';
|
import type { Node, Edge, ViewportHelperFunctions } from '.';
|
||||||
|
|
||||||
export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
|
export type ReactFlowJsonObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
|
||||||
nodes: Node<NodeData>[];
|
nodes: NodeType[];
|
||||||
edges: Edge<EdgeData>[];
|
edges: EdgeType[];
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,30 +15,33 @@ export type DeleteElementsOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export namespace Instance {
|
export namespace Instance {
|
||||||
export type GetNodes<NodeData> = () => Node<NodeData>[];
|
export type GetNodes<NodeType extends Node = Node> = () => NodeType[];
|
||||||
export type SetNodes<NodeData> = (
|
export type SetNodes<NodeType extends Node = Node> = (
|
||||||
payload: Node<NodeData>[] | ((nodes: Node<NodeData>[]) => Node<NodeData>[])
|
payload: NodeType[] | ((nodes: NodeType[]) => NodeType[])
|
||||||
) => void;
|
) => void;
|
||||||
export type AddNodes<NodeData> = (payload: Node<NodeData>[] | Node<NodeData>) => void;
|
export type AddNodes<NodeType extends Node = Node> = (payload: NodeType[] | NodeType) => void;
|
||||||
export type GetNode<NodeData> = (id: string) => Node<NodeData> | undefined;
|
export type GetNode<NodeType extends Node = Node> = (id: string) => NodeType | undefined;
|
||||||
export type GetEdges<EdgeData> = () => Edge<EdgeData>[];
|
export type GetEdges<EdgeType extends Edge = Edge> = () => EdgeType[];
|
||||||
export type SetEdges<EdgeData> = (
|
export type SetEdges<EdgeType extends Edge = Edge> = (
|
||||||
payload: Edge<EdgeData>[] | ((edges: Edge<EdgeData>[]) => Edge<EdgeData>[])
|
payload: EdgeType[] | ((edges: EdgeType[]) => EdgeType[])
|
||||||
) => void;
|
) => void;
|
||||||
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
|
export type GetEdge<EdgeType extends Edge = Edge> = (id: string) => EdgeType | undefined;
|
||||||
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
|
export type AddEdges<EdgeType extends Edge = Edge> = (payload: EdgeType[] | EdgeType) => void;
|
||||||
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
|
export type ToObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = () => ReactFlowJsonObject<
|
||||||
|
NodeType,
|
||||||
|
EdgeType
|
||||||
|
>;
|
||||||
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => {
|
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => {
|
||||||
deletedNodes: Node[];
|
deletedNodes: Node[];
|
||||||
deletedEdges: Edge[];
|
deletedEdges: Edge[];
|
||||||
};
|
};
|
||||||
export type GetIntersectingNodes<NodeData> = (
|
export type GetIntersectingNodes<NodeType extends Node = Node> = (
|
||||||
node: Node<NodeData> | { id: Node['id'] } | Rect,
|
node: NodeType | { id: Node['id'] } | Rect,
|
||||||
partially?: boolean,
|
partially?: boolean,
|
||||||
nodes?: Node<NodeData>[]
|
nodes?: NodeType[]
|
||||||
) => Node<NodeData>[];
|
) => NodeType[];
|
||||||
export type IsNodeIntersecting<NodeData> = (
|
export type IsNodeIntersecting<NodeType extends Node = Node> = (
|
||||||
node: Node<NodeData> | { id: Node['id'] } | Rect,
|
node: NodeType | { id: Node['id'] } | Rect,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
partially?: boolean
|
partially?: boolean
|
||||||
) => boolean;
|
) => boolean;
|
||||||
@@ -46,32 +49,32 @@ export namespace Instance {
|
|||||||
export type getIncomers = (node: string | Node | { id: Node['id'] }) => Node[];
|
export type getIncomers = (node: string | Node | { id: Node['id'] }) => Node[];
|
||||||
export type getOutgoers = (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,
|
id: string,
|
||||||
dataUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
nodeUpdate: Partial<NodeType> | ((node: NodeType) => Partial<NodeType>),
|
||||||
options?: { replace: boolean }
|
options?: { replace: boolean }
|
||||||
) => void;
|
) => void;
|
||||||
export type UpdateNodeData = (
|
export type UpdateNodeData<NodeType extends Node = Node> = (
|
||||||
id: string,
|
id: string,
|
||||||
dataUpdate: object | ((node: Node) => object),
|
dataUpdate: object | ((node: NodeType) => object),
|
||||||
options?: { replace: boolean }
|
options?: { replace: boolean }
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
|
export type ReactFlowInstance<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
|
||||||
getNodes: Instance.GetNodes<NodeData>;
|
getNodes: Instance.GetNodes<NodeType>;
|
||||||
setNodes: Instance.SetNodes<NodeData>;
|
setNodes: Instance.SetNodes<NodeType>;
|
||||||
addNodes: Instance.AddNodes<NodeData>;
|
addNodes: Instance.AddNodes<NodeType>;
|
||||||
getNode: Instance.GetNode<NodeData>;
|
getNode: Instance.GetNode<NodeType>;
|
||||||
getEdges: Instance.GetEdges<EdgeData>;
|
getEdges: Instance.GetEdges<EdgeType>;
|
||||||
setEdges: Instance.SetEdges<EdgeData>;
|
setEdges: Instance.SetEdges<EdgeType>;
|
||||||
addEdges: Instance.AddEdges<EdgeData>;
|
addEdges: Instance.AddEdges<EdgeType>;
|
||||||
getEdge: Instance.GetEdge<EdgeData>;
|
getEdge: Instance.GetEdge<EdgeType>;
|
||||||
toObject: Instance.ToObject<NodeData, EdgeData>;
|
toObject: Instance.ToObject<NodeType, EdgeType>;
|
||||||
deleteElements: Instance.DeleteElements;
|
deleteElements: Instance.DeleteElements;
|
||||||
getIntersectingNodes: Instance.GetIntersectingNodes<NodeData>;
|
getIntersectingNodes: Instance.GetIntersectingNodes<NodeType>;
|
||||||
isNodeIntersecting: Instance.IsNodeIntersecting<NodeData>;
|
isNodeIntersecting: Instance.IsNodeIntersecting<NodeType>;
|
||||||
updateNode: Instance.UpdateNode;
|
updateNode: Instance.UpdateNode<NodeType>;
|
||||||
updateNodeData: Instance.UpdateNodeData;
|
updateNodeData: Instance.UpdateNodeData<NodeType>;
|
||||||
viewportInitialized: boolean;
|
viewportInitialized: boolean;
|
||||||
} & Omit<ViewportHelperFunctions, 'initialized'>;
|
} & Omit<ViewportHelperFunctions, 'initialized'>;
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||||
import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/system';
|
import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/system';
|
||||||
|
|
||||||
import { NodeTypes } from './general';
|
import { NodeTypes } from './general';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// 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<
|
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase<
|
||||||
NodeData,
|
NodeData,
|
||||||
NodeType
|
NodeType
|
||||||
@@ -10,6 +15,7 @@ export type Node<NodeData = any, NodeType extends string | undefined = string |
|
|||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
resizing?: boolean;
|
resizing?: boolean;
|
||||||
|
focusable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
||||||
|
|||||||
@@ -142,12 +142,52 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
|||||||
return updatedElements;
|
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 => ({
|
export const createSelectionChange = (id: string, selected: boolean): NodeSelectionChange | EdgeSelectionChange => ({
|
||||||
|
|||||||
@@ -10,10 +10,68 @@ import {
|
|||||||
|
|
||||||
import type { Edge, Node } from '../types';
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ function getMediaQuery() {
|
|||||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
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> {
|
export function useColorModeClass(colorMode: ColorMode = 'light'): Readable<ColorModeClass> {
|
||||||
const colorModeClass = readable<ColorModeClass>('light', (set) => {
|
const colorModeClass = readable<ColorModeClass>('light', (set) => {
|
||||||
if (colorMode !== 'system') {
|
if (colorMode !== 'system') {
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import type { Readable } from 'svelte/store';
|
|||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import type { ConnectionProps } from '$lib/store/derived-connection-props';
|
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> {
|
export function useConnection(): Readable<ConnectionProps> {
|
||||||
const { connection } = useStore();
|
const { connection } = useStore();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ export type useHandleConnectionsParams = {
|
|||||||
|
|
||||||
const initialConnections: Connection[] = [];
|
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) {
|
export function useHandleConnections({ nodeId, type, id = null }: useHandleConnectionsParams) {
|
||||||
const { edges, connectionLookup } = useStore();
|
const { edges, connectionLookup } = useStore();
|
||||||
let prevConnections: Map<string, Connection> | undefined = undefined;
|
let prevConnections: Map<string, Connection> | undefined = undefined;
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) {
|
|||||||
return true;
|
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>(
|
export function useNodesData<NodeType extends Node = Node>(
|
||||||
nodeId: string
|
nodeId: string
|
||||||
): Readable<NodeType['data'] | null>;
|
): Readable<NodeType['data'] | null>;
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
import { useStore } from '$lib/store';
|
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() {
|
export function useNodes() {
|
||||||
const { nodes } = useStore();
|
const { nodes } = useStore();
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for getting the current edges from the store.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns store with an array of edges
|
||||||
|
*/
|
||||||
export function useEdges() {
|
export function useEdges() {
|
||||||
const { edges } = useStore();
|
const { edges } = useStore();
|
||||||
return edges;
|
return edges;
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ import { useStore } from '$lib/store';
|
|||||||
import type { Edge, FitViewOptions, Node } from '$lib/types';
|
import type { Edge, FitViewOptions, Node } from '$lib/types';
|
||||||
import { isNode } from '$lib/utils';
|
import { isNode } from '$lib/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for accessing the ReactFlow instance.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns helper functions
|
||||||
|
*/
|
||||||
export function useSvelteFlow(): {
|
export function useSvelteFlow(): {
|
||||||
zoomIn: ZoomInOut;
|
zoomIn: ZoomInOut;
|
||||||
zoomOut: ZoomInOut;
|
zoomOut: ZoomInOut;
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import type { UpdateNodeInternals } from '@xyflow/system';
|
|||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for updating node internals.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns function for updating node internals
|
||||||
|
*/
|
||||||
export function useUpdateNodeInternals(): UpdateNodeInternals {
|
export function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||||
const { domNode, updateNodeDimensions } = useStore();
|
const { domNode, updateNodeDimensions } = useStore();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type {
|
|||||||
DefaultEdgeOptionsBase,
|
DefaultEdgeOptionsBase,
|
||||||
EdgePosition,
|
EdgePosition,
|
||||||
SmoothStepPathOptions,
|
SmoothStepPathOptions,
|
||||||
Optional,
|
|
||||||
StepPathOptions
|
StepPathOptions
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
@@ -34,12 +33,18 @@ type StepEdgeType<T> = DefaultEdge<T> & {
|
|||||||
pathOptions?: StepPathOptions;
|
pathOptions?: StepPathOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Edge type is mainly used for the `edges` that get passed to the SvelteFlow component.
|
||||||
|
*/
|
||||||
export type Edge<T = any> =
|
export type Edge<T = any> =
|
||||||
| DefaultEdge<T>
|
| DefaultEdge<T>
|
||||||
| SmoothStepEdgeType<T>
|
| SmoothStepEdgeType<T>
|
||||||
| BezierEdgeType<T>
|
| BezierEdgeType<T>
|
||||||
| StepEdgeType<T>;
|
| StepEdgeType<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom edge component props.
|
||||||
|
*/
|
||||||
export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' | 'type'> &
|
export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' | 'type'> &
|
||||||
EdgePosition & {
|
EdgePosition & {
|
||||||
markerStart?: string;
|
markerStart?: string;
|
||||||
@@ -48,30 +53,48 @@ export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' |
|
|||||||
targetHandleId?: string | null;
|
targetHandleId?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EdgeComponentProps<T = any> = Optional<
|
/**
|
||||||
Omit<
|
* Helper type for edge components that get exported by the library.
|
||||||
EdgeProps<T>,
|
*/
|
||||||
'source' | 'target' | 'sourceHandleId' | 'targetHandleId' | 'animated' | 'selected' | 'data'
|
export type EdgeComponentProps = EdgePosition & {
|
||||||
>,
|
id?: EdgeProps['id'];
|
||||||
'id'
|
hidden?: EdgeProps['hidden'];
|
||||||
>;
|
deletable?: EdgeProps['deletable'];
|
||||||
|
selectable?: EdgeProps['selectable'];
|
||||||
export type BezierEdgeProps<T = any> = EdgeComponentProps<T> & {
|
markerStart?: EdgeProps['markerStart'];
|
||||||
pathOptions?: BezierPathOptions;
|
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> & {
|
export type EdgeComponentWithPathOptions<PathOptions> = EdgeComponentProps & {
|
||||||
pathOptions?: SmoothStepPathOptions;
|
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>,
|
* SmoothStepEdge component props
|
||||||
'sourcePosition' | 'targetPosition'
|
*/
|
||||||
>;
|
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>>>;
|
export type EdgeTypes = Record<string, ComponentType<SvelteComponent<EdgeProps>>>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import type { ComponentType, SvelteComponent } from 'svelte';
|
import type { ComponentType, SvelteComponent } from 'svelte';
|
||||||
import type { NodeBase, NodeProps } from '@xyflow/system';
|
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
|
* The node data structure that gets used for the nodes prop.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
* @public
|
||||||
|
*/
|
||||||
export type Node<
|
export type Node<
|
||||||
NodeData = any,
|
NodeData = any,
|
||||||
NodeType extends string | undefined = string | undefined
|
NodeType extends string | undefined = string | undefined
|
||||||
|
|||||||
@@ -10,10 +10,68 @@ import {
|
|||||||
|
|
||||||
import type { Edge, Node } from '$lib/types';
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export type NodeBase<T = any, U extends string | undefined = string | undefined>
|
|||||||
extent?: 'parent' | CoordinateExtent;
|
extent?: 'parent' | CoordinateExtent;
|
||||||
expandParent?: boolean;
|
expandParent?: boolean;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
focusable?: boolean;
|
|
||||||
origin?: NodeOrigin;
|
origin?: NodeOrigin;
|
||||||
handles?: NodeHandle[];
|
handles?: NodeHandle[];
|
||||||
computed?: {
|
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({
|
export function getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
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>(
|
export const addEdgeBase = <EdgeType extends EdgeBase>(
|
||||||
edgeParams: EdgeType | Connection,
|
edgeParams: EdgeType | Connection,
|
||||||
edges: EdgeType[]
|
edges: EdgeType[]
|
||||||
@@ -129,6 +137,14 @@ export type UpdateEdgeOptions = {
|
|||||||
shouldReplaceId?: boolean;
|
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>(
|
export const updateEdgeBase = <EdgeType extends EdgeBase>(
|
||||||
oldEdge: EdgeType,
|
oldEdge: EdgeType,
|
||||||
newConnection: Connection,
|
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}`;
|
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({
|
export function getSmoothStepPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
|
|||||||
@@ -7,6 +7,26 @@ export type GetStraightPathParams = {
|
|||||||
targetY: number;
|
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({
|
export function getStraightPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import type {
|
|||||||
NodeOrigin,
|
NodeOrigin,
|
||||||
SnapGrid,
|
SnapGrid,
|
||||||
Transform,
|
Transform,
|
||||||
Viewport,
|
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
import { type Viewport } from '../types';
|
||||||
import { getNodePositionWithOrigin } from './graph';
|
import { getNodePositionWithOrigin } from './graph';
|
||||||
|
|
||||||
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max);
|
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]),
|
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 => {
|
const calcAutoPanVelocity = (value: number, min: number, max: number): number => {
|
||||||
if (value < min) {
|
if (value < min) {
|
||||||
return clamp(Math.abs(value - min), 1, 50) / 50;
|
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 {
|
return {
|
||||||
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
|
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
|
||||||
y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
|
y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const pointToRendererPoint = (
|
export const pointToRendererPoint = (
|
||||||
{ x, y }: XYPosition,
|
{ 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 = (
|
export const getViewportForBounds = (
|
||||||
bounds: Rect,
|
bounds: Rect,
|
||||||
width: number,
|
width: number,
|
||||||
|
|||||||
@@ -25,12 +25,34 @@ import {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
import { errorMessages } from '../constants';
|
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 =>
|
export const isEdgeBase = <EdgeType extends EdgeBase = EdgeBase>(element: any): element is EdgeType =>
|
||||||
'id' in element && 'source' in element && 'target' in element;
|
'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 =>
|
export const isNodeBase = <NodeType extends NodeBase = NodeBase>(element: any): element is NodeType =>
|
||||||
'id' in element && !('source' in element) && !('target' in element);
|
'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>(
|
export const getOutgoersBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||||
node: NodeType | { id: string },
|
node: NodeType | { id: string },
|
||||||
nodes: NodeType[],
|
nodes: NodeType[],
|
||||||
@@ -50,6 +72,14 @@ export const getOutgoersBase = <NodeType extends NodeBase = NodeBase, EdgeType e
|
|||||||
return nodes.filter((n) => outgoerIds.has(n.id));
|
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>(
|
export const getIncomersBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||||
node: NodeType | { id: string },
|
node: NodeType | { id: string },
|
||||||
nodes: NodeType[],
|
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 => {
|
export const getNodesBounds = (nodes: NodeBase[], nodeOrigin: NodeOrigin = [0, 0]): Rect => {
|
||||||
if (nodes.length === 0) {
|
if (nodes.length === 0) {
|
||||||
return { x: 0, y: 0, width: 0, height: 0 };
|
return { x: 0, y: 0, width: 0, height: 0 };
|
||||||
@@ -167,6 +205,12 @@ export const getNodesInside = <NodeType extends NodeBase>(
|
|||||||
return visibleNodes;
|
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>(
|
export const getConnectedEdgesBase = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>(
|
||||||
nodes: NodeType[],
|
nodes: NodeType[],
|
||||||
edges: EdgeType[]
|
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
|
* Pass in nodes & edges to delete, get arrays of nodes and edges that actually can be deleted
|
||||||
// and the function only returns elements that are deletable and also handles connected nodes and child nodes
|
* @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>({
|
export function getElementsToRemove<NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>({
|
||||||
nodesToRemove,
|
nodesToRemove,
|
||||||
edgesToRemove,
|
edgesToRemove,
|
||||||
|
|||||||
Reference in New Issue
Block a user