feat(connection-line): add status class (valid or invalid) while in connection radius

This commit is contained in:
moklick
2023-02-03 21:10:44 +01:00
parent 4ca10a3ae2
commit 23d1e65a72
8 changed files with 55 additions and 8 deletions

View File

@@ -31,3 +31,11 @@
.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,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, Connection } from '../../types';
import type { OnConnect, HandleType, ReactFlowState, Connection, ConnectionStatus } from '../../types';
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
import {
ConnectionHandle,
getClosestHandle,
getConnectionStatus,
getHandleLookup,
getHandleType,
isValidHandle,
@@ -91,6 +92,7 @@ export function handlePointerDown({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType,
connectionStatus: null,
});
onConnectStart?.(event, { nodeId, handleId, handleType });
@@ -123,7 +125,7 @@ export function handlePointerDown({
setState({
connectionPosition:
prevClosestHandle && isValid
prevClosestHandle && result.isValid
? rendererPointToPoint(
{
x: prevClosestHandle.x,
@@ -132,6 +134,7 @@ export function handlePointerDown({
transform
)
: connectionPosition,
connectionStatus: getConnectionStatus(!!prevClosestHandle, result.isValid),
});
if (!prevClosestHandle) {

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';
@@ -167,3 +167,15 @@ export function getHandleType(
export function resetRecentHandle(handleDomNode: Element): void {
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

@@ -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;