.node {
fill: #000;
}
.cursor {
fill: green;
stroke: brown;
pointer-events: none;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
#licensing {
fill: green;
}
.link.licensing {
stroke: green;
}
.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: green;
stroke: red;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var width = 500, height = 300;
var links = [{source:"simulator",target:"monitor" ,type:"resolved"} , {source:"web",target:"monitor" ,type:"resolved"} ];
var nodes = [ {"id":"monitor", "grp":"system"}, {"id":"simulator", "grp":"system"}, {id:"web", grp:"client"}];
function reset() {
}
function contextMenu() {
var height,
width,
margin = 0.1, // fraction of width
items = [],
rescale = false,
style = {
'rect': {
'mouseout': {
'fill': 'rgb(244,244,244)',
'stroke': 'white',
'stroke-width': '1px'
},
'mouseover': {
'fill': 'rgb(200,200,200)'
}
},
'text': {
'fill': 'steelblue',
'font-size': '13'
}
};
function menu(x, y) {
d3.select('.context-menu').remove();
scaleItems();
// Draw the menu
d3.select('svg')
.append('g').attr('class', 'context-menu')
.selectAll('tmp')
.data(items).enter()
.append('g').attr('class', 'menu-entry')
.style({'cursor': 'pointer'})
.on('mouseover', function(){
d3.select(this).select('rect').style(style.rect.mouseover) })
.on('mouseout', function(){
d3.select(this).select('rect').style(style.rect.mouseout) });
d3.selectAll('.menu-entry')
.append('rect')
.attr('x', x)
.attr('y', function(d, i){ return y + (i * height); })
.attr('width', width)
.attr('height', height)
.style(style.rect.mouseout);
d3.selectAll('.menu-entry')
.append('text')
.text(function(d){ return d; })
.attr('x', x)
.attr('y', function(d, i){ return y + (i * height); })
.attr('dy', height - margin / 2)
.attr('dx', margin)
.style(style.text);
// Other interactions
d3.select('body')
.on('click', function() {
d3.select('.context-menu').remove();
});
}
menu.items = function(e) {
if (!arguments.length) return items;
for (i in arguments) items.push(arguments[i]);
rescale = true;
return menu;
}
// Automatically set width, height, and margin;
function scaleItems() {
if (rescale) {
d3.select('svg').selectAll('tmp')
.data(items).enter()
.append('text')
.text(function(d){ return d; })
.style(style.text)
.attr('x', -1000)
.attr('y', -1000)
.attr('class', 'tmp');
var z = d3.selectAll('.tmp')[0]
.map(function(x){ return x.getBBox(); });
width = d3.max(z.map(function(x){ return x.width; }));
margin = margin * width;
width = width + 2 * margin;
height = d3.max(z.map(function(x){ return x.height + margin / 2; }));
// cleanup
d3.selectAll('.tmp').remove();
rescale = false;
}
}
return menu;
}
var width = 400,
height = 200,
radius = 8;
var map = {}
nodes.forEach(function(d,i){
map[d.id] = i;
})
links.forEach(function(d) {
d.source = map[d.source];
d.target = map[d.target];
})
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(50)
.charge(-200)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var menu = contextMenu().items('first item', 'second option', 'whatever, man');
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 6)
.call(force.drag)
.on('contextmenu', function(d){
d3.event.preventDefault();
var self = d3.select(this);
var n1=(self[0])[0];
console.log(d.id);
menu(d3.mouse(svg.node())[0], d3.mouse(svg.node())[1]);
});
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.id; });
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
function mousedownNode(d, i) {
nodes.splice(i, 1);
links = links.filter(function(l) {
return l.source !== d && l.target !== d;
});
d3.event.stopPropagation();
refresh();
}
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", linkArc);
circle.attr("transform", transform);
text.attr("transform", transform);
}
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
</script>