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

178
dist/mermaid.js vendored

File diff suppressed because one or more lines are too long

6
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

178
dist/mermaid.slim.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

175
dist/mermaidAPI.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -32202,7 +32202,8 @@ exports.getClasses = function (text, isDot) {
// Add default class if undefined
if(typeof(classes.default) === 'undefined') {
classes.default = {id:'default'};
classes.default.styles = ['fill:#ffa','stroke:#666','stroke-width:3px'];
//classes.default.styles = ['fill:#ffa','stroke:#666','stroke-width:3px'];
classes.default.styles = [];
classes.default.clusterStyles = ['rx:4px','fill: rgb(255, 255, 222)','rx: 4px','stroke: rgb(170, 170, 51)','stroke-width: 1px'];
classes.default.nodeLabelStyles = ['fill:#000','stroke:none','font-weight:300','font-family:"Helvetica Neue",Helvetica,Arial,sans-serf','font-size:14px'];
classes.default.edgeLabelStyles = ['fill:#000','stroke:none','font-weight:300','font-family:"Helvetica Neue",Helvetica,Arial,sans-serf','font-size:14px'];
@ -38220,7 +38221,7 @@ module.exports.cloneCssStyles = function(svg, classes){
if (classes.hasOwnProperty(className) && typeof(className) != "undefined") {
if (className === 'default') {
if (classes.default.styles instanceof Array) {
defaultStyles += "#" + svg.id.trim() + ' .node' + ' { ' + classes[className].styles.join("; ") + '; }\n';
defaultStyles += "#" + svg.id.trim() + ' .node' + '>rect { ' + classes[className].styles.join("; ") + '; }\n';
}
if (classes.default.nodeLabelStyles instanceof Array) {
defaultStyles += "#" + svg.id.trim() + ' .node text ' + ' { ' + classes[className].nodeLabelStyles.join("; ") + '; }\n';

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;');

View File

@ -97,11 +97,12 @@ var init = function () {
mermaidAPI.initialize({gantt:mermaid.ganttConfig});
}
var insertSvg = function(svgCode){
var insertSvg = function(svgCode, bindFunctions){
element.innerHTML = svgCode;
if(typeof callback !== 'undefined'){
callback(id);
}
bindFunctions(element);
};
for (i = 0; i < nodes.length; i++) {

View File

@ -323,7 +323,6 @@ var render = function(id, txt, cb, container){
classes = flowRenderer.getClasses(txt, false);
utils.cloneCssStyles(element.firstChild, classes);
}
graph.bindFunctions();
break;
case 'dotGraph':
flowRenderer.setConf(config.flowchart);

81
test/examples/links.html Normal file
View File

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../../dist/mermaid.css"/>
<script src="../../dist/mermaid.js"></script>
<link rel="stylesheet" href="../../dist/mermaid.css"/>
<style>
body{
background-color: #2d2d22;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 1px solid black;
border-radius: 3px;
pointer-events: none;
}
</style>
<script>
function itsTime(){
function getScreenCoords(x, y, ctm) {
var xn = ctm.e + x*ctm.a;
var yn = ctm.f + y*ctm.d;
return { x: xn, y: yn };
}
setTimeout(function(){
},250);
}
function callback(id){
alert(id);
}
function callback2(id){
alert('x'+id);
}
</script>
</head>
<body onload="itsTime()">
<h1>Links to callbacks</h1>
A has a tooltip
<div class="mermaid" id="i211">
graph LR;
A-->B;
B-->C;
click A callback "Tooltip"
click B "http://www.github.com" "This is a link"
click C callback "Tooltip C1"
</div>
<h1>Links to urls</h1>
<div class="mermaid" id="i213">
graph LR;
A-->B
B-->C
click A callback2 "Tooltip3"
</div>
<div class="mermaid" id="i213">
graph LR;
subgraph S1
a-->b
end
subgraph S2
c-->d
c-->d
end
</div>
</body>
</html>