Draft implementation of tooltips and hyperlinks as described in issue #34. More tests and documentation to follow.

This commit is contained in:
knsv
2015-10-02 00:18:47 +02:00
parent 30a755221b
commit e406fda9cd
16 changed files with 820 additions and 234 deletions

View File

@@ -105,6 +105,10 @@ exports.addVertices = function (vert, g) {
case 'circle':
_shape = 'circle';
break;
case 'group':
_shape = 'rect';
verticeText = '';
break;
default:
_shape = 'rect';
}
@@ -277,7 +281,7 @@ exports.draw = function (text, id,isDot) {
var i = 0;
for(i=subGraphs.length-1;i>=0;i--){
subG = subGraphs[i];
graph.addVertex(subG.id,undefined,undefined,undefined);
graph.addVertex(subG.id,subG.title,'group',undefined);
}
// Fetch the verices/nodes and edges/links from the parsed graph definition
@@ -394,7 +398,19 @@ exports.draw = function (text, id,isDot) {
svgGroup = d3.select("#" + id + " g");
// Run the renderer. This is what draws the final graph.
render(d3.select("#" + id + " g"), g);
var element = d3.select("#" + id + " g");
render(element, g);
//var tip = d3.tip().html(function(d) { return d; });
element.selectAll("g.node")
.attr("title", function(){
return graph.getTooltip(this.id);
});
//
//element.selectAll("g.node")
// .attr("title", function(v) { return styleTooltip(v, g.node(v).description) })
// .each(function(v) { $(this).tipsy({ gravity: "w", opacity: 1, html: true }); });
var svgb = document.querySelector("#" + id);
/*
@@ -430,7 +446,7 @@ exports.draw = function (text, id,isDot) {
// Index nodes
graph.indexNodes('sunGraph'+i);
graph.indexNodes('subGraph'+i);
for(i=0;i<subGraphs.length;i++){
var pos = graph.getDepthFirstPos(i);

View File

@@ -6,6 +6,7 @@ var vertices = {};
var edges = [];
var classes = [];
var subGraphs = [];
var tooltips = {};
var subCount=0;
var direction;
// Functions to be run after graph rendering
@@ -136,48 +137,69 @@ exports.setClass = function (id,className) {
}
}
};
var setTooltip = function(id,tooltip){
if(typeof tooltip !== 'undefined'){
tooltips[id]=tooltip;
}
};
var setClickFun = function(id, functionName){
if(typeof functionName === 'undefined'){
return;
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function (element) {
var elem = d3.select(element).select('#'+id);
if (elem !== null) {
elem.on('click', function () {
eval(functionName + '(\'' + id + '\')'); // jshint ignore:line
});
}
});
}
};
var setLink = function(id, linkStr){
if(typeof linkStr === 'undefined'){
return;
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function (element) {
var elem = d3.select(element).select('#'+id);
if (elem !== null) {
elem.on('click', function () {
window.open(linkStr,'newTab'); // jshint ignore:line
});
}
});
}
};
exports.getTooltip = function(id){
return tooltips[id];
};
var clickEvents = [];
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
exports.setClickEvent = function (id,functionName) {
exports.setClickEvent = function (id,functionName, link,tooltip) {
if(id.indexOf(',')>0){
id.split(',').forEach(function(id2) {
if (typeof vertices[id2] !== 'undefined') {
funs.push(function () {
var elem = document.getElementById(id2);
if (elem !== null) {
elem.onclick = function () {
eval(functionName + '(\'' + id2 + '\')'); // jshint ignore:line
};
}
});
}
setTooltip(id2,tooltip);
setClickFun(id2, functionName);
setLink(id2, link);
});
}else{
//log.debug('Checking now for ::'+id);
if(typeof vertices[id] !== 'undefined'){
funs.push(function(){
var elem = document.getElementById(id);
if(elem !== null){
elem.onclick = function(){eval(functionName+'(\'' + id + '\')');}; // jshint ignore:line
}
else{
//log.debug('id was null: '+id);
}
});
}
setTooltip(id,tooltip);
setClickFun(id, functionName);
setLink(id, link);
}
};
exports.bindFunctions = function(){
exports.bindFunctions = function(element){
funs.forEach(function(fun){
fun();
fun(element);
});
};
exports.getDirection = function () {
@@ -207,6 +229,49 @@ exports.getClasses = function () {
return classes;
};
var setupToolTips = function(element){
var tooltipElem = d3.select('.mermaidTooltip');
if(tooltipElem[0][0] === null){
tooltipElem = d3.select("body")
.append("div")
.attr("class", "mermaidTooltip")
.style("opacity", 0);
}
var svg = d3.select(element).select('svg');
var nodes = svg.selectAll("g.node");
nodes
.on("mouseover", function(d) {
var el = d3.select(this);
var title = el.attr('title');
// Dont try to draw a tooltip if no data is provided
if(title === null){
return;
}
var rect = this.getBoundingClientRect();
tooltipElem.transition()
.duration(200)
.style("opacity", .9);
tooltipElem.html(el.attr('title'))
.style("left", (rect.left+(rect.right-rect.left)/2) + "px")
.style("top", (rect.top-28) + "px");
var el = d3.select(this);
el.classed('hover',true);
})
.on("mouseout", function(d) {
tooltipElem.transition()
.duration(500)
.style("opacity", 0);
var el = d3.select(this);
el.classed('hover',false);
});
};
funs.push(setupToolTips);
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
@@ -214,9 +279,11 @@ exports.clear = function () {
vertices = {};
classes = {};
edges = [];
//funs = [];
funs = [];
funs.push(setupToolTips);
subGraphs = [];
subCount = 0;
tooltips = [];
};
/**
*

View File

@@ -386,8 +386,11 @@ classStatement:CLASS SPACE alphaNum SPACE alphaNum
{$$ = $1;yy.setClass($3, $5);}
;
clickStatement:CLICK SPACE alphaNum SPACE alphaNum
{$$ = $1;yy.setClickEvent($3, $5);}
clickStatement
: CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined, undefined);}
| CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, undefined, $7) ;}
| CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, undefined, $5, undefined);}
| CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setClickEvent($3, undefined, $5, $7 );}
;
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt

File diff suppressed because one or more lines are too long

View File

@@ -431,6 +431,49 @@ describe('when parsing ',function(){
expect(edges[0].type).toBe('arrow');
});
ddescribe("it should handle interaction, ",function(){
it('it should be possible to use click to a callback',function(){
spyOn(graph,'setClickEvent');
var res = flow.parser.parse('graph TD\nA-->B\nclick A callback');
var vert = flow.parser.yy.getVertices();
var edges = flow.parser.yy.getEdges();
expect(graph.setClickEvent).toHaveBeenCalledWith('A','callback',undefined,undefined);
});
it('it should be possible to use click to a callback with toolip',function(){
spyOn(graph,'setClickEvent');
var res = flow.parser.parse('graph TD\nA-->B\nclick A callback "tooltip"');
var vert = flow.parser.yy.getVertices();
var edges = flow.parser.yy.getEdges();
expect(graph.setClickEvent).toHaveBeenCalledWith('A','callback',undefined,'tooltip');
});
it('should handle interaction - click to a link',function(){
spyOn(graph,'setClickEvent');
var res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html"');
var vert = flow.parser.yy.getVertices();
var edges = flow.parser.yy.getEdges();
expect(graph.setClickEvent).toHaveBeenCalledWith('A',undefined,'click.html',undefined);
});
it('should handle interaction - click to a link with tooltip',function(){
spyOn(graph,'setClickEvent');
var res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"');
var vert = flow.parser.yy.getVertices();
var edges = flow.parser.yy.getEdges();
expect(graph.setClickEvent).toHaveBeenCalledWith('A',undefined,'click.html','tooltip');
});
});
describe("it should handle text on edges",function(){
it('it should handle text without space',function(){
var res = flow.parser.parse('graph TD;A--x|textNoSpace|B;');