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
|
import React from 'react'
import './App.css'
import Graph from './Graph'
import GraphSettingsMenu from './GraphSettingsMenu'
import GraphInfoMenu from './GraphInfoMenu'
import _ from 'underscore'
import { nodeProperty, edgeProperty } from './requests'
import * as constants from './constants'
var emptyState = {
selectedSchema: '', // currently selected schema
graph: {
nodeNames: [], // names of nodes
edges: [], // edges (each edge has source, target, type and a list of key value properties for tooltip)
paths: [] // all paths between start node and end node
},
displayedProperties: [], // properties of currently thing (edge or node)
nodeStates: {}, // possible states CLICKED - the currently selected node PATH - currently displayed path
pathIndex: 0, // array index to paths i.e. which path from paths is currently displayed
selectedEdge: { source: 'none', target: 'none' } // defines currently selected edge like like a js object - {source: "pserver", target: "vserver"}
}
class App extends React.Component {
constructor (props, context) {
super(props, context)
this.graphdata = this.graphdata.bind(this)
this.changePaths = this.changePaths.bind(this)
this.loadNodeProperties = this.loadNodeProperties.bind(this)
this.loadEdgeProperties = this.loadEdgeProperties.bind(this)
this.computeNodeStatesFromPath = this.computeNodeStatesFromPath.bind(this)
this.computeNodeStates = this.computeNodeStates.bind(this)
this.state = emptyState
}
loadNodeProperties (nodeName) {
var s = this.state
fetch(nodeProperty(s.selectedSchema, nodeName))
.then(response => response.json())
.then(p => {
s['displayedProperties'] = p
s['nodeStates'] = this.computeNodeStates(s['pathIndex'])
// select node
s['nodeStates'][nodeName] = constants.CLICKED
// unselect edge
s['selectedEdge']['source'] = ''
s['selectedEdge']['target'] = ''
this.setState(s)
})
}
loadEdgeProperties (source, target) {
var s = this.state
fetch(edgeProperty(s.selectedSchema, source, target))
.then(response => response.json())
.then(p => {
s['displayedProperties'] = p
// select edge
s['selectedEdge']['source'] = source
s['selectedEdge']['target'] = target
// unselect node
s['nodeStates'] = this.computeNodeStates(s['pathIndex'])
this.setState(s)
})
}
graphdata (data, selectedGraphSchema, graphFingerprint) {
var s = this.state
s['selectedSchema'] = selectedGraphSchema
s['graphFingerprint'] = graphFingerprint
s['graph'] = data
// TODO this should be handled more gracefully ...
if (_.isEmpty(data.edges)) {
alert('The graph has no edges, nothing to display')
}
s['displayedProperties'] = data.startNodeProperties
if (_.isArray(data.paths) && !_.isEmpty(data.paths)) {
s['paths'] = data.paths
s['nodeStates'] = this.computeNodeStatesFromPath(data.paths[0])
s['pathIndex'] = 0
} else {
s['paths'] = []
s['nodeStates'] = {}
}
this.setState(s)
return data
}
computeNodeStatesFromPath (path) {
return _.reduce(path, (acc, node) => {
acc[node.id] = constants.PATH
return acc
}, {})
}
computeNodeStates (pathIndex) {
return this.computeNodeStatesFromPath(this.state.paths[pathIndex])
}
changePaths (pathIndex, selectedNode) {
var s = this.state
s['pathIndex'] = pathIndex
this.setState(s)
this.loadNodeProperties(selectedNode)
}
render () {
let n = _.invert(this.state.nodeStates)[constants.CLICKED]
let selectedNode = _.isUndefined(n) ? '' : n
return (
<div className="App">
<GraphSettingsMenu graphData={this.graphdata} nodePropsLoader={this.loadNodeProperties} selectedNode={selectedNode}/>
<div className='graph-area'>
<Graph graphFingerprint={this.state.graphFingerprint} edgePropsLoader={this.loadEdgeProperties} selectedEdge={this.state.selectedEdge} nodes={this.state.graph.nodeNames} edges={this.state.graph.edges} nodeStates={this.state.nodeStates} nodePropsLoader={this.loadNodeProperties}/>
</div>
<GraphInfoMenu pathCallback={this.changePaths} paths={ this.state.graph.paths } nodeProperties={ this.state.displayedProperties} />
</div>
)
}
}
export default App
|