diff --git a/README.md b/README.md index 3b068061..b02e62e7 100644 --- a/README.md +++ b/README.md @@ -509,8 +509,8 @@ The React Flow wrapper has the className `react-flow`. If you want to change the ### Used Class Names * `.react-flow` - Outer container -* `.react-flow__renderer` - Inner container -* `.react-flow__zoompane` - Zoom & pan pane +* `.react-flow__renderer` - Flow container, used for zooming and panning +* `.react-flow__pane` - Background Pane for click and wheel events, previously `.react-flow__zoompane` * `.react-flow__selectionpane` - Selection pane * `.react-flow__selection` - User selection * `.react-flow__edges` - Edges wrapper diff --git a/cypress/integration/flow/basic.spec.js b/cypress/integration/flow/basic.spec.js index 27e27b56..2d39054f 100644 --- a/cypress/integration/flow/basic.spec.js +++ b/cypress/integration/flow/basic.spec.js @@ -119,7 +119,7 @@ describe('Basic Flow Rendering', () => { // for d3 we have to pass the window to the event // https://github.com/cypress-io/cypress/issues/3441 cy.window().then((win) => { - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('mousedown', 'topLeft', { which: 1, view: win }) .trigger('mousemove', 'bottomLeft') .trigger('mouseup', { force: true, view: win }) @@ -133,7 +133,7 @@ describe('Basic Flow Rendering', () => { it('zooms the pane', () => { const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('wheel', 'topLeft', { deltaY: -200 }) .then(() => { const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); diff --git a/cypress/integration/flow/controls.spec.js b/cypress/integration/flow/controls.spec.js index 3975fadb..98e0def9 100644 --- a/cypress/integration/flow/controls.spec.js +++ b/cypress/integration/flow/controls.spec.js @@ -36,7 +36,7 @@ describe('Controls Testing', () => { // for d3 we have to pass the window to the event // https://github.com/cypress-io/cypress/issues/3441 cy.window().then((win) => { - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('mousedown', 'topLeft', { which: 1, view: win }) .trigger('mousemove', 'bottomLeft') .trigger('mouseup', { force: true, view: win }) diff --git a/cypress/integration/flow/empty.spec.js b/cypress/integration/flow/empty.spec.js index 300dc850..52c32b16 100644 --- a/cypress/integration/flow/empty.spec.js +++ b/cypress/integration/flow/empty.spec.js @@ -16,6 +16,8 @@ describe('Empty Flow Rendering', () => { .trigger('mousedown', 'topLeft', { which: 1, force: true }) .trigger('mousemove', 'bottomLeft', { which: 1 }) .trigger('mouseup', 'bottomLeft', { force: true }); + + cy.get('body').type('{shift}', { release: true }); }); it('renders an empty mini map', () => { diff --git a/cypress/integration/flow/interaction.spec.js b/cypress/integration/flow/interaction.spec.js index 8846aade..5810c485 100644 --- a/cypress/integration/flow/interaction.spec.js +++ b/cypress/integration/flow/interaction.spec.js @@ -52,7 +52,7 @@ describe('Interaction Flow Rendering', () => { it('tries to zoom by scroll', () => { const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .dblclick() .then(() => { const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); @@ -63,7 +63,7 @@ describe('Interaction Flow Rendering', () => { it('tries to zoom by double click', () => { const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('wheel', 'topLeft', { deltaY: -200 }) .then(() => { const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); @@ -122,7 +122,7 @@ describe('Interaction Flow Rendering', () => { it('zooms by scroll', () => { const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('wheel', 'topLeft', { deltaY: 200 }) .then(() => { const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); @@ -137,7 +137,7 @@ describe('Interaction Flow Rendering', () => { it('zooms by double click', () => { const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .dblclick() .then(() => { const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); diff --git a/cypress/integration/flow/minimap.spec.js b/cypress/integration/flow/minimap.spec.js index 1ce5bb5a..c9235ea8 100644 --- a/cypress/integration/flow/minimap.spec.js +++ b/cypress/integration/flow/minimap.spec.js @@ -19,7 +19,7 @@ describe('Minimap Testing', () => { const viewBoxBeforeZoom = Cypress.$('.react-flow__minimap').attr('viewBox'); const maskPathBeforeZoom = Cypress.$('.react-flow__minimap-mask').attr('d'); - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('wheel', 'topLeft', { deltaY: -200 }) .then(() => { const viewBoxAfterZoom = Cypress.$('.react-flow__minimap').attr('viewBox'); @@ -53,7 +53,7 @@ describe('Minimap Testing', () => { // for d3 we have to pass the window to the event // https://github.com/cypress-io/cypress/issues/3441 cy.window().then((win) => { - cy.get('.react-flow__zoompane') + cy.get('.react-flow__renderer') .trigger('mousedown', 'topLeft', { which: 1, view: win }) .trigger('mousemove', 'bottomLeft') .trigger('mouseup', { force: true, view: win }) diff --git a/example/src/Overview/index.js b/example/src/Overview/index.js index d5703ead..1f1f230d 100644 --- a/example/src/Overview/index.js +++ b/example/src/Overview/index.js @@ -4,6 +4,9 @@ import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background, isNo const onNodeDragStart = (event, node) => console.log('drag start', node); const onNodeDragStop = (event, node) => console.log('drag stop', node); +const onPaneClick = (event) => console.log('pane click', event); +const onPaneScroll = (event) => console.log('pane scroll', event); +const onPaneContextMenu = (event) => console.log('pane context menu', event); 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); @@ -125,6 +128,9 @@ const OverviewFlow = () => { onElementClick={onElementClick} onElementsRemove={onElementsRemove} onConnect={onConnect} + onPaneClick={onPaneClick} + onPaneScroll={onPaneScroll} + onPaneContextMenu={onPaneContextMenu} onNodeDragStart={onNodeDragStart} onNodeDragStop={onNodeDragStop} onSelectionDragStart={onSelectionDragStart} diff --git a/src/components/UserSelection/index.tsx b/src/components/UserSelection/index.tsx index 4cb0238a..14a8a13a 100644 --- a/src/components/UserSelection/index.tsx +++ b/src/components/UserSelection/index.tsx @@ -12,7 +12,7 @@ type UserSelectionProps = { }; function getMousePosition(event: React.MouseEvent): XYPosition | void { - const reactFlowNode = document.querySelector('.react-flow'); + const reactFlowNode = (event.target as Element).closest('.react-flow'); if (!reactFlowNode) { return; } @@ -51,6 +51,7 @@ export default memo(({ selectionKeyPressed }: UserSelectionProps) => { const setUserSelection = useStoreActions((actions) => actions.setUserSelection); const updateUserSelection = useStoreActions((actions) => actions.updateUserSelection); const unsetUserSelection = useStoreActions((actions) => actions.unsetUserSelection); + const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection); const renderUserSelectionPane = selectionActive || selectionKeyPressed; if (!elementsSelectable || !renderUserSelectionPane) { @@ -81,12 +82,18 @@ export default memo(({ selectionKeyPressed }: UserSelectionProps) => { const onMouseUp = () => unsetUserSelection(); + const onMouseLeave = () => { + unsetUserSelection(); + unsetNodesSelection(); + }; + return (