badge position for nodes - cytoscape.js

I'm trying to add badges to my cytoscape.js nodes. Badges are HTML elements. I'm using bootstrap badges
Here are elements with badges. (the colors of the badges are irrelevant)
When I zoom out, the position of the badges is not set correctly. They go down and right a bit. WHY IS THAT?
Here is my code to set the positions. I but badges to to top left of the node. But I remove width of the HTML element to make it look like inside the node
let z1 = cy.zoom() / 2; // badges look too big with normal size so I downscale them
// e is cytoscape.js element, a node
const p = e.renderedPosition();
const eW = e.renderedWidth() / 2;
const eH = e.renderedHeight() / 2;
// div is an HTML element which is the badge
const w = div.clientWidth;
div.style.transform = `translate(${p.x + eW - w * z1}px, ${p.y - eH}px) scale(${z1})`;

I would personally prefer a solution using cytoscape.js resources/extensions, namely the popper.js extension.
As far as I understand your problem, you add bootstrap elements to cytoscape.js in some way (you didn't specify this, so I have to guess).
Nomrally, a sticky popper div does the trick for this problem:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "160px",
shape: "round-rectangle"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "n0"
}
},
{
data: {
id: "n1"
}
},
{
data: {
id: "n2"
}
},
{
data: {
id: "n3"
}
},
{
data: {
id: "n4"
}
},
{
data: {
id: "n5"
}
},
{
data: {
id: "n6"
}
},
{
data: {
id: "n7"
}
},
{
data: {
id: "n8"
}
},
{
data: {
id: "n9"
}
},
{
data: {
id: "n10"
}
},
{
data: {
id: "n11"
}
},
{
data: {
id: "n12"
}
},
{
data: {
id: "n13"
}
},
{
data: {
id: "n14"
}
},
{
data: {
id: "n15"
}
},
{
data: {
id: "n16"
}
}
],
edges: [{
data: {
source: "n0",
target: "n1"
}
},
{
data: {
source: "n1",
target: "n2"
}
},
{
data: {
source: "n1",
target: "n3"
}
},
{
data: {
source: "n4",
target: "n5"
}
},
{
data: {
source: "n4",
target: "n6"
}
},
{
data: {
source: "n6",
target: "n7"
}
},
{
data: {
source: "n6",
target: "n8"
}
},
{
data: {
source: "n8",
target: "n9"
}
},
{
data: {
source: "n8",
target: "n10"
}
},
{
data: {
source: "n11",
target: "n12"
}
},
{
data: {
source: "n12",
target: "n13"
}
},
{
data: {
source: "n13",
target: "n14"
}
},
{
data: {
source: "n13",
target: "n15"
}
}
]
},
layout: {
name: "dagre",
padding: 5,
rankSep: 100
}
}));
var makeTippy = function(node, text) {
var ref = node.popperRef();
var dummyDomEle = document.createElement("div");
var tip = tippy(dummyDomEle, {
onCreate: function(instance) {
instance.popperInstance.reference = ref;
},
lazy: false, // mandatory
trigger: "manual", // mandatory
// dom element inside the tippy:
content: function() {
var div = document.createElement("div");
div.innerHTML = text;
return div;
},
// your own preferences:
arrow: false,
placement: 'top-end',
hideOnClick: false,
multiple: true,
sticky: true
});
return tip;
};
cy.ready(function() {
cy.zoom(0.75);
cy.center();
cy.ready(function() {
let nodes = cy.nodes();
nodes.each(function(node) {
let tippy = makeTippy(node, node.id());
tippy.show();
});
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
.tippy-popper {
transition: none !important;
}
<html>
<head>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-dagre#2.1.0/cytoscape-dagre.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-popper#1.0.6/cytoscape-popper.min.js"></script>
<script src="https://unpkg.com/tippy.js#5.1.3/dist/tippy-bundle.iife.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/tippy.js#5.1.3/dist/tippy.css" />
</head>
<body>
<div id="cy"></div>
</body>
</html>
This snippet cuts some of the container off, so try this in your application for better results and take a look at the extensions used here

The problem stems from CSS scale. When I scale the element, the center point of the element remains invariant. The below sample shows What I mean
div {
position: absolute;
}
span {
font-size: 64px;
}
.s1 {
transform: scale(1);
background: red;
}
.s2 {
transform: scale(0.5);
background: blue;
}
<div class="s1"><span>00</span></div>
<div class="s2"><span>00</span></div>
So I have to consider the center point of the div. Below code does that
let z1 = this._g.cy.zoom() / 2;
const bb = e.renderedBoundingBox({ includeLabels: false, includeOverlays: false });
const w = div.clientWidth;
const h = div.clientHeight;
const deltaW4Scale = (1 - z1) * w / 2;
const deltaH4Scale = (1 - z1) * h / 2;
div.style.transform = `translate(${bb.x2 - deltaW4Scale - w * z1}px, ${bb.y1 - deltaH4Scale}px) scale(${z1})`;

Related

Filtering graph by connectivity to a specific node in cytoscape.js

New to Cytoscape. I have a graph with dominant main network and some smaller networks unconnected to the main one that I want to remove. Looking through the documentation I can't see an obvious solution to this. I'm guessing maybe a custom approach is required that loops through all nodes, checks their graph distance from the most central node in the main cluster, and if this distance is undefined remove that node and all others it does connect to. But keen to get a steer from others with more experience with the library. Any advice is much appreciated. I note this unanswered but related question.
Here is an example graph. Though I can't get working on jsfiddle here is working version:
<!DOCTYPE>
<html>
<head>
<title>cytoscape-dagre.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="cytoscape-dagre.js"></script>
<style>
#cy {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 999;
}
</style>
<script>
window.addEventListener('DOMContentLoaded', function(){
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
layout: {
name: 'dagre'
},
style: [
{
selector: 'node',
style: {
'background-color': '#11479e'
}
},
{
selector: 'edge',
style: {
'width': 4,
'target-arrow-shape': 'triangle',
'line-color': '#9dbaea',
'target-arrow-color': '#9dbaea',
'curve-style': 'bezier'
}
}
],
elements: {
nodes: [
{ data: { id: 'n0' } },
{ data: { id: 'n1' } },
{ data: { id: 'n2' } },
{ data: { id: 'n3' } },
{ data: { id: 'n4' } },
{ data: { id: 'n5' } },
{ data: { id: 'n6' } },
{ data: { id: 'n7' } },
{ data: { id: 'n8' } },
{ data: { id: 'n9' } },
{ data: { id: 'n10' } },
{ data: { id: 'n11' } },
{ data: { id: 'n12' } },
{ data: { id: 'n13' } },
{ data: { id: 'n14' } },
{ data: { id: 'n15' } },
{ data: { id: 'n16' } }
],
edges: [
{ data: { source: 'n0', target: 'n1' } },
{ data: { source: 'n1', target: 'n2' } },
{ data: { source: 'n1', target: 'n3' } },
{ data: { source: 'n4', target: 'n5' } },
{ data: { source: 'n4', target: 'n6' } },
{ data: { source: 'n6', target: 'n7' } },
{ data: { source: 'n6', target: 'n8' } },
{ data: { source: 'n8', target: 'n9' } },
{ data: { source: 'n8', target: 'n10' } },
{ data: { source: 'n11', target: 'n12' } },
{ data: { source: 'n12', target: 'n13' } },
{ data: { source: 'n13', target: 'n14' } },
{ data: { source: 'n13', target: 'n15' } },
]
}
});
});
</script>
</head>
<body>
<h1>cytoscape-dagre demo</h1>
<div id="cy"></div>
</body>
</html>
You can do this with the filtering methods provided in the docs, if you find some method better suited for this problem, just fiddle around with them until you get the right result. The important part here is the .union() and the .not() method. You can use these to:
traverse the graph in a controlled way, saving the important nodes on the way
and then filter the collection to fit your needs
You mentioned not being able to get the jsfiddle to work, you can test the code below in here
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "60px",
"border-color": "black",
"border-opacity": "1",
"border-width": "10px"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "n0"
}
},
{
data: {
id: "n1"
}
},
{
data: {
id: "n2"
}
},
{
data: {
id: "n3"
}
},
{
data: {
id: "n4"
}
},
{
data: {
id: "n5"
}
},
{
data: {
id: "n6"
}
},
{
data: {
id: "n7"
}
},
{
data: {
id: "n8"
}
},
{
data: {
id: "n9"
}
},
{
data: {
id: "n10"
}
},
{
data: {
id: "n11"
}
},
{
data: {
id: "n12"
}
},
{
data: {
id: "n13"
}
},
{
data: {
id: "n14"
}
},
{
data: {
id: "n15"
}
},
{
data: {
id: "n16"
}
}
],
edges: [{
data: {
source: "n0",
target: "n1"
}
},
{
data: {
source: "n1",
target: "n2"
}
},
{
data: {
source: "n1",
target: "n3"
}
},
{
data: {
source: "n4",
target: "n5"
}
},
{
data: {
source: "n4",
target: "n6"
}
},
{
data: {
source: "n6",
target: "n7"
}
},
{
data: {
source: "n6",
target: "n8"
}
},
{
data: {
source: "n8",
target: "n9"
}
},
{
data: {
source: "n8",
target: "n10"
}
},
{
data: {
source: "n11",
target: "n12"
}
},
{
data: {
source: "n12",
target: "n13"
}
},
{
data: {
source: "n13",
target: "n14"
}
},
{
data: {
source: "n13",
target: "n15"
}
}
]
},
layout: {
name: "dagre",
padding: 5
}
}));
cy.unbind('click')
cy.bind('click', 'node', function(event) {
// .union() takes two collections and adds both together without duplicates
var connected = event.target
connected = connected.union(event.target.predecessors())
connected = connected.union(connected.successors())
// in one line:
// event.target.union(event.target.predecessors().union(event.target.successors()))
// .not() filters out whatever is not specified in connected, e.g. every other node/edge not present in connected
var notConnected = cy.elements().not(connected)
// if you want, you can later add the saved elements again
var saved = cy.remove(notConnected)
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<html>
<head>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-dagre#2.1.0/cytoscape-dagre.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Cytoscape remove element layout animation

Is there a way to rerender the layout while ignoring some nodes which will be removed with custom animation?
cy.layout(
{
name: "dagre",
rankDir: "LR",
spacingFactor: 1,
animate: true,
animationDuration: 5000,
rankSep: 200,
}).run();
setTimeout(() => {
cy.elements()[2].animate(
{
style: {
opacity: 0
}
},
{
duration: 5000
);
}, 5000);
cy.layout(
{
name: "dagre",
rankDir: "LR",
spacingFactor: 1,
animate: true,
animationDuration: 5000,
rankSep: 200,
}).run();
I want to filter the node that is animating while I rerender the layout excluding this node
First things first, you can always run layouts on subsets of the elements by calling the layout function on the desired collection:
cy.elements().layout({...})
Also, you can filter out specific elements by asserting a difference between two collections:
cy.elements().not(filterOutElements)
If you put that together, you should get something like this:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "60px",
"border-color": "black",
"border-opacity": "1",
"border-width": "10px"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "n0"
}
},
{
data: {
id: "n1"
}
},
{
data: {
id: "n2"
}
},
{
data: {
id: "n3"
}
},
{
data: {
id: "n4"
}
},
{
data: {
id: "n5"
}
},
{
data: {
id: "n6"
}
},
{
data: {
id: "n7"
}
},
{
data: {
id: "n8"
}
},
{
data: {
id: "n9"
}
},
{
data: {
id: "n10"
}
},
{
data: {
id: "n11"
}
},
{
data: {
id: "n12"
}
},
{
data: {
id: "n13"
}
},
{
data: {
id: "n14"
}
},
{
data: {
id: "n15"
}
},
{
data: {
id: "n16"
}
}
],
edges: [{
data: {
source: "n0",
target: "n1"
}
},
{
data: {
source: "n1",
target: "n2"
}
},
{
data: {
source: "n1",
target: "n3"
}
},
{
data: {
source: "n2",
target: "n7"
}
},
{
data: {
source: "n2",
target: "n11"
}
},
{
data: {
source: "n2",
target: "n16"
}
},
{
data: {
source: "n3",
target: "n4"
}
},
{
data: {
source: "n3",
target: "n16"
}
},
{
data: {
source: "n4",
target: "n5"
}
},
{
data: {
source: "n4",
target: "n6"
}
},
{
data: {
source: "n6",
target: "n8"
}
},
{
data: {
source: "n8",
target: "n9"
}
},
{
data: {
source: "n8",
target: "n10"
}
},
{
data: {
source: "n11",
target: "n12"
}
},
{
data: {
source: "n12",
target: "n13"
}
},
{
data: {
source: "n13",
target: "n14"
}
},
{
data: {
source: "n13",
target: "n15"
}
}
]
},
layout: {
name: "dagre",
padding: 5
}
}));
cy.elements().layout({
name: "dagre",
rankDir: "LR",
spacingFactor: 1,
animate: true,
animationDuration: 5000,
rankSep: 200
}).run();
setTimeout(() => {
cy.elements()[0].animate({
style: {
opacity: 0
}
}, {
duration: 5000
});
cy.elements().not(cy.elements()[0]).layout({
name: "dagre",
spacingFactor: 1,
animate: true,
animationDuration: 5000,
rankSep: 200
}).run();
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
left: 0;
top: 0;
float: left;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.3.0/dist/cytoscape.min.js">
</script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<!-- cyposcape dagre -->
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

With cytoscape.js how to automatically expand the height of the container of a graph?

Is it possible to automatically expand the height of the container of a graph, according to the size of the graph?
I want to display lots of graphs of different sizes on a web page, without having to specify the height of each DOM container.
Does it make sense?
Answer:
It is not the best solution to change anything regarding the container of cytoscape.
Alternative for you:
It is far better to work with the padding of the graph in order to display the graph in a dynamic/suitable way. The easiest way to do that, is to add your nodes and run your layout and then, when the layout is loaded, you can call cy.fit(cy.elements(), yourPadding).
In cytoscape, padding is handled exactly the same way as in css, so you just calculate (based on the number of nodes maybe?) the appropriate padding for your graph and fit it to the viewport.
Additionally, you can always wrap things up with a slick ease-in animation to top things off.
Code:
Snippet works when you click on edit the above snippet and run it in the snippet editor, here the function does not work due to the size limitation and I can't be bothered to adjust it without seeing the outcome in the editor.
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [
{
selector: "node",
css: {
content: "data(id)",
"text-valign": "bottom",
"text-halign": "center",
height: "60px",
width: "60px",
"border-color": "black",
"border-opacity": "1",
"background-image":
"https://farm8.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
},
{
selector: ":selected",
css: {
"background-color": "black",
"line-color": "black",
"target-arrow-color": "black",
"source-arrow-color": "black"
}
}
],
elements: {
nodes: [
{ data: { id: "n0" } },
{ data: { id: "n1" } },
{ data: { id: "n2" } },
{ data: { id: "n3" } },
{ data: { id: "n4" } },
{ data: { id: "n5" } },
{ data: { id: "n6" } },
{ data: { id: "n7" } },
{ data: { id: "n8" } },
{ data: { id: "n9" } },
{ data: { id: "n10" } },
{ data: { id: "n11" } },
{ data: { id: "n12" } },
{ data: { id: "n13" } },
{ data: { id: "n14" } },
{ data: { id: "n15" } },
{ data: { id: "n16" } }
],
edges: [
{ data: { source: "n0", target: "n1" } },
{ data: { source: "n1", target: "n2" } },
{ data: { source: "n1", target: "n3" } },
{ data: { source: "n2", target: "n7" } },
{ data: { source: "n2", target: "n11" } },
{ data: { source: "n2", target: "n16" } },
{ data: { source: "n3", target: "n4" } },
{ data: { source: "n3", target: "n16" } },
{ data: { source: "n4", target: "n5" } },
{ data: { source: "n4", target: "n6" } },
{ data: { source: "n6", target: "n8" } },
{ data: { source: "n8", target: "n9" } },
{ data: { source: "n8", target: "n10" } },
{ data: { source: "n11", target: "n12" } },
{ data: { source: "n12", target: "n13" } },
{ data: { source: "n13", target: "n14" } },
{ data: { source: "n13", target: "n15" } }
]
},
layout: {
name: "dagre",
padding: 5
}
}));
cy.unbind("click");
cy.bind("click", "node", function(event) { // just for demonstration purposes here
var coll = cy.$(event.target).successors(); // get all outgoing nodes
coll = coll.add(event.target); // add their source
var removed = cy.remove(cy.elements().not(coll)); // remove all other elements
var len = cy.nodes().length;
var pad = (len < 10 ? (len < 5 ? (len < 3 ? (len < 2 ? 150 : 100 ) : 75 ) : 50 ) : 25); // custom padding function here
cy.animate({
fit: {
eles: cy.elements(),
padding: pad
}
}, {
duration: 500,
easing: 'ease-in'
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Content and Label on a Node - Cytoscape

I'm trying to display nodes that have a font icon in the center of the node using 'content' and a text label underneath.
My styling is currently:
{
'selector': 'node[icon]',
'style': {
'content': 'data(icon)',
'font-family': 'Material Icons',
'text-valign': 'center',
'text-halign': 'center'
}
},
{
'selector': 'node[label]',
'style': {
'label': 'data(label)',
'text-valign': 'bottom',
'text-halign': 'center'
}
}
However, this doesn't work as i assume both of the styles is used on one element (the node).
There are a few solutions I've considered, such as:
Putting the label on a parent node
Use Popper.js or similar to show the label
Use a multi-lined label
The first 2 seem 'hacky', and the third could cause a lot of alignment problems. Is there a better solution to this?
I've found a solution using the extension: https://github.com/kaluginserg/cytoscape-node-html-label.
You can create custom HTML labels for nodes which do not interfere with the base Cytoscape labels. An example of using the Material Icons:
// Initialise the HTML Label
this.cy.nodeHtmlLabel([{
query: '.nodeIcon',
halign: 'center',
valign: 'center',
halignBox: 'center',
valignBox: 'center',
tpl: (data) => {
return '<i class="material-icons">' + data.icon + '</i>';
}
}]);
// Add the HTML Label to the node:
const node = {
group: 'nodes',
data: {
id: data.id,
label: data.label,
icon: data.icon
},
classes: 'nodeIcon' // <---- Add the HTML Label class here
};
With the method you can dynamically create nodes with font icons without the need to download a load of images.
You can even add CSS styling to change the colour of the icon:
If you want an icon as the nodes body, you can use it as the background image and define the label like you do:
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: 'node',
css: {
'label': 'data(id)',
'text-valign': 'bottom',
'text-halign': 'center',
'height': '60px',
'width': '60px',
'border-color': 'black',
'border-opacity': '1',
'background-image': 'https://farm8.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg',
"text-background-opacity": 1,
"text-background-color": "lightgray"
}
},
{
selector: ':selected',
css: {
'background-color': 'black',
'line-color': 'black',
'target-arrow-color': 'black',
'source-arrow-color': 'black'
}
}
],
elements: {
nodes: [{
data: {
id: 'n0'
}
},
{
data: {
id: 'n1'
}
},
{
data: {
id: 'n2'
}
},
{
data: {
id: 'n3'
}
},
{
data: {
id: 'n4'
}
},
{
data: {
id: 'n5'
}
},
{
data: {
id: 'n6'
}
},
{
data: {
id: 'n7'
}
},
{
data: {
id: 'n8'
}
},
{
data: {
id: 'n9'
}
},
{
data: {
id: 'n10'
}
},
{
data: {
id: 'n11'
}
},
{
data: {
id: 'n12'
}
},
{
data: {
id: 'n13'
}
},
{
data: {
id: 'n14'
}
},
{
data: {
id: 'n15'
}
},
{
data: {
id: 'n16'
}
}
],
edges: [{
data: {
source: 'n0',
target: 'n1'
}
},
{
data: {
source: 'n1',
target: 'n2'
}
},
{
data: {
source: 'n1',
target: 'n3'
}
},
{
data: {
source: 'n2',
target: 'n7'
}
},
{
data: {
source: 'n2',
target: 'n11'
}
},
{
data: {
source: 'n2',
target: 'n16'
}
},
{
data: {
source: 'n3',
target: 'n4'
}
},
{
data: {
source: 'n3',
target: 'n16'
}
},
{
data: {
source: 'n4',
target: 'n5'
}
},
{
data: {
source: 'n4',
target: 'n6'
}
},
{
data: {
source: 'n6',
target: 'n8'
}
},
{
data: {
source: 'n8',
target: 'n9'
}
},
{
data: {
source: 'n8',
target: 'n10'
}
},
{
data: {
source: 'n11',
target: 'n12'
}
},
{
data: {
source: 'n12',
target: 'n13'
}
},
{
data: {
source: 'n13',
target: 'n14'
}
},
{
data: {
source: 'n13',
target: 'n15'
}
},
]
},
layout: {
name: 'dagre',
padding: 5
}
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 75%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Drilldown in Map with Vue.js

I'm trying to use the Drilldown in Map (vue-Highchart), but cannot get it working.
like this: https://www.highcharts.com/maps/demo/map-drilldown
Anyone have any examples of this in Vue.js? Please.
Tks.
Here is simple example of drilldown functionality(with vue-highcharts) which provides drilldown and drillup event from Vue-instance:
Vue.use(VueHighcharts, { Highcharts: Highcharts });
// helper script to load external script
let loadScript = function(url, onLoad){
var scriptTag = document.createElement('script');
scriptTag.src = url;
scriptTag.onload = onLoad;
scriptTag.onreadystatechange = onLoad;
document.body.appendChild(scriptTag);
};
// simple chart options
var options = {
chart: {},
title: {
text: 'Highcharts-Vue Map Drilldown Example'
},
subtitle: {
text: '',
floating: true,
align: 'right',
y: 50,
style: {
fontSize: '16px'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
colorAxis: {
min: 0,
minColor: '#E6E7E8',
maxColor: '#005645'
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
plotOptions: {
map: {
states: {
hover: {
color: '#EEDD66'
}
}
}
},
drilldown: {
activeDataLabelStyle: {
color: '#FFFFFF',
textDecoration: 'none',
textOutline: '1px #000000'
},
drillUpButton: {
relativeTo: 'plotBox',
position: {
x: 70,
y: 280
}
}
},
series: [{
data: Highcharts.geojson(Highcharts.maps['countries/us/us-all']).map((d, i) => {
d.drilldown = true;
// set value just for example
d.value = i;
return d;
}),
name: 'USA',
dataLabels: {
enabled: true,
format: '{point.properties.postal-code}'
}
}]
};
let vm = new Vue({
el: '#app',
data: {
isLoading: false,
options: options
},
created() {
// prepare events for chart from Vue instance
this.options.chart.events = {
drilldown: this.drilldown.bind(this),
drillup: this.drillup.bind(this)
}
},
methods: {
drilldown(e) {
let { chart } = this.$refs.highcharts;
if (!e.seriesOptions) {
mapKey = 'countries/us/' + e.point.properties['hc-key'] + '-all';
if (Highcharts.maps[mapKey]) {
this.prepareDrilldownData(mapKey, e.point);
return;
}
this.isLoading = true;
loadScript('https://code.highcharts.com/mapdata/' + mapKey + '.js', () => {
this.isLoading = false;
this.prepareDrilldownData(mapKey, e.point);
});
}
chart.setTitle(null, { text: e.point.name });
},
drillup(e) {
let { chart } = this.$refs.highcharts;
chart.setTitle(null, { text: '' });
},
prepareDrilldownData(mapKey, point) {
let { chart } = this.$refs.highcharts;
data = Highcharts.geojson(Highcharts.maps[mapKey]).map((d, i) => {
// set value just for example
d.value = i;
return d;
});
chart.addSeriesAsDrilldown(point, {
name: point.name,
data: data,
dataLabels: {
enabled: true,
format: '{point.name}'
}
});
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<script src="https://code.highcharts.com/maps/highmaps.js"></script>
<script src="https://code.highcharts.com/maps/modules/drilldown.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-highcharts/dist/vue-highcharts.min.js"></script>
<script src="https://code.highcharts.com/mapdata/countries/us/us-all.js"></script>
<div id="app">
<highmaps ref="highcharts" :options="options"></highmaps>
<div v-if="isLoading" style="text-align: center; margin-top: 15px; font-size: 20px;">Loading...</div>
</div>
There is also jsfiddle if you want.