process flow
d3 visualisation of a process flow
HTML
<div id="chart"></div>
CSS
.action {
fill: #ccc;
stroke: #000;
stroke-width: 1.5px;
cursor: pointer;
}
.action.done {
fill: #cfc;
stroke: #0b0;
}
.action.active {
stroke: #bb0;
fill: #ffc;
filter: url(#active-glow);
}
.link {
stroke: #000;
stroke-width: 1.5px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 12px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
JavaScript
var process = [
['submit', [], 'done', 'Application submitted'],
['MS approve', ['submit'], 'active', 'Application verification'],
['RS approve', ['submit'], 'done', 'Resource approval'],
['invoice', ['MS approve', 'RS approve'], 'pending', 'Invoice issued'],
['payment', ['invoice'], 'pending', 'Payment received'],
['allocate', ['payment'], 'pending', 'Allocation made'],
['infopack', ['payment'], 'pending', 'Welcome letter sent'],
['complete', ['infopack', 'allocate'], 'pending', 'Application complete']
];
var flow = processFlow();
d3.select("#chart").datum(process).call(flow);
function processFlow() {
// TODO: sort each column to minimise vertical cross-over
// TODO: auto-height option based on a given ygap
// TODO: fix ygap for entire chart, offset from centre line
var width = 400,
height = 75,
margins = {
top: 20,
right: 20,
bottom: 20,
left: 20
},
xsize = width - margins.right - margins.left,
ysize = (height - margins.top - margins.bottom);
function my(selection) {
selection.each(function (data) {
var actions = {},
columns = [],
deps = [],
nodes = [],
maxHeight = 0;
// Convert a list of steps into columns
data.forEach(function (d) {
var action = {
id: d[0],
deps: d[1].map(function (d) {
return actions[d];
}),
status: d[2]
};
action.column = d3.max(action.deps, function (d) {
return d.column + 1;
});
if (action.column === undefined) {
action.column = 0;
}
actions[action.id] = action;
if (columns[action.column] === undefined) {
...