From 121952bba73839ae31216548a179e914f44c6044 Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 11 Sep 2020 02:01:33 +0200 Subject: [PATCH] feat(selection): add context menu handler closes #478 --- README.md | 1 + example/src/Overview/index.js | 5 ++ src/components/NodesSelection/index.tsx | 106 ++++++++++++++---------- src/container/GraphView/index.tsx | 3 + src/container/ReactFlow/index.tsx | 3 + 5 files changed, 74 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index f6436671..ea3ff932 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ const BasicFlow = () => ; - `onSelectionDragStart(evt: MouseEvent, nodes: Node[])`: called when user starts to drag a selection - `onSelectionDrag(evt: MouseEvent, nodes: Node[])`: called when user drags a selection - `onSelectionDragStop(evt: MouseEvent, nodes: Node[])`: called when user stops to drag a selection +- `onSelectionContextMenu(event: MouseEvent, nodes: Node[])`: called when user does a right-click on a selection - `onPaneClick(event: MouseEvent)`: called when user clicks directly on the canvas - `onPaneContextMenu(event: MouseEvent)`: called when user does a right-click on the canvas - `onPaneScroll(event: WheelEvent)`: called when user scrolls pane (only works when `zoomOnScroll` is set to `false) diff --git a/example/src/Overview/index.js b/example/src/Overview/index.js index 045d5684..20246191 100644 --- a/example/src/Overview/index.js +++ b/example/src/Overview/index.js @@ -7,6 +7,10 @@ const onNodeDragStop = (event, node) => console.log('drag stop', node); const onSelectionDrag = (event, nodes) => console.log('selection drag', nodes); const onSelectionDragStart = (event, nodes) => console.log('selection drag start', nodes); const onSelectionDragStop = (event, nodes) => console.log('selection drag stop', nodes); +const onSelectionContextMenu = (event, nodes) => { + event.preventDefault(); + console.log('selection context menu', nodes); +}; const onElementClick = (event, element) => console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element); const onSelectionChange = (elements) => console.log('selection change', elements); const onLoad = (reactFlowInstance) => { @@ -126,6 +130,7 @@ const OverviewFlow = () => { onSelectionDragStart={onSelectionDragStart} onSelectionDrag={onSelectionDrag} onSelectionDragStop={onSelectionDragStop} + onSelectionContextMenu={onSelectionContextMenu} onSelectionChange={onSelectionChange} onMoveEnd={onMoveEnd} onLoad={onLoad} diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index fddd652a..a2f6d1d2 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -3,7 +3,7 @@ * made a selectio with on or several nodes */ -import React, { MouseEvent } from 'react'; +import React, { useMemo, useCallback, MouseEvent } from 'react'; import ReactDraggable, { DraggableData } from 'react-draggable'; import { useStoreState, useStoreActions } from '../../store/hooks'; @@ -14,9 +14,15 @@ export interface NodesSelectionProps { onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void; + onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; } -export default ({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop }: NodesSelectionProps) => { +export default ({ + onSelectionDragStart, + onSelectionDrag, + onSelectionDragStop, + onSelectionContextMenu, +}: NodesSelectionProps) => { const [tX, tY, tScale] = useStoreState((state) => state.transform); const selectedNodesBbox = useStoreState((state) => state.selectedNodesBbox); const selectedElements = useStoreState((state) => state.selectedElements); @@ -32,31 +38,30 @@ export default ({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop }: return null; } - const onStart = (event: MouseEvent) => { - const selectedNodes = selectedElements - ? selectedElements - .filter(isNode) - .map((selectedNode) => nodes.find((node) => node.id === selectedNode.id)! as Node) - : []; + const selectedNodes = useMemo( + () => + selectedElements + ? selectedElements + .filter(isNode) + .map((selectedNode) => nodes.find((node) => node.id === selectedNode.id)! as Node) + : [], + [selectedElements] + ); - if (onSelectionDragStart) { - onSelectionDragStart(event, selectedNodes); - } - }; - - const onDrag = (event: MouseEvent, data: DraggableData) => { - if (selectedElements) { - const selectedNodes = selectedElements ? selectedElements.filter(isNode) : []; + const onStart = useCallback( + (event: MouseEvent) => { + onSelectionDragStart?.(event, selectedNodes); + }, + [onSelectionDragStart, selectedNodes] + ); + const onDrag = useCallback( + (event: MouseEvent, data: DraggableData) => { if (onSelectionDrag) { - const selectionNodes = selectedNodes.map( - (selectedNode) => nodes.find((node) => node.id === selectedNode.id)! as Node - ); - - onSelectionDrag(event, selectionNodes); + onSelectionDrag(event, selectedNodes); } - selectedNodes.forEach((node) => { + selectedNodes?.forEach((node) => { updateNodePosDiff({ id: node.id, diff: { @@ -65,26 +70,47 @@ export default ({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop }: }, }); }); - } - }; + }, + [onSelectionDrag, selectedNodes, updateNodePosDiff] + ); - const onStop = (event: MouseEvent) => { - if (selectedElements && onSelectionDragStop) { + const onStop = useCallback( + (event: MouseEvent) => { + onSelectionDragStop?.(event, selectedNodes); + }, + [selectedNodes, onSelectionDragStop] + ); + + const onContextMenu = useCallback( + (event: MouseEvent) => { const selectedNodes = selectedElements ? selectedElements.filter(isNode).map((selectedNode) => nodes.find((node) => node.id === selectedNode.id)!) : []; - onSelectionDragStop(event, selectedNodes); - } - }; + onSelectionContextMenu?.(event, selectedNodes); + }, + [onSelectionContextMenu] + ); + + const style = useMemo( + () => ({ + transform: `translate(${tX}px,${tY}px) scale(${tScale})`, + }), + [tX, tY, tScale] + ); + + const innerStyle = useMemo( + () => ({ + width: selectedNodesBbox.width, + height: selectedNodesBbox.height, + top: selectedNodesBbox.y, + left: selectedNodesBbox.x, + }), + [selectedNodesBbox] + ); return ( -
+
onDrag(event as MouseEvent, data)} onStop={(event) => onStop(event as MouseEvent)} > -
+
); diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 88e2013c..1d806186 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -51,6 +51,7 @@ export interface GraphViewProps { onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void; + onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; selectionKeyCode: number; nodeTypes: NodeTypesType; edgeTypes: EdgeTypesType; @@ -93,6 +94,7 @@ const GraphView = ({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop, + onSelectionContextMenu, connectionLineType, connectionLineStyle, selectionKeyCode, @@ -292,6 +294,7 @@ const GraphView = ({ onSelectionDragStart={onSelectionDragStart} onSelectionDrag={onSelectionDrag} onSelectionDragStop={onSelectionDragStop} + onSelectionContextMenu={onSelectionContextMenu} /> )}
, 'on onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void; onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void; + onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; onPaneScroll?: (event?: WheelEvent) => void; onPaneClick?: (event: MouseEvent) => void; onPaneContextMenu?: (event: MouseEvent) => void; @@ -112,6 +113,7 @@ const ReactFlow = ({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop, + onSelectionContextMenu, connectionLineType, connectionLineStyle, deleteKeyCode, @@ -191,6 +193,7 @@ const ReactFlow = ({ onSelectionDragStart={onSelectionDragStart} onSelectionDrag={onSelectionDrag} onSelectionDragStop={onSelectionDragStop} + onSelectionContextMenu={onSelectionContextMenu} /> {onSelectionChange && } {children}