Merge branch 'main' into bug-fix-node-positions
This commit is contained in:
@@ -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