187 lines
4.3 KiB
JavaScript
187 lines
4.3 KiB
JavaScript
import _ from 'lodash'
|
|
import { layout } from 'dagre-layout'
|
|
|
|
import positionNodes from './position-nodes'
|
|
import positionEdgeLabels from './position-edge-labels'
|
|
import positionClusters from './position-clusters'
|
|
import createNodes from './create-nodes'
|
|
import createClusters from './create-clusters'
|
|
import createEdgeLabels from './create-edge-labels'
|
|
import createEdgePaths from './create-edge-paths'
|
|
import shapes from './shapes'
|
|
import arrows from './arrows'
|
|
|
|
// This design is based on http://bost.ocks.org/mike/chart/.
|
|
function render () {
|
|
let _createNodes = createNodes
|
|
let _createClusters = createClusters
|
|
let _createEdgeLabels = createEdgeLabels
|
|
let _createEdgePaths = createEdgePaths
|
|
let _shapes = shapes
|
|
let _arrows = arrows
|
|
|
|
const fn = function (svg, g) {
|
|
preProcessGraph(g)
|
|
|
|
const outputGroup = createOrSelectGroup(svg, 'output')
|
|
const clustersGroup = createOrSelectGroup(outputGroup, 'clusters')
|
|
const edgePathsGroup = createOrSelectGroup(outputGroup, 'edgePaths')
|
|
const edgeLabels = _createEdgeLabels(createOrSelectGroup(outputGroup, 'edgeLabels'), g)
|
|
const nodes = _createNodes(createOrSelectGroup(outputGroup, 'nodes'), g, _shapes)
|
|
|
|
layout(g)
|
|
|
|
positionNodes(nodes, g)
|
|
positionEdgeLabels(edgeLabels, g)
|
|
_createEdgePaths(edgePathsGroup, g, _arrows)
|
|
|
|
const clusters = _createClusters(clustersGroup, g)
|
|
positionClusters(clusters, g)
|
|
|
|
postProcessGraph(g)
|
|
}
|
|
|
|
fn.createNodes = function (value) {
|
|
if (!arguments.length) {
|
|
return _createNodes
|
|
}
|
|
_createNodes = value
|
|
return fn
|
|
}
|
|
|
|
fn.createClusters = function (value) {
|
|
if (!arguments.length) {
|
|
return _createClusters
|
|
}
|
|
_createClusters = value
|
|
return fn
|
|
}
|
|
|
|
fn.createEdgeLabels = function (value) {
|
|
if (!arguments.length) {
|
|
return _createEdgeLabels
|
|
}
|
|
_createEdgeLabels = value
|
|
return fn
|
|
}
|
|
|
|
fn.createEdgePaths = function (value) {
|
|
if (!arguments.length) {
|
|
return _createEdgePaths
|
|
}
|
|
_createEdgePaths = value
|
|
return fn
|
|
}
|
|
|
|
fn.shapes = function (value) {
|
|
if (!arguments.length) {
|
|
return _shapes
|
|
}
|
|
_shapes = value
|
|
return fn
|
|
}
|
|
|
|
fn.arrows = function (value) {
|
|
if (!arguments.length) {
|
|
return _arrows
|
|
}
|
|
_arrows = value
|
|
return fn
|
|
}
|
|
|
|
return fn
|
|
}
|
|
|
|
const NODE_DEFAULT_ATTRS = {
|
|
paddingLeft: 10,
|
|
paddingRight: 10,
|
|
paddingTop: 10,
|
|
paddingBottom: 10,
|
|
rx: 0,
|
|
ry: 0,
|
|
shape: 'rect'
|
|
}
|
|
|
|
const EDGE_DEFAULT_ATTRS = {
|
|
arrowhead: 'normal',
|
|
lineInterpolate: 'linear'
|
|
}
|
|
|
|
function preProcessGraph (g) {
|
|
g.nodes().forEach(function (v) {
|
|
const node = g.node(v)
|
|
if (!_.has(node, 'label') && !g.children(v).length) { node.label = v }
|
|
|
|
if (_.has(node, 'paddingX')) {
|
|
_.defaults(node, {
|
|
paddingLeft: node.paddingX,
|
|
paddingRight: node.paddingX
|
|
})
|
|
}
|
|
|
|
if (_.has(node, 'paddingY')) {
|
|
_.defaults(node, {
|
|
paddingTop: node.paddingY,
|
|
paddingBottom: node.paddingY
|
|
})
|
|
}
|
|
|
|
if (_.has(node, 'padding')) {
|
|
_.defaults(node, {
|
|
paddingLeft: node.padding,
|
|
paddingRight: node.padding,
|
|
paddingTop: node.padding,
|
|
paddingBottom: node.padding
|
|
})
|
|
}
|
|
|
|
_.defaults(node, NODE_DEFAULT_ATTRS)
|
|
|
|
_.each(['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'], function (k) {
|
|
node[k] = Number(node[k])
|
|
})
|
|
|
|
// Save dimensions for restore during post-processing
|
|
if (_.has(node, 'width')) { node._prevWidth = node.width }
|
|
if (_.has(node, 'height')) { node._prevHeight = node.height }
|
|
})
|
|
|
|
g.edges().forEach(function (e) {
|
|
const edge = g.edge(e)
|
|
if (!_.has(edge, 'label')) { edge.label = '' }
|
|
_.defaults(edge, EDGE_DEFAULT_ATTRS)
|
|
})
|
|
}
|
|
|
|
function postProcessGraph (g) {
|
|
_.each(g.nodes(), function (v) {
|
|
const node = g.node(v)
|
|
|
|
// Restore original dimensions
|
|
if (_.has(node, '_prevWidth')) {
|
|
node.width = node._prevWidth
|
|
} else {
|
|
delete node.width
|
|
}
|
|
|
|
if (_.has(node, '_prevHeight')) {
|
|
node.height = node._prevHeight
|
|
} else {
|
|
delete node.height
|
|
}
|
|
|
|
delete node._prevWidth
|
|
delete node._prevHeight
|
|
})
|
|
}
|
|
|
|
function createOrSelectGroup (root, name) {
|
|
let selection = root.select('g.' + name)
|
|
if (selection.empty()) {
|
|
selection = root.append('g').attr('class', name)
|
|
}
|
|
return selection
|
|
}
|
|
|
|
export default render
|