diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 15c0e7c1..20348106 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -50,6 +50,8 @@ export default (NodeComponent: ComponentType) => { dragHandle, zIndex, isParent, + noPanClassName, + noDragClassName, }: WrapNodeProps) => { const { addSelectedElements, @@ -217,6 +219,7 @@ export default (NodeComponent: ComponentType) => { const nodeClasses = cc([ 'react-flow__node', `react-flow__node-${type}`, + noPanClassName, className, { selected, @@ -232,7 +235,7 @@ export default (NodeComponent: ComponentType) => { onStop={onDragStop} scale={scale} disabled={!isDraggable} - cancel=".nodrag" + cancel={`.${noDragClassName}`} nodeRef={nodeElement} grid={grid} enableUserSelectHack={false} diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index 95fac2c0..b75c5ea6 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -5,6 +5,7 @@ import React, { useMemo, useCallback, useRef, MouseEvent } from 'react'; import ReactDraggable, { DraggableData } from 'react-draggable'; +import cc from 'classcat'; import { useStore } from '../../store'; import { Node, ReactFlowState } from '../../types'; @@ -14,6 +15,7 @@ export interface NodesSelectionProps { onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void; onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; + noPanClassName?: string; } // @TODO: work with nodeInternals instead of converting it to an array const selector = (s: ReactFlowState) => ({ @@ -33,6 +35,7 @@ export default ({ onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, + noPanClassName, }: NodesSelectionProps) => { const { transform, selectedNodesBbox, selectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = useStore(selector); @@ -105,7 +108,7 @@ export default ({ } return ( -
+
void; onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void; edgeUpdaterRadius?: number; + noPanClassName?: string; } interface EdgeWrapperProps { @@ -63,6 +65,7 @@ interface EdgeWrapperProps { targetNodeX?: number; targetNodeY?: number; targetNodeHandleBounds?: NodeHandleBounds; + noPanClassName?: string; } const Edge = memo( @@ -92,6 +95,7 @@ const Edge = memo( targetNodeX, targetNodeY, targetNodeHandleBounds, + noPanClassName, }: EdgeWrapperProps) => { const sourceHandleId = edge.sourceHandle || null; const targetHandleId = edge.targetHandle || null; @@ -163,7 +167,7 @@ const Edge = memo( { onEdgeUpdate={props.onEdgeUpdate} edgeTypes={props.edgeTypes} connectionMode={connectionMode} + noPanClassName={props.noPanClassName} /> ); })} diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index 9cfc3216..458e9c59 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -59,6 +59,8 @@ const FlowRenderer = ({ onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, + noWheelClassName, + noPanClassName, }: FlowRendererProps) => { const { unsetNodesSelection, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow); @@ -107,6 +109,8 @@ const FlowRenderer = ({ defaultZoom={defaultZoom} zoomActivationKeyCode={zoomActivationKeyCode} preventScrolling={preventScrolling} + noWheelClassName={noWheelClassName} + noPanClassName={noPanClassName} > {children} @@ -116,6 +120,7 @@ const FlowRenderer = ({ onSelectionDrag={onSelectionDrag} onSelectionDragStop={onSelectionDragStop} onSelectionContextMenu={onSelectionContextMenu} + noPanClassName={noPanClassName} /> )}
{ useOnLoadHandler(onLoad); @@ -106,6 +112,9 @@ const GraphView = ({ onSelectionDragStop={onSelectionDragStop} onSelectionContextMenu={onSelectionContextMenu} preventScrolling={preventScrolling} + noDragClassName={noDragClassName} + noWheelClassName={noWheelClassName} + noPanClassName={noPanClassName} > diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 2dee0dd5..a60fdb88 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -17,6 +17,8 @@ interface NodeRendererProps { onNodeDrag?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; onlyRenderVisibleElements: boolean; + noPanClassName: string; + noDragClassName: string; } const selector = (s: ReactFlowState) => ({ @@ -113,6 +115,8 @@ const NodeRenderer = (props: NodeRendererProps) => { dragHandle={node.dragHandle} zIndex={internals?.z || 0} isParent={!!internals?.isParent} + noDragClassName={props.noDragClassName} + noPanClassName={props.noPanClassName} /> ); })} diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index fdfc5756..87c3bbfb 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -5,6 +5,7 @@ import React, { MouseEvent as ReactMouseEvent, WheelEvent, forwardRef, + FunctionComponent, } from 'react'; import cc from 'classcat'; @@ -129,6 +130,9 @@ export interface ReactFlowProps extends Omit, 'on onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge) => void; onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void; edgeUpdaterRadius?: number; + noDragClassName?: string; + noWheelClassName?: string; + noPanClassName?: string; } export type ReactFlowRefType = HTMLDivElement; @@ -136,7 +140,7 @@ export type ReactFlowRefType = HTMLDivElement; const initSnapGrid: [number, number] = [15, 15]; const initDefaultPosition: [number, number] = [0, 0]; -const ReactFlow = forwardRef( +const ReactFlow: FunctionComponent = forwardRef( ( { nodes = [], @@ -212,6 +216,9 @@ const ReactFlow = forwardRef( edgeUpdaterRadius = 10, onNodesChange, onEdgesChange, + noDragClassName = 'nodrag', + noWheelClassName = 'nowheel', + noPanClassName = 'nopan', ...rest }, ref @@ -276,6 +283,9 @@ const ReactFlow = forwardRef( onEdgeUpdateEnd={onEdgeUpdateEnd} edgeUpdaterRadius={edgeUpdaterRadius} defaultMarkerColor={defaultMarkerColor} + noDragClassName={noDragClassName} + noWheelClassName={noWheelClassName} + noPanClassName={noPanClassName} /> @@ -40,7 +42,7 @@ const eventToFlowTransform = (eventTransform: any): FlowTransform => ({ zoom: eventTransform.k, }); -const hasNoWheelClass = (event: any) => event.target.closest('.nowheel'); +const isWrappedWithClass = (event: any, className: string | undefined) => event.target.closest(`.${className}`); const selector = (s: ReactFlowState) => ({ d3Zoom: s.d3Zoom, @@ -69,6 +71,8 @@ const ZoomPane = ({ zoomActivationKeyCode, preventScrolling = true, children, + noWheelClassName, + noPanClassName, }: ZoomPaneProps) => { const store = useStoreApi(); const zoomPane = useRef(null); @@ -106,7 +110,7 @@ const ZoomPane = ({ if (panOnScroll && !zoomActivationKeyPressed) { d3Selection .on('wheel', (event: any) => { - if (hasNoWheelClass(event)) { + if (isWrappedWithClass(event, noWheelClassName)) { return false; } event.preventDefault(); @@ -140,7 +144,7 @@ const ZoomPane = ({ } else if (typeof d3ZoomHandler !== 'undefined') { d3Selection .on('wheel', (event: any) => { - if (!preventScrolling || hasNoWheelClass(event)) { + if (!preventScrolling || isWrappedWithClass(event, noWheelClassName)) { return null; } @@ -158,6 +162,7 @@ const ZoomPane = ({ zoomActivationKeyPressed, zoomOnPinch, preventScrolling, + noWheelClassName, ]); useEffect(() => { @@ -230,22 +235,28 @@ const ZoomPane = ({ return false; } - if (hasNoWheelClass(event) && event.type === 'wheel') { + // if the target element is inside the nowheel class, we prevent zooming + if (isWrappedWithClass(event, noWheelClassName) && event.type === 'wheel') { + return false; + } + + // if the target element is inside the nopan class, we prevent panning + if (isWrappedWithClass(event, noPanClassName) && event.type !== 'wheel') { return false; } // when the target element is a node, we still allow zooming - if ( - (event.target.closest('.react-flow__node') || event.target.closest('.react-flow__edge')) && - event.type !== 'wheel' - ) { - return false; - } + // if ( + // (event.target.closest('.react-flow__node') || event.target.closest('.react-flow__edge')) && + // event.type !== 'wheel' + // ) { + // return false; + // } - // when the target element is a node selection, we still allow zooming - if (event.target.closest('.react-flow__nodesselection') && event.type !== 'wheel') { - return false; - } + // // when the target element is a node selection, we still allow zooming + // if (event.target.closest('.react-flow__nodesselection') && event.type !== 'wheel') { + // return false; + // } if (!zoomOnPinch && event.ctrlKey && event.type === 'wheel') { return false; diff --git a/src/types/nodes.ts b/src/types/nodes.ts index ed9fcfdc..76d0cd34 100644 --- a/src/types/nodes.ts +++ b/src/types/nodes.ts @@ -82,6 +82,8 @@ export interface WrapNodeProps { dragHandle?: string; zIndex: number; isParent: boolean; + noPanClassName: string; + noDragClassName: string; } export type NodeHandleBounds = {