Calendars and Duration
Source code
---
import GanttScheduling from '@revolist/revogrid-examples/components/gantt/GanttScheduling.vue';
---
<GanttScheduling client:only="vue" />// src/components/gantt/GanttScheduling.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';
// Calendar-aware scheduling: durations are in working days (Mon–Fri, excluding holidays)
const ganttConfig = {
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'America/New_York',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
statusDate: '2026-04-06',
zoomPreset: 'week' as const,
scheduling: {
excludeHolidaysFromDuration: true, // durations skip weekends and holidays
},
visuals: {
shadeNonWorkingTime: true, // shade weekend columns on the timeline
projectLineDate: '2026-04-06', // vertical status-date line
},
};
// US calendar with public holidays
const calendars: CalendarEntity[] = [
{
id: CALENDAR_ID,
name: 'US Standard',
timeZone: 'America/New_York',
workingDays: [1, 2, 3, 4, 5], // Mon–Fri
holidays: [
'2026-05-25', // Memorial Day
'2026-07-04', // Independence Day
],
hoursPerDay: 8,
},
];
const tasks: GanttTaskSourceRow[] = [
{
id: 't1', parentId: null,
name: 'Design', type: 'summary', workflowStatus: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
percentDone: 60, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't2', parentId: 't1',
name: 'Wireframes', type: 'task', workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't3', parentId: 't1',
name: 'Design Review', type: 'milestone', workflowStatus: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't4', parentId: 't1',
name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
percentDone: 40, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't5', parentId: null,
name: 'Development', type: 'summary', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't6', parentId: 't5',
name: 'Frontend', type: 'task', workflowStatus: 'not-started',
// constraint: cannot start before May 4 (waiting on external API)
startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
constraintType: 'start-no-earlier-than',
constraintDate: '2026-05-04',
},
{
id: 't7', parentId: 't5',
name: 'Backend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
deadlineDate: '2026-05-22', // deadline marker — project expects early delivery
},
{
id: 't8', parentId: null,
name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, 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 },
];
export function load(parentSelector: string) {
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.source = tasks;
grid.columns = [
createDefaultTaskTableColumn('wbs'),
createDefaultTaskTableColumn('name'),
];
document.querySelector(parentSelector)?.appendChild(grid);
}
<template>
<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"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentThemeVue } from '../composables/useRandomData';
const { isDark } = currentThemeVue();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';
const plugins = ref([GanttPlugin]);
const ganttConfig = ref({
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'America/New_York',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
statusDate: '2026-04-06',
zoomPreset: 'week' as const,
scheduling: {
excludeHolidaysFromDuration: true,
},
visuals: {
shadeNonWorkingTime: true,
projectLineDate: '2026-04-06',
},
});
const calendars = ref<CalendarEntity[]>([
{
id: CALENDAR_ID,
name: 'US Standard',
timeZone: 'America/New_York',
workingDays: [1, 2, 3, 4, 5],
holidays: ['2026-05-25', '2026-07-04'],
hoursPerDay: 8,
},
]);
const tasks = ref<GanttTaskSourceRow[]>([
{
id: 't1', parentId: null,
name: 'Design', type: 'summary', workflowStatus: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
percentDone: 60, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't2', parentId: 't1',
name: 'Wireframes', type: 'task', workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't3', parentId: 't1',
name: 'Design Review', type: 'milestone', workflowStatus: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't4', parentId: 't1',
name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
percentDone: 40, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't5', parentId: null,
name: 'Development', type: 'summary', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't6', parentId: 't5',
name: 'Frontend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
constraintType: 'start-no-earlier-than',
constraintDate: '2026-05-04',
},
{
id: 't7', parentId: 't5',
name: 'Backend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
deadlineDate: '2026-05-22',
},
{
id: 't8', parentId: null,
name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, 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 columns = ref([
createDefaultTaskTableColumn('wbs'),
createDefaultTaskTableColumn('name'),
]);
</script>
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';
const tasks: GanttTaskSourceRow[] = [
{
id: 't1', parentId: null,
name: 'Design', type: 'summary', workflowStatus: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
percentDone: 60, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't2', parentId: 't1',
name: 'Wireframes', type: 'task', workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't3', parentId: 't1',
name: 'Design Review', type: 'milestone', workflowStatus: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't4', parentId: 't1',
name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
percentDone: 40, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't5', parentId: null,
name: 'Development', type: 'summary', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't6', parentId: 't5',
name: 'Frontend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
constraintType: 'start-no-earlier-than',
constraintDate: '2026-05-04',
},
{
id: 't7', parentId: 't5',
name: 'Backend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
deadlineDate: '2026-05-22',
},
{
id: 't8', parentId: null,
name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, 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: 'US Standard',
timeZone: 'America/New_York',
workingDays: [1, 2, 3, 4, 5],
holidays: ['2026-05-25', '2026-07-04'],
hoursPerDay: 8,
},
];
function GanttScheduling() {
const ganttConfig = useMemo(() => ({
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'America/New_York',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
statusDate: '2026-04-06',
zoomPreset: 'week' as const,
scheduling: {
excludeHolidaysFromDuration: true,
},
visuals: {
shadeNonWorkingTime: true,
projectLineDate: '2026-04-06',
},
}), []);
const columns = useMemo(() => [
createDefaultTaskTableColumn('wbs'),
createDefaultTaskTableColumn('name'),
], []);
return (
<RevoGrid
style={{ height: '500px' }}
theme={isDark() ? 'darkCompact' : 'compact'}
hideAttribution
plugins={[GanttPlugin]}
source={tasks}
columns={columns}
gantt={ganttConfig}
ganttDependencies={dependencies}
ganttCalendars={calendars}
/>
);
}
export default GanttScheduling;
import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';
@Component({
selector: 'gantt-scheduling-grid',
standalone: true,
imports: [RevoGrid],
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
template: `
<revo-grid
style="min-height: 500px"
[theme]="theme"
[hideAttribution]="true"
[plugins]="plugins"
[source]="tasks"
[columns]="columns"
[gantt]="ganttConfig"
[ganttDependencies]="dependencies"
[ganttCalendars]="calendars"
></revo-grid>
`,
encapsulation: ViewEncapsulation.None,
})
export class GanttSchedulingGridComponent {
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
plugins = [GanttPlugin];
ganttConfig = {
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'America/New_York',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
statusDate: '2026-04-06',
zoomPreset: 'week' as const,
scheduling: {
excludeHolidaysFromDuration: true,
},
visuals: {
shadeNonWorkingTime: true,
projectLineDate: '2026-04-06',
},
};
calendars: CalendarEntity[] = [
{
id: CALENDAR_ID,
name: 'US Standard',
timeZone: 'America/New_York',
workingDays: [1, 2, 3, 4, 5],
holidays: ['2026-05-25', '2026-07-04'],
hoursPerDay: 8,
},
];
tasks: GanttTaskSourceRow[] = [
{
id: 't1', parentId: null,
name: 'Design', type: 'summary', workflowStatus: 'in-progress',
startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
percentDone: 60, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't2', parentId: 't1',
name: 'Wireframes', type: 'task', workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't3', parentId: 't1',
name: 'Design Review', type: 'milestone', workflowStatus: 'done',
startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't4', parentId: 't1',
name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
percentDone: 40, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't5', parentId: null,
name: 'Development', type: 'summary', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't6', parentId: 't5',
name: 'Frontend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
constraintType: 'start-no-earlier-than',
constraintDate: '2026-05-04',
},
{
id: 't7', parentId: 't5',
name: 'Backend', type: 'task', workflowStatus: 'not-started',
startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
deadlineDate: '2026-05-22',
},
{
id: 't8', parentId: null,
name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, 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 },
];
columns = [
createDefaultTaskTableColumn('wbs'),
createDefaultTaskTableColumn('name'),
];
}
duration is stored in hours and can be displayed or entered as minutes, hours, days, weeks, months, quarters, or years. Unit conversion uses durationSettings and the effective calendar; see Duration Units and Conversion. Enable calendar exclusion when weekends, holidays, and closed hours should be skipped:
grid.gantt = { scheduling: { excludeHolidaysFromDuration: true, },};Defining Calendars
Section titled “Defining Calendars”Calendars use ISO weekday numbers where Monday is 1 and Sunday is 7:
grid.ganttCalendars = [{ id: 'cal-standard', name: 'Standard', timeZone: 'UTC', workingDays: [1, 2, 3, 4, 5], holidays: ['2026-05-25'], hoursPerDay: 8,}];Each task references a calendar through calendarId. Resource capacity checks use the resource calendar when a resource is assigned.
CalendarEntity.hoursPerDay is the default conversion for task durations on that calendar. Project durationSettings.hoursPerDay can override it globally. Conversion settings do not change workingDays, holidays, or workingHours automatically.
Working-Day Lag
Section titled “Working-Day Lag”Use working-day lag when dependency offsets should skip weekends and holidays:
grid.gantt = { scheduling: { lagCalendar: 'working-days', },};Example: A one-day lag after a Friday skips the weekend and a Monday holiday, landing on Tuesday.