ReactCytoscape.jsx 3.3 KB
Newer Older
Patrik Meijer's avatar
Patrik Meijer committed
1
2
3
4
5
6
7
8
9
10
/**
 * React wrapper around cytoscape. If scalability ever becomes and issue - consider implementing a fine-grained
 * element update.
 * @author pmeijer / https://github.com/pmeijer
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import cytoscape from 'cytoscape';
Patrik Meijer's avatar
Patrik Meijer committed
11
import edgeConnections from 'cytoscape-edge-connections';
Patrik Meijer's avatar
Patrik Meijer committed
12
13
14
15
import coseBilkent from 'cytoscape-cose-bilkent';
import dagre from 'cytoscape-dagre';


Patrik Meijer's avatar
Patrik Meijer committed
16
cytoscape.use(edgeConnections);
Patrik Meijer's avatar
Patrik Meijer committed
17
18
19
20
21
22
23
cytoscape.use(coseBilkent);
cytoscape.use(dagre);

export default class ReactCytoscape extends Component {
    static propTypes = {
        cyRef: PropTypes.func.isRequired,
        elements: PropTypes.object.isRequired,
Patrik Meijer's avatar
Patrik Meijer committed
24
        hyperEdges: PropTypes.arrayOf(PropTypes.object).isRequired,
Patrik Meijer's avatar
Patrik Meijer committed
25
26
27
28
29
30
31
32
33
34
35
36
        width: PropTypes.number.isRequired,
        height: PropTypes.number.isRequired,
        containerID: PropTypes.string,
        layout: PropTypes.object,
        style: PropTypes.arrayOf(PropTypes.object),
        cytoscapeOptions: PropTypes.object,
    };

    static defaultProps = {
        containerID: 'cy',
        layout: {name: 'cola'},
        cytoscapeOptions: {},
37
        style: [],
Patrik Meijer's avatar
Patrik Meijer committed
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    };

    componentDidMount() {
        const opts = Object.assign({
            container: this.container,

            boxSelectionEnabled: false,
            autounselectify: true,

            style: this.props.style,
            layout: this.props.layout,
        }, this.props.cytoscapeOptions);

        this.cy = cytoscape(opts);
Patrik Meijer's avatar
Patrik Meijer committed
52
        this.edgeConnHandler = this.cy.edgeConnections();
Patrik Meijer's avatar
Patrik Meijer committed
53
        this.cy.json({elements: this.props.elements});
Patrik Meijer's avatar
Patrik Meijer committed
54
        this.edgeConnHandler.addEdges(this.props.hyperEdges);
Patrik Meijer's avatar
Patrik Meijer committed
55
56
57
58
59
60
61
62
63
64
65

        if (this.props.cyRef) {
            this.props.cyRef(this.cy, this.container);
        }

        return this.cy;
    }

    componentWillReceiveProps(nextProps) {
        const {
            elements,
Patrik Meijer's avatar
Patrik Meijer committed
66
            hyperEdges,
Patrik Meijer's avatar
Patrik Meijer committed
67
68
69
70
71
            width,
            height,
            style,
        } = this.props;

72
73
74
75
        if (JSON.stringify(style) !== JSON.stringify(nextProps.style)) {
            this.cy.style(nextProps.style);
        }

Patrik Meijer's avatar
Patrik Meijer committed
76
        // TODO: Consider making more fine-grained updates here instead.
77
78
        if (JSON.stringify(elements) !== JSON.stringify(nextProps.elements)
            || JSON.stringify(hyperEdges) !== JSON.stringify(nextProps.hyperEdges)) {
Patrik Meijer's avatar
Patrik Meijer committed
79
            this.cy.json({elements: nextProps.elements});
Patrik Meijer's avatar
Patrik Meijer committed
80
            this.edgeConnHandler.addEdges(nextProps.hyperEdges);
Patrik Meijer's avatar
Patrik Meijer committed
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
        }

        if (width !== nextProps.width || height !== nextProps.height) {
            this.cy.resize();
        }

        if (elements.nodes && elements.nodes.length === 0 &&
            nextProps.elements.nodes.length > 0) {
            // Initial elements received -> fit graph to canvas.
            this.cy.fit();
        }
    }

    componentWillUnmount() {
        if (this.cy) {
            this.cy.destroy();
        }
    }

    render() {
        const styleContainer = Object.assign({
            height: '100%',
            width: '100%',
            display: 'block',
        }, ...this.props.style);
        return (
            <div
                className="graph"
                id={this.props.containerID}
                ref={(elt) => {
                    this.container = elt;
                }}
                style={styleContainer}
            />);
    }
}