Experimental support for gantt diagrams
This commit is contained in:
parent
1b016bd412
commit
2877501ff5
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,15 @@
|
|||
/* global window */
|
||||
|
||||
var d3;
|
||||
|
||||
if (require) {
|
||||
try {
|
||||
d3 = require("d3");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (!d3) {
|
||||
d3 = window.d3;
|
||||
}
|
||||
|
||||
module.exports = d3;
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Created by knut on 15-01-14.
|
||||
*/
|
||||
var moment = require('moment');
|
||||
|
||||
var dateFormat = '';
|
||||
var title = '';
|
||||
|
@ -29,7 +30,7 @@ exports.setTitle = function(txt){
|
|||
title = txt;
|
||||
};
|
||||
|
||||
exports.gettitle = function(){
|
||||
exports.getTitle = function(){
|
||||
return title;
|
||||
};
|
||||
|
||||
|
@ -48,12 +49,22 @@ exports.findTaskById = function(id) {
|
|||
};
|
||||
|
||||
exports.getTasks=function(){
|
||||
var i;
|
||||
for(i=10000;i<tasks.length;i++){
|
||||
tasks[i].startTime = moment(tasks[i].startTime).format('YYYY-MM-DD');
|
||||
tasks[i].endTime = moment(tasks[i].endTime).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
return tasks;
|
||||
};
|
||||
|
||||
|
||||
var getStartDate = function(prevTime, dateFormat, str){
|
||||
var moment = require('moment');
|
||||
//console.log('Deciding start date:'+str);
|
||||
//console.log('with dateformat:'+dateFormat);
|
||||
|
||||
str = str.trim();
|
||||
|
||||
// Test for after
|
||||
var re = /^after\s+([\d\w\-]+)/;
|
||||
var afterStatement = re.exec(str.trim());
|
||||
|
@ -70,6 +81,9 @@ var getStartDate = function(prevTime, dateFormat, str){
|
|||
// Check for actual date set
|
||||
if(moment(str,dateFormat,true).isValid()){
|
||||
return moment(str,dateFormat).toDate();
|
||||
}else{
|
||||
console.log('Invalid date:'+str);
|
||||
console.log('With date format:'+dateFormat);
|
||||
}
|
||||
|
||||
// Default date - now
|
||||
|
@ -77,7 +91,7 @@ var getStartDate = function(prevTime, dateFormat, str){
|
|||
};
|
||||
|
||||
var getEndDate = function(prevTime, dateFormat, str){
|
||||
var moment = require('moment');
|
||||
str = str.trim();
|
||||
|
||||
// Check for actual date
|
||||
if(moment(str,dateFormat,true).isValid()){
|
||||
|
@ -127,7 +141,16 @@ var parseId = function(idStr){
|
|||
// length
|
||||
|
||||
var compileData = function(prevTask, dataStr){
|
||||
var data = dataStr.split(',');
|
||||
var ds;
|
||||
|
||||
if(dataStr.substr(0,1) === ':'){
|
||||
ds = dataStr.substr(1,dataStr.length);
|
||||
}
|
||||
else{
|
||||
ds=dataStr;
|
||||
}
|
||||
|
||||
var data = ds.split(',');
|
||||
var task = {};
|
||||
var df = exports.getDateFormat();
|
||||
|
||||
|
@ -150,6 +173,7 @@ var compileData = function(prevTask, dataStr){
|
|||
default:
|
||||
|
||||
}
|
||||
|
||||
return task;
|
||||
};
|
||||
|
||||
|
@ -159,7 +183,9 @@ exports.addTask = function(descr,data){
|
|||
|
||||
var newTask = {
|
||||
section:currentSection,
|
||||
description:descr
|
||||
type:currentSection,
|
||||
description:descr,
|
||||
task:descr
|
||||
};
|
||||
var taskInfo = compileData(lastTask, data);
|
||||
newTask.startTime = taskInfo.startTime;
|
||||
|
|
|
@ -2,38 +2,51 @@ var gantt = require('./parser/gantt').parser;
|
|||
gantt.yy = require('./ganttDb');
|
||||
var d3 = require('./d3');
|
||||
|
||||
var conf = {
|
||||
titleTopMargin:25,
|
||||
barHeight:20,
|
||||
barGap:4,
|
||||
topPadding:75,
|
||||
sidePadding:75
|
||||
};
|
||||
module.exports.setConf = function(cnf){
|
||||
var keys = Object.keys(cnf);
|
||||
|
||||
|
||||
keys.forEach(function(key){
|
||||
conf[key] = cnf[key];
|
||||
});
|
||||
};
|
||||
var w;
|
||||
module.exports.draw = function (text, id) {
|
||||
gantt.yy.clear();
|
||||
gantt.parse(text);
|
||||
console.log(gantt.yy.getTasks());
|
||||
var w = 1200;
|
||||
var h = 400;
|
||||
var elem = document.getElementById(id);
|
||||
w = elem.offsetWidth;
|
||||
|
||||
var svg = d3.select('#'+id)
|
||||
.attr("width", w)
|
||||
.attr("height", h);
|
||||
/*var svg = d3.selectAll(".svg")
|
||||
//.selectAll("svg")
|
||||
.append("svg")
|
||||
.attr("width", w)
|
||||
.attr("height", h)
|
||||
.attr("class", "svg");*/
|
||||
if(typeof w === 'undefined'){
|
||||
w = 800;
|
||||
}
|
||||
|
||||
var taskArray = gantt.yy.getTasks();
|
||||
|
||||
// Set height based on number of tasks
|
||||
var h = taskArray.length*(conf.barHeight+conf.barGap)+2*conf.topPadding;
|
||||
|
||||
elem.style.height = h+'px';
|
||||
var svg = d3.select('#'+id);
|
||||
|
||||
// http://codepen.io/anon/pen/azLvWR
|
||||
|
||||
var taskArray = gantt.yy.getTasks();
|
||||
|
||||
var dateFormat = d3.time.format("%Y-%m-%d");
|
||||
|
||||
// Set timescale
|
||||
var timeScale = d3.time.scale()
|
||||
.domain([d3.min(taskArray, function (d) {
|
||||
return dateFormat.parse(d.startTime);
|
||||
return d.startTime;
|
||||
}),
|
||||
d3.max(taskArray, function (d) {
|
||||
return dateFormat.parse(d.endTime);
|
||||
return d.endTime;
|
||||
})])
|
||||
.range([0, w - 150]);
|
||||
|
||||
|
@ -53,7 +66,7 @@ module.exports.draw = function (text, id) {
|
|||
var title = svg.append("text")
|
||||
.text(gantt.yy.getTitle())
|
||||
.attr("x", w / 2)
|
||||
.attr("y", 25)
|
||||
.attr("y", conf.titleTopMargin)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", 18)
|
||||
.attr("fill", "#009FFC");
|
||||
|
@ -61,10 +74,10 @@ module.exports.draw = function (text, id) {
|
|||
|
||||
function makeGant(tasks, pageWidth, pageHeight) {
|
||||
|
||||
var barHeight = 20;
|
||||
var gap = barHeight + 4;
|
||||
var topPadding = 75;
|
||||
var sidePadding = 75;
|
||||
var barHeight = conf.barHeight;
|
||||
var gap = barHeight + conf.barGap;
|
||||
var topPadding = conf.topPadding;
|
||||
var sidePadding = conf.sidePadding;
|
||||
|
||||
var colorScale = d3.scale.linear()
|
||||
.domain([0, categories.length])
|
||||
|
@ -96,7 +109,7 @@ module.exports.draw = function (text, id) {
|
|||
.attr("stroke", "none")
|
||||
.attr("fill", function (d) {
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d.type == categories[i]) {
|
||||
if (d.type === categories[i]) {
|
||||
return d3.rgb(theColorScale(i));
|
||||
}
|
||||
}
|
||||
|
@ -114,19 +127,19 @@ module.exports.draw = function (text, id) {
|
|||
.attr("rx", 3)
|
||||
.attr("ry", 3)
|
||||
.attr("x", function (d) {
|
||||
return timeScale(dateFormat.parse(d.startTime)) + theSidePad;
|
||||
return timeScale(d.startTime) + theSidePad;
|
||||
})
|
||||
.attr("y", function (d, i) {
|
||||
return i * theGap + theTopPad;
|
||||
})
|
||||
.attr("width", function (d) {
|
||||
return (timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime)));
|
||||
return (timeScale(d.endTime) - timeScale(d.startTime));
|
||||
})
|
||||
.attr("height", theBarHeight)
|
||||
.attr("stroke", "none")
|
||||
.attr("fill", function (d) {
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d.type == categories[i]) {
|
||||
if (d.type === categories[i]) {
|
||||
return d3.rgb(theColorScale(i));
|
||||
}
|
||||
}
|
||||
|
@ -137,81 +150,45 @@ module.exports.draw = function (text, id) {
|
|||
.text(function (d) {
|
||||
return d.task;
|
||||
})
|
||||
.attr("font-size", 11)
|
||||
.attr("x", function (d) {
|
||||
return (timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime))) / 2 + timeScale(dateFormat.parse(d.startTime)) + theSidePad;
|
||||
var startX = timeScale(d.startTime),
|
||||
endX = timeScale(d.endTime),
|
||||
textWidth = this.getBBox().width;
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
if(textWidth>(endX-startX)){
|
||||
if (endX + textWidth > w){
|
||||
return startX + theSidePad;
|
||||
}else {
|
||||
return endX + theSidePad;
|
||||
}
|
||||
}else{
|
||||
return (endX - startX) / 2 + startX + theSidePad;
|
||||
}
|
||||
})
|
||||
.attr("y", function (d, i) {
|
||||
return i * theGap + 14 + theTopPad;
|
||||
})
|
||||
.attr("font-size", 11)
|
||||
.attr("text-anchor", "middle")
|
||||
//.attr("text-anchor", "middle")
|
||||
.attr("text-height", theBarHeight)
|
||||
.attr("fill", "#fff");
|
||||
.attr("class",function (d) {
|
||||
var startX = timeScale(d.startTime),
|
||||
endX = timeScale(d.endTime),
|
||||
textWidth = this.getBBox().width;
|
||||
|
||||
|
||||
rectText.on('mouseover', function (e) {
|
||||
// console.log(this.x.animVal.getItem(this));
|
||||
var tag = "";
|
||||
|
||||
if (typeof d3.select(this).data()[0].details !== 'undefined') {
|
||||
tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
|
||||
"Type: " + d3.select(this).data()[0].type + "<br/>" +
|
||||
"Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
|
||||
"Ends: " + d3.select(this).data()[0].endTime + "<br/>" +
|
||||
"Details: " + d3.select(this).data()[0].details;
|
||||
// Check id text width > width of rectangle
|
||||
if(textWidth>(endX-startX)) {
|
||||
if (endX + textWidth > w){
|
||||
return 'taskTextOutsideLeft';
|
||||
}else {
|
||||
tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
|
||||
"Type: " + d3.select(this).data()[0].type + "<br/>" +
|
||||
"Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
|
||||
"Ends: " + d3.select(this).data()[0].endTime;
|
||||
return 'taskTextOutsideRight';
|
||||
}
|
||||
var output = document.getElementById("tag");
|
||||
|
||||
var x = this.x.animVal.getItem(this) + "px";
|
||||
var y = this.y.animVal.getItem(this) + 25 + "px";
|
||||
|
||||
output.innerHTML = tag;
|
||||
output.style.top = y;
|
||||
output.style.left = x;
|
||||
output.style.display = "block";
|
||||
}).on('mouseout', function () {
|
||||
var output = document.getElementById("tag");
|
||||
output.style.display = "none";
|
||||
});
|
||||
|
||||
|
||||
innerRects.on('mouseover', function (e) {
|
||||
//console.log(this);
|
||||
var tag = "";
|
||||
|
||||
if (typeof d3.select(this).data()[0].details !== 'undefined') {
|
||||
tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
|
||||
"Type: " + d3.select(this).data()[0].type + "<br/>" +
|
||||
"Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
|
||||
"Ends: " + d3.select(this).data()[0].endTime + "<br/>" +
|
||||
"Details: " + d3.select(this).data()[0].details;
|
||||
}else{
|
||||
tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
|
||||
"Type: " + d3.select(this).data()[0].type + "<br/>" +
|
||||
"Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
|
||||
"Ends: " + d3.select(this).data()[0].endTime;
|
||||
return 'taskText';
|
||||
}
|
||||
var output = document.getElementById("tag");
|
||||
|
||||
var x = (this.x.animVal.value + this.width.animVal.value / 2) + "px";
|
||||
var y = this.y.animVal.value + 25 + "px";
|
||||
|
||||
output.innerHTML = tag;
|
||||
output.style.top = y;
|
||||
output.style.left = x;
|
||||
output.style.display = "block";
|
||||
}).on('mouseout', function () {
|
||||
var output = document.getElementById("tag");
|
||||
output.style.display = "none";
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -220,7 +197,7 @@ module.exports.draw = function (text, id) {
|
|||
var xAxis = d3.svg.axis()
|
||||
.scale(timeScale)
|
||||
.orient('bottom')
|
||||
.ticks(d3.time.weeks, 5)
|
||||
//.ticks(d3.time.days, 5)
|
||||
.tickSize(-h + theTopPad + 20, 0, 0)
|
||||
.tickFormat(d3.time.format('%d %b'));
|
||||
|
||||
|
@ -269,7 +246,7 @@ module.exports.draw = function (text, id) {
|
|||
.attr("text-height", 14)
|
||||
.attr("fill", function (d) {
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d[0] == categories[i]) {
|
||||
if (d[0] === categories[i]) {
|
||||
// console.log("true!");
|
||||
return d3.rgb(theColorScale(i)).darker();
|
||||
}
|
||||
|
@ -294,7 +271,9 @@ module.exports.draw = function (text, id) {
|
|||
function getCounts(arr) {
|
||||
var i = arr.length, // var to loop over
|
||||
obj = {}; // obj to store results
|
||||
while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
|
||||
while (i){
|
||||
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
12
src/main.js
12
src/main.js
|
@ -11,6 +11,9 @@ var dotParser = require('./diagrams/flowchart/parser/dot');
|
|||
var sequenceParser = require('./diagrams/sequenceDiagram/parser/sequenceDiagram');
|
||||
var sequenceDb = require('./diagrams/sequenceDiagram/sequenceDb');
|
||||
var infoDb = require('./diagrams/example/exampleDb');
|
||||
var gantt = require('./diagrams/gantt/ganttRenderer');
|
||||
var ganttParser = require('./diagrams/gantt/parser/gantt');
|
||||
var ganttDb = require('./diagrams/gantt/ganttDb');
|
||||
|
||||
/**
|
||||
* Function that parses a mermaid diagram defintion. If parsing fails the parseError callback is called and an error is
|
||||
|
@ -38,6 +41,10 @@ var parse = function(text){
|
|||
parser = infoParser;
|
||||
parser.parser.yy = infoDb;
|
||||
break;
|
||||
case 'gantt':
|
||||
parser = ganttParser;
|
||||
parser.parser.yy = ganttDb;
|
||||
break;
|
||||
}
|
||||
|
||||
try{
|
||||
|
@ -115,7 +122,10 @@ var init = function (sequenceConfig) {
|
|||
break;
|
||||
case 'sequenceDiagram':
|
||||
seq.draw(txt,id);
|
||||
// TODO - Get styles for sequence diagram
|
||||
utils.cloneCssStyles(element.firstChild, []);
|
||||
break;
|
||||
case 'gantt':
|
||||
gantt.draw(txt,id);
|
||||
utils.cloneCssStyles(element.firstChild, []);
|
||||
break;
|
||||
case 'info':
|
||||
|
|
|
@ -27,6 +27,11 @@ module.exports.detectType = function(text,a){
|
|||
return "info";
|
||||
}
|
||||
|
||||
if(text.match(/^\s*gantt/)) {
|
||||
//console.log('Detected info syntax');
|
||||
return "gantt";
|
||||
}
|
||||
|
||||
return "graph";
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<script src="../dist/mermaid.full.js"></script>
|
||||
<link rel="stylesheet" href="seq.css"/>
|
||||
<script>
|
||||
var mermaid_config = {
|
||||
startOnLoad:true
|
||||
}
|
||||
//mermaid.sequenceConfig = '{"diagramMarginX":50,"diagramMarginY":10,"actorMargin":50,"width":150,"height":45,"boxMargin":10,"boxTextMargin":5,"noteMargin":10,"messageMargin":35, "mirrorActors":false}';
|
||||
//mermaid.sequenceConfig = JSON.parse('{"diagramMarginX":50,"diagramMarginY":10,"actorMargin":50,"width":150,"height":165,"boxMargin":10,"boxTextMargin":5,"noteMargin":10,"messageMargin":35}');
|
||||
|
||||
</script>
|
||||
<script>
|
||||
function apa(){
|
||||
console.log('CLICKED');
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
font-family: 'Open-Sans',sans-serif;
|
||||
|
||||
}
|
||||
|
||||
#container{
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
width:800px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
|
||||
.svg {
|
||||
width:800px;
|
||||
height:400px;
|
||||
overflow: visible;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.grid .tick {
|
||||
stroke: lightgrey;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
#tag {
|
||||
color: white;
|
||||
background: #FA283D;
|
||||
width: 150px;
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding:3px 6px;
|
||||
margin-left: -80px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#tag:before {
|
||||
border: solid transparent;
|
||||
content: ' ';
|
||||
height: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
border-width: 10px;
|
||||
border-bottom-color: #FA283D;
|
||||
top: -20px;
|
||||
}
|
||||
.taskText {
|
||||
fill:white;
|
||||
text-anchor:middle;
|
||||
}
|
||||
.taskTextOutsideRight {
|
||||
fill:black;
|
||||
text-anchor:start;
|
||||
}
|
||||
.taskTextOutsideLeft {
|
||||
fill:black;
|
||||
text-anchor:end;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>No line breaks</h1>
|
||||
<div class="mermaid">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram functionality to mermaid
|
||||
|
||||
section Design
|
||||
Design jison grammar :des1, 2014-01-01, 2014-01-04
|
||||
Create example text :des2, 2014-01-01, 3d
|
||||
Bounce gantt example with users :des3, after des2, 5d
|
||||
|
||||
section Implementation
|
||||
update build script :2014-01-02,24h
|
||||
Implement parser and jison :after des1, 2d
|
||||
Create tests for parser :3d
|
||||
Create renderer :5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid core bore tore gore bore lore :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :a1, 2014-01-10, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :after a1 , 48h
|
||||
</div>
|
||||
<div class="mermaid">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title A small test
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :a1, 2014-01-01, 30d
|
||||
Add gantt diagram to demo page :after a1 , 20d
|
||||
Add another diagram to demo page :after a1 , 48d
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -6,7 +6,8 @@
|
|||
<script src="mermaid.full.js"></script>
|
||||
<script>
|
||||
var mermaid_config = {
|
||||
startOnLoad:true
|
||||
startOnLoad:true,
|
||||
htmlLabels:false
|
||||
}
|
||||
mermaid.startOnLoad=true;
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue