Merge pull request #6479 from Shahir-47/feature/3250/add_vertline_to_gantt_plot

feat: Add vertical line to gantt plot at specified time
This commit is contained in:
Sidharth Vinod
2025-04-29 06:25:57 +00:00
committed by GitHub
12 changed files with 114 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: Add Vertical Line To Gantt Plot At Specified Time

View File

@@ -358,6 +358,23 @@ describe('Gantt diagram', () => {
);
});
it('should render a gantt diagram with a vert tag', () => {
imgSnapshotTest(
`
gantt
title A Gantt Diagram
dateFormat ss
axisFormat %Ss
section Section
A task : a1, 00, 6s
Milestone : vert, 01,
section Another
Task in sec : 06, 3s
another task : 3s
`
);
});
it('should render a gantt diagram with tick is 2 milliseconds', () => {
imgSnapshotTest(
`

View File

@@ -229,6 +229,30 @@ gantt
Final milestone : milestone, m2, 18:08, 4m
```
### Vertical Markers
The `vert` keyword lets you add vertical lines to your Gantt chart, making it easy to highlight important dates like deadlines, events, or checkpoints. These markers extend across the entire chart and are positioned based on the date you provide. Unlike milestones, vertical markers dont take up a row. Theyre purely visual reference points that help break up the timeline and make important moments easier to spot.
```mermaid-example
gantt
dateFormat HH:mm
axisFormat %H:%M
Initial vert : vert, v1, 17:30, 2m
Task A : 3m
Task B : 8m
Final vert : vert, v2, 17:58, 4m
```
```mermaid
gantt
dateFormat HH:mm
axisFormat %H:%M
Initial vert : vert, v1, 17:30, 2m
Task A : 3m
Task B : 8m
Final vert : vert, v2, 17:58, 4m
```
## Setting dates
`dateFormat` defines the format of the date **input** of your gantt elements. How these dates are represented in the rendered chart **output** are defined by `axisFormat`.

View File

@@ -33,7 +33,7 @@ let sections = [];
let tasks = [];
let currentSection = '';
let displayMode = '';
const tags = ['active', 'done', 'crit', 'milestone'];
const tags = ['active', 'done', 'crit', 'milestone', 'vert'];
let funs = [];
let inclusiveEndDates = false;
let topAxis = false;
@@ -422,7 +422,7 @@ const compileData = function (prevTask, dataStr) {
const task = {};
// Get tags like active, done, crit and milestone
// Get tags like active, done, crit, milestone, and vert
getTaskTags(data, task, tags);
for (let i = 0; i < data.length; i++) {
@@ -470,7 +470,7 @@ const parseData = function (prevTaskId, dataStr) {
const task = {};
// Get tags like active, done, crit and milestone
// Get tags like active, done, crit, milestone, and vert
getTaskTags(data, task, tags);
for (let i = 0; i < data.length; i++) {
@@ -538,6 +538,7 @@ export const addTask = function (descr, data) {
rawTask.done = taskInfo.done;
rawTask.crit = taskInfo.crit;
rawTask.milestone = taskInfo.milestone;
rawTask.vert = taskInfo.vert;
rawTask.order = lastOrder;
lastOrder++;
@@ -570,6 +571,7 @@ export const addTaskOrg = function (descr, data) {
newTask.done = taskInfo.done;
newTask.crit = taskInfo.crit;
newTask.milestone = taskInfo.milestone;
newTask.vert = taskInfo.vert;
lastTask = newTask;
tasks.push(newTask);
};

View File

@@ -231,10 +231,11 @@ export const draw = function (text, id, version, diagObj) {
* @param w
*/
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
// Sort theArray so that tasks with `vert` come last
theArray.sort((a, b) => (a.vert === b.vert ? 0 : a.vert ? 1 : -1));
// Get unique task orders. Required to draw the background rects when display mode is compact.
const uniqueTaskOrderIds = [...new Set(theArray.map((item) => item.order))];
const uniqueTasks = uniqueTaskOrderIds.map((id) => theArray.find((item) => item.order === id));
// Draw background rects covering the entire width of the graph, these form the section rows.
svg
.append('g')
@@ -259,7 +260,8 @@ export const draw = function (text, id, version, diagObj) {
}
}
return 'section section0';
});
})
.enter();
// Draw the rects representing the tasks
const rectangles = svg.append('g').selectAll('rect').data(theArray).enter();
@@ -289,15 +291,26 @@ export const draw = function (text, id, version, diagObj) {
.attr('y', function (d, i) {
// Ignore the incoming i value and use our order instead
i = d.order;
if (d.vert) {
return conf.gridLineStartPadding;
}
return i * theGap + theTopPad;
})
.attr('width', function (d) {
if (d.milestone) {
return theBarHeight;
}
if (d.vert) {
return 0.08 * theBarHeight;
}
return timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime);
})
.attr('height', theBarHeight)
.attr('height', function (d) {
if (d.vert) {
return taskArray.length * (conf.barHeight + conf.barGap) + conf.barHeight * 2;
}
return theBarHeight;
})
.attr('transform-origin', function (d, i) {
// Ignore the incoming i value and use our order instead
i = d.order;
@@ -354,6 +367,9 @@ export const draw = function (text, id, version, diagObj) {
if (d.milestone) {
taskClass = ' milestone ' + taskClass;
}
if (d.vert) {
taskClass = ' vert ' + taskClass;
}
taskClass += secNum;
@@ -377,10 +393,13 @@ export const draw = function (text, id, version, diagObj) {
let endX = timeScale(d.renderEndTime || d.endTime);
if (d.milestone) {
startX += 0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) - 0.5 * theBarHeight;
}
if (d.milestone) {
endX = startX + theBarHeight;
}
if (d.vert) {
return timeScale(d.startTime) + theSidePad;
}
const textWidth = this.getBBox().width;
// Check id text width > width of rectangle
@@ -396,6 +415,9 @@ export const draw = function (text, id, version, diagObj) {
})
.attr('y', function (d, i) {
// Ignore the incoming i value and use our order instead
if (d.vert) {
return conf.gridLineStartPadding + taskArray.length * (conf.barHeight + conf.barGap) + 60;
}
i = d.order;
return i * theGap + conf.barHeight / 2 + (conf.fontSize / 2 - 2) + theTopPad;
})
@@ -406,6 +428,7 @@ export const draw = function (text, id, version, diagObj) {
if (d.milestone) {
endX = startX + theBarHeight;
}
const textWidth = this.getBBox().width;
let classStr = '';
@@ -445,6 +468,10 @@ export const draw = function (text, id, version, diagObj) {
taskType += ' milestoneText';
}
if (d.vert) {
taskType += ' vertText';
}
// Check id text width > width of rectangle
if (textWidth > endX - startX) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) {

View File

@@ -237,6 +237,16 @@ const getStyles = (options) =>
fill: ${options.taskTextDarkColor} !important;
}
.vert {
stroke: ${options.vertLineColor};
}
.vertText {
font-size: 15px;
text-anchor: middle;
fill: ${options.vertLineColor} !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,

View File

@@ -150,6 +150,20 @@ gantt
Final milestone : milestone, m2, 18:08, 4m
```
### Vertical Markers
The `vert` keyword lets you add vertical lines to your Gantt chart, making it easy to highlight important dates like deadlines, events, or checkpoints. These markers extend across the entire chart and are positioned based on the date you provide. Unlike milestones, vertical markers dont take up a row. Theyre purely visual reference points that help break up the timeline and make important moments easier to spot.
```mermaid-example
gantt
dateFormat HH:mm
axisFormat %H:%M
Initial vert : vert, v1, 17:30, 2m
Task A : 3m
Task B : 8m
Final vert : vert, v2, 17:58, 4m
```
## Setting dates
`dateFormat` defines the format of the date **input** of your gantt elements. How these dates are represented in the rendered chart **output** are defined by `axisFormat`.

View File

@@ -98,6 +98,7 @@ class Theme {
this.critBorderColor = this.critBorderColor || '#ff8888';
this.critBkgColor = this.critBkgColor || 'red';
this.todayLineColor = this.todayLineColor || 'red';
this.vertLineColor = this.vertLineColor || 'navy';
this.taskTextColor = this.taskTextColor || this.textColor;
this.taskTextOutsideColor = this.taskTextOutsideColor || this.textColor;
this.taskTextLightColor = this.taskTextLightColor || this.textColor;

View File

@@ -79,6 +79,7 @@ class Theme {
this.critBkgColor = '#E83737';
this.taskTextDarkColor = 'calculated';
this.todayLineColor = '#DB5757';
this.vertLineColor = '#00BFFF';
/* C4 Context Diagram variables */
this.personBorder = this.primaryBorderColor;

View File

@@ -88,6 +88,7 @@ class Theme {
this.critBorderColor = 'calculated';
this.critBkgColor = 'calculated';
this.todayLineColor = 'calculated';
this.vertLineColor = 'calculated';
this.sectionBkgColor = rgba(102, 102, 255, 0.49);
this.altSectionBkgColor = 'white';
@@ -107,6 +108,7 @@ class Theme {
this.critBorderColor = '#ff8888';
this.critBkgColor = 'red';
this.todayLineColor = 'red';
this.vertLineColor = 'navy';
/* C4 Context Diagram variables */
this.personBorder = this.primaryBorderColor;

View File

@@ -81,6 +81,7 @@ class Theme {
this.critBorderColor = '#ff8888';
this.critBkgColor = 'red';
this.todayLineColor = 'red';
this.vertLineColor = '#00BFFF';
/* C4 Context Diagram variables */
this.personBorder = this.primaryBorderColor;

View File

@@ -93,6 +93,7 @@ class Theme {
this.critBkgColor = 'calculated';
this.critBorderColor = 'calculated';
this.todayLineColor = 'calculated';
this.vertLineColor = 'calculated';
/* C4 Context Diagram variables */
this.personBorder = this.primaryBorderColor;
@@ -209,6 +210,7 @@ class Theme {
this.critBorderColor = darken(this.critBkgColor, 10);
this.todayLineColor = this.critBkgColor;
this.vertLineColor = this.critBkgColor;
/* Architecture Diagram variables */
this.archEdgeColor = this.lineColor;