feat(graph): more adjustable interaction closes #329, closes #291

This commit is contained in:
moklick
2020-07-13 17:36:15 +02:00
parent 32f6101d37
commit 1d8a9d6d87
21 changed files with 259 additions and 114 deletions
+6 -5
View File
@@ -118,7 +118,7 @@ function renderEdge(
props: EdgeRendererProps,
nodes: Node[],
selectedElements: Elements | null,
isInteractive: boolean
elementsSelectable: boolean
) {
const [sourceId, sourceHandleId] = edge.source.split('__');
const [targetId, targetHandleId] = edge.target.split('__');
@@ -181,7 +181,7 @@ function renderEdge(
targetY={targetY}
sourcePosition={sourcePosition}
targetPosition={targetPosition}
isInteractive={isInteractive}
elementsSelectable={elementsSelectable}
/>
);
}
@@ -194,7 +194,8 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
const connectionHandleType = useStoreState((s) => s.connectionHandleType);
const connectionPosition = useStoreState((s) => s.connectionPosition);
const selectedElements = useStoreState((s) => s.selectedElements);
const isInteractive = useStoreState((s) => s.isInteractive);
const nodesConnectable = useStoreState((s) => s.nodesConnectable);
const elementsSelectable = useStoreState((s) => s.elementsSelectable);
const { width, height, connectionLineStyle, connectionLineType } = props;
@@ -208,7 +209,7 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
return (
<svg width={width} height={height} className="react-flow__edges">
<g transform={transformStyle}>
{edges.map((e: Edge) => renderEdge(e, props, nodes, selectedElements, isInteractive))}
{edges.map((e: Edge) => renderEdge(e, props, nodes, selectedElements, elementsSelectable))}
{renderConnectionLine && (
<ConnectionLine
nodes={nodes}
@@ -219,7 +220,7 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
transform={[tX, tY, tScale]}
connectionLineStyle={connectionLineStyle}
connectionLineType={connectionLineType}
isInteractive={isInteractive}
isConnectable={nodesConnectable}
/>
)}
</g>
+21 -9
View File
@@ -1,5 +1,4 @@
import React, { useEffect, useRef, memo, CSSProperties, MouseEvent } from 'react';
import classnames from 'classnames';
import { ResizeObserver } from 'resize-observer';
import { useStoreState, useStoreActions } from '../../store/hooks';
@@ -46,7 +45,9 @@ export interface GraphViewProps {
snapToGrid: boolean;
snapGrid: [number, number];
onlyRenderVisibleNodes: boolean;
isInteractive: boolean;
nodesDraggable: boolean;
nodesConnectable: boolean;
elementsSelectable: boolean;
selectNodesOnDrag: boolean;
minZoom: number;
maxZoom: number;
@@ -76,7 +77,9 @@ const GraphView = memo(
snapToGrid,
snapGrid,
onlyRenderVisibleNodes,
isInteractive,
nodesDraggable,
nodesConnectable,
elementsSelectable,
selectNodesOnDrag,
minZoom,
maxZoom,
@@ -92,14 +95,15 @@ const GraphView = memo(
const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection);
const setOnConnect = useStoreActions((a) => a.setOnConnect);
const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid);
const setInteractive = useStoreActions((actions) => actions.setInteractive);
const setNodesDraggable = useStoreActions((actions) => actions.setNodesDraggable);
const setNodesConnectable = useStoreActions((actions) => actions.setNodesConnectable);
const setElementsSelectable = useStoreActions((actions) => actions.setElementsSelectable);
const updateTransform = useStoreActions((actions) => actions.updateTransform);
const setMinMaxZoom = useStoreActions((actions) => actions.setMinMaxZoom);
const fitView = useStoreActions((actions) => actions.fitView);
const zoom = useStoreActions((actions) => actions.zoom);
const selectionKeyPressed = useKeyPress(selectionKeyCode);
const rendererClasses = classnames('react-flow__renderer', { 'is-interactive': isInteractive });
const onZoomPaneClick = () => setNodesSelection({ isActive: false });
@@ -169,8 +173,16 @@ const GraphView = memo(
}, [snapToGrid]);
useEffect(() => {
setInteractive(isInteractive);
}, [isInteractive]);
setNodesDraggable(nodesDraggable);
}, [nodesDraggable]);
useEffect(() => {
setNodesConnectable(nodesConnectable);
}, [nodesConnectable]);
useEffect(() => {
setElementsSelectable(elementsSelectable);
}, [elementsSelectable]);
useEffect(() => {
setMinMaxZoom({ minZoom, maxZoom });
@@ -180,7 +192,7 @@ const GraphView = memo(
useElementUpdater(elements);
return (
<div className={rendererClasses} ref={rendererNode}>
<div className="react-flow__renderer" ref={rendererNode}>
<NodeRenderer
nodeTypes={nodeTypes}
onElementClick={onElementClick}
@@ -201,7 +213,7 @@ const GraphView = memo(
connectionLineType={connectionLineType}
connectionLineStyle={connectionLineStyle}
/>
<UserSelection selectionKeyPressed={selectionKeyPressed} isInteractive={isInteractive} />
<UserSelection selectionKeyPressed={selectionKeyPressed} />
{nodesSelectionActive && <NodesSelection />}
<div className="react-flow__zoompane" onClick={onZoomPaneClick} ref={zoomPane} />
</div>
+13 -4
View File
@@ -22,7 +22,9 @@ function renderNode(
props: NodeRendererProps,
transform: Transform,
selectedElements: Elements | null,
isInteractive: boolean
nodesDraggable: boolean,
nodesConnectable: boolean,
elementsSelectable: boolean
) {
const nodeType = node.type || 'default';
const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>;
@@ -51,7 +53,9 @@ function renderNode(
selected={isSelected}
style={node.style}
className={node.className}
isInteractive={isInteractive}
isDraggable={nodesDraggable}
isSelectable={elementsSelectable}
isConnectable={nodesConnectable}
sourcePosition={node.sourcePosition}
targetPosition={node.targetPosition}
selectNodesOnDrag={props.selectNodesOnDrag}
@@ -65,7 +69,10 @@ const NodeRenderer = memo(({ onlyRenderVisibleNodes = true, ...props }: NodeRend
const selectedElements = useStoreState((s) => s.selectedElements);
const width = useStoreState((s) => s.width);
const height = useStoreState((s) => s.height);
const isInteractive = useStoreState((s) => s.isInteractive);
const nodesDraggable = useStoreState((s) => s.nodesDraggable);
const nodesConnectable = useStoreState((s) => s.nodesConnectable);
const elementsSelectable = useStoreState((s) => s.elementsSelectable);
const [tX, tY, tScale] = transform;
const transformStyle = {
transform: `translate(${tX}px,${tY}px) scale(${tScale})`,
@@ -77,7 +84,9 @@ const NodeRenderer = memo(({ onlyRenderVisibleNodes = true, ...props }: NodeRend
return (
<div className="react-flow__nodes" style={transformStyle}>
{renderNodes.map((node) => renderNode(node, props, transform, selectedElements, isInteractive))}
{renderNodes.map((node) =>
renderNode(node, props, transform, selectedElements, nodesDraggable, nodesConnectable, elementsSelectable)
)}
</div>
);
});
+12 -4
View File
@@ -56,7 +56,9 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
snapToGrid: boolean;
snapGrid: [number, number];
onlyRenderVisibleNodes: boolean;
isInteractive: boolean;
nodesDraggable: boolean;
nodesConnectable: boolean;
elementsSelectable: boolean;
selectNodesOnDrag: boolean;
minZoom: number;
maxZoom: number;
@@ -89,7 +91,9 @@ const ReactFlow = ({
snapToGrid,
snapGrid,
onlyRenderVisibleNodes,
isInteractive,
nodesDraggable,
nodesConnectable,
elementsSelectable,
selectNodesOnDrag,
minZoom,
maxZoom,
@@ -123,7 +127,9 @@ const ReactFlow = ({
snapToGrid={snapToGrid}
snapGrid={snapGrid}
onlyRenderVisibleNodes={onlyRenderVisibleNodes}
isInteractive={isInteractive}
nodesDraggable={nodesDraggable}
nodesConnectable={nodesConnectable}
elementsSelectable={elementsSelectable}
selectNodesOnDrag={selectNodesOnDrag}
minZoom={minZoom}
maxZoom={maxZoom}
@@ -156,7 +162,9 @@ ReactFlow.defaultProps = {
snapToGrid: false,
snapGrid: [16, 16],
onlyRenderVisibleNodes: true,
isInteractive: true,
nodesDraggable: true,
nodesConnectable: true,
elementsSelectable: true,
selectNodesOnDrag: true,
minZoom: 0.5,
maxZoom: 2,