-
Notifications
You must be signed in to change notification settings - Fork 177
/
DiagramNode.js
167 lines (156 loc) · 5.19 KB
/
DiagramNode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import React, { useMemo, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import getDiagramNodeStyle from './getDiagramNodeStyle';
import { usePortRegistration, useNodeRegistration } from '../../shared/internal_hooks/useContextRegistration';
import { PortType } from '../../shared/Types';
import portGenerator from './portGenerator';
import useDrag from '../../shared/internal_hooks/useDrag';
import useNodeUnregistration from '../../shared/internal_hooks/useNodeUnregistration';
/**
* A Diagram Node component displays a single diagram node, handles the drag n drop business logic and fires the
* related callback. Displays input and output ports if existing and takes care of firing the `onPortRegister` callback
* when a port is ready (aka rendered),
*/
const DiagramNode = (props) => {
const {
id, content, coordinates, type, inputs, outputs, data, onPositionChange, onPortRegister, onNodeRemove,
onDragNewSegment, onMount, onSegmentFail, onSegmentConnect, render, className, disableDrag,
} = props;
const registerPort = usePortRegistration(inputs, outputs, onPortRegister); // get the port registration method
const { ref, onDragStart, onDrag } = useDrag({ throttleBy: 14 }); // get the drag n drop methods
const dragStartPoint = useRef(coordinates); // keeps the drag start point in a persistent reference
if (!disableDrag) {
// when drag starts, save the starting coordinates into the `dragStartPoint` ref
onDragStart(() => {
dragStartPoint.current = coordinates;
});
// whilst dragging calculates the next coordinates and perform the `onPositionChange` callback
onDrag((event, info) => {
if (onPositionChange) {
event.stopImmediatePropagation();
event.stopPropagation();
const nextCoords = [
dragStartPoint.current[0] - info.offset[0],
dragStartPoint.current[1] - info.offset[1],
];
onPositionChange(id, nextCoords);
}
});
}
// on component unmount, remove its references
useNodeUnregistration(onNodeRemove, inputs, outputs, id);
// perform the onMount callback when the node is allowed to register
useNodeRegistration(ref, onMount, id);
const classList = useMemo(() => classNames('bi bi-diagram-node', {
[`bi-diagram-node-${type}`]: !!type && !render,
}, className), [type, className]);
// generate ports
const options = { registerPort, onDragNewSegment, onSegmentFail, onSegmentConnect };
const InputPorts = inputs.map(portGenerator(options, 'input'));
const OutputPorts = outputs.map(portGenerator(options, 'output'));
const customRenderProps = { id, render, content, type, inputs: InputPorts, outputs: OutputPorts, data, className };
return (
<div className={classList} ref={ref} style={getDiagramNodeStyle(coordinates, disableDrag)}>
{render && typeof render === 'function' && render(customRenderProps)}
{!render && (
<>
{content}
<div className="bi-port-wrapper">
<div className="bi-input-ports">
{InputPorts}
</div>
<div className="bi-output-ports">
{OutputPorts}
</div>
</div>
</>
)}
</div>
);
};
DiagramNode.propTypes = {
/**
* The diagram node id
*/
id: PropTypes.oneOfType([PropTypes.string]).isRequired,
/**
* The diagram current coordinates, relative to the container
*/
coordinates: PropTypes.arrayOf(PropTypes.number).isRequired,
/**
* The diagram content
*/
content: PropTypes.oneOfType([PropTypes.elementType, PropTypes.node]),
/**
* An array of input ports
*/
inputs: PropTypes.arrayOf(PortType),
/**
* An array of output ports
*/
outputs: PropTypes.arrayOf(PortType),
/**
* The node type
*/
type: PropTypes.oneOf(['default']),
/**
* An object to possibly keep data between renders
*/
data: PropTypes.shape({}),
/**
* Defines a custom render function
*/
render: PropTypes.func,
/**
* The callback to be fired when position changes
*/
onPositionChange: PropTypes.func,
/**
* The callback to be fired when a new diagram is mounted
*/
onMount: PropTypes.func,
/**
* The callback to be fired when a new port is settled
*/
onPortRegister: PropTypes.func,
/**
* The callback to be fired when component unmount
*/
onNodeRemove: PropTypes.func,
/**
* The callback to be fired when dragging a new segment from one of the node's port
*/
onDragNewSegment: PropTypes.func,
/**
* The callback to be fired when a new segment fails to connect
*/
onSegmentFail: PropTypes.func,
/**
* The callback to be fired when a new segment connects to a port
*/
onSegmentConnect: PropTypes.func,
/**
* The possible className
*/
className: PropTypes.string,
disableDrag: PropTypes.bool,
};
DiagramNode.defaultProps = {
type: 'default',
content: '',
inputs: [],
outputs: [],
data: {},
onPositionChange: undefined,
render: undefined,
onMount: undefined,
onPortRegister: undefined,
onNodeRemove: undefined,
onDragNewSegment: undefined,
onSegmentFail: undefined,
onSegmentConnect: undefined,
className: '',
disableDrag: false,
};
export default React.memo(DiagramNode);