Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gantt milestones #788

Merged
merged 10 commits into from
Jun 9, 2019
40 changes: 39 additions & 1 deletion src/diagrams/gantt/gantt.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint-env jasmine */
/* eslint-disable no-eval */
import { parser } from './parser/gantt'
import ganttDb from './ganttDb'
import moment from 'moment'

describe('when parsing a gantt diagram it', function () {
beforeEach(function () {
parser.yy = ganttDb
parser.yy.clear()
})

it('should handle a dateFormat definition', function () {
Expand Down Expand Up @@ -38,11 +41,46 @@ describe('when parsing a gantt diagram it', function () {
*/
it('should handle a task definition', function () {
const str = 'gantt\n' +
'dateFormat yyyy-mm-dd\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'Design jison grammar:des1, 2014-01-01, 2014-01-04'

parser.parse(str)

const tasks = parser.yy.getTasks()

expect(tasks[0].startTime).toEqual(moment('2014-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2014-01-04', 'YYYY-MM-DD').toDate())
gijswijs marked this conversation as resolved.
Show resolved Hide resolved
expect(tasks[0].id).toEqual('des1')
expect(tasks[0].task).toEqual('Design jison grammar')
})
it.each`
tags | milestone | done | crit | active
${'milestone'} | ${true} | ${false} | ${false} | ${false}
${'done'} | ${false} | ${true} | ${false} | ${false}
${'crit'} | ${false} | ${false} | ${true} | ${false}
${'active'} | ${false} | ${false} | ${false} | ${true}
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
const str = 'gantt\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'test task:' + tags + ', 2014-01-01, 2014-01-04'

const allowedTags = ['active', 'done', 'crit', 'milestone']

parser.parse(str)

const tasks = parser.yy.getTasks()

allowedTags.forEach(function (t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy()
} else {
expect(tasks[0][t]).toBeFalsy()
}
})
})
})
65 changes: 25 additions & 40 deletions src/diagrams/gantt/ganttDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let title = ''
let sections = []
let tasks = []
let currentSection = ''
const tags = ['active', 'done', 'crit', 'milestone']

export const clear = function () {
sections = []
Expand Down Expand Up @@ -157,26 +158,9 @@ const compileData = function (prevTask, dataStr) {

const task = {}

// Get tags like active, done cand crit
let matchFound = true
while (matchFound) {
matchFound = false
if (data[0].match(/^\s*active\s*$/)) {
task.active = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*done\s*$/)) {
task.done = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*crit\s*$/)) {
task.crit = true
data.shift(1)
matchFound = true
}
}
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)

for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
}
Expand Down Expand Up @@ -215,26 +199,9 @@ const parseData = function (prevTaskId, dataStr) {

const task = {}

// Get tags like active, done cand crit
let matchFound = true
while (matchFound) {
matchFound = false
if (data[0].match(/^\s*active\s*$/)) {
task.active = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*done\s*$/)) {
task.done = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*crit\s*$/)) {
task.crit = true
data.shift(1)
matchFound = true
}
}
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)

for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
}
Expand Down Expand Up @@ -281,6 +248,7 @@ export const addTask = function (descr, data) {
rawTask.active = taskInfo.active
rawTask.done = taskInfo.done
rawTask.crit = taskInfo.crit
rawTask.milestone = taskInfo.milestone

const pos = rawTasks.push(rawTask)

Expand Down Expand Up @@ -308,6 +276,7 @@ export const addTaskOrg = function (descr, data) {
newTask.active = taskInfo.active
newTask.done = taskInfo.done
newTask.crit = taskInfo.crit
newTask.milestone = taskInfo.milestone
lastTask = newTask
tasks.push(newTask)
}
Expand Down Expand Up @@ -361,3 +330,19 @@ export default {
findTaskById,
addTaskOrg
}

function getTaskTags (data, task, tags) {
let matchFound = true
while (matchFound) {
matchFound = false
tags.forEach(function (t) {
const pattern = '^\\s*' + t + '\\s*$'
const regex = new RegExp(pattern)
if (data[0].match(regex)) {
task[t] = true
data.shift(1)
matchFound = true
}
})
}
}
61 changes: 45 additions & 16 deletions src/diagrams/gantt/ganttRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const draw = function (text, id) {
}

function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
// Draw background rects covering the entire width of the graph, these form the section rows.
svg.append('g')
.selectAll('rect')
.data(theArray)
Expand All @@ -120,6 +121,7 @@ export const draw = function (text, id) {
return 'section section0'
})

// Draw the rects representing the tasks
const rectangles = svg.append('g')
.selectAll('rect')
.data(theArray)
Expand All @@ -129,17 +131,26 @@ export const draw = function (text, id) {
.attr('rx', 3)
.attr('ry', 3)
.attr('x', function (d) {
if (d.milestone) {
return timeScale(d.startTime) + theSidePad + (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
}
return timeScale(d.startTime) + theSidePad
})
.attr('y', function (d, i) {
return i * theGap + theTopPad
})
.attr('width', function (d) {
if (d.milestone) {
return theBarHeight
}
return (timeScale(d.endTime) - timeScale(d.startTime))
})
.attr('height', theBarHeight)
.attr('transform-origin', function (d, i) {
return (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() + 'px ' + (i * theGap + theTopPad + 0.5 * theBarHeight).toString() + 'px'
})
.attr('class', function (d) {
const res = 'task '
const res = 'task'

let secNum = 0
for (let i = 0; i < categories.length; i++) {
Expand All @@ -148,37 +159,48 @@ export const draw = function (text, id) {
}
}

let taskClass = ''
if (d.active) {
if (d.crit) {
return res + ' activeCrit' + secNum
taskClass = ' active'
} else if (d.done) {
taskClass = ' done'
}
if (d.crit) {
if (taskClass.length > 0) {
taskClass += 'Crit'
} else {
return res + ' active' + secNum
taskClass = ' crit'
}
}

if (d.done) {
if (d.crit) {
return res + ' doneCrit' + secNum
} else {
return res + ' done' + secNum
}
if (taskClass.length === 0) {
taskClass = ' task'
}

if (d.crit) {
return res + ' crit' + secNum
if (d.milestone) {
taskClass = ' milestone' + taskClass
}

return res + ' task' + secNum
taskClass += secNum

return res + taskClass
})

// Append task labels
rectangles.append('text')
.text(function (d) {
return d.task
})
.attr('font-size', conf.fontSize)
.attr('x', function (d) {
const startX = timeScale(d.startTime)
const endX = timeScale(d.endTime)
let startX = timeScale(d.startTime)
let endX = timeScale(d.endTime)
if (d.milestone) {
startX += (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
}
if (d.milestone) {
endX = startX + theBarHeight
}
const textWidth = this.getBBox().width

// Check id text width > width of rectangle
Expand All @@ -198,7 +220,10 @@ export const draw = function (text, id) {
.attr('text-height', theBarHeight)
.attr('class', function (d) {
const startX = timeScale(d.startTime)
const endX = timeScale(d.endTime)
let endX = timeScale(d.endTime)
if (d.milestone) {
endX = startX + theBarHeight
}
const textWidth = this.getBBox().width
let secNum = 0
for (let i = 0; i < categories.length; i++) {
Expand Down Expand Up @@ -228,6 +253,10 @@ export const draw = function (text, id) {
}
}

if (d.milestone) {
taskType += ' milestoneText'
}

// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
Expand Down
7 changes: 7 additions & 0 deletions src/themes/gantt.scss
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@
shape-rendering: crispEdges;
}

.milestone {
transform: rotate(45deg) scale(0.8,0.8);
}

.milestoneText {
font-style: italic;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
Expand Down