Gantt Baselines
A baseline is a saved snapshot of task dates and progress at a specific point in time (e.g. when the project was approved). The Gantt chart can overlay these baseline bars beneath the live bars, making schedule drift immediately visible.
Source code
---
import GanttBaseline from '@revolist/revogrid-examples/components/gantt/GanttBaseline.vue';
---
<GanttBaseline client:only="vue" /> // src/components/gantt/GanttBaseline.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity, BaselineSnapshot } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-standard';
const ganttConfig = {
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'UTC',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
zoomPreset: 'week' as const,
visuals: {
showBaseline: true,
baselineId: 'baseline-approved',
},
};
const baselineColumnProps = [
'wbs',
'name',
'startDate',
'endDate',
'startVarianceDays',
'finishVarianceDays',
'durationVarianceDays',
'progressVariancePercent',
] as const;
const calendars: CalendarEntity[] = [
{
id: CALENDAR_ID, name: 'Standard', timeZone: 'UTC',
workingDays: [1, 2, 3, 4, 5], holidays: [], hoursPerDay: 8,
},
];
// Current (live) tasks — note Design slipped by 3 days vs baseline
const tasks: TaskEntity[] = [
{
id: 't1', projectId: PROJECT_ID, parentId: null,
wbsCode: '1', name: 'Design', type: 'summary',
status: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18,
progressPercent: 50, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't2', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.1', name: 'Wireframes', type: 'task',
status: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't3', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.2', name: 'Design Review', type: 'milestone',
status: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
{
id: 't4', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.3', name: 'Visual Design', type: 'task',
status: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-27', durationDays: 13, // slipped from 10 → 13
progressPercent: 30, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't5', projectId: PROJECT_ID, parentId: null,
wbsCode: '2', name: 'Development', type: 'summary',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't6', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.1', name: 'Frontend', type: 'task',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't7', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.2', name: 'Backend', type: 'task',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't8', projectId: PROJECT_ID, parentId: null,
wbsCode: '3', name: 'Launch', type: 'milestone',
status: 'not-started',
startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
];
const dependencies: DependencyEntity[] = [
{ id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
{ id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
{ id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
{ id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
{ id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
{ id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];
// Baseline — the original approved plan before Visual Design slipped
const baselines: BaselineSnapshot[] = [
{
id: 'baseline-approved',
name: 'Approved Plan',
capturedAt: '2026-04-01T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 0 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 0 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 0 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 0 },
{ taskId: 't5', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-27', endDate: '2026-05-13', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-20', endDate: '2026-05-20', durationDays: 0, progressPercent: 0 },
],
},
{
id: 'baseline-replan',
name: 'Replan After Design Slip',
capturedAt: '2026-04-15T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18, progressPercent: 25 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-25', durationDays: 13, progressPercent: 20 },
{ taskId: 't5', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0, progressPercent: 0 },
],
},
];
export function load(parentSelector: string) {
const container = document.createElement('div');
container.className = 'gantt-example-container';
const toolbar = document.createElement('div');
toolbar.className = 'gantt-toolbar';
Object.assign(toolbar.style, {
marginBottom: '1rem',
display: 'flex',
gap: '1rem',
alignItems: 'center',
});
const label = document.createElement('label');
Object.assign(label.style, {
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
cursor: 'pointer',
});
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = ganttConfig.visuals.showBaseline;
label.appendChild(checkbox);
label.appendChild(document.createTextNode('Show Baseline'));
toolbar.appendChild(label);
const selectLabel = document.createElement('label');
Object.assign(selectLabel.style, {
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
});
const select = document.createElement('select');
for (const baseline of baselines) {
const option = document.createElement('option');
option.value = baseline.id;
option.textContent = baseline.name;
select.appendChild(option);
}
select.value = ganttConfig.visuals.baselineId;
selectLabel.appendChild(document.createTextNode('Baseline'));
selectLabel.appendChild(select);
toolbar.appendChild(selectLabel);
container.appendChild(toolbar);
const grid = document.createElement('revo-grid');
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.hideAttribution = true;
grid.plugins = [GanttPlugin];
grid.gantt = ganttConfig;
grid.ganttCalendars = calendars;
grid.ganttDependencies = dependencies;
grid.ganttBaselines = baselines;
grid.source = tasks;
grid.columns = baselineColumnProps.map((prop) => createDefaultTaskTableColumn(prop));
container.appendChild(grid);
document.querySelector(parentSelector)?.appendChild(container);
checkbox.addEventListener('change', (e) => {
const showBaseline = (e.target as HTMLInputElement).checked;
grid.gantt = {
...grid.gantt,
visuals: {
...grid.gantt.visuals,
showBaseline,
},
};
});
select.addEventListener('change', (e) => {
grid.gantt = {
...grid.gantt,
visuals: {
...grid.gantt.visuals,
baselineId: (e.target as HTMLSelectElement).value,
},
};
});
}
<template>
<div class="gantt-example-container">
<div class="gantt-toolbar" :style="{ marginBottom: '1rem', display: 'flex', gap: '1rem', alignItems: 'center' }">
<label :style="{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }">
<input
type="checkbox"
v-model="showBaseline"
/>
Show Baseline
</label>
<label :style="{ display: 'flex', alignItems: 'center', gap: '0.5rem' }">
Baseline
<select v-model="baselineId">
<option v-for="baseline in baselines" :key="baseline.id" :value="baseline.id">
{{ baseline.name }}
</option>
</select>
</label>
</div>
<RevoGrid
hide-attribution
style="height: 500px"
:theme="isDark ? 'darkCompact' : 'compact'"
:plugins="plugins"
:source="tasks"
:columns="columns"
:gantt.prop="ganttConfig"
:gantt-dependencies.prop="dependencies"
:gantt-calendars.prop="calendars"
:gantt-baselines.prop="baselines"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity, BaselineSnapshot } from '@revolist/revogrid-enterprise';
import { currentThemeVue } from '../composables/useRandomData';
const { isDark } = currentThemeVue();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-standard';
const plugins = ref([GanttPlugin]);
const showBaseline = ref(true);
const baselineId = ref('baseline-approved');
const ganttConfig = computed(() => ({
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'UTC',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
zoomPreset: 'week' as const,
visuals: {
showBaseline: showBaseline.value,
baselineId: baselineId.value,
},
}));
const calendars = ref<CalendarEntity[]>([
{
id: CALENDAR_ID, name: 'Standard', timeZone: 'UTC',
workingDays: [1, 2, 3, 4, 5], holidays: [], hoursPerDay: 8,
},
]);
const tasks = ref<TaskEntity[]>([
{
id: 't1', projectId: PROJECT_ID, parentId: null,
wbsCode: '1', name: 'Design', type: 'summary',
status: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18,
progressPercent: 50, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't2', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.1', name: 'Wireframes', type: 'task',
status: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't3', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.2', name: 'Design Review', type: 'milestone',
status: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
{
id: 't4', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.3', name: 'Visual Design', type: 'task',
status: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-27', durationDays: 13,
progressPercent: 30, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't5', projectId: PROJECT_ID, parentId: null,
wbsCode: '2', name: 'Development', type: 'summary',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't6', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.1', name: 'Frontend', type: 'task',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't7', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.2', name: 'Backend', type: 'task',
status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't8', projectId: PROJECT_ID, parentId: null,
wbsCode: '3', name: 'Launch', type: 'milestone',
status: 'not-started',
startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
]);
const dependencies = ref<DependencyEntity[]>([
{ id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
{ id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
{ id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
{ id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
{ id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
{ id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
]);
const baselines = ref<BaselineSnapshot[]>([
{
id: 'baseline-approved',
name: 'Approved Plan',
capturedAt: '2026-04-01T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 0 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 0 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 0 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 0 },
{ taskId: 't5', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-27', endDate: '2026-05-13', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-20', endDate: '2026-05-20', durationDays: 0, progressPercent: 0 },
],
},
{
id: 'baseline-replan',
name: 'Replan After Design Slip',
capturedAt: '2026-04-15T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18, progressPercent: 25 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-25', durationDays: 13, progressPercent: 20 },
{ taskId: 't5', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0, progressPercent: 0 },
],
},
]);
const baselineColumnProps = [
'wbs',
'name',
'startDate',
'endDate',
'startVarianceDays',
'finishVarianceDays',
'durationVarianceDays',
'progressVariancePercent',
] as const;
const columns = ref(baselineColumnProps.map((prop) => createDefaultTaskTableColumn(prop)));
</script>
// src/components/gantt/GanttBaseline.tsx
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity, BaselineSnapshot } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-standard';
const baselineColumnProps = [
'wbs',
'name',
'startDate',
'endDate',
'startVarianceDays',
'finishVarianceDays',
'durationVarianceDays',
'progressVariancePercent',
] as const;
const tasks: TaskEntity[] = [
{
id: 't1', projectId: PROJECT_ID, parentId: null,
wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18,
progressPercent: 50, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't2', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't3', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
{
id: 't4', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-27', durationDays: 13, // slipped
progressPercent: 30, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't5', projectId: PROJECT_ID, parentId: null,
wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't6', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't7', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't8', projectId: PROJECT_ID, parentId: null,
wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
];
const dependencies: DependencyEntity[] = [
{ id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
{ id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
{ id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
{ id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
{ id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
{ id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];
const calendars: CalendarEntity[] = [
{ id: CALENDAR_ID, name: 'Standard', timeZone: 'UTC', workingDays: [1, 2, 3, 4, 5], holidays: [], hoursPerDay: 8 },
];
const baselines: BaselineSnapshot[] = [
{
id: 'baseline-approved',
name: 'Approved Plan',
capturedAt: '2026-04-01T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 0 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 0 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 0 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 0 },
{ taskId: 't5', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-27', endDate: '2026-05-13', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-20', endDate: '2026-05-20', durationDays: 0, progressPercent: 0 },
],
},
{
id: 'baseline-replan',
name: 'Replan After Design Slip',
capturedAt: '2026-04-15T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18, progressPercent: 25 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-25', durationDays: 13, progressPercent: 20 },
{ taskId: 't5', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0, progressPercent: 0 },
],
},
];
function GanttBaseline() {
const [showBaseline, setShowBaseline] = React.useState(true);
const [baselineId, setBaselineId] = React.useState('baseline-approved');
const ganttConfig = useMemo(() => ({
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'UTC',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
zoomPreset: 'week' as const,
visuals: { showBaseline, baselineId },
}), [baselineId, showBaseline]);
const columns = useMemo(() => baselineColumnProps.map((prop) => createDefaultTaskTableColumn(prop)), []);
return (
<div className="gantt-example-container">
<div className="gantt-toolbar" style={{ marginBottom: '1rem', display: 'flex', gap: '1rem', alignItems: 'center' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}>
<input
type="checkbox"
checked={showBaseline}
onChange={(e) => setShowBaseline(e.target.checked)}
/>
Show Baseline
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
Baseline
<select value={baselineId} onChange={(event) => setBaselineId(event.target.value)}>
{baselines.map((baseline) => (
<option key={baseline.id} value={baseline.id}>{baseline.name}</option>
))}
</select>
</label>
</div>
<RevoGrid
style={{ height: '500px' }}
theme={isDark() ? 'darkCompact' : 'compact'}
hideAttribution
plugins={[GanttPlugin]}
source={tasks}
columns={columns}
gantt={ganttConfig}
ganttDependencies={dependencies}
ganttCalendars={calendars}
ganttBaselines={baselines}
/>
</div>
);
}
export default GanttBaseline;
// src/components/gantt/GanttBaselineAngular.ts
import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity, BaselineSnapshot } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-standard';
@Component({
selector: 'gantt-baseline-grid',
standalone: true,
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
imports: [CommonModule, RevoGrid],
template: `
<div class="gantt-example-container">
<div class="gantt-toolbar" style="margin-bottom: 1rem; display: flex; gap: 1rem; align-items: center">
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer">
<input
type="checkbox"
[checked]="showBaseline"
(change)="toggleBaseline($event)"
/>
Show Baseline
</label>
<label style="display: flex; align-items: center; gap: 0.5rem">
Baseline
<select [value]="baselineId" (change)="selectBaseline($event)">
<option *ngFor="let baseline of baselines" [value]="baseline.id">{{ baseline.name }}</option>
</select>
</label>
</div>
<revo-grid
style="height: 500px"
[theme]="theme"
[hideAttribution]="true"
[plugins]="plugins"
[source]="tasks"
[columns]="columns"
[gantt]="ganttConfig"
[ganttDependencies]="dependencies"
[ganttCalendars]="calendars"
[ganttBaselines]="baselines"
></revo-grid>
</div>
`,
encapsulation: ViewEncapsulation.None,
})
export class GanttBaselineGridComponent {
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
plugins = [GanttPlugin];
showBaseline = true;
baselineId = 'baseline-approved';
ganttConfig = this.createGanttConfig();
toggleBaseline(event: Event) {
this.showBaseline = (event.target as HTMLInputElement).checked;
this.updateGanttConfig();
}
selectBaseline(event: Event) {
this.baselineId = (event.target as HTMLSelectElement).value;
this.updateGanttConfig();
}
private createGanttConfig() {
return {
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'UTC',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
zoomPreset: 'week' as const,
visuals: { showBaseline: this.showBaseline, baselineId: this.baselineId },
};
}
private updateGanttConfig() {
this.ganttConfig = this.createGanttConfig();
}
calendars: CalendarEntity[] = [
{ id: CALENDAR_ID, name: 'Standard', timeZone: 'UTC', workingDays: [1, 2, 3, 4, 5], holidays: [], hoursPerDay: 8 },
];
tasks: TaskEntity[] = [
{
id: 't1', projectId: PROJECT_ID, parentId: null,
wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18,
progressPercent: 50, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't2', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't3', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
{
id: 't4', projectId: PROJECT_ID, parentId: 't1',
wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-27', durationDays: 13,
progressPercent: 30, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't5', projectId: PROJECT_ID, parentId: null,
wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't6', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
},
{
id: 't7', projectId: PROJECT_ID, parentId: 't5',
wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
},
{
id: 't8', projectId: PROJECT_ID, parentId: null,
wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0,
progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
},
];
dependencies: DependencyEntity[] = [
{ id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
{ id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
{ id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
{ id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
{ id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
{ id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];
baselines: BaselineSnapshot[] = [
{
id: 'baseline-approved',
name: 'Approved Plan',
capturedAt: '2026-04-01T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 0 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 0 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 0 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 0 },
{ taskId: 't5', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-27', endDate: '2026-05-13', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-27', endDate: '2026-05-20', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-20', endDate: '2026-05-20', durationDays: 0, progressPercent: 0 },
],
},
{
id: 'baseline-replan',
name: 'Replan After Design Slip',
capturedAt: '2026-04-15T08:00:00Z',
tasks: [
{ taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-27', durationDays: 18, progressPercent: 25 },
{ taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100 },
{ taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100 },
{ taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-25', durationDays: 13, progressPercent: 20 },
{ taskId: 't5', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't6', startDate: '2026-04-28', endDate: '2026-05-14', durationDays: 13, progressPercent: 0 },
{ taskId: 't7', startDate: '2026-04-28', endDate: '2026-05-21', durationDays: 18, progressPercent: 0 },
{ taskId: 't8', startDate: '2026-05-21', endDate: '2026-05-21', durationDays: 0, progressPercent: 0 },
],
},
];
columns = [
'wbs',
'name',
'startDate',
'endDate',
'startVarianceDays',
'finishVarianceDays',
'durationVarianceDays',
'progressVariancePercent',
].map((prop) => createDefaultTaskTableColumn(prop as any));
}
Adding a Baseline
Section titled “Adding a Baseline”Pass one or more BaselineSnapshot objects via grid.ganttBaselines:
grid.ganttBaselines = [ { id: 'baseline-approved', name: 'Approved Plan', capturedAt: '2026-04-01T08:00:00Z', tasks: [ { taskId: 't1', startDate: '2026-04-06', endDate: '2026-04-22', durationDays: 13, progressPercent: 0 }, { taskId: 't2', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 0 }, { taskId: 't3', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 0 }, { taskId: 't4', startDate: '2026-04-13', endDate: '2026-04-22', durationDays: 8, progressPercent: 0 }, { taskId: 't5', startDate: '2026-04-23', endDate: '2026-05-16', durationDays: 18, progressPercent: 0 }, { taskId: 't6', startDate: '2026-04-23', endDate: '2026-05-09', durationDays: 13, progressPercent: 0 }, { taskId: 't7', startDate: '2026-04-23', endDate: '2026-05-16', durationDays: 18, progressPercent: 0 }, { taskId: 't8', startDate: '2026-05-16', endDate: '2026-05-16', durationDays: 0, progressPercent: 0 }, ], },];You can also capture a snapshot from the current scheduled state:
const plugins = await grid.getPlugins();const gantt = plugins.find((plugin) => plugin.constructor?.name === 'GanttPlugin');const baseline = gantt?.captureBaseline?.({ id: 'baseline-current', name: 'Current Approved Plan', capturedAt: '2026-04-15T08:00:00Z',});
if (baseline) { grid.ganttBaselines = [ ...(grid.ganttBaselines ?? []), baseline, ];}Enabling the Overlay
Section titled “Enabling the Overlay”Turn on baseline rendering through the visuals key in your project config:
grid.gantt = { // …required fields visuals: { showBaseline: true, baselineId: 'baseline-approved', // optional — defaults to the latest snapshot },};When showBaseline is true, a translucent bar is drawn behind each live task bar showing its baseline position.
Multiple Baselines
Section titled “Multiple Baselines”You can store several snapshots and switch between them at runtime:
// Initially show the approved plangrid.gantt = { ...config, visuals: { showBaseline: true, baselineId: 'baseline-approved' } };
// Switch to a re-baseline after scope changegrid.gantt = { ...config, visuals: { showBaseline: true, baselineId: 'baseline-replan' } };Variance Columns
Section titled “Variance Columns”When a baseline is visible, each projected task row also exposes optional variance fields:
| Field | Description |
|---|---|
startVarianceDays | Current scheduled start minus baseline start, in calendar days |
finishVarianceDays | Current scheduled finish minus baseline finish, in calendar days |
durationVarianceDays | Current scheduled duration minus baseline duration |
progressVariancePercent | Current progress minus baseline progress, in percentage points |
Add the default columns when users need to inspect drift numerically:
grid.columns = [ createDefaultTaskTableColumn('name'), createDefaultTaskTableColumn('startVarianceDays'), createDefaultTaskTableColumn('finishVarianceDays'), createDefaultTaskTableColumn('durationVarianceDays'), createDefaultTaskTableColumn('progressVariancePercent'),];BaselineSnapshot Fields
Section titled “BaselineSnapshot Fields”| Field | Type | Description |
|---|---|---|
id | BaselineId | Unique identifier for this snapshot |
name | string | Human-readable label (e.g. "Q1 Approved Plan") |
capturedAt | ISODateTimeString | When the snapshot was taken |
tasks | BaselineTaskSnapshot[] | Per-task date and progress records |
BaselineTaskSnapshot Fields
Section titled “BaselineTaskSnapshot Fields”| Field | Type | Description |
|---|---|---|
taskId | TaskId | References the live task |
startDate | ISODateString | Baseline start date |
endDate | ISODateString | Baseline end date |
durationDays | number | Baseline duration in calendar days |
progressPercent | number | Baseline progress (0–100) |