Merge branch 'next' into absolute-origin
This commit is contained in:
@@ -39,7 +39,7 @@ const ConnectionLine = ({
|
||||
CustomComponent,
|
||||
connectionStatus,
|
||||
}: ConnectionLineProps) => {
|
||||
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
|
||||
const { fromNode, handleId, toX, toY, connectionMode, endPosition, isValid } = useStore(
|
||||
useCallback(
|
||||
(s: ReactFlowStore) => ({
|
||||
fromNode: s.nodeLookup.get(nodeId),
|
||||
@@ -47,11 +47,14 @@ const ConnectionLine = ({
|
||||
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
||||
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
||||
connectionMode: s.connectionMode,
|
||||
endPosition: s.connectionEndHandle?.position,
|
||||
isValid: s.connectionStatus === 'valid',
|
||||
}),
|
||||
[nodeId]
|
||||
),
|
||||
shallow
|
||||
);
|
||||
|
||||
const fromHandleBounds = fromNode?.internals.handleBounds;
|
||||
let handleBounds = fromHandleBounds?.[handleType];
|
||||
|
||||
@@ -69,7 +72,7 @@ const ConnectionLine = ({
|
||||
const fromX = fromNode.internals.positionAbsolute.x + fromHandleX;
|
||||
const fromY = fromNode.internals.positionAbsolute.y + fromHandleY;
|
||||
const fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
|
||||
const toPosition = isValid && endPosition ? endPosition : fromPosition ? oppositePosition[fromPosition] : null;
|
||||
|
||||
if (!fromPosition || !toPosition) {
|
||||
return null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Updatable edges have a anchors around their handles to update the edge.
|
||||
// Reconnectable edges have a anchors around their handles to reconnect the edge.
|
||||
import { XYHandle, type Connection, EdgePosition } from '@xyflow/system';
|
||||
|
||||
import { EdgeAnchor } from '../Edges/EdgeAnchor';
|
||||
@@ -7,20 +7,20 @@ import { useStoreApi } from '../../hooks/useStore';
|
||||
|
||||
type EdgeUpdateAnchorsProps<EdgeType extends Edge = Edge> = {
|
||||
edge: EdgeType;
|
||||
isUpdatable: boolean | 'source' | 'target';
|
||||
edgeUpdaterRadius: EdgeWrapperProps['edgeUpdaterRadius'];
|
||||
isReconnectable: boolean | 'source' | 'target';
|
||||
reconnectRadius: EdgeWrapperProps['reconnectRadius'];
|
||||
sourceHandleId: Edge['sourceHandle'];
|
||||
targetHandleId: Edge['targetHandle'];
|
||||
onEdgeUpdate: EdgeWrapperProps<EdgeType>['onEdgeUpdate'];
|
||||
onEdgeUpdateStart: EdgeWrapperProps<EdgeType>['onEdgeUpdateStart'];
|
||||
onEdgeUpdateEnd: EdgeWrapperProps<EdgeType>['onEdgeUpdateEnd'];
|
||||
onReconnect: EdgeWrapperProps<EdgeType>['onReconnect'];
|
||||
onReconnectStart: EdgeWrapperProps<EdgeType>['onReconnectStart'];
|
||||
onReconnectEnd: EdgeWrapperProps<EdgeType>['onReconnectEnd'];
|
||||
setUpdateHover: (hover: boolean) => void;
|
||||
setUpdating: (updating: boolean) => void;
|
||||
setReconnecting: (updating: boolean) => void;
|
||||
} & EdgePosition;
|
||||
|
||||
export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
isUpdatable,
|
||||
edgeUpdaterRadius,
|
||||
isReconnectable,
|
||||
reconnectRadius,
|
||||
edge,
|
||||
targetHandleId,
|
||||
sourceHandleId,
|
||||
@@ -30,10 +30,10 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
onEdgeUpdate,
|
||||
onEdgeUpdateStart,
|
||||
onEdgeUpdateEnd,
|
||||
setUpdating,
|
||||
onReconnect,
|
||||
onReconnectStart,
|
||||
onReconnectEnd,
|
||||
setReconnecting,
|
||||
setUpdateHover,
|
||||
}: EdgeUpdateAnchorsProps<EdgeType>) {
|
||||
const store = useStoreApi();
|
||||
@@ -65,15 +65,15 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
|
||||
const isTarget = isSourceHandle;
|
||||
|
||||
setUpdating(true);
|
||||
onEdgeUpdateStart?.(event, edge, handleType);
|
||||
setReconnecting(true);
|
||||
onReconnectStart?.(event, edge, handleType);
|
||||
|
||||
const _onEdgeUpdateEnd = (evt: MouseEvent | TouchEvent) => {
|
||||
setUpdating(false);
|
||||
onEdgeUpdateEnd?.(evt, edge, handleType);
|
||||
const _onReconnectEnd = (evt: MouseEvent | TouchEvent) => {
|
||||
setReconnecting(false);
|
||||
onReconnectEnd?.(evt, edge, handleType);
|
||||
};
|
||||
|
||||
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
||||
const onConnectEdge = (connection: Connection) => onReconnect?.(edge, connection);
|
||||
|
||||
XYHandle.onPointerDown(event.nativeEvent, {
|
||||
autoPanOnConnect,
|
||||
@@ -93,43 +93,43 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
onConnect: onConnectEdge,
|
||||
onConnectStart,
|
||||
onConnectEnd,
|
||||
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
||||
onReconnectEnd: _onReconnectEnd,
|
||||
updateConnection,
|
||||
getTransform: () => store.getState().transform,
|
||||
getConnectionStartHandle: () => store.getState().connectionStartHandle,
|
||||
});
|
||||
};
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||
const onReconnectSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||
handleEdgeUpdater(event, true);
|
||||
const onEdgeUpdaterTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||
const onReconnectTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||
handleEdgeUpdater(event, false);
|
||||
const onEdgeUpdaterMouseEnter = () => setUpdateHover(true);
|
||||
const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
|
||||
const onReconnectMouseEnter = () => setUpdateHover(true);
|
||||
const onReconnectMouseOut = () => setUpdateHover(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{(isUpdatable === 'source' || isUpdatable === true) && (
|
||||
{(isReconnectable === 'source' || isReconnectable === true) && (
|
||||
<EdgeAnchor
|
||||
position={sourcePosition}
|
||||
centerX={sourceX}
|
||||
centerY={sourceY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
radius={reconnectRadius}
|
||||
onMouseDown={onReconnectSourceMouseDown}
|
||||
onMouseEnter={onReconnectMouseEnter}
|
||||
onMouseOut={onReconnectMouseOut}
|
||||
type="source"
|
||||
/>
|
||||
)}
|
||||
{(isUpdatable === 'target' || isUpdatable === true) && (
|
||||
{(isReconnectable === 'target' || isReconnectable === true) && (
|
||||
<EdgeAnchor
|
||||
position={targetPosition}
|
||||
centerX={targetX}
|
||||
centerY={targetY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
radius={reconnectRadius}
|
||||
onMouseDown={onReconnectTargetMouseDown}
|
||||
onMouseEnter={onReconnectMouseEnter}
|
||||
onMouseOut={onReconnectMouseOut}
|
||||
type="target"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { Edge, EdgeWrapperProps } from '../../types';
|
||||
export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
id,
|
||||
edgesFocusable,
|
||||
edgesUpdatable,
|
||||
edgesReconnectable,
|
||||
elementsSelectable,
|
||||
onClick,
|
||||
onDoubleClick,
|
||||
@@ -26,10 +26,10 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
onMouseEnter,
|
||||
onMouseMove,
|
||||
onMouseLeave,
|
||||
edgeUpdaterRadius,
|
||||
onEdgeUpdate,
|
||||
onEdgeUpdateStart,
|
||||
onEdgeUpdateEnd,
|
||||
reconnectRadius,
|
||||
onReconnect,
|
||||
onReconnectStart,
|
||||
onReconnectEnd,
|
||||
rfId,
|
||||
edgeTypes,
|
||||
noPanClassName,
|
||||
@@ -50,14 +50,14 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
}
|
||||
|
||||
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
||||
const isUpdatable =
|
||||
typeof onEdgeUpdate !== 'undefined' &&
|
||||
(edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
|
||||
const isReconnectable =
|
||||
typeof onReconnect !== 'undefined' &&
|
||||
(edge.reconnectable || (edgesReconnectable && typeof edge.reconnectable === 'undefined'));
|
||||
const isSelectable = !!(edge.selectable || (elementsSelectable && typeof edge.selectable === 'undefined'));
|
||||
|
||||
const edgeRef = useRef<SVGGElement>(null);
|
||||
const [updateHover, setUpdateHover] = useState<boolean>(false);
|
||||
const [updating, setUpdating] = useState<boolean>(false);
|
||||
const [reconnecting, setReconnecting] = useState<boolean>(false);
|
||||
const store = useStoreApi();
|
||||
|
||||
const { zIndex, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition } = useStore(
|
||||
@@ -207,7 +207,7 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
||||
ref={edgeRef}
|
||||
>
|
||||
{!updating && (
|
||||
{!reconnecting && (
|
||||
<EdgeComponent
|
||||
id={id}
|
||||
source={edge.source}
|
||||
@@ -215,6 +215,8 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
type={edge.type}
|
||||
selected={edge.selected}
|
||||
animated={edge.animated}
|
||||
selectable={isSelectable}
|
||||
deletable={edge.deletable ?? true}
|
||||
label={edge.label}
|
||||
labelStyle={edge.labelStyle}
|
||||
labelShowBg={edge.labelShowBg}
|
||||
@@ -237,14 +239,14 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
interactionWidth={edge.interactionWidth}
|
||||
/>
|
||||
)}
|
||||
{isUpdatable && (
|
||||
{isReconnectable && (
|
||||
<EdgeUpdateAnchors<EdgeType>
|
||||
edge={edge}
|
||||
isUpdatable={isUpdatable}
|
||||
edgeUpdaterRadius={edgeUpdaterRadius}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
isReconnectable={isReconnectable}
|
||||
reconnectRadius={reconnectRadius}
|
||||
onReconnect={onReconnect}
|
||||
onReconnectStart={onReconnectStart}
|
||||
onReconnectEnd={onReconnectEnd}
|
||||
sourceX={sourceX}
|
||||
sourceY={sourceY}
|
||||
targetX={targetX}
|
||||
@@ -252,7 +254,7 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
sourcePosition={sourcePosition}
|
||||
targetPosition={targetPosition}
|
||||
setUpdateHover={setUpdateHover}
|
||||
setUpdating={setUpdating}
|
||||
setReconnecting={setReconnecting}
|
||||
sourceHandleId={edge.sourceHandle}
|
||||
targetHandleId={edge.targetHandle}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { memo, useState, useCallback } from 'react';
|
||||
import { memo, useState, useEffect, useRef } from 'react';
|
||||
import cc from 'classcat';
|
||||
import type { Rect } from '@xyflow/system';
|
||||
|
||||
@@ -19,19 +19,20 @@ function EdgeTextComponent({
|
||||
}: EdgeTextProps) {
|
||||
const [edgeTextBbox, setEdgeTextBbox] = useState<Rect>({ x: 1, y: 0, width: 0, height: 0 });
|
||||
const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
|
||||
const edgeTextRef = useRef<SVGTextElement | null>(null);
|
||||
|
||||
const onEdgeTextRefChange = useCallback((edgeRef: SVGTextElement) => {
|
||||
if (edgeRef === null) return;
|
||||
useEffect(() => {
|
||||
if (edgeTextRef.current) {
|
||||
const textBbox = edgeTextRef.current.getBBox();
|
||||
|
||||
const textBbox = edgeRef.getBBox();
|
||||
|
||||
setEdgeTextBbox({
|
||||
x: textBbox.x,
|
||||
y: textBbox.y,
|
||||
width: textBbox.width,
|
||||
height: textBbox.height,
|
||||
});
|
||||
}, []);
|
||||
setEdgeTextBbox({
|
||||
x: textBbox.x,
|
||||
y: textBbox.y,
|
||||
width: textBbox.width,
|
||||
height: textBbox.height,
|
||||
});
|
||||
}
|
||||
}, [label]);
|
||||
|
||||
if (typeof label === 'undefined' || !label) {
|
||||
return null;
|
||||
@@ -60,7 +61,7 @@ function EdgeTextComponent({
|
||||
className="react-flow__edge-text"
|
||||
y={edgeTextBbox.height / 2}
|
||||
dy="0.3em"
|
||||
ref={onEdgeTextRefChange}
|
||||
ref={edgeTextRef}
|
||||
style={labelStyle}
|
||||
>
|
||||
{label}
|
||||
|
||||
@@ -202,12 +202,16 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
positionAbsoluteX={clampedPosition.x}
|
||||
positionAbsoluteY={clampedPosition.y}
|
||||
selected={node.selected}
|
||||
selectable={isSelectable}
|
||||
draggable={isDraggable}
|
||||
deletable={node.deletable ?? true}
|
||||
isConnectable={isConnectable}
|
||||
sourcePosition={node.sourcePosition}
|
||||
targetPosition={node.targetPosition}
|
||||
dragging={dragging}
|
||||
dragHandle={node.dragHandle}
|
||||
zIndex={internals.z}
|
||||
parentId={node.parentId}
|
||||
{...nodeDimensions}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
@@ -26,17 +26,17 @@ export function useNodeObserver({
|
||||
const prevSourcePosition = useRef(node.sourcePosition);
|
||||
const prevTargetPosition = useRef(node.targetPosition);
|
||||
const prevType = useRef(nodeType);
|
||||
const isInitialized = hasDimensions && !!node.internals.handleBounds && !node.hidden;
|
||||
const isInitialized = hasDimensions && !!node.internals.handleBounds;
|
||||
|
||||
useEffect(() => {
|
||||
if (nodeRef.current && (!isInitialized || observedNode.current !== nodeRef.current)) {
|
||||
if (nodeRef.current && !node.hidden && (!isInitialized || observedNode.current !== nodeRef.current)) {
|
||||
if (observedNode.current) {
|
||||
resizeObserver?.unobserve(observedNode.current);
|
||||
}
|
||||
resizeObserver?.observe(nodeRef.current);
|
||||
observedNode.current = nodeRef.current;
|
||||
}
|
||||
}, [isInitialized]);
|
||||
}, [isInitialized, node.hidden]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ const reactFlowFieldsToTrack = [
|
||||
'nodesConnectable',
|
||||
'nodesFocusable',
|
||||
'edgesFocusable',
|
||||
'edgesUpdatable',
|
||||
'edgesReconnectable',
|
||||
'elevateNodesOnSelect',
|
||||
'elevateEdgesOnSelect',
|
||||
'minZoom',
|
||||
|
||||
Reference in New Issue
Block a user