Merge pull request #219 from wbkd/develop
refactor(inactivemode): no pointer events for nodes and edges
This commit is contained in:
37
README.md
37
README.md
@@ -153,7 +153,40 @@ nodeTypes={{
|
||||
|
||||
You can now use type `special` for a node.
|
||||
The `default`, `input` and `output` types will be still available except you overwrite one of them.
|
||||
You can find an example of how to implement a custom node in the [custom node example](example/src/CustomNode).
|
||||
You can find an example of how to implement a custom node in the [custom node example](/example/src/CustomNode).
|
||||
|
||||
## Handle
|
||||
|
||||
We export a `Handle` component as a helper for your custom nodes:
|
||||
|
||||
```javascript
|
||||
import { Handle } from 'react-flow-renderer';
|
||||
|
||||
const targetHandleWithValidation = (
|
||||
<Handle
|
||||
type="target"
|
||||
position="left"
|
||||
isValidConnection={(connection) => connection.source === 'some-id'}
|
||||
onConnect={params => console.log('handle onConnect', params)}
|
||||
style={{ background: '#fff' }}
|
||||
/>
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
- `type`: 'source' | 'target'
|
||||
- `id`: string - you only need this when you have multiple source or target handles otherwise the node id is used
|
||||
- `position`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top' for type target, 'bottom' for type source
|
||||
- `onConnect`: function that gets triggered on connect
|
||||
- `isValidConnection`: function receives a connection `{ target: 'some-id', source: 'another-id' }` as param, returns a boolean - true by default
|
||||
- `style`: css properties
|
||||
|
||||
### Multiple Handles
|
||||
|
||||
If you need multiple source our target handles you can achieve this by creating a custom node. Normally you just use the id of a node for the `source` or `target` of an edge. If you have multiple source or target handles you need to pass an id to these handles. These ids get then added to the node id, so that you can connect to a specific handle. If you have a node with an id = `1` and a handle with an id = `a` you can connect to this handle by using the id = `1__a`.
|
||||
You can find an example of how to implement a custom node with multiple handles in the [custom node example](/example/src/CustomNode/ColorSelectorNode.js#L18-L29).
|
||||
|
||||
## Edges
|
||||
|
||||
@@ -281,7 +314,7 @@ const FlowWithControls = () => (
|
||||
|
||||
You can find all examples in the [example](example) folder or check out the live versions:
|
||||
|
||||
- [rich](https://react-flow.netlify.app/)
|
||||
- [overview](https://react-flow.netlify.app/)
|
||||
- [basic](https://react-flow.netlify.app/basic)
|
||||
- [custom node](https://react-flow.netlify.app/custom-node)
|
||||
- [horizontal](https://react-flow.netlify.app/horizontal)
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('Empty Flow Rendering', () => {
|
||||
});
|
||||
|
||||
it('uses lock view control', () => {
|
||||
cy.get('.react-flow__controls-interactive').click();
|
||||
cy.get('.react-flow__controls-interactive');
|
||||
});
|
||||
|
||||
it('renders an empty mini map', () => {
|
||||
|
||||
@@ -9,11 +9,13 @@ describe('Inactive Graph Rendering', () => {
|
||||
});
|
||||
|
||||
it('tries to select a node by click', () => {
|
||||
cy.get('.react-flow__node:first').click().should('have.not.class', 'selected');
|
||||
const pointerEvents = Cypress.$('.react-flow__node:first').css('pointer-events');
|
||||
expect(pointerEvents).to.equal('none');
|
||||
});
|
||||
|
||||
it('tries to select an edge by click', () => {
|
||||
cy.get('.react-flow__edge:first').click().should('have.not.class', 'selected');
|
||||
const pointerEvents = Cypress.$('.react-flow__edge:first').css('pointer-events');
|
||||
expect(pointerEvents).to.equal('none');
|
||||
});
|
||||
|
||||
it('tries to do a selection', () => {
|
||||
@@ -22,30 +24,6 @@ describe('Inactive Graph Rendering', () => {
|
||||
cy.get('body').type('{shift}', { release: true });
|
||||
});
|
||||
|
||||
it('tries to drag a node', () => {
|
||||
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 500, y: 25 }).then(($el) => {
|
||||
const styleAfterDrag = $el.css('transform');
|
||||
expect(styleBeforeDrag).to.equal(styleAfterDrag);
|
||||
});
|
||||
});
|
||||
|
||||
it('tries to connect nodes', () => {
|
||||
cy.get('.react-flow__node')
|
||||
.contains('Node 3')
|
||||
.find('.react-flow__handle.source')
|
||||
.trigger('mousedown', { which: 1 });
|
||||
|
||||
cy.get('.react-flow__node')
|
||||
.contains('Node 4')
|
||||
.find('.react-flow__handle.target')
|
||||
.trigger('mousemove')
|
||||
.trigger('mouseup', { force: true });
|
||||
|
||||
cy.get('.react-flow__edge').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('toggles interactive mode', () => {
|
||||
cy.get('.react-flow__interactive').click();
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ const initialElements = [
|
||||
},
|
||||
{
|
||||
id: '4', position: { x: 250, y: 200 },
|
||||
data: { label: <>You can find the docs on <a href="https://github.com/wbkd/react-flow" target="_blank">Github</a></> }
|
||||
data: { label: <>You can find the docs on <a href="https://github.com/wbkd/react-flow" target="_blank" rel="noopener noreferrer">Github</a></> }
|
||||
},
|
||||
{ id: '5', data: { label: <>Or check out the other <strong>examples</strong></> }, position: { x: 250, y: 300 } },
|
||||
{ id: '6', type: 'output', data: { label: <>An <strong>output node</strong></> }, position: { x: 100, y: 450 } },
|
||||
|
||||
@@ -37,6 +37,9 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
||||
...rest
|
||||
}: EdgeWrapperProps) => {
|
||||
const edgeClasses = cx('react-flow__edge', { selected, animated });
|
||||
const edgeGroupStyle: CSSProperties = {
|
||||
pointerEvents: isInteractive ? 'all' : 'none',
|
||||
};
|
||||
const onEdgeClick = (): void => {
|
||||
if (!isInteractive) {
|
||||
return;
|
||||
@@ -47,7 +50,7 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<g className={edgeClasses} onClick={onEdgeClick}>
|
||||
<g className={edgeClasses} onClick={onEdgeClick} style={edgeGroupStyle}>
|
||||
<EdgeComponent
|
||||
id={id}
|
||||
source={source}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState, memo, ComponentType } from 'react';
|
||||
import React, { useEffect, useRef, useState, memo, ComponentType, CSSProperties } from 'react';
|
||||
import { DraggableCore } from 'react-draggable';
|
||||
import cx from 'classnames';
|
||||
import { ResizeObserver } from 'resize-observer';
|
||||
@@ -101,9 +101,10 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
const [isDragging, setDragging] = useState(false);
|
||||
const position = { x: xPos, y: yPos };
|
||||
const nodeClasses = cx('react-flow__node', { selected });
|
||||
const nodeStyle = {
|
||||
const nodeStyle: CSSProperties = {
|
||||
zIndex: selected ? 10 : 3,
|
||||
transform: `translate(${xPos}px,${yPos}px)`,
|
||||
pointerEvents: isInteractive ? 'all' : 'none',
|
||||
};
|
||||
|
||||
const updateNode = (): void => {
|
||||
|
||||
@@ -100,20 +100,6 @@
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.is-interactive {
|
||||
.react-flow__node {
|
||||
cursor: grab;
|
||||
|
||||
&:hover > * {
|
||||
box-shadow: 0 1px 5px 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
cursor: crosshair;
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow__node {
|
||||
position: absolute;
|
||||
color: #222;
|
||||
@@ -123,10 +109,20 @@
|
||||
user-select: none;
|
||||
pointer-events: all;
|
||||
transform-origin: 0 0;
|
||||
cursor: grab;
|
||||
|
||||
&.selected > * {
|
||||
&.selected > *,
|
||||
&.selected:hover > * {
|
||||
box-shadow: 0 0 0 2px #555;
|
||||
}
|
||||
|
||||
&:hover > * {
|
||||
box-shadow: 0 1px 5px 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
cursor: crosshair;
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
|
||||
Reference in New Issue
Block a user