Last active
September 30, 2018 20:17
-
-
Save Saigesp/514a2c292c13ae53685c2f3e1971712e to your computer and use it in GitHub Desktop.
Flow GUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Premier</title> | |
</head> | |
<style> | |
body { | |
position: relative; | |
width: 100%; | |
margin: 0; | |
} | |
#board { | |
width: 100%; | |
height: 400px; | |
position: relative; | |
} | |
#board svg { | |
background: #e8e8e8; | |
} | |
/* Box */ | |
.box { | |
fill: #f5f5f5; | |
font-size: 14px; | |
font-family: sans-serif; | |
stroke: #CCC; | |
} | |
/* Pins */ | |
.pins .pin { | |
fill: white; | |
stroke: #cac9c9; | |
stroke-width: 2; | |
} | |
.pins .pin.to-link { | |
stroke: #ffa400; | |
} | |
.pins .pin.linkable { | |
stroke: #00b1ce; | |
} | |
</style> | |
<body> | |
<div id="board"></div> | |
</body> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
var oidcount = 0; | |
var data = {'nodes':[],'links':[]}; // Where to store data | |
// Creatables boxes | |
var boxes = [ // It will come from database | |
{ | |
'category': 'source', | |
'_id': 'misp', | |
'type': 'misp', | |
'pin_in':[ | |
], | |
'pin_out': [ | |
{'type': 'kke', 'oid': 'misp', 'soid': 'misp-'+oidcount+'-kke-0'}, | |
{'type': 'ioc', 'oid': 'misp', 'soid': 'misp-'+oidcount+'-ioc-0'}, | |
]}, | |
{ | |
'category': 'source', | |
'_id': 'delfos', | |
'type': 'delfos', | |
'pin_in':[ | |
], | |
'pin_out': [ | |
{'type': 'ioc', 'oid': 'delfos', 'soid': 'delf-'+oidcount+'-kke-0'}, | |
]}, | |
{ | |
'category': 'consumer', | |
'_id': 'consum', | |
'type': 'consum', | |
'pin_in':[ | |
{'type': 'ioc', 'oid': 'consum', 'soid': 'consum-'+oidcount+'-ioc-0'}, | |
{'type': 'nfc', 'oid': 'consum', 'soid': 'consum-'+oidcount+'-nfc-0'}, | |
{'type': 'hdp', 'oid': 'consum', 'soid': 'consum-'+oidcount+'-hdp-0'}, | |
], | |
'pin_out': [ | |
]}, | |
] | |
// Box properties | |
var boxprop = { // Maybe configurable | |
'width': 300, | |
'lineheight': 30, | |
'radius': 3, | |
'pin': { | |
'radius': 8, | |
'padding': 13, | |
'top': 4, | |
} | |
} | |
// Vars | |
var board = d3.select('#board'); | |
var width = parseInt(board.style('width'), 10), | |
height = parseInt(board.style('height'), 10); | |
var svg = board.append('svg') | |
.datum(data) | |
.attr('width', width) | |
.attr('height', height); | |
var box = svg.selectAll('.box') | |
.data(data['nodes']); | |
var link = svg.selectAll('.link') | |
.data(data['links']); | |
var graphline = d3.line() | |
.x(function(d, i) { | |
xbox = d3.selectAll('.box').filter(function(s){ return s.oid == d.oid; }).data()[0].x; // Box's X position | |
if (i==0) return xbox + boxprop.width + boxprop.pin.padding + boxprop.pin.radius; | |
if (i==1) return xbox + boxprop.width +60; | |
if (i==2) return xbox - 60; | |
if (i==3) return xbox - boxprop.pin.padding - boxprop.pin.radius; | |
}) | |
.y(function(d, i) { | |
var ind = 0, | |
boxdata = d3.selectAll('.box').filter(function(s){ return s.oid == d.oid; }).data(), | |
ybox = boxdata[0].y; | |
if(i <= 1) boxdata[0].pin_out.forEach(function(j,m){ if(j.soid==d.soid) ind = m; }); | |
else boxdata[0].pin_in.forEach(function(j,m){ if(j.soid==d.soid) ind = m; }); | |
return ybox + boxprop.pin.padding + boxprop.pin.top + (boxprop.lineheight*ind); | |
}) | |
.curve(d3.curveBasis); | |
function appendNode(d){ | |
node = {'pin_in':[],'pin_out':[], 'category': d.category, 'type': d.type, '_id': d._id }; | |
node.oid = d._id + '-' + oidcount; | |
d.pin_out.forEach(function(j,m){ | |
node.pin_out.push({'oid': node.oid, 'soid': node.oid +'-'+ j.type, 'type': j.type }); | |
}) | |
d.pin_in.forEach(function(j){ | |
node.pin_in.push({'oid': node.oid, 'soid': node.oid +'-'+ j.type, 'type': j.type }); | |
}) | |
data['nodes'].push(node) | |
updateDashboard(data); | |
oidcount += 1; | |
} | |
// External HTML | |
function createDashboardControls(){ | |
board.append('div') | |
.attr('class', 'buttons') | |
.selectAll('button') | |
.data(boxes) | |
.enter().append('button') | |
.attr('class', 'newbox') | |
.text(function(d){ return '+ '+d.type }) | |
.on('click', function(d){ | |
appendNode(d); | |
}); | |
} | |
// Update Splines | |
function updateSplines(data){ | |
datas = []; | |
data['links'].forEach(function(d,i){ | |
datas.push([ | |
{'oid': d.from.oid, 'soid': d.from.soid }, | |
{'oid': d.from.oid, 'soid': d.from.soid }, | |
{'oid': d.to.oid, 'soid': d.to.soid }, | |
{'oid': d.to.oid, 'soid': d.to.soid }, | |
]); | |
}) | |
d3.selectAll('.link').remove(); | |
var spline = svg.selectAll('.ĺink') | |
.data(datas).enter() | |
.append('path') | |
.attr('class',function(d){ | |
return 'link'; | |
}) | |
.attr('stroke', '#CCC') | |
.attr('stroke-width', 2) | |
.attr('fill', 'transparent') | |
.attr('d', graphline ) | |
} | |
// Update function | |
function updateDashboard(data){ | |
console.log('global data: ',data); | |
// Nodes | |
var tbox = svg.selectAll('.box').data(data['nodes']).enter().append('g') | |
.attr('id', function(d){ return d.oid; }) | |
.attr('class', 'wrap') | |
.call(d3.drag() | |
//.on("start", dragstarted) | |
.on("drag", dragged) | |
.on("end", dragended)) | |
.attr('transform', function(d){ | |
d.x = d.x == undefined ? (width-boxprop.width)/2 + d3.randomUniform(1, 80)(): d.x; | |
d.y = d.y == undefined ? height/2 + d3.randomUniform(1, 80)(): d.y; | |
return 'translate('+(d.x)+','+(d.y)+')'; | |
}); | |
tbox.append('rect') | |
.attr('class', 'box') | |
.attr('width', boxprop.width) | |
.attr('rx', boxprop.radius) | |
.attr('ry', boxprop.radius) | |
.attr('height', function(d){ | |
d.h = d3.max([d.pin_in.length, d.pin_out.length]) * 30; | |
return d.h; | |
}); | |
tbox.append('text') | |
.attr('x', boxprop.width/2) | |
.attr('y', function(d){ | |
return d.h/2 + 4; | |
}) | |
.attr('text-anchor', 'middle') | |
.text(function(d){ | |
return d.type + ' (' + d.category + ')'; | |
}); | |
var pins = tbox.append('g') | |
.attr('class', 'pins'); | |
var pins_out = pins.append('g') | |
.attr('transform', 'translate('+(boxprop.width+boxprop.pin.padding)+',0)') | |
.attr('class', 'pins__out'); | |
var pins_in = pins.append('g') | |
.attr('transform', 'translate('+(-boxprop.pin.padding)+',0)') | |
.attr('class', 'pins__in'); | |
pins_out.selectAll('.pin--out') | |
.data(function(d){return d['pin_out'];}) | |
.enter().append('circle') | |
.attr('class', 'pin pin--out') | |
.attr('r', boxprop.pin.radius) | |
.attr('cy', function(j,m){ | |
console.log((boxprop.lineheight/2)+(m*boxprop.lineheight) + boxprop.pin.top); | |
return (boxprop.lineheight/2)+(m*boxprop.lineheight) + boxprop.pin.top; | |
}) | |
.on('click', function(j){ | |
if(d3.select(this).classed('to-link')){ | |
d3.selectAll('.pin').classed('to-link', false); | |
d3.selectAll('.pin').classed('linkable', false); | |
}else{ | |
d3.selectAll('.pin').classed('to-link', false); | |
d3.selectAll('.pin').classed('linkable', false); | |
d3.select(this).classed('to-link', true); | |
d3.selectAll('.pin--in').filter(function(d){ return d.type == j.type }) | |
.classed('linkable', true) | |
.on('click', function(d){ | |
data['links'].push({ | |
'from': {'oid': j.oid, 'soid': j.soid}, | |
'to': {'oid': d.oid, 'soid': d.soid} | |
}); | |
updateSplines(data); | |
d3.selectAll('.pin').classed('to-link', false); | |
d3.selectAll('.pin').classed('linkable', false); | |
}); | |
} | |
}); | |
pins_in.selectAll('.pin--in') | |
.data(function(d){return d['pin_in'];}) | |
.enter().append('circle') | |
.attr('class', 'pin pin--in') | |
.attr('r', boxprop.pin.radius) | |
.attr('cy', function(j,m){ | |
return (boxprop.lineheight/2)+(m*boxprop.lineheight) + boxprop.pin.top; | |
}); | |
pins_in.selectAll('.label--out') | |
.data(function(d){return d['pin_out'];}) | |
.enter().append('text') | |
.attr('class', '.label .label--out') | |
.attr('text-anchor', 'end') | |
.attr('x', boxprop.width + 4) | |
.attr('font-size', '12px') | |
.attr('y', function(j,m){ | |
return (boxprop.lineheight/2)+(m*boxprop.lineheight) + boxprop.pin.top + 3; | |
}) | |
.text(function(j,m){ | |
return j.type; | |
}); | |
pins_in.selectAll('.label--in') | |
.data(function(d){return d['pin_in'];}) | |
.enter().append('text') | |
.attr('class', '.label .label--in') | |
.attr('text-anchor', 'start') | |
.attr('x', 20) | |
.attr('font-size', '12px') | |
.attr('y', function(j,m){ | |
return (boxprop.lineheight/2)+(m*boxprop.lineheight) + boxprop.pin.top + 3; | |
}) | |
.text(function(j,m){ | |
return j.type; | |
}); | |
updateSplines(data); | |
} | |
// Tools functions | |
function dragstarted(d) { | |
d3.selectAll('.pin').classed('to-link', false); | |
d3.selectAll('.pin').classed('linkable', false); | |
d3.select(this).raise().classed("dragging", true); | |
} | |
function dragged(d) { | |
d3.select(this).attr("transform", function(){ | |
d.x = d3.event.x; | |
d.y = d3.event.y; | |
return 'translate('+d.x+','+d.y+')' | |
}); | |
updateSplines(data); | |
} | |
function dragended(d) { | |
d3.select(this).classed("dragging", false); | |
} | |
function mousemove() { | |
var coordinates = [0, 0]; | |
coordinates = d3.mouse(this); | |
} | |
createDashboardControls(); | |
updateDashboard(data); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment