Merge branch 'next' into absolute-origin

This commit is contained in:
Moritz Klack
2024-06-24 12:57:24 +02:00
committed by GitHub
91 changed files with 8822 additions and 7060 deletions
@@ -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',