Merge branch 'main' into feat/packages
This commit is contained in:
@@ -54,6 +54,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
rfId,
|
||||
ariaLabel,
|
||||
isFocusable,
|
||||
isUpdatable,
|
||||
pathOptions,
|
||||
interactionWidth,
|
||||
}: WrapEdgeProps): JSX.Element | null => {
|
||||
@@ -137,7 +138,6 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
|
||||
|
||||
const inactive = !elementsSelectable && !onClick;
|
||||
const handleEdgeUpdate = typeof onEdgeUpdate !== 'undefined';
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (elementSelectionKeys.includes(event.key) && elementsSelectable) {
|
||||
@@ -204,28 +204,32 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
interactionWidth={interactionWidth}
|
||||
/>
|
||||
)}
|
||||
{handleEdgeUpdate && (
|
||||
{isUpdatable && (
|
||||
<>
|
||||
<EdgeAnchor
|
||||
position={sourcePosition}
|
||||
centerX={sourceX}
|
||||
centerY={sourceY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
type="source"
|
||||
/>
|
||||
<EdgeAnchor
|
||||
position={targetPosition}
|
||||
centerX={targetX}
|
||||
centerY={targetY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
type="target"
|
||||
/>
|
||||
{(isUpdatable === 'source' || isUpdatable === true) && (
|
||||
<EdgeAnchor
|
||||
position={sourcePosition}
|
||||
centerX={sourceX}
|
||||
centerY={sourceY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
type="source"
|
||||
/>
|
||||
)}
|
||||
{(isUpdatable === 'target' || isUpdatable === true) && (
|
||||
<EdgeAnchor
|
||||
position={targetPosition}
|
||||
centerX={targetX}
|
||||
centerY={targetY}
|
||||
radius={edgeUpdaterRadius}
|
||||
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||
onMouseOut={onEdgeUpdaterMouseOut}
|
||||
type="target"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</g>
|
||||
|
||||
@@ -95,10 +95,17 @@ export function handlePointerDown({
|
||||
|
||||
setState({
|
||||
connectionPosition,
|
||||
connectionStatus: null,
|
||||
// connectionNodeId etc will be removed in the next major in favor of connectionStartHandle
|
||||
connectionNodeId: nodeId,
|
||||
connectionHandleId: handleId,
|
||||
connectionHandleType: handleType,
|
||||
connectionStatus: null,
|
||||
connectionStartHandle: {
|
||||
nodeId,
|
||||
handleId,
|
||||
type: handleType,
|
||||
},
|
||||
connectionEndHandle: null,
|
||||
});
|
||||
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
@@ -145,6 +152,7 @@ export function handlePointerDown({
|
||||
)
|
||||
: connectionPosition,
|
||||
connectionStatus: getConnectionStatus(!!prevClosestHandle, isValid),
|
||||
connectionEndHandle: result.endHandle,
|
||||
});
|
||||
|
||||
if (!prevClosestHandle && !isValid && !handleDomNode) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { errorMessages, Position, type HandleProps, type Connection } from '@reactflow/system';
|
||||
import { errorMessages, Position, type HandleProps, type Connection, type HandleType } from '@reactflow/system';
|
||||
import { getHostForElement, isMouseEvent } from '@reactflow/utils';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||
import { handlePointerDown } from './handler';
|
||||
import { addEdge } from '../../utils/';
|
||||
import { type ReactFlowState } from '../../types';
|
||||
import { isValidHandle } from './utils';
|
||||
import { addEdge } from '../../utils';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
const alwaysValid = () => true;
|
||||
|
||||
@@ -21,6 +21,23 @@ const selector = (s: ReactFlowState) => ({
|
||||
noPanClassName: s.noPanClassName,
|
||||
});
|
||||
|
||||
const connectingSelector =
|
||||
(nodeId: string | null, handleId: string | null, type: HandleType) => (state: ReactFlowState) => {
|
||||
const {
|
||||
connectionStartHandle: startHandle,
|
||||
connectionEndHandle: endHandle,
|
||||
connectionClickStartHandle: clickHandle,
|
||||
} = state;
|
||||
|
||||
return {
|
||||
connecting:
|
||||
(startHandle?.nodeId === nodeId && startHandle?.handleId === handleId && startHandle?.type === type) ||
|
||||
(endHandle?.nodeId === nodeId && endHandle?.handleId === handleId && endHandle?.type === type),
|
||||
clickConnecting:
|
||||
clickHandle?.nodeId === nodeId && clickHandle?.handleId === handleId && clickHandle?.type === type,
|
||||
};
|
||||
};
|
||||
|
||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
(
|
||||
{
|
||||
@@ -28,6 +45,8 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
position = Position.Top,
|
||||
isValidConnection,
|
||||
isConnectable = true,
|
||||
isConnectableStart = true,
|
||||
isConnectableEnd = true,
|
||||
id,
|
||||
onConnect,
|
||||
children,
|
||||
@@ -38,19 +57,16 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const store = useStoreApi();
|
||||
const nodeId = useNodeId();
|
||||
|
||||
if (!nodeId) {
|
||||
store.getState().onError?.('010', errorMessages['010']());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const { connectionStartHandle, connectOnClick, noPanClassName } = useStore(selector, shallow);
|
||||
|
||||
const handleId = id || null;
|
||||
const isTarget = type === 'target';
|
||||
const store = useStoreApi();
|
||||
const nodeId = useNodeId();
|
||||
const { connectOnClick, noPanClassName } = useStore(selector, shallow);
|
||||
const { connecting, clickConnecting } = useStore(connectingSelector(nodeId, handleId, type));
|
||||
|
||||
if (!nodeId) {
|
||||
store.getState().onError?.('010', errorMessages['error010']());
|
||||
}
|
||||
|
||||
const onConnectExtended = (params: Connection) => {
|
||||
const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
|
||||
@@ -69,9 +85,16 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
};
|
||||
|
||||
const onPointerDown = (event: ReactMouseEvent<HTMLDivElement> | ReactTouchEvent<HTMLDivElement>) => {
|
||||
if (!nodeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMouseTriggered = isMouseEvent(event.nativeEvent);
|
||||
|
||||
if ((isMouseTriggered && (event as ReactMouseEvent<HTMLDivElement>).button === 0) || !isMouseTriggered) {
|
||||
if (
|
||||
isConnectableStart &&
|
||||
((isMouseTriggered && (event as ReactMouseEvent<HTMLDivElement>).button === 0) || !isMouseTriggered)
|
||||
) {
|
||||
handlePointerDown({
|
||||
event,
|
||||
handleId,
|
||||
@@ -95,12 +118,18 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
const {
|
||||
onClickConnectStart,
|
||||
onClickConnectEnd,
|
||||
connectionClickStartHandle,
|
||||
connectionMode,
|
||||
isValidConnection: isValidConnectionStore,
|
||||
} = store.getState();
|
||||
if (!connectionStartHandle) {
|
||||
|
||||
if (!nodeId || (!connectionClickStartHandle && !isConnectableStart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connectionClickStartHandle) {
|
||||
onClickConnectStart?.(event, { nodeId, handleId, handleType: type });
|
||||
store.setState({ connectionStartHandle: { nodeId, type, handleId } });
|
||||
store.setState({ connectionClickStartHandle: { nodeId, type, handleId } });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,9 +143,9 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
type,
|
||||
},
|
||||
connectionMode,
|
||||
connectionStartHandle.nodeId,
|
||||
connectionStartHandle.handleId || null,
|
||||
connectionStartHandle.type,
|
||||
connectionClickStartHandle.nodeId,
|
||||
connectionClickStartHandle.handleId || null,
|
||||
connectionClickStartHandle.type,
|
||||
isValidConnectionHandler,
|
||||
doc
|
||||
);
|
||||
@@ -127,7 +156,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
|
||||
onClickConnectEnd?.(event as unknown as MouseEvent);
|
||||
|
||||
store.setState({ connectionStartHandle: null });
|
||||
store.setState({ connectionClickStartHandle: null });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -146,10 +175,12 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
source: !isTarget,
|
||||
target: isTarget,
|
||||
connectable: isConnectable,
|
||||
connecting:
|
||||
connectionStartHandle?.nodeId === nodeId &&
|
||||
connectionStartHandle?.handleId === handleId &&
|
||||
connectionStartHandle?.type === type,
|
||||
connectablestart: isConnectableStart,
|
||||
connectableend: isConnectableEnd,
|
||||
connecting: clickConnecting,
|
||||
// this class is used to style the handle when the user is connecting
|
||||
connectionindicator:
|
||||
isConnectable && ((isConnectableStart && !connecting) || (isConnectableEnd && connecting)),
|
||||
},
|
||||
])}
|
||||
onMouseDown={onPointerDown}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||
import {
|
||||
internalsSymbol,
|
||||
ConnectionMode,
|
||||
ConnectionStatus,
|
||||
type ConnectingHandle,
|
||||
type Connection,
|
||||
type HandleType,
|
||||
type XYPosition,
|
||||
type NodeHandleBounds,
|
||||
type XYPosition,
|
||||
} from '@reactflow/system';
|
||||
import { getEventPosition } from '@reactflow/utils';
|
||||
|
||||
@@ -67,6 +67,7 @@ type Result = {
|
||||
handleDomNode: Element | null;
|
||||
isValid: boolean;
|
||||
connection: Connection;
|
||||
endHandle: ConnectingHandle | null;
|
||||
};
|
||||
|
||||
const nullConnection: Connection = { source: null, target: null, sourceHandle: null, targetHandle: null };
|
||||
@@ -78,7 +79,7 @@ export function isValidHandle(
|
||||
connectionMode: ConnectionMode,
|
||||
fromNodeId: string,
|
||||
fromHandleId: string | null,
|
||||
fromType: string,
|
||||
fromType: HandleType,
|
||||
isValidConnection: ValidConnectionFunc,
|
||||
doc: Document | ShadowRoot
|
||||
) {
|
||||
@@ -94,12 +95,15 @@ export function isValidHandle(
|
||||
handleDomNode: handleToCheck,
|
||||
isValid: false,
|
||||
connection: nullConnection,
|
||||
endHandle: null,
|
||||
};
|
||||
|
||||
if (handleToCheck) {
|
||||
const handleType = getHandleType(undefined, handleToCheck);
|
||||
const handleNodeId = handleToCheck.getAttribute('data-nodeid');
|
||||
const handleId = handleToCheck.getAttribute('data-handleid');
|
||||
const connectable = handleToCheck.classList.contains('connectable');
|
||||
const connectableEnd = handleToCheck.classList.contains('connectableend');
|
||||
|
||||
const connection: Connection = {
|
||||
source: isTarget ? handleNodeId : fromNodeId,
|
||||
@@ -110,14 +114,21 @@ export function isValidHandle(
|
||||
|
||||
result.connection = connection;
|
||||
|
||||
const isConnectable = connectable && connectableEnd;
|
||||
// in strict mode we don't allow target to target or source to source connections
|
||||
const isValid =
|
||||
handleToCheck.classList.contains('connectable') &&
|
||||
isConnectable &&
|
||||
(connectionMode === ConnectionMode.Strict
|
||||
? (isTarget && handleType === 'source') || (!isTarget && handleType === 'target')
|
||||
: handleNodeId !== fromNodeId || handleId !== fromHandleId);
|
||||
|
||||
if (isValid) {
|
||||
result.endHandle = {
|
||||
nodeId: handleNodeId as string,
|
||||
handleId,
|
||||
type: handleType as HandleType,
|
||||
};
|
||||
|
||||
result.isValid = isValidConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ type StoreUpdaterProps = Pick<
|
||||
| 'nodesConnectable'
|
||||
| 'nodesFocusable'
|
||||
| 'edgesFocusable'
|
||||
| 'edgesUpdatable'
|
||||
| 'minZoom'
|
||||
| 'maxZoom'
|
||||
| 'nodeExtent'
|
||||
@@ -99,6 +100,7 @@ const StoreUpdater = ({
|
||||
nodesConnectable,
|
||||
nodesFocusable,
|
||||
edgesFocusable,
|
||||
edgesUpdatable,
|
||||
elevateNodesOnSelect,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
@@ -163,6 +165,7 @@ const StoreUpdater = ({
|
||||
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
|
||||
useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState);
|
||||
useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState);
|
||||
useDirectStoreUpdater('edgesUpdatable', edgesUpdatable, store.setState);
|
||||
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
|
||||
useDirectStoreUpdater('elevateNodesOnSelect', elevateNodesOnSelect, store.setState);
|
||||
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
|
||||
|
||||
Reference in New Issue
Block a user