Merge pull request #2802 from wbkd/next-release

v11.5.4
This commit is contained in:
Moritz Klack
2023-02-05 22:16:21 +01:00
committed by GitHub
24 changed files with 179 additions and 66 deletions

View File

@@ -22,6 +22,7 @@ const initialNodes: Node[] = [
data: { label: 'Node 1' },
position: { x: 250, y: 5 },
className: 'light',
draggable: false,
},
{
id: '2',

View File

@@ -24,10 +24,18 @@
background: #fff;
}
.validationflow :global .react-flow__handle-connecting {
.validationflow :global .connecting {
background: #ff6060;
}
.validationflow :global .react-flow__handle-valid {
.validationflow :global .valid {
background: #55dd99;
}
.validationflow :global .valid .react-flow__connection-path {
stroke: #55dd99;
}
.validationflow :global .invalid .react-flow__connection-path {
stroke: #ff6060;
}

View File

@@ -1,5 +1,12 @@
# @reactflow/background
## 11.1.6
### Patch Changes
- Updated dependencies [[`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c), [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd), [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2)]:
- @reactflow/core@11.5.3
## 11.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@reactflow/background",
"version": "11.1.5",
"version": "11.1.6",
"description": "Background component with different variants for React Flow",
"keywords": [
"react",

View File

@@ -1,5 +1,12 @@
# @reactflow/controls
## 11.1.6
### Patch Changes
- Updated dependencies [[`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c), [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd), [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2)]:
- @reactflow/core@11.5.3
## 11.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@reactflow/controls",
"version": "11.1.5",
"version": "11.1.6",
"description": "Component to control the viewport of a React Flow instance",
"keywords": [
"react",

View File

@@ -1,5 +1,15 @@
# @reactflow/core
## 11.5.3
This release fixes some issues with the newly introduced connection radius feature. We are now not only checking the radius but the handle itself too (like in the old version). That means that you can connect to a handle that is bigger than the connection radius. We are also not snapping connections anymore when they are not valid and pass a status class to the connection line that says if the current connection is valid or not. More over we fixed a connection issue with iOS.
### Patch Changes
- [#2800](https://github.com/wbkd/react-flow/pull/2800) [`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c) - When node is not draggable, you can't move it with a selection either
- [#2803](https://github.com/wbkd/react-flow/pull/2803) [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd) - connection: add status class (valid or invalid) while in connection radius
- [#2801](https://github.com/wbkd/react-flow/pull/2801) [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2) - fix(ios): connection error + dont snap invalid connection lines, check handle and connection radius
## 11.5.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@reactflow/core",
"version": "11.5.2",
"version": "11.5.3",
"description": "Core components and util functions of React Flow.",
"keywords": [
"react",

View File

@@ -1,12 +1,19 @@
import { CSSProperties, useCallback } from 'react';
import { shallow } from 'zustand/shallow';
import cc from 'classcat';
import { useStore } from '../../hooks/useStore';
import { getBezierPath } from '../Edges/BezierEdge';
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
import { internalsSymbol } from '../../utils';
import type { ConnectionLineComponent, HandleType, ReactFlowState, ReactFlowStore } from '../../types';
import type {
ConnectionLineComponent,
ConnectionStatus,
HandleType,
ReactFlowState,
ReactFlowStore,
} from '../../types';
import { Position, ConnectionLineType, ConnectionMode } from '../../types';
type ConnectionLineProps = {
@@ -15,6 +22,7 @@ type ConnectionLineProps = {
type: ConnectionLineType;
style?: CSSProperties;
CustomComponent?: ConnectionLineComponent;
connectionStatus: ConnectionStatus | null;
};
const oppositePosition = {
@@ -30,6 +38,7 @@ const ConnectionLine = ({
style,
type = ConnectionLineType.Bezier,
CustomComponent,
connectionStatus,
}: ConnectionLineProps) => {
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
useCallback(
@@ -80,6 +89,7 @@ const ConnectionLine = ({
toY={toY}
fromPosition={fromPosition}
toPosition={toPosition}
connectionStatus={connectionStatus}
/>
);
}
@@ -127,12 +137,13 @@ const selector = (s: ReactFlowState) => ({
nodeId: s.connectionNodeId,
handleType: s.connectionHandleType,
nodesConnectable: s.nodesConnectable,
connectionStatus: s.connectionStatus,
width: s.width,
height: s.height,
});
function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
const { nodeId, handleType, nodesConnectable, width, height } = useStore(selector, shallow);
const { nodeId, handleType, nodesConnectable, width, height, connectionStatus } = useStore(selector, shallow);
const isValid = !!(nodeId && handleType && width && nodesConnectable);
if (!isValid) {
@@ -146,8 +157,15 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }: Conne
height={height}
className="react-flow__edges react-flow__connectionline react-flow__container"
>
<g className="react-flow__connection">
<ConnectionLine nodeId={nodeId} handleType={handleType} style={style} type={type} CustomComponent={component} />
<g className={cc(['react-flow__connection', connectionStatus])}>
<ConnectionLine
nodeId={nodeId}
handleType={handleType}
style={style}
type={type}
CustomComponent={component}
connectionStatus={connectionStatus}
/>
</g>
</svg>
);

View File

@@ -2,11 +2,12 @@ import type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } fro
import { StoreApi } from 'zustand';
import { getHostForElement, calcAutoPan, getEventPosition } from '../../utils';
import type { OnConnect, HandleType, ReactFlowState } from '../../types';
import type { OnConnect, HandleType, ReactFlowState, Connection } from '../../types';
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
import {
ConnectionHandle,
getClosestHandle,
getConnectionStatus,
getHandleLookup,
getHandleType,
isValidHandle,
@@ -45,7 +46,6 @@ export function handlePointerDown({
autoPanOnConnect,
connectionRadius,
onConnectStart,
onConnectEnd,
panBy,
getNodes,
cancelConnection,
@@ -65,6 +65,8 @@ export function handlePointerDown({
let prevActiveHandle: Element;
let connectionPosition = getEventPosition(event, containerBounds);
let autoPanStarted = false;
let connection: Connection | null = null;
let isValid = false;
const handleLookup = getHandleLookup({
nodes: getNodes(),
@@ -89,6 +91,7 @@ export function handlePointerDown({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType,
connectionStatus: null,
});
onConnectStart?.(event, { nodeId, handleId, handleType });
@@ -108,23 +111,7 @@ export function handlePointerDown({
autoPanStarted = true;
}
setState({
connectionPosition: prevClosestHandle
? rendererPointToPoint(
{
x: prevClosestHandle.x,
y: prevClosestHandle.y,
},
transform
)
: connectionPosition,
});
if (!prevClosestHandle) {
return resetRecentHandle(prevActiveHandle);
}
const { connection, handleDomNode, isValid } = isValidHandle(
const { handleDomNode, ...result } = isValidHandle(
event,
prevClosestHandle,
connectionMode,
@@ -135,44 +122,56 @@ export function handlePointerDown({
doc
);
setState({
connectionPosition:
prevClosestHandle && result.isValid
? rendererPointToPoint(
{
x: prevClosestHandle.x,
y: prevClosestHandle.y,
},
transform
)
: connectionPosition,
connectionStatus: getConnectionStatus(!!prevClosestHandle, result.isValid),
});
if (!prevClosestHandle && !result.isValid) {
return resetRecentHandle(prevActiveHandle);
}
connection = result.connection;
isValid = result.isValid;
if (connection.source !== connection.target && handleDomNode) {
resetRecentHandle(prevActiveHandle);
prevActiveHandle = handleDomNode;
handleDomNode.classList.add('react-flow__handle-connecting');
// @todo: remove the old class names "react-flow__handle-" in the next major version
handleDomNode.classList.add('connecting', 'react-flow__handle-connecting');
handleDomNode.classList.toggle('valid', isValid);
handleDomNode.classList.toggle('react-flow__handle-valid', isValid);
}
}
function onPointerUp(event: MouseEvent | TouchEvent) {
cancelAnimationFrame(autoPanId);
autoPanStarted = false;
if (prevClosestHandle) {
const { connection, isValid } = isValidHandle(
event,
prevClosestHandle,
connectionMode,
nodeId,
handleId,
isTarget ? 'target' : 'source',
isValidConnection,
doc
);
if (isValid) {
onConnect?.(connection);
}
if (connection && isValid) {
onConnect?.(connection);
}
onConnectEnd?.(event);
// it's important to get a fresh reference from the store here
// in order to get the latest state of onConnectEnd
getState().onConnectEnd?.(event);
if (edgeUpdaterType) {
onEdgeUpdateEnd?.(event);
}
resetRecentHandle(prevActiveHandle);
cancelConnection();
cancelAnimationFrame(autoPanId);
autoPanStarted = false;
isValid = false;
connection = null;
doc.removeEventListener('mousemove', onPointerMove as EventListener);
doc.removeEventListener('mouseup', onPointerUp as EventListener);

View File

@@ -1,6 +1,6 @@
import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import { ConnectionMode } from '../../types';
import { ConnectionMode, ConnectionStatus } from '../../types';
import { getEventPosition, internalsSymbol } from '../../utils';
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
@@ -61,10 +61,12 @@ type Result = {
connection: Connection;
};
const nullConnection: Connection = { source: null, target: null, sourceHandle: null, targetHandle: null };
// checks if and returns connection in fom of an object { source: 123, target: 312 }
export function isValidHandle(
event: MouseEvent | TouchEvent | ReactMouseEvent | ReactTouchEvent,
handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'>,
handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'> | null,
connectionMode: ConnectionMode,
fromNodeId: string,
fromHandleId: string | null,
@@ -83,7 +85,7 @@ export function isValidHandle(
const result: Result = {
handleDomNode: handleToCheck,
isValid: false,
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
connection: nullConnection,
};
if (handleToCheck) {
@@ -92,9 +94,9 @@ export function isValidHandle(
const handleId = handleToCheck.getAttribute('data-handleid');
const connection: Connection = {
source: isTarget ? handle.nodeId : fromNodeId,
source: isTarget ? handleNodeId : fromNodeId,
sourceHandle: isTarget ? handleId : fromHandleId,
target: isTarget ? fromNodeId : handle.nodeId,
target: isTarget ? fromNodeId : handleNodeId,
targetHandle: isTarget ? fromHandleId : handleId,
};
@@ -155,6 +157,17 @@ export function getHandleType(
}
export function resetRecentHandle(handleDomNode: Element): void {
handleDomNode?.classList.remove('react-flow__handle-valid');
handleDomNode?.classList.remove('react-flow__handle-connecting');
handleDomNode?.classList.remove('valid', 'connecting', 'react-flow__handle-valid', 'react-flow__handle-connecting');
}
export function getConnectionStatus(isInsideConnectionRadius: boolean, isHandleValid: boolean) {
let connectionStatus = null;
if (isHandleValid) {
connectionStatus = 'valid';
} else if (isInsideConnectionRadius && !isHandleValid) {
connectionStatus = 'invalid';
}
return connectionStatus as ConnectionStatus;
}

View File

@@ -133,10 +133,11 @@ function useDrag({
const {
nodeInternals,
multiSelectionActive,
domNode,
nodesDraggable,
unselectNodesAndEdges,
onNodeDragStart,
onSelectionDragStart,
domNode,
} = store.getState();
const onStart = nodeId ? onNodeDragStart : wrapSelectionDragFunc(onSelectionDragStart);
@@ -157,7 +158,7 @@ function useDrag({
const pointerPos = getPointerPosition(event);
lastPos.current = pointerPos;
dragItems.current = getDragItems(nodeInternals, pointerPos, nodeId);
dragItems.current = getDragItems(nodeInternals, nodesDraggable, pointerPos, nodeId);
if (onStart && dragItems.current) {
const [currentNode, nodes] = getEventHandlerParams({

View File

@@ -36,9 +36,19 @@ export function hasSelector(target: Element, selector: string, nodeRef: RefObjec
}
// looks for all selected nodes and created a NodeDragItem for each of them
export function getDragItems(nodeInternals: NodeInternals, mousePos: XYPosition, nodeId?: string): NodeDragItem[] {
export function getDragItems(
nodeInternals: NodeInternals,
nodesDraggable: boolean,
mousePos: XYPosition,
nodeId?: string
): NodeDragItem[] {
return Array.from(nodeInternals.values())
.filter((n) => (n.selected || n.id === nodeId) && (!n.parentNode || !isParentSelected(n, nodeInternals)))
.filter(
(n) =>
(n.selected || n.id === nodeId) &&
(!n.parentNode || !isParentSelected(n, nodeInternals)) &&
(n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'))
)
.map((n) => ({
id: n.id,
position: n.position || { x: 0, y: 0 },

View File

@@ -7,9 +7,11 @@ function useUpdateNodePositions() {
const store = useStoreApi();
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
const { nodeInternals, nodeExtent, updateNodePositions, getNodes, snapToGrid, snapGrid, onError } =
const { nodeInternals, nodeExtent, updateNodePositions, getNodes, snapToGrid, snapGrid, onError, nodesDraggable } =
store.getState();
const selectedNodes = getNodes().filter((n) => n.selected);
const selectedNodes = getNodes().filter(
(n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'))
);
// by default a node moves 5px on each key press, or 20px if shift is pressed
// if snap grid is enabled, we use that for the velocity.
const xVelo = snapToGrid ? snapGrid[0] : 5;

View File

@@ -274,6 +274,7 @@ const createRFStore = () =>
connectionNodeId: initialState.connectionNodeId,
connectionHandleId: initialState.connectionHandleId,
connectionHandleType: initialState.connectionHandleType,
connectionStatus: initialState.connectionStatus,
}),
reset: () => set({ ...initialState }),
}));

View File

@@ -32,6 +32,7 @@ const initialState: ReactFlowStore = {
connectionHandleId: null,
connectionHandleType: 'source',
connectionPosition: { x: 0, y: 0 },
connectionStatus: null,
connectionMode: ConnectionMode.Strict,
domNode: null,
paneDragging: false,

View File

@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CSSProperties, ComponentType, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent } from 'react';
import { Position } from '.';
import { ConnectionStatus, Position } from '.';
import type { Connection, HandleElement, HandleType, Node } from '.';
type EdgeLabelOptions = {
@@ -155,6 +155,7 @@ export type ConnectionLineComponentProps = {
toY: number;
fromPosition: Position;
toPosition: Position;
connectionStatus: ConnectionStatus | null;
};
export type ConnectionLineComponent = ComponentType<ConnectionLineComponentProps>;

View File

@@ -61,6 +61,8 @@ export interface Connection {
targetHandle: string | null;
}
export type ConnectionStatus = 'valid' | 'invalid';
export enum ConnectionMode {
Strict = 'strict',
Loose = 'loose',
@@ -166,6 +168,7 @@ export type ReactFlowStore = {
connectionHandleId: string | null;
connectionHandleType: HandleType | null;
connectionPosition: XYPosition;
connectionStatus: ConnectionStatus | null;
connectionMode: ConnectionMode;
snapToGrid: boolean;

View File

@@ -1,5 +1,12 @@
# @reactflow/minimap
## 11.3.6
### Patch Changes
- Updated dependencies [[`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c), [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd), [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2)]:
- @reactflow/core@11.5.3
## 11.3.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@reactflow/minimap",
"version": "11.3.5",
"version": "11.3.6",
"description": "Minimap component for React Flow.",
"keywords": [
"react",

View File

@@ -1,5 +1,12 @@
# @reactflow/node-toolbar
## 1.1.6
### Patch Changes
- Updated dependencies [[`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c), [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd), [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2)]:
- @reactflow/core@11.5.3
## 1.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@reactflow/node-toolbar",
"version": "1.1.5",
"version": "1.1.6",
"description": "A toolbar component for React Flow that can be attached to a node.",
"keywords": [
"react",

View File

@@ -1,5 +1,22 @@
# reactflow
## 11.5.4
This release fixes some issues with the newly introduced connection radius feature. We are now not only checking the radius but the handle itself too (like in the old version). That means that you can connect to a handle that is bigger than the connection radius. We are also not snapping connections anymore when they are not valid and pass a status class to the connection line that says if the current connection is valid or not. More over we fixed a connection issue with iOS.
### Patch Changes
- [#2800](https://github.com/wbkd/react-flow/pull/2800) [`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c) - When node is not draggable, you can't move it with a selection either
- [#2803](https://github.com/wbkd/react-flow/pull/2803) [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd) - connection: add status class (valid or invalid) while in connection radius
- [#2801](https://github.com/wbkd/react-flow/pull/2801) [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2) - fix(ios): connection error + dont snap invalid connection lines, check handle and connection radius
- Updated dependencies [[`be8097ac`](https://github.com/wbkd/react-flow/commit/be8097acadca3054c3b236ce4296fc516010ef8c), [`1527795d`](https://github.com/wbkd/react-flow/commit/1527795d18c3af38c8ec7059436ea0fbf6c27bbd), [`3b6348a8`](https://github.com/wbkd/react-flow/commit/3b6348a8d1573afb39576327318bc172e33393c2)]:
- @reactflow/core@11.5.3
- @reactflow/background@11.1.6
- @reactflow/controls@11.1.6
- @reactflow/minimap@11.3.6
- @reactflow/node-toolbar@1.1.6
## 11.5.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "reactflow",
"version": "11.5.3",
"version": "11.5.4",
"description": "A highly customizable React library for building node-based editors and interactive flow charts",
"keywords": [
"react",