Merge branch 'feat/zindexmode' of github.com:xyflow/xyflow into feat/zindexmode

This commit is contained in:
moklick
2025-12-02 13:43:40 +01:00
21 changed files with 125 additions and 37 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Prevent unnecessary re-render in `FlowRenderer`
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/system': patch
---
Allow custom `getEdgeId` function in `addEdge` and `reconnectEdge` options to enable custom edge ID schemes.
-5
View File
@@ -1,5 +0,0 @@
---
'@xyflow/svelte': patch
---
Fix onPaneClick always firing when moving the viewport
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Always create a new measured object in apply changes.
-5
View File
@@ -1,5 +0,0 @@
---
'@xyflow/svelte': patch
---
Fix wrong positions when resizing nodes with a non-default node-origin
-7
View File
@@ -1,7 +0,0 @@
---
'@xyflow/react': minor
'@xyflow/svelte': minor
'@xyflow/system': patch
---
Pass current pointer position to connection
+7
View File
@@ -0,0 +1,7 @@
---
'@xyflow/react': patch
'@xyflow/svelte': patch
'@xyflow/system': patch
---
Update an ongoing connection when user moves node with keyboard.
+1 -1
View File
@@ -156,7 +156,7 @@ For releasing packages we are using [changesets](https://github.com/changesets/c
1. create PRs for new features, updates and fixes (with a changeset if relevant for changelog) 1. create PRs for new features, updates and fixes (with a changeset if relevant for changelog)
2. merge into main 2. merge into main
3. changset creates a PR that bumps all packages based on the changesets 3. changeset creates a PR that bumps all packages based on the changesets
4. merge changeset PR if you want to release to Github and npm 4. merge changeset PR if you want to release to Github and npm
## Built by [xyflow](https://xyflow.com) ## Built by [xyflow](https://xyflow.com)
+11
View File
@@ -1,5 +1,16 @@
# @xyflow/react # @xyflow/react
## 12.9.3
### Patch Changes
- [#5621](https://github.com/xyflow/xyflow/pull/5621) [`c1304dba7`](https://github.com/xyflow/xyflow/commit/c1304dba7a20bb8d74c7aceb23cd80b56e4c0482) Thanks [@moklick](https://github.com/moklick)! - Set `paneClickDistance` default value to `1`.
- [#5578](https://github.com/xyflow/xyflow/pull/5578) [`00bcb9f5f`](https://github.com/xyflow/xyflow/commit/00bcb9f5f45f49814b9ac19b3f55cfe069ee3773) Thanks [@peterkogo](https://github.com/peterkogo)! - Pass current pointer position to connection
- Updated dependencies [[`00bcb9f5f`](https://github.com/xyflow/xyflow/commit/00bcb9f5f45f49814b9ac19b3f55cfe069ee3773)]:
- @xyflow/system@0.0.73
## 12.9.2 ## 12.9.2
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/react", "name": "@xyflow/react",
"version": "12.9.2", "version": "12.9.3",
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.", "description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
"keywords": [ "keywords": [
"react", "react",
@@ -1,4 +1,5 @@
import { memo, type ReactNode } from 'react'; import { memo, type ReactNode } from 'react';
import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import { useGlobalKeyHandler } from '../../hooks/useGlobalKeyHandler'; import { useGlobalKeyHandler } from '../../hooks/useGlobalKeyHandler';
@@ -72,7 +73,7 @@ function FlowRendererComponent<NodeType extends Node = Node>({
onViewportChange, onViewportChange,
isControlledViewport, isControlledViewport,
}: FlowRendererProps<NodeType>) { }: FlowRendererProps<NodeType>) {
const { nodesSelectionActive, userSelectionActive } = useStore(selector); const { nodesSelectionActive, userSelectionActive } = useStore(selector, shallow);
const selectionKeyPressed = useKeyPress(selectionKeyCode, { target: win }); const selectionKeyPressed = useKeyPress(selectionKeyCode, { target: win });
const panActivationKeyPressed = useKeyPress(panActivationKeyCode, { target: win }); const panActivationKeyPressed = useKeyPress(panActivationKeyCode, { target: win });
@@ -104,7 +104,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
onPaneMouseLeave, onPaneMouseLeave,
onPaneScroll, onPaneScroll,
onPaneContextMenu, onPaneContextMenu,
paneClickDistance = 0, paneClickDistance = 1,
nodeClickDistance = 0, nodeClickDistance = 0,
children, children,
onReconnect, onReconnect,
+9 -2
View File
@@ -14,7 +14,9 @@ import {
NodeOrigin, NodeOrigin,
CoordinateExtent, CoordinateExtent,
fitViewport, fitViewport,
ZIndexMode, getHandlePosition,
Position,
ZIndexMode
} from '@xyflow/system'; } from '@xyflow/system';
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes'; import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
@@ -191,7 +193,7 @@ const createStore = ({
updateNodePositions: (nodeDragItems, dragging = false) => { updateNodePositions: (nodeDragItems, dragging = false) => {
const parentExpandChildren: ParentExpandChild[] = []; const parentExpandChildren: ParentExpandChild[] = [];
const changes = []; const changes = [];
const { nodeLookup, triggerNodeChanges } = get(); const { nodeLookup, triggerNodeChanges, connection, updateConnection } = get();
for (const [id, dragItem] of nodeDragItems) { for (const [id, dragItem] of nodeDragItems) {
// we are using the nodelookup to be sure to use the current expandParent and parentId value // we are using the nodelookup to be sure to use the current expandParent and parentId value
@@ -210,6 +212,11 @@ const createStore = ({
dragging, dragging,
}; };
if (node && connection.inProgress && connection.fromNode.id === node.id) {
const updatedFrom = getHandlePosition(node, connection.fromHandle, Position.Left, true);
updateConnection({ ...connection, from: updatedFrom });
}
if (expandParent && node.parentId) { if (expandParent && node.parentId) {
parentExpandChildren.push({ parentExpandChildren.push({
id, id,
+3 -3
View File
@@ -125,9 +125,9 @@ function applyChange(change: any, element: any): any {
case 'dimensions': { case 'dimensions': {
if (typeof change.dimensions !== 'undefined') { if (typeof change.dimensions !== 'undefined') {
element.measured ??= {}; element.measured = {
element.measured.width = change.dimensions.width; ...change.dimensions,
element.measured.height = change.dimensions.height; };
if (change.setAttributes) { if (change.setAttributes) {
if (change.setAttributes === true || change.setAttributes === 'width') { if (change.setAttributes === true || change.setAttributes === 'width') {
+13
View File
@@ -1,5 +1,18 @@
# @xyflow/svelte # @xyflow/svelte
## 1.4.2
### Patch Changes
- [#5603](https://github.com/xyflow/xyflow/pull/5603) [`17a175791`](https://github.com/xyflow/xyflow/commit/17a175791b2420b32d55a8fcbbb35649862b85cf) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix onPaneClick always firing when moving the viewport
- [#5615](https://github.com/xyflow/xyflow/pull/5615) [`8b35c77eb`](https://github.com/xyflow/xyflow/commit/8b35c77ebe5a4f6cba33c597d94eba1ead64fdfc) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix wrong positions when resizing nodes with a non-default node-origin
- [#5578](https://github.com/xyflow/xyflow/pull/5578) [`00bcb9f5f`](https://github.com/xyflow/xyflow/commit/00bcb9f5f45f49814b9ac19b3f55cfe069ee3773) Thanks [@peterkogo](https://github.com/peterkogo)! - Pass current pointer position to connection
- Updated dependencies [[`00bcb9f5f`](https://github.com/xyflow/xyflow/commit/00bcb9f5f45f49814b9ac19b3f55cfe069ee3773)]:
- @xyflow/system@0.0.73
## 1.4.1 ## 1.4.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/svelte", "name": "@xyflow/svelte",
"version": "1.4.1", "version": "1.4.2",
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
"keywords": [ "keywords": [
"svelte", "svelte",
+12 -1
View File
@@ -15,7 +15,9 @@ import {
updateAbsolutePositions, updateAbsolutePositions,
snapPosition, snapPosition,
calculateNodePosition, calculateNodePosition,
type SetCenterOptions type SetCenterOptions,
getHandlePosition,
Position
} from '@xyflow/system'; } from '@xyflow/system';
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types'; import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
@@ -51,6 +53,15 @@ export function createStore<NodeType extends Node = Node, EdgeType extends Edge
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => { const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
store.nodes = store.nodes.map((node) => { store.nodes = store.nodes.map((node) => {
if (store.connection.inProgress && store.connection.fromNode.id === node.id) {
const internalNode = store.nodeLookup.get(node.id);
if (internalNode) {
store.connection = {
...store.connection,
from: getHandlePosition(internalNode, store.connection.fromHandle, Position.Left, true)
};
}
}
const dragItem = nodeDragItems.get(node.id); const dragItem = nodeDragItems.get(node.id);
return dragItem ? { ...node, position: dragItem.position, dragging } : node; return dragItem ? { ...node, position: dragItem.position, dragging } : node;
}); });
+6
View File
@@ -1,5 +1,11 @@
# @xyflow/system # @xyflow/system
## 0.0.73
### Patch Changes
- [#5578](https://github.com/xyflow/xyflow/pull/5578) [`00bcb9f5f`](https://github.com/xyflow/xyflow/commit/00bcb9f5f45f49814b9ac19b3f55cfe069ee3773) Thanks [@peterkogo](https://github.com/peterkogo)! - Pass current pointer position to connection
## 0.0.72 ## 0.0.72
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/system", "name": "@xyflow/system",
"version": "0.0.72", "version": "0.0.73",
"description": "xyflow core system that powers React Flow and Svelte Flow.", "description": "xyflow core system that powers React Flow and Svelte Flow.",
"keywords": [ "keywords": [
"node-based UI", "node-based UI",
+33 -4
View File
@@ -101,7 +101,19 @@ export function isEdgeVisible({ sourceNode, targetNode, width, height, transform
return getOverlappingArea(viewRect, boxToRect(edgeBox)) > 0; 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 || ''}`; `xy-edge__${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => {
@@ -114,11 +126,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. * 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 * @public
* @param edgeParams - Either an `Edge` or a `Connection` you want to add. * @param edgeParams - Either an `Edge` or a `Connection` you want to add.
* @param edges - The array of all current edges. * @param edges - The array of all current edges.
* @param options - Optional configuration object.
* @returns A new array of edges with the new edge added. * @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 * @remarks If an edge with the same `target` and `source` already exists (and the same
@@ -128,7 +148,8 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => {
*/ */
export const addEdge = <EdgeType extends EdgeBase>( export const addEdge = <EdgeType extends EdgeBase>(
edgeParams: EdgeType | Connection, edgeParams: EdgeType | Connection,
edges: EdgeType[] edges: EdgeType[],
options: AddEdgeOptions = {}
): EdgeType[] => { ): EdgeType[] => {
if (!edgeParams.source || !edgeParams.target) { if (!edgeParams.source || !edgeParams.target) {
devWarn('006', errorMessages['error006']()); devWarn('006', errorMessages['error006']());
@@ -136,13 +157,15 @@ export const addEdge = <EdgeType extends EdgeBase>(
return edges; return edges;
} }
const edgeIdGenerator = options.getEdgeId || getEdgeId;
let edge: EdgeType; let edge: EdgeType;
if (isEdgeBase(edgeParams)) { if (isEdgeBase(edgeParams)) {
edge = { ...edgeParams }; edge = { ...edgeParams };
} else { } else {
edge = { edge = {
...edgeParams, ...edgeParams,
id: getEdgeId(edgeParams), id: edgeIdGenerator(edgeParams),
} as EdgeType; } as EdgeType;
} }
@@ -167,6 +190,10 @@ export type ReconnectEdgeOptions = {
* @default true * @default true
*/ */
shouldReplaceId?: boolean; shouldReplaceId?: boolean;
/**
* Custom function to generate edge IDs. If not provided, the default `getEdgeId` function is used.
*/
getEdgeId?: GetEdgeId;
}; };
/** /**
@@ -207,10 +234,12 @@ export const reconnectEdge = <EdgeType extends EdgeBase>(
return edges; return edges;
} }
const edgeIdGenerator = options.getEdgeId || getEdgeId;
// Remove old edge and create the new edge with parameters of old edge. // Remove old edge and create the new edge with parameters of old edge.
const edge = { const edge = {
...rest, ...rest,
id: options.shouldReplaceId ? getEdgeId(newConnection) : oldEdgeId, id: options.shouldReplaceId ? edgeIdGenerator(newConnection) : oldEdgeId,
source: newConnection.source, source: newConnection.source,
target: newConnection.target, target: newConnection.target,
sourceHandle: newConnection.sourceHandle, sourceHandle: newConnection.sourceHandle,
+9 -4
View File
@@ -93,8 +93,8 @@ function onPointerDown(
position: fromHandleInternal.position, position: fromHandleInternal.position,
}; };
const fromNodeInternal = nodeLookup.get(nodeId)!; const fromInternalNode = nodeLookup.get(nodeId)!;
const from = getHandlePosition(fromNodeInternal, fromHandle, Position.Left, true); const from = getHandlePosition(fromInternalNode, fromHandle, Position.Left, true);
let previousConnection: ConnectionInProgress = { let previousConnection: ConnectionInProgress = {
inProgress: true, inProgress: true,
@@ -103,7 +103,7 @@ function onPointerDown(
from, from,
fromHandle, fromHandle,
fromPosition: fromHandle.position, fromPosition: fromHandle.position,
fromNode: fromNodeInternal, fromNode: fromInternalNode,
to: position, to: position,
toHandle: null, toHandle: null,
@@ -172,9 +172,14 @@ function onPointerDown(
connection = result.connection; connection = result.connection;
isValid = isConnectionValid(!!closestHandle, result.isValid); isValid = isConnectionValid(!!closestHandle, result.isValid);
const fromInternalNode = nodeLookup.get(nodeId);
const from = fromInternalNode
? getHandlePosition(fromInternalNode, fromHandle, Position.Left, true)
: previousConnection.from;
const newConnection: ConnectionInProgress = { const newConnection: ConnectionInProgress = {
// from stays the same
...previousConnection, ...previousConnection,
from,
isValid, isValid,
to: to:
result.toHandle && isValid result.toHandle && isValid