Skip to content

Commit

Permalink
feat(nodes): connect on touch device
Browse files Browse the repository at this point in the history
  • Loading branch information
moklick committed Dec 17, 2021
1 parent 639aa1e commit 3b25086
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 7 deletions.
49 changes: 49 additions & 0 deletions example/src/TouchDevice/index.tsx
@@ -0,0 +1,49 @@
import { useCallback } from 'react';
import ReactFlow, {
Node,
Edge,
useNodesState,
useEdgesState,
Position,
Connection,
addEdge,
} from 'react-flow-renderer';

import './touch-device.css';

const initialNodes: Node[] = [
{
id: '1',
data: { label: 'Node 1' },
position: { x: 100, y: 100 },
sourcePosition: Position.Right,
targetPosition: Position.Left,
},
{
id: '2',
data: { label: 'Node 2' },
position: { x: 300, y: 100 },
sourcePosition: Position.Right,
targetPosition: Position.Left,
},
];

const initialEdges: Edge[] = [];

const TouchDeviceFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection: Connection) => setEdges((eds) => addEdge(connection, eds)), []);

return (
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>
);
};

export default TouchDeviceFlow;
19 changes: 19 additions & 0 deletions example/src/TouchDevice/touch-device.css
@@ -0,0 +1,19 @@
.react-flow .react-flow__handle {
width: 20px;
height: 20px;
border-radius: 3px;
background-color: #9f7aea;
}

.react-flow__handle.connecting {
animation: bounce 1600ms infinite ease-out;
}

@keyframes bounce {
0% {
transform: translate(0, -50%) scale(1);
}
50% {
transform: translate(0, -50%) scale(1.1);
}
}
5 changes: 5 additions & 0 deletions example/src/index.tsx
Expand Up @@ -11,6 +11,7 @@ import Layouting from './Layouting';
import NestedNodes from './NestedNodes';
import Hidden from './Hidden';
import UpdatableEdge from './UpdatableEdge';
import TouchDevice from './TouchDevice';

import './index.css';

Expand Down Expand Up @@ -51,6 +52,10 @@ const routes = [
path: '/updatable-edge',
component: UpdatableEdge,
},
{
path: '/touch-device',
component: TouchDevice,
},
];

const Header = withRouter(({ history, location }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Handle/handler.ts
Expand Up @@ -23,7 +23,7 @@ type Result = {
};

// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
function checkElementBelowIsValid(
export function checkElementBelowIsValid(
event: MouseEvent,
connectionMode: ConnectionMode,
isTarget: boolean,
Expand Down
56 changes: 51 additions & 5 deletions src/components/Handle/index.tsx
Expand Up @@ -5,7 +5,8 @@ import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../store';
import NodeIdContext from '../../contexts/NodeIdContext';
import { HandleProps, Connection, ReactFlowState, Position } from '../../types';
import { onMouseDown } from './handler';
import { checkElementBelowIsValid, onMouseDown } from './handler';
import { getHostForElement } from '../../utils';

const alwaysValid = () => true;

Expand All @@ -17,6 +18,7 @@ const selector = (s: ReactFlowState) => ({
onConnectStop: s.onConnectStop,
onConnectEnd: s.onConnectEnd,
connectionMode: s.connectionMode,
connectionStartHandle: s.connectionStartHandle,
});

const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
Expand All @@ -36,10 +38,8 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
) => {
const store = useStoreApi();
const nodeId = useContext(NodeIdContext) as string;
const { onConnectAction, onConnectStart, onConnectStop, onConnectEnd, connectionMode } = useStore(
selector,
shallow
);
const { onConnectAction, onConnectStart, onConnectStop, onConnectEnd, connectionMode, connectionStartHandle } =
useStore(selector, shallow);

const handleId = id || null;
const isTarget = type === 'target';
Expand Down Expand Up @@ -83,6 +83,47 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
]
);

const onClick = useCallback(
(event: React.MouseEvent) => {
if (!connectionStartHandle) {
onConnectStart?.(event, { nodeId, handleId, handleType: type });
store.setState({ connectionStartHandle: { nodeId, type, handleId } });
} else {
const doc = getHostForElement(event.target as HTMLElement);
const { connection, isValid } = checkElementBelowIsValid(
event as unknown as MouseEvent,
connectionMode,
connectionStartHandle.type === 'target',
connectionStartHandle.nodeId,
connectionStartHandle.handleId || null,
isValidConnection,
doc
);

onConnectStop?.(event as unknown as MouseEvent);

if (isValid) {
onConnectExtended(connection);
}

onConnectEnd?.(event as unknown as MouseEvent);

store.setState({ connectionStartHandle: null });
}
},
[
connectionStartHandle,
onConnectStart,
onConnectExtended,
onConnectStop,
onConnectEnd,
isTarget,
nodeId,
handleId,
type,
]
);

const handleClasses = cc([
'react-flow__handle',
`react-flow__handle-${position}`,
Expand All @@ -92,6 +133,10 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
source: !isTarget,
target: isTarget,
connectable: isConnectable,
connecting:
connectionStartHandle?.nodeId === nodeId &&
connectionStartHandle?.handleId === handleId &&
connectionStartHandle?.type === type,
},
]);

Expand All @@ -102,6 +147,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
data-handlepos={position}
className={handleClasses}
onMouseDown={onMouseDownHandler}
onClick={onClick}
ref={ref}
{...rest}
>
Expand Down
2 changes: 2 additions & 0 deletions src/store/initialState.ts
Expand Up @@ -42,6 +42,8 @@ const initialState: ReactFlowStore = {

fitViewOnInit: false,
fitViewOnInitDone: false,

connectionStartHandle: null,
};

export default initialState;
4 changes: 3 additions & 1 deletion src/types/general.ts
Expand Up @@ -5,7 +5,7 @@ import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
import { NodeChange, EdgeChange } from './changes';
import { Node, NodeInternals, NodeDimensionUpdate, NodeDiffUpdate } from './nodes';
import { Edge } from './edges';
import { HandleType } from './handles';
import { HandleType, StartHandle } from './handles';

export type FlowElement<T = any> = Node<T> | Edge<T>;

Expand Down Expand Up @@ -178,6 +178,8 @@ export type ReactFlowStore = {
fitViewOnInit: boolean;
fitViewOnInitDone: boolean;

connectionStartHandle: StartHandle | null;

onConnect?: OnConnect;
onConnectStart?: OnConnectStart;
onConnectStop?: OnConnectStop;
Expand Down
6 changes: 6 additions & 0 deletions src/types/handles.ts
Expand Up @@ -8,6 +8,12 @@ export interface HandleElement extends XYPosition, Dimensions {
position: Position;
}

export interface StartHandle {
nodeId: string;
type: HandleType;
handleId?: string | null;
}

export interface HandleProps {
type: HandleType;
position: Position;
Expand Down

0 comments on commit 3b25086

Please sign in to comment.