From e1f864f270a81e2d289c203981b862c7b1658ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 18 Nov 2021 16:29:06 +0100 Subject: [PATCH] feat(flow): add properties for configuring no-interaction classes --- src/container/EdgeRenderer/index.tsx | 7 ++++- src/container/FlowRenderer/index.tsx | 6 ++++ src/container/GraphView/index.tsx | 8 ++++++ src/container/NodeRenderer/index.tsx | 4 ++- src/container/ReactFlow/index.tsx | 9 ++++++ src/container/ZoomPane/index.tsx | 41 ++++++++++++++++++---------- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 7d6329fc..9d242090 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -1,5 +1,6 @@ import React, { memo, CSSProperties, useCallback } from 'react'; import shallow from 'zustand/shallow'; +import cc from 'classcat'; import { useStore } from '../../store'; import ConnectionLine from '../../components/ConnectionLine/index'; @@ -35,6 +36,7 @@ interface EdgeRendererProps { onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge) => 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..9a34403d 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -59,6 +59,9 @@ const FlowRenderer = ({ onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, + noDragClassName, + noZoomClassName, + noPanClassName, }: FlowRendererProps) => { const { unsetNodesSelection, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow); @@ -107,6 +110,9 @@ const FlowRenderer = ({ defaultZoom={defaultZoom} zoomActivationKeyCode={zoomActivationKeyCode} preventScrolling={preventScrolling} + noDragClassName={noDragClassName} + noZoomClassName={noZoomClassName} + noPanClassName={noPanClassName} > {children} diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 34c7c668..e08dd5ee 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -76,6 +76,9 @@ const GraphView = ({ edgeUpdaterRadius, onEdgeUpdateStart, onEdgeUpdateEnd, + noDragClassName, + noZoomClassName, + noPanClassName, }: GraphViewProps) => { useOnLoadHandler(onLoad); @@ -106,6 +109,9 @@ const GraphView = ({ onSelectionDragStop={onSelectionDragStop} onSelectionContextMenu={onSelectionContextMenu} preventScrolling={preventScrolling} + noDragClassName={noDragClassName} + noZoomClassName={noZoomClassName} + noPanClassName={noPanClassName} > diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 3482db92..94c74c0e 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -1,5 +1,6 @@ import React, { memo, useMemo, ComponentType, MouseEvent } from 'react'; import shallow from 'zustand/shallow'; +import cc from 'classcat'; import { useStore } from '../../store'; import { Node, NodeTypesType, ReactFlowState, WrapNodeProps } from '../../types'; @@ -19,6 +20,7 @@ interface NodeRendererProps { onNodeDrag?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; onlyRenderVisibleElements: boolean; + noPanClassName?: string; } const selector = (s: ReactFlowState) => ({ @@ -76,7 +78,7 @@ const NodeRenderer = (props: NodeRendererProps) => { , 'on onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge) => void; onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void; edgeUpdaterRadius?: number; + noDragClassName?: string; + noZoomClassName?: string; + noPanClassName?: string; } export type ReactFlowRefType = HTMLDivElement; @@ -212,6 +215,9 @@ const ReactFlow = forwardRef( edgeUpdaterRadius = 10, onNodesChange, onEdgesChange, + noDragClassName = 'nodrag', + noZoomClassName = 'nowheel', + noPanClassName = 'nopan', ...rest }, ref @@ -276,6 +282,9 @@ const ReactFlow = forwardRef( onEdgeUpdateEnd={onEdgeUpdateEnd} edgeUpdaterRadius={edgeUpdaterRadius} defaultMarkerColor={defaultMarkerColor} + noDragClassName={noDragClassName} + noZoomClassName={noZoomClassName} + noPanClassName={noPanClassName} /> @@ -40,7 +43,8 @@ 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 hasNoWheelClass = (event: any) => event.target.closest('.nowheel'); const selector = (s: ReactFlowState) => ({ d3Zoom: s.d3Zoom, @@ -69,6 +73,8 @@ const ZoomPane = ({ zoomActivationKeyCode, preventScrolling = true, children, + noZoomClassName, + noPanClassName, }: ZoomPaneProps) => { const store = useStoreApi(); const zoomPane = useRef(null); @@ -106,7 +112,7 @@ const ZoomPane = ({ if (panOnScroll && !zoomActivationKeyPressed) { d3Selection .on('wheel', (event: any) => { - if (hasNoWheelClass(event)) { + if (isWrappedWithClass(event, noZoomClassName)) { return false; } event.preventDefault(); @@ -140,7 +146,7 @@ const ZoomPane = ({ } else if (typeof d3ZoomHandler !== 'undefined') { d3Selection .on('wheel', (event: any) => { - if (!preventScrolling || hasNoWheelClass(event)) { + if (!preventScrolling || isWrappedWithClass(event, noZoomClassName)) { return null; } @@ -158,6 +164,7 @@ const ZoomPane = ({ zoomActivationKeyPressed, zoomOnPinch, preventScrolling, + noZoomClassName, ]); useEffect(() => { @@ -230,22 +237,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, noZoomClassName) && 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;