Add custom rendering of milestone tasks

If a task is a milestone, the rect shape will be square in the center of
the original width of the rectangular calculated if it weren't a
milestone. The placement of the label text is adjusted accordingly.
Both the rect and the text get a 'milestone' and 'milestoneText' class
accordingly.
The scss rotates the square and scales it a bit down, so that it's a
diamond, which appears to be the defacto standard for milestone icons.
The label text is put in italics.
The rotational transform is done in the scss, so that it's easy for
users to create their own milestone icon-style.
This commit is contained in:
Gijs van Dam
2019-02-01 12:34:55 +08:00
parent a3eef7298e
commit 48f8c3f85a
2 changed files with 48 additions and 11 deletions

View File

@@ -98,6 +98,7 @@ export const draw = function (text, id) {
} }
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) { 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') svg.append('g')
.selectAll('rect') .selectAll('rect')
.data(theArray) .data(theArray)
@@ -120,6 +121,7 @@ export const draw = function (text, id) {
return 'section section0' return 'section section0'
}) })
//draw the rects representing the tasks
const rectangles = svg.append('g') const rectangles = svg.append('g')
.selectAll('rect') .selectAll('rect')
.data(theArray) .data(theArray)
@@ -129,17 +131,26 @@ export const draw = function (text, id) {
.attr('rx', 3) .attr('rx', 3)
.attr('ry', 3) .attr('ry', 3)
.attr('x', function (d) { .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 return timeScale(d.startTime) + theSidePad
}) })
.attr('y', function (d, i) { .attr('y', function (d, i) {
return i * theGap + theTopPad return i * theGap + theTopPad
}) })
.attr('width', function (d) { .attr('width', function (d) {
if (d.milestone) {
return theBarHeight
}
return (timeScale(d.endTime) - timeScale(d.startTime)) return (timeScale(d.endTime) - timeScale(d.startTime))
}) })
.attr('height', theBarHeight) .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) { .attr('class', function (d) {
const res = 'task ' const res = 'task'
let secNum = 0 let secNum = 0
for (let i = 0; i < categories.length; i++) { for (let i = 0; i < categories.length; i++) {
@@ -148,37 +159,49 @@ export const draw = function (text, id) {
} }
} }
let milestone = ''
if (d.milestone) {
milestone = ' milestone'
}
if (d.active) { if (d.active) {
if (d.crit) { if (d.crit) {
return res + ' activeCrit' + secNum return res + milestone + ' activeCrit' + secNum
} else { } else {
return res + ' active' + secNum return res + milestone + ' active' + secNum
} }
} }
if (d.done) { if (d.done) {
if (d.crit) { if (d.crit) {
return res + ' doneCrit' + secNum return res + milestone + ' doneCrit' + secNum
} else { } else {
return res + ' done' + secNum return res + milestone + ' done' + secNum
} }
} }
if (d.crit) { if (d.crit) {
return res + ' crit' + secNum return res + milestone + ' crit' + secNum
} }
return res + ' task' + secNum return res + milestone + ' task' + secNum
}) })
//Append task labels
rectangles.append('text') rectangles.append('text')
.text(function (d) { .text(function (d) {
return d.task return d.task
}) })
.attr('font-size', conf.fontSize) .attr('font-size', conf.fontSize)
.attr('x', function (d) { .attr('x', function (d) {
const startX = timeScale(d.startTime) let startX = timeScale(d.startTime)
const endX = timeScale(d.endTime) 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 const textWidth = this.getBBox().width
// Check id text width > width of rectangle // Check id text width > width of rectangle
@@ -198,7 +221,10 @@ export const draw = function (text, id) {
.attr('text-height', theBarHeight) .attr('text-height', theBarHeight)
.attr('class', function (d) { .attr('class', function (d) {
const startX = timeScale(d.startTime) 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 const textWidth = this.getBBox().width
let secNum = 0 let secNum = 0
for (let i = 0; i < categories.length; i++) { for (let i = 0; i < categories.length; i++) {
@@ -228,6 +254,10 @@ export const draw = function (text, id) {
} }
} }
if (d.milestone) {
taskType += ' milestoneText'
}
// Check id text width > width of rectangle // Check id text width > width of rectangle
if (textWidth > (endX - startX)) { if (textWidth > (endX - startX)) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) { if (endX + textWidth + 1.5 * conf.leftPadding > w) {

View File

@@ -188,6 +188,13 @@
shape-rendering: crispEdges; shape-rendering: crispEdges;
} }
.milestone {
transform: rotate(45deg) scale(0.8,0.8);
}
.milestoneText {
font-style: italic;
}
.doneCritText0, .doneCritText0,
.doneCritText1, .doneCritText1,
.doneCritText2, .doneCritText2,