const { map, values, flatten, merge, indexBy, } = require('ramda'); const React = require('react'); const r = require('r-dom'); const { connect } = require('react-redux'); const { jsPlumb, } = require('jsplumb'); const key = pao => `${pao.type}-${pao.index}`; class Graph extends React.Component { constructor(props) { super(props); this.container = React.createRef(); this.connectionToPao = new WeakMap(); } componentDidMount() { this.jsPlumb = jsPlumb.getInstance({ Container: this.container.current, }); } _connectSinkInput(sinkInput) { const connection = this.jsPlumb.connect({ source: `client-${sinkInput.info.clientIndex}`, target: `sink-${sinkInput.info.sinkIndex}`, anchor: 'Continuous', overlays: [ [ 'Arrow', { location: -1 } ] ], }); this.connectionToPao.set(connection, sinkInput); } _connectSourceOutput(sourceOutput) { const connection = this.jsPlumb.connect({ source: `source-${sourceOutput.info.sourceIndex}`, target: `client-${sourceOutput.info.clientIndex}`, anchor: 'Continuous', overlays: [ [ 'Arrow', { location: -1 } ] ], }); this.connectionToPao.set(connection, sourceOutput); } _connectByType(pao) { if (pao.type === 'sinkInput') { this._connectSinkInput(pao); } else { this._connectSourceOutput(pao); } } componentDidUpdate() { this.jsPlumb.batch(() => { const propsPaos = merge( indexBy(key, values(this.props.sinkInputs)), indexBy(key, values(this.props.sourceOutputs)), ); this.jsPlumb.getAllConnections().forEach(connection => { const connectionPao = this.connectionToPao.get(connection); const k = key(connectionPao); const propsPao = propsPaos[k]; if (!propsPao) { this.jsPlumb.deleteConnection(connection); } else if (propsPao === connectionPao) { // Noop } else if (propsPao.info) { this.jsPlumb.deleteConnection(connection); this._connectByType(propsPao); } delete propsPaos[k]; }); values(propsPaos).forEach(propsPao => { if (propsPao.info) { this._connectByType(propsPao); } }); }); } render() { return r.div({ ref: this.container, style: { position: 'relative' }, }, flatten( map(paos => map(pao => r.div({ id: key(pao), key: key(pao), className: 'jtk-node', style: { border: '1px solid black', userSelect: 'none', cursor: 'default', }, ref: el => { if (el) { this.jsPlumb.draggable(el, {}); /// this.jsPlumb.addEndpoint(); } }, }, [ key(pao), ]), values(paos)), [ this.props.sinks, this.props.sources, this.props.clients, ]), )); } } module.exports = connect( state => state.pulse, )(Graph);