Merge branch 'main' into bug-fix-node-positions
This commit is contained in:
@@ -1,5 +1,47 @@
|
||||
# @xyflow/system
|
||||
|
||||
## 0.0.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5370](https://github.com/xyflow/xyflow/pull/5370) [`26f2cdd7`](https://github.com/xyflow/xyflow/commit/26f2cdd720fc2c8fb337d3af13b82dab6a90fb60) Thanks [@moklick](https://github.com/moklick)! - Only fire connection end events if connection was started
|
||||
|
||||
## 0.0.64
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5362](https://github.com/xyflow/xyflow/pull/5362) [`72dc1d60`](https://github.com/xyflow/xyflow/commit/72dc1d602110947e3db83c37b9a9125ee85cf4bc) Thanks [@moklick](https://github.com/moklick)! - Remove pointer events from Panel via CSS while a selection gets dragged
|
||||
|
||||
- [#5361](https://github.com/xyflow/xyflow/pull/5361) [`90e9247a`](https://github.com/xyflow/xyflow/commit/90e9247adbdfa9d06db97e1d0d895e35c960551c) Thanks [@peterkogo](https://github.com/peterkogo)! - Render edges above nodes when they are within a subflow
|
||||
|
||||
- [#5344](https://github.com/xyflow/xyflow/pull/5344) [`2441bf8d`](https://github.com/xyflow/xyflow/commit/2441bf8d97a6b72494f216915d52d5acbeefefde) Thanks [@moklick](https://github.com/moklick)! - Add connectionDragThreshold prop
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5354](https://github.com/xyflow/xyflow/pull/5354) [`c4312d89`](https://github.com/xyflow/xyflow/commit/c4312d8997ecdc7ef12cfa4efc1fde7131a2b950) Thanks [@moklick](https://github.com/moklick)! - Add TSDoc annotations
|
||||
|
||||
- [#5333](https://github.com/xyflow/xyflow/pull/5333) [`3d7e8b6b`](https://github.com/xyflow/xyflow/commit/3d7e8b6bb10001ee84d79ca4f6a9fd0053c4a276) Thanks [@peterkogo](https://github.com/peterkogo)! - Add missing type exports
|
||||
|
||||
- [#5350](https://github.com/xyflow/xyflow/pull/5350) [`9c61000c`](https://github.com/xyflow/xyflow/commit/9c61000cac6277ce97274cc626fa7266f82dec27) Thanks [@moklick](https://github.com/moklick)! - Only send node position updates if positions changed
|
||||
|
||||
## 0.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5299](https://github.com/xyflow/xyflow/pull/5299) [`848b486b`](https://github.com/xyflow/xyflow/commit/848b486b2201b650ecb3317f367a723edb2458e1) Thanks [@printerscanner](https://github.com/printerscanner)! - Add `ariaRole` prop to nodes and edges
|
||||
|
||||
- [#5280](https://github.com/xyflow/xyflow/pull/5280) [`dba6faf2`](https://github.com/xyflow/xyflow/commit/dba6faf20e7ec2524d5270d177331d3bd260f3ac) Thanks [@moklick](https://github.com/moklick)! - Improve typing for Nodes
|
||||
|
||||
- [#5276](https://github.com/xyflow/xyflow/pull/5276) [`6ce44a05`](https://github.com/xyflow/xyflow/commit/6ce44a05c829068ff5a8416ce3fa4ee6e0eced48) Thanks [@moklick](https://github.com/moklick)! - Add an `ease` and `interpolate` option to all function that alter the viewport
|
||||
|
||||
- [#5277](https://github.com/xyflow/xyflow/pull/5277) [`f59730ce`](https://github.com/xyflow/xyflow/commit/f59730ce3530a91f579f6bbd2ea9335680f552ef) Thanks [@printerscanner](https://github.com/printerscanner)! - Add `ariaLabelConfig` prop for customizing UI text like aria labels and descriptions.
|
||||
|
||||
- [#5317](https://github.com/xyflow/xyflow/pull/5317) [`09458f52`](https://github.com/xyflow/xyflow/commit/09458f52ff57356e03404c58e9bfdbfd50579850) Thanks [@moklick](https://github.com/moklick)! - Add `domAttributes` option for nodes and edges
|
||||
|
||||
- [#5326](https://github.com/xyflow/xyflow/pull/5326) [`050b511c`](https://github.com/xyflow/xyflow/commit/050b511cd6966ba526299f7ca11f9ca4791fd2cf) Thanks [@peterkogo](https://github.com/peterkogo)! - Prevent NodeResizer controls to become too small when zooming out
|
||||
|
||||
## 0.0.61
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,47 +1,62 @@
|
||||
# @xyflow/system
|
||||
|
||||
Core system that powers React Flow and Svelte Flow.
|
||||
Core system utilities powering [React Flow](https://reactflow.dev) and [Svelte Flow](https://svelteflow.dev).
|
||||
|
||||
## Installation
|
||||
> **Note:** This package is designed as a shared vanilla utility layer for React Flow and Svelte Flow. It is not intended for use with unrelated libraries.
|
||||
|
||||
```sh
|
||||
npm install @xyflow/system
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @xyflow/system
|
||||
```
|
||||
|
||||
## What is this package about?
|
||||
## What is this package?
|
||||
|
||||
The @xyflow/system package was created to have a place for vanilla utils for React Flow and Svelte Flow. The package exports helpers for edge creation, pan and zoom, dragging of nodes, general utils and lots of types. All the helpers are specifically built for React Flow and Svelte Flow so it's probably not too interesting to use them with other libraries.
|
||||
`@xyflow/system` provides core, framework-agnostic helpers and types for building node-based editors and flow diagrams. It contains the logic and utilities that are shared between React Flow and Svelte Flow, such as edge path calculations, pan/zoom, node dragging, and more.
|
||||
|
||||
### XYPanZoom
|
||||
## Features
|
||||
|
||||
Adds zoom and pan for the pane.
|
||||
- **Pan & Zoom (`XYPanZoom`)**: Utilities for adding interactive pan and zoom to your canvas.
|
||||
- **Dragging (`XYDrag`)**: Helpers for node and selection dragging.
|
||||
- **Handles/Connections (`XYHandle`)**: Logic for drawing and managing connection lines between nodes.
|
||||
- **Minimap (`XYMiniMap`)**: Interactive minimap utilities for overview and navigation.
|
||||
- **Edge Utilities**: Functions for SVG edge path creation (bezier, straight, step, smoothstep, etc.).
|
||||
- **Store Utilities**: Helpers for managing and updating flow state.
|
||||
- **DOM Utilities**: Functions for DOM measurements and interactions.
|
||||
- **Marker Utilities**: Helpers for SVG markers on edges.
|
||||
- **Graph Utilities**: Functions for working with nodes, edges, and graph structure.
|
||||
- **General Utilities**: Miscellaneous helpers used throughout the system.
|
||||
- **Types & Constants**: Shared types, enums, and constants for consistent data structures.
|
||||
|
||||
### XYDrag
|
||||
## Usage
|
||||
|
||||
Adds drag for nodes and selection.
|
||||
You can import any utility, type, or helper directly from the package:
|
||||
|
||||
### XYHandle
|
||||
```ts
|
||||
import { getBezierPath, getConnectedEdges, Position, XYPanZoom } from '@xyflow/system';
|
||||
```
|
||||
|
||||
Adds connection line drawing.
|
||||
### Example: Bezier Edge Path Creation
|
||||
|
||||
### XYMinimap
|
||||
```ts
|
||||
import { getBezierPath, Position } from '@xyflow/system';
|
||||
|
||||
Adds interactive mini map (zoom and pan).
|
||||
const [path, labelX, labelY] = getBezierPath({
|
||||
sourceX: 0,
|
||||
sourceY: 20,
|
||||
sourcePosition: Position.Right,
|
||||
targetX: 150,
|
||||
targetY: 100,
|
||||
targetPosition: Position.Left,
|
||||
});
|
||||
```
|
||||
|
||||
### Edge utils
|
||||
## API Reference
|
||||
|
||||
Util function for SVG edge path creating.
|
||||
There is currently no dedicated API documentation for this package. For details on available utilities, types, and helpers, please refer to the [source code](./src) or check out the [React Flow documentation](https://reactflow.dev/api-reference/) where we are documenting a lot of stuff from this package.
|
||||
|
||||
### Store utils
|
||||
## Contributing
|
||||
|
||||
Helpers for store functions.
|
||||
|
||||
### Dom utils
|
||||
|
||||
### Marker utils
|
||||
|
||||
### Graph utils
|
||||
|
||||
### General utils
|
||||
See the main [xyflow repository](https://github.com/xyflow/xyflow) for contribution guidelines.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xyflow/system",
|
||||
"version": "0.0.61",
|
||||
"version": "0.0.65",
|
||||
"description": "xyflow core system that powers React Flow and Svelte Flow.",
|
||||
"keywords": [
|
||||
"node-based UI",
|
||||
|
||||
@@ -265,6 +265,10 @@ svg.xy-flow__connectionline {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.xy-flow__pane.selection .xy-flow__panel {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.xy-flow__panel {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
|
||||
/* handle styles */
|
||||
.xy-flow__resize-control.handle {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 1px;
|
||||
background-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default));
|
||||
transform: translate(-50%, -50%);
|
||||
translate: -50% -50%;
|
||||
}
|
||||
|
||||
.xy-flow__resize-control.handle.left {
|
||||
|
||||
@@ -40,11 +40,6 @@ export type EdgeBase<
|
||||
* This property sets the width of that invisible path.
|
||||
*/
|
||||
interactionWidth?: number;
|
||||
/**
|
||||
* A description of the edge's, used for accessibility.
|
||||
* @default "edge"
|
||||
*/
|
||||
ariaRoleDescription?: string;
|
||||
};
|
||||
|
||||
export type SmoothStepPathOptions = {
|
||||
|
||||
@@ -13,16 +13,56 @@ import { EdgeBase } from '..';
|
||||
|
||||
export type Project = (position: XYPosition) => XYPosition;
|
||||
|
||||
/**
|
||||
* This type is used to define the `onMove` handler.
|
||||
*/
|
||||
export type OnMove = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void;
|
||||
export type OnMoveStart = OnMove;
|
||||
export type OnMoveEnd = OnMove;
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type ZoomInOut = (options?: ViewportHelperFunctionOptions) => Promise<boolean>;
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type ZoomTo = (zoomLevel: number, options?: ViewportHelperFunctionOptions) => Promise<boolean>;
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type GetZoom = () => number;
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type GetViewport = () => Viewport;
|
||||
|
||||
/**
|
||||
* The `SetViewport` function is used to set the viewport of the flow.
|
||||
*
|
||||
* @inline
|
||||
* @param viewport - The viewport to set.
|
||||
* @param options - Optional parameters to control the animation and easing of the viewport change.
|
||||
*/
|
||||
export type SetViewport = (viewport: Viewport, options?: ViewportHelperFunctionOptions) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* The `SetCenter` function is used to set the center of the flow viewport to a specific position
|
||||
*
|
||||
* @inline
|
||||
* @param x - x coordinate
|
||||
* @param y - y coordinate
|
||||
* @param options - Optional parameters to control the animation and easing of the viewport change.
|
||||
*/
|
||||
export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* The `FitBounds` function is used to fit the flow viewport to the bounds of the nodes.
|
||||
*
|
||||
* @inline
|
||||
* @param bounds - The bounds to fit the viewport to.
|
||||
* @param options - Optional parameters to control the animation and easing of the viewport change.
|
||||
*/
|
||||
export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
@@ -179,10 +219,16 @@ export type ViewportHelperFunctionOptions = {
|
||||
interpolate?: 'smooth' | 'linear';
|
||||
};
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type SetCenterOptions = ViewportHelperFunctionOptions & {
|
||||
zoom?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inline
|
||||
*/
|
||||
export type FitBoundsOptions = ViewportHelperFunctionOptions & {
|
||||
padding?: number;
|
||||
};
|
||||
|
||||
@@ -73,11 +73,6 @@ export type NodeBase<
|
||||
*/
|
||||
origin?: NodeOrigin;
|
||||
handles?: NodeHandle[];
|
||||
/**
|
||||
* A description of the node's role, used for accessibility.
|
||||
* @default "node"
|
||||
*/
|
||||
ariaRoleDescription?: string;
|
||||
measured?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
|
||||
@@ -30,21 +30,30 @@ export type GetEdgeZIndexParams = {
|
||||
elevateOnSelect?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the z-index for an edge based on the node it connects and whether it is selected.
|
||||
* By default, edges are rendered below nodes. This behaviour is different for edges that are
|
||||
* connected to nodes with a parent, as they are rendered above the parent node.
|
||||
*/
|
||||
export function getElevatedEdgeZIndex({
|
||||
sourceNode,
|
||||
targetNode,
|
||||
selected = false,
|
||||
zIndex = 0,
|
||||
zIndex,
|
||||
elevateOnSelect = false,
|
||||
}: GetEdgeZIndexParams): number {
|
||||
if (!elevateOnSelect) {
|
||||
if (zIndex !== undefined) {
|
||||
return zIndex;
|
||||
}
|
||||
|
||||
const edgeOrConnectedNodeSelected = selected || targetNode.selected || sourceNode.selected;
|
||||
const selectedZIndex = Math.max(sourceNode.internals.z || 0, targetNode.internals.z || 0, 1000);
|
||||
const edgeZ = elevateOnSelect && selected ? 1000 : 0;
|
||||
|
||||
return zIndex + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
|
||||
const nodeZ = Math.max(
|
||||
sourceNode.parentId ? sourceNode.internals.z : 0,
|
||||
targetNode.parentId ? targetNode.internals.z : 0
|
||||
);
|
||||
|
||||
return edgeZ + nodeZ;
|
||||
}
|
||||
|
||||
type IsEdgeVisibleParams = {
|
||||
|
||||
@@ -232,7 +232,7 @@ function calculateChildXYZ<NodeType extends NodeBase>(
|
||||
return {
|
||||
x: absolutePosition.x,
|
||||
y: absolutePosition.y,
|
||||
z: parentZ > childZ ? parentZ : childZ,
|
||||
z: parentZ >= childZ ? parentZ + 1 : childZ,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
let dragStarted = false;
|
||||
let d3Selection: Selection<Element, unknown, null, undefined> | null = null;
|
||||
let abortDrag = false; // prevents unintentional dragging on multitouch
|
||||
let nodePositionsChanged = false;
|
||||
|
||||
// public functions
|
||||
function update({
|
||||
@@ -191,6 +192,8 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
dragItem.internals.positionAbsolute = positionAbsolute;
|
||||
}
|
||||
|
||||
nodePositionsChanged = nodePositionsChanged || hasChange;
|
||||
|
||||
if (!hasChange) {
|
||||
return;
|
||||
}
|
||||
@@ -294,6 +297,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
containerBounds = domNode?.getBoundingClientRect() || null;
|
||||
|
||||
abortDrag = false;
|
||||
nodePositionsChanged = false;
|
||||
|
||||
if (nodeDragThreshold === 0) {
|
||||
startDrag(event);
|
||||
@@ -354,7 +358,10 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
if (dragItems.size > 0) {
|
||||
const { nodeLookup, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems();
|
||||
|
||||
updateNodePositions(dragItems, false);
|
||||
if (nodePositionsChanged) {
|
||||
updateNodePositions(dragItems, false);
|
||||
nodePositionsChanged = false;
|
||||
}
|
||||
|
||||
if (onDragStop || onNodeDragStop || (!nodeId && onSelectionDragStop)) {
|
||||
const [currentNode, currentNodes] = getEventHandlerParams({
|
||||
|
||||
@@ -45,6 +45,7 @@ function onPointerDown(
|
||||
getTransform,
|
||||
getFromHandle,
|
||||
autoPanSpeed,
|
||||
dragThreshold = 1,
|
||||
}: OnPointerDownParams
|
||||
) {
|
||||
// when xyflow is used inside a shadow root we can't use document
|
||||
@@ -56,6 +57,7 @@ function onPointerDown(
|
||||
const clickedHandle = doc?.elementFromPoint(x, y);
|
||||
const handleType = getHandleType(edgeUpdaterType, clickedHandle);
|
||||
const containerBounds = domNode?.getBoundingClientRect();
|
||||
let connectionStarted = false;
|
||||
|
||||
if (!containerBounds || !handleType) {
|
||||
return;
|
||||
@@ -92,10 +94,9 @@ function onPointerDown(
|
||||
};
|
||||
|
||||
const fromNodeInternal = nodeLookup.get(nodeId)!;
|
||||
|
||||
const from = getHandlePosition(fromNodeInternal, fromHandle, Position.Left, true);
|
||||
|
||||
const newConnection: ConnectionInProgress = {
|
||||
let previousConnection: ConnectionInProgress = {
|
||||
inProgress: true,
|
||||
isValid: null,
|
||||
|
||||
@@ -110,12 +111,30 @@ function onPointerDown(
|
||||
toNode: null,
|
||||
};
|
||||
|
||||
updateConnection(newConnection);
|
||||
let previousConnection: ConnectionInProgress = newConnection;
|
||||
function startConnection() {
|
||||
connectionStarted = true;
|
||||
updateConnection(previousConnection);
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
}
|
||||
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
if (dragThreshold === 0) {
|
||||
startConnection();
|
||||
}
|
||||
|
||||
function onPointerMove(event: MouseEvent | TouchEvent) {
|
||||
if (!connectionStarted) {
|
||||
const { x: evtX, y: evtY } = getEventPosition(event);
|
||||
const dx = evtX - x;
|
||||
const dy = evtY - y;
|
||||
const nextConnectionStarted = dx * dx + dy * dy > dragThreshold * dragThreshold;
|
||||
|
||||
if (!nextConnectionStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
startConnection();
|
||||
}
|
||||
|
||||
if (!getFromHandle() || !fromHandle) {
|
||||
onPointerUp(event);
|
||||
return;
|
||||
@@ -188,24 +207,27 @@ function onPointerDown(
|
||||
}
|
||||
|
||||
function onPointerUp(event: MouseEvent | TouchEvent) {
|
||||
if ((closestHandle || handleDomNode) && connection && isValid) {
|
||||
onConnect?.(connection);
|
||||
}
|
||||
if (connectionStarted) {
|
||||
if ((closestHandle || handleDomNode) && connection && isValid) {
|
||||
onConnect?.(connection);
|
||||
}
|
||||
|
||||
/*
|
||||
* it's important to get a fresh reference from the store here
|
||||
* in order to get the latest state of onConnectEnd
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { inProgress, ...connectionState } = previousConnection;
|
||||
const finalConnectionState = {
|
||||
...connectionState,
|
||||
toPosition: previousConnection.toHandle ? previousConnection.toPosition : null,
|
||||
};
|
||||
onConnectEnd?.(event, finalConnectionState);
|
||||
/*
|
||||
* it's important to get a fresh reference from the store here
|
||||
* in order to get the latest state of onConnectEnd
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { inProgress, ...connectionState } = previousConnection;
|
||||
const finalConnectionState = {
|
||||
...connectionState,
|
||||
toPosition: previousConnection.toHandle ? previousConnection.toPosition : null,
|
||||
};
|
||||
|
||||
if (edgeUpdaterType) {
|
||||
onReconnectEnd?.(event, finalConnectionState);
|
||||
onConnectEnd?.(event, finalConnectionState);
|
||||
|
||||
if (edgeUpdaterType) {
|
||||
onReconnectEnd?.(event, finalConnectionState);
|
||||
}
|
||||
}
|
||||
|
||||
cancelConnection();
|
||||
|
||||
@@ -37,6 +37,7 @@ export type OnPointerDownParams = {
|
||||
getTransform: () => Transform;
|
||||
getFromHandle: () => Handle | null;
|
||||
autoPanSpeed?: number;
|
||||
dragThreshold?: number;
|
||||
};
|
||||
|
||||
export type IsValidParams = {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
createZoomOnScrollHandler,
|
||||
} from './eventhandler';
|
||||
import { createFilter } from './filter';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { transition } from 'd3-transition';
|
||||
|
||||
export type ZoomPanValues = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { transition } from 'd3-transition';
|
||||
|
||||
import { type D3SelectionInstance, type Viewport } from '../types';
|
||||
|
||||
Reference in New Issue
Block a user