develop (#43)
* fix(ts): use strict mode strictNullChecks etc * chore: Use extended React.HTMLAttributes<> (#41) * refactor(code-format): add prettier closes #42 * feat(renderer): add snap to grid option closes #20 * chore(dependabot): use develop as target branch
This commit is contained in:
@@ -1,41 +1,54 @@
|
||||
import React, { memo, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { HandleType, ElementId, Position, XYPosition, OnConnectFunc, Connection } from '../../types';
|
||||
import {
|
||||
HandleType,
|
||||
ElementId,
|
||||
Position,
|
||||
XYPosition,
|
||||
OnConnectFunc,
|
||||
Connection,
|
||||
} from '../../types';
|
||||
|
||||
type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
type SetSourceIdFunc = (nodeId: ElementId | null) => void;
|
||||
|
||||
interface BaseHandleProps {
|
||||
type: HandleType;
|
||||
nodeId: ElementId;
|
||||
onConnect: OnConnectFunc;
|
||||
position: Position;
|
||||
setSourceId: (nodeId: ElementId) => void;
|
||||
setSourceId: SetSourceIdFunc;
|
||||
setPosition: (pos: XYPosition) => void;
|
||||
isValidConnection: ValidConnectionFunc;
|
||||
id?: ElementId | boolean;
|
||||
id?: ElementId | boolean;
|
||||
className?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element;
|
||||
elementBelow: Element | null;
|
||||
isValid: boolean;
|
||||
connection: Connection;
|
||||
isHoveringHandle: boolean;
|
||||
};
|
||||
|
||||
function onMouseDown(
|
||||
evt: ReactMouseEvent, nodeId: ElementId, setSourceId: (nodeId: ElementId) => void, setPosition: (pos: XYPosition) => any,
|
||||
onConnect: OnConnectFunc, isTarget: boolean, isValidConnection: ValidConnectionFunc
|
||||
evt: ReactMouseEvent,
|
||||
nodeId: ElementId,
|
||||
setSourceId: SetSourceIdFunc,
|
||||
setPosition: (pos: XYPosition) => any,
|
||||
onConnect: OnConnectFunc,
|
||||
isTarget: boolean,
|
||||
isValidConnection: ValidConnectionFunc
|
||||
): void {
|
||||
const reactFlowNode = document.querySelector('.react-flow');
|
||||
|
||||
if (!reactFlowNode) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
const containerBounds = reactFlowNode.getBoundingClientRect();
|
||||
let recentHoveredHandle: Element = null;
|
||||
let recentHoveredHandle: Element;
|
||||
|
||||
setPosition({
|
||||
x: evt.clientX - containerBounds.left,
|
||||
@@ -43,9 +56,9 @@ function onMouseDown(
|
||||
});
|
||||
setSourceId(nodeId);
|
||||
|
||||
function resetRecentHandle() {
|
||||
function resetRecentHandle(): void {
|
||||
if (!recentHoveredHandle) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
recentHoveredHandle.classList.remove('valid');
|
||||
@@ -55,14 +68,19 @@ function onMouseDown(
|
||||
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
||||
function checkElementBelowIsValid(evt: MouseEvent) {
|
||||
const elementBelow = document.elementFromPoint(evt.clientX, evt.clientY);
|
||||
|
||||
const result: Result = {
|
||||
elementBelow,
|
||||
isValid: false,
|
||||
connection: { source: null, target: null },
|
||||
isHoveringHandle: false
|
||||
isHoveringHandle: false,
|
||||
};
|
||||
|
||||
if (elementBelow && (elementBelow.classList.contains('target') || elementBelow.classList.contains('source'))) {
|
||||
if (
|
||||
elementBelow &&
|
||||
(elementBelow.classList.contains('target') ||
|
||||
elementBelow.classList.contains('source'))
|
||||
) {
|
||||
let connection: Connection = { source: null, target: null };
|
||||
|
||||
if (isTarget) {
|
||||
@@ -89,7 +107,12 @@ function onMouseDown(
|
||||
y: evt.clientY - containerBounds.top,
|
||||
});
|
||||
|
||||
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(evt);
|
||||
const {
|
||||
connection,
|
||||
elementBelow,
|
||||
isValid,
|
||||
isHoveringHandle,
|
||||
} = checkElementBelowIsValid(evt);
|
||||
|
||||
if (!isHoveringHandle) {
|
||||
return resetRecentHandle();
|
||||
@@ -97,7 +120,7 @@ function onMouseDown(
|
||||
|
||||
const isOwnHandle = connection.source === connection.target;
|
||||
|
||||
if (!isOwnHandle) {
|
||||
if (!isOwnHandle && elementBelow) {
|
||||
recentHoveredHandle = elementBelow;
|
||||
elementBelow.classList.add('connecting');
|
||||
elementBelow.classList.toggle('valid', isValid);
|
||||
@@ -105,7 +128,7 @@ function onMouseDown(
|
||||
}
|
||||
|
||||
function onMouseUp(evt: MouseEvent) {
|
||||
const { connection, isValid } = checkElementBelowIsValid(evt);
|
||||
const { connection, isValid } = checkElementBelowIsValid(evt);
|
||||
|
||||
if (isValid) {
|
||||
onConnect(connection);
|
||||
@@ -119,37 +142,51 @@ function onMouseDown(
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp)
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
}
|
||||
|
||||
const BaseHandle = memo(({
|
||||
type, nodeId, onConnect, position,
|
||||
setSourceId, setPosition, className,
|
||||
id = false, isValidConnection, ...rest
|
||||
}: BaseHandleProps) => {
|
||||
const isTarget = type === 'target';
|
||||
const handleClasses = cx(
|
||||
'react-flow__handle',
|
||||
className,
|
||||
const BaseHandle = memo(
|
||||
({
|
||||
type,
|
||||
nodeId,
|
||||
onConnect,
|
||||
position,
|
||||
{ source: !isTarget, target: isTarget }
|
||||
);
|
||||
setSourceId,
|
||||
setPosition,
|
||||
className,
|
||||
id = false,
|
||||
isValidConnection,
|
||||
...rest
|
||||
}: BaseHandleProps) => {
|
||||
const isTarget = type === 'target';
|
||||
const handleClasses = cx('react-flow__handle', className, position, {
|
||||
source: !isTarget,
|
||||
target: isTarget,
|
||||
});
|
||||
|
||||
const nodeIdWithHandleId = id ? `${nodeId}__${id}` : nodeId;
|
||||
const nodeIdWithHandleId = id ? `${nodeId}__${id}` : nodeId;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-nodeid={nodeIdWithHandleId}
|
||||
data-handlepos={position}
|
||||
className={handleClasses}
|
||||
onMouseDown={evt => onMouseDown(evt,
|
||||
nodeIdWithHandleId, setSourceId, setPosition,
|
||||
onConnect, isTarget, isValidConnection
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div
|
||||
data-nodeid={nodeIdWithHandleId}
|
||||
data-handlepos={position}
|
||||
className={handleClasses}
|
||||
onMouseDown={evt =>
|
||||
onMouseDown(
|
||||
evt,
|
||||
nodeIdWithHandleId,
|
||||
setSourceId,
|
||||
setPosition,
|
||||
onConnect,
|
||||
isTarget,
|
||||
isValidConnection
|
||||
)
|
||||
}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
BaseHandle.displayName = 'BaseHandle';
|
||||
|
||||
|
||||
@@ -2,45 +2,56 @@ import React, { memo, useContext } from 'react';
|
||||
|
||||
import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
import BaseHandle from './BaseHandle';
|
||||
import NodeIdContext from '../../contexts/NodeIdContext'
|
||||
import NodeIdContext from '../../contexts/NodeIdContext';
|
||||
|
||||
import { HandleType, ElementId, Position, OnConnectParams, OnConnectFunc } from '../../types';
|
||||
import {
|
||||
HandleType,
|
||||
ElementId,
|
||||
Position,
|
||||
Connection,
|
||||
OnConnectFunc,
|
||||
} from '../../types';
|
||||
|
||||
interface HandleProps {
|
||||
type: HandleType,
|
||||
position: Position,
|
||||
onConnect?: OnConnectFunc,
|
||||
isValidConnection?: () => boolean
|
||||
};
|
||||
type: HandleType;
|
||||
position: Position;
|
||||
onConnect?: OnConnectFunc;
|
||||
isValidConnection?: () => boolean;
|
||||
}
|
||||
|
||||
const Handle = memo(({
|
||||
onConnect = _ => {}, type = 'source', position = 'top', isValidConnection = () => true,
|
||||
...rest
|
||||
}: HandleProps) => {
|
||||
const nodeId = useContext(NodeIdContext) as ElementId;
|
||||
const { setPosition, setSourceId } = useStoreActions(a => ({
|
||||
setPosition: a.setConnectionPosition,
|
||||
setSourceId: a.setConnectionSourceId
|
||||
}));
|
||||
const onConnectAction = useStoreState(s => s.onConnect);
|
||||
const onConnectExtended = (params: OnConnectParams) => {
|
||||
onConnectAction(params);
|
||||
onConnect(params);
|
||||
};
|
||||
const Handle = memo(
|
||||
({
|
||||
onConnect = _ => {},
|
||||
type = 'source',
|
||||
position = 'top',
|
||||
isValidConnection = () => true,
|
||||
...rest
|
||||
}: HandleProps) => {
|
||||
const nodeId = useContext(NodeIdContext) as ElementId;
|
||||
const { setPosition, setSourceId } = useStoreActions(a => ({
|
||||
setPosition: a.setConnectionPosition,
|
||||
setSourceId: a.setConnectionSourceId,
|
||||
}));
|
||||
const onConnectAction = useStoreState(s => s.onConnect);
|
||||
const onConnectExtended = (params: Connection) => {
|
||||
onConnectAction(params);
|
||||
onConnect(params);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseHandle
|
||||
nodeId={nodeId}
|
||||
setPosition={setPosition}
|
||||
setSourceId={setSourceId}
|
||||
onConnect={onConnectExtended}
|
||||
type={type}
|
||||
position={position}
|
||||
isValidConnection={isValidConnection}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<BaseHandle
|
||||
nodeId={nodeId}
|
||||
setPosition={setPosition}
|
||||
setSourceId={setSourceId}
|
||||
onConnect={onConnectExtended}
|
||||
type={type}
|
||||
position={position}
|
||||
isValidConnection={isValidConnection}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Handle.displayName = 'Handle';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user