diff --git a/.changeset/custom-edge-id-generator.md b/.changeset/custom-edge-id-generator.md new file mode 100644 index 00000000..65947b75 --- /dev/null +++ b/.changeset/custom-edge-id-generator.md @@ -0,0 +1,5 @@ +--- +'@xyflow/system': minor +--- + +Allow custom `getEdgeId` function in `addEdge` and `reconnectEdge`. You can now pass a custom edge ID generator via the options parameter, enabling custom edge ID schemes. Closes #5180. diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index 58139394..515a285a 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -84,7 +84,19 @@ export function isEdgeVisible({ sourceNode, targetNode, width, height, transform return getOverlappingArea(viewRect, boxToRect(edgeBox)) > 0; } -const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | EdgeBase): string => +/** + * Type for a custom edge ID generator function. + * @public + */ +export type GetEdgeId = (params: Connection | EdgeBase) => string; + +/** + * The default edge ID generator function. Generates an ID based on the source, target, and handles. + * @public + * @param params - The connection or edge to generate an ID for. + * @returns The generated edge ID. + */ +export const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | EdgeBase): string => `xy-edge__${source}${sourceHandle || ''}-${target}${targetHandle || ''}`; const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { @@ -97,11 +109,19 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { ); }; +export type AddEdgeOptions = { + /** + * Custom function to generate edge IDs. If not provided, the default `getEdgeId` function is used. + */ + getEdgeId?: GetEdgeId; +}; + /** * This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. * @public * @param edgeParams - Either an `Edge` or a `Connection` you want to add. * @param edges - The array of all current edges. + * @param options - Optional configuration object. * @returns A new array of edges with the new edge added. * * @remarks If an edge with the same `target` and `source` already exists (and the same @@ -111,7 +131,8 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { */ export const addEdge = ( edgeParams: EdgeType | Connection, - edges: EdgeType[] + edges: EdgeType[], + options: AddEdgeOptions = {} ): EdgeType[] => { if (!edgeParams.source || !edgeParams.target) { devWarn('006', errorMessages['error006']()); @@ -119,13 +140,15 @@ export const addEdge = ( return edges; } + const edgeIdGenerator = options.getEdgeId || getEdgeId; + let edge: EdgeType; if (isEdgeBase(edgeParams)) { edge = { ...edgeParams }; } else { edge = { ...edgeParams, - id: getEdgeId(edgeParams), + id: edgeIdGenerator(edgeParams), } as EdgeType; } @@ -150,6 +173,10 @@ export type ReconnectEdgeOptions = { * @default true */ shouldReplaceId?: boolean; + /** + * Custom function to generate edge IDs. If not provided, the default `getEdgeId` function is used. + */ + getEdgeId?: GetEdgeId; }; /** @@ -190,10 +217,12 @@ export const reconnectEdge = ( return edges; } + const edgeIdGenerator = options.getEdgeId || getEdgeId; + // Remove old edge and create the new edge with parameters of old edge. const edge = { ...rest, - id: options.shouldReplaceId ? getEdgeId(newConnection) : oldEdgeId, + id: options.shouldReplaceId ? edgeIdGenerator(newConnection) : oldEdgeId, source: newConnection.source, target: newConnection.target, sourceHandle: newConnection.sourceHandle,