Tasks, Summaries, And Milestones
Source code
// src/components/gantt/GanttBasic.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
import { calendars, dependencies, ganttConfig, initiallyHiddenColumnProps, tasks, taskTableColumnProps } from './gantt-basic-data';
import './gantt-basic.css';
const { isDark } = currentTheme();
const columns = taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop));
export function load(parentSelector: string) {
const parent = document.querySelector(parentSelector);
if (!parent) {
return () => {};
}
const root = document.createElement('section');
const grid = document.createElement('revo-grid');
root.className = 'gantt-basic-demo';
grid.className = 'gantt-basic-grid';
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.hideAttribution = true;
grid.canMoveColumns = true;
grid.plugins = [GanttPlugin];
grid.gantt = ganttConfig;
grid.ganttCalendars = calendars;
grid.ganttDependencies = dependencies;
grid.columns = columns;
grid.hideColumns = [...initiallyHiddenColumnProps];
root.appendChild(grid);
parent.appendChild(root);
grid.source = tasks.map((task) => ({ ...task }));
return () => root.remove();
}
import type {
CalendarEntity,
DependencyEntity,
GanttToolbarColumnOption,
GanttTaskSourceRow,
TaskTableColumnProp,
} from '@revolist/gantt';
export const PROJECT_ID = 'project-web-redesign';
export const CALENDAR_ID = 'cal-standard';
export const ganttConfig = {
id: PROJECT_ID,
name: 'Website Redesign',
version: '1',
currency: 'USD',
timeZone: 'UTC',
primaryCalendarId: CALENDAR_ID,
updatedAt: '2026-04-06T00:00:00Z',
statusDate: '2026-04-06',
zoomPreset: 'day-week' as const,
allowTaskCreate: true,
taskCreateRow: true,
scheduling: {
excludeHolidaysFromDuration: true,
},
};
export const taskTableColumnProps: TaskTableColumnProp[] = [
'wbs',
'name',
'status',
];
export const initiallyHiddenColumnProps: TaskTableColumnProp[] = ['wbs'];
const initiallyVisibleColumnProps = new Set<TaskTableColumnProp>(
taskTableColumnProps.filter((prop) => !initiallyHiddenColumnProps.includes(prop)),
);
const allToolbarColumns = [
{ prop: 'status', label: 'Status' },
{ prop: 'name', label: 'Task' },
{ prop: 'wbs', label: 'WBS' },
] satisfies Array<Omit<GanttToolbarColumnOption, 'visible'>>;
export const toolbarColumns: GanttToolbarColumnOption[] = [...allToolbarColumns]
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))
.map((column) => ({
...column,
visible: initiallyVisibleColumnProps.has(column.prop as TaskTableColumnProp),
}));
export const calendars: CalendarEntity[] = [
{
id: CALENDAR_ID,
name: 'Standard',
timeZone: 'UTC',
workingDays: [1, 2, 3, 4, 5],
holidays: [],
hoursPerDay: 8,
},
];
export const tasks: GanttTaskSourceRow[] = [
{
id: 't1', parentId: null,
name: 'Discovery', type: 'summary',
workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-17', duration: 10,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't2', parentId: 't1',
name: 'Stakeholder Interviews', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-06', endDate: '2026-04-08', duration: 3,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't3', parentId: 't1',
name: 'Analytics Review', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-07', endDate: '2026-04-10', duration: 4,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't4', parentId: 't1',
name: 'Content Inventory', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-09', endDate: '2026-04-14', duration: 4,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't5', parentId: 't1',
name: 'Requirements Workshop', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-13', endDate: '2026-04-16', duration: 4,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't6', parentId: 't1',
name: 'Discovery Sign-off', type: 'milestone',
workflowStatus: 'done',
startDate: '2026-04-17', endDate: '2026-04-17', duration: 0,
percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't7', parentId: null,
name: 'Design', type: 'summary',
workflowStatus: 'in-progress',
startDate: '2026-04-20', endDate: '2026-05-08', duration: 15,
percentDone: 55, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't8', parentId: 't7',
name: 'Information Architecture', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-20', endDate: '2026-04-23', duration: 4,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't9', parentId: 't7',
name: 'Wireframes', type: 'task',
workflowStatus: 'done',
startDate: '2026-04-24', endDate: '2026-04-29', duration: 4,
percentDone: 100, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't10', parentId: 't7',
name: 'Design System Updates', type: 'task',
workflowStatus: 'in-progress',
startDate: '2026-04-27', endDate: '2026-05-01', duration: 5,
percentDone: 70, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't11', parentId: 't7',
name: 'Visual Design', type: 'task',
workflowStatus: 'in-progress',
startDate: '2026-04-30', endDate: '2026-05-07', duration: 6,
percentDone: 45, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't12', parentId: 't7',
name: 'Prototype Review', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-05', endDate: '2026-05-07', duration: 3,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't13', parentId: 't7',
name: 'Design Approval', type: 'milestone',
workflowStatus: 'not-started',
startDate: '2026-05-08', endDate: '2026-05-08', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't14', parentId: null,
name: 'Development', type: 'summary',
workflowStatus: 'not-started',
startDate: '2026-05-11', endDate: '2026-06-05', duration: 20,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't15', parentId: 't14',
name: 'Frontend Shell', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-11', endDate: '2026-05-15', duration: 5,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't16', parentId: 't14',
name: 'CMS Integration', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-11', endDate: '2026-05-20', duration: 8,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't17', parentId: 't14',
name: 'Reusable Page Sections', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-18', endDate: '2026-05-27', duration: 8,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't18', parentId: 't14',
name: 'Search Experience', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-19', endDate: '2026-05-26', duration: 6,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't19', parentId: 't14',
name: 'Analytics Events', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-25', endDate: '2026-05-29', duration: 5,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't20', parentId: 't14',
name: 'Accessibility Pass', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-05-28', endDate: '2026-06-02', duration: 4,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't21', parentId: 't14',
name: 'Performance Tuning', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-06-01', endDate: '2026-06-04', duration: 4,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't22', parentId: 't14',
name: 'Feature Complete', type: 'milestone',
workflowStatus: 'not-started',
startDate: '2026-06-05', endDate: '2026-06-05', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't23', parentId: null,
name: 'QA and Release Prep', type: 'summary',
workflowStatus: 'not-started',
startDate: '2026-06-08', endDate: '2026-06-18', duration: 9,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't24', parentId: 't23',
name: 'Test Plan', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-06-08', endDate: '2026-06-10', duration: 3,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't25', parentId: 't23',
name: 'Cross-browser QA', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-06-10', endDate: '2026-06-15', duration: 4,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't26', parentId: 't23',
name: 'Content QA', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-06-11', endDate: '2026-06-16', duration: 4,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't27', parentId: 't23',
name: 'Release Candidate', type: 'milestone',
workflowStatus: 'not-started',
startDate: '2026-06-18', endDate: '2026-06-18', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
},
{
id: 't28', parentId: null,
name: 'Launch', type: 'task',
workflowStatus: 'not-started',
startDate: '2026-06-19', endDate: '2026-06-23', duration: 3,
percentDone: 0, calendarId: CALENDAR_ID, tags: [],
},
{
id: 't29', parentId: null,
name: 'Post-launch Review', type: 'milestone',
workflowStatus: 'not-started',
startDate: '2026-06-26', endDate: '2026-06-26', duration: 0,
percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
},
];
export const dependencies: DependencyEntity[] = [
{ id: 'd1', predecessorTaskId: 't2', successorTaskId: 't5', type: 'finish-to-start', lagDays: 0 },
{ id: 'd2', predecessorTaskId: 't3', successorTaskId: 't5', type: 'finish-to-start', lagDays: 0 },
{ id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 0 },
{ id: 'd4', predecessorTaskId: 't5', successorTaskId: 't6', type: 'finish-to-start', lagDays: 0 },
{ id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 1 },
{ id: 'd6', predecessorTaskId: 't8', successorTaskId: 't9', type: 'finish-to-start', lagDays: 0 },
{ id: 'd7', predecessorTaskId: 't9', successorTaskId: 't11', type: 'finish-to-start', lagDays: 0 },
{ id: 'd8', predecessorTaskId: 't10', successorTaskId: 't12', type: 'finish-to-start', lagDays: 0 },
{ id: 'd9', predecessorTaskId: 't11', successorTaskId: 't13', type: 'finish-to-start', lagDays: 0 },
{ id: 'd10', predecessorTaskId: 't12', successorTaskId: 't13', type: 'finish-to-start', lagDays: 0 },
{ id: 'd11', predecessorTaskId: 't13', successorTaskId: 't15', type: 'finish-to-start', lagDays: 1 },
{ id: 'd12', predecessorTaskId: 't15', successorTaskId: 't17', type: 'finish-to-start', lagDays: 0 },
{ id: 'd13', predecessorTaskId: 't16', successorTaskId: 't18', type: 'finish-to-start', lagDays: 0 },
{ id: 'd14', predecessorTaskId: 't17', successorTaskId: 't20', type: 'finish-to-start', lagDays: 0 },
{ id: 'd15', predecessorTaskId: 't18', successorTaskId: 't19', type: 'finish-to-start', lagDays: 0 },
{ id: 'd16', predecessorTaskId: 't19', successorTaskId: 't21', type: 'finish-to-start', lagDays: 0 },
{ id: 'd17', predecessorTaskId: 't20', successorTaskId: 't21', type: 'finish-to-start', lagDays: 0 },
{ id: 'd18', predecessorTaskId: 't21', successorTaskId: 't22', type: 'finish-to-start', lagDays: 0 },
{ id: 'd19', predecessorTaskId: 't22', successorTaskId: 't24', type: 'finish-to-start', lagDays: 1 },
{ id: 'd20', predecessorTaskId: 't24', successorTaskId: 't25', type: 'finish-to-start', lagDays: 0 },
{ id: 'd21', predecessorTaskId: 't24', successorTaskId: 't26', type: 'finish-to-start', lagDays: 0 },
{ id: 'd22', predecessorTaskId: 't25', successorTaskId: 't27', type: 'finish-to-start', lagDays: 0 },
{ id: 'd23', predecessorTaskId: 't26', successorTaskId: 't27', type: 'finish-to-start', lagDays: 0 },
{ id: 'd24', predecessorTaskId: 't27', successorTaskId: 't28', type: 'finish-to-start', lagDays: 0 },
{ id: 'd25', predecessorTaskId: 't28', successorTaskId: 't29', type: 'finish-to-start', lagDays: 2 },
];
<template>
<section class="gantt-basic-demo">
<RevoGrid
class="gantt-basic-grid"
hide-attribution
:can-move-columns="true"
:theme="isDark ? 'darkCompact' : 'compact'"
:plugins="plugins"
:source="tasks"
:columns="columns"
:hide-columns.prop="hiddenColumns"
:gantt.prop="ganttConfig"
:gantt-dependencies.prop="dependencies"
:gantt-calendars.prop="calendars"
/>
</section>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import { currentThemeVue } from '../composables/useRandomData';
import { calendars as basicCalendars, dependencies as basicDependencies, ganttConfig as basicGanttConfig, initiallyHiddenColumnProps, tasks as basicTasks, taskTableColumnProps } from './gantt-basic-data';
import './gantt-basic.css';
const { isDark } = currentThemeVue();
const plugins = [GanttPlugin];
const ganttConfig = ref(basicGanttConfig);
const calendars = ref(basicCalendars);
const tasks = ref(basicTasks.map((task) => ({ ...task })));
const dependencies = ref(basicDependencies);
const hiddenColumns = ref([...initiallyHiddenColumnProps]);
const columns = ref(taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop)));
</script>
// src/components/gantt/GanttBasic.tsx
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
import { calendars, dependencies, ganttConfig, initiallyHiddenColumnProps, tasks, taskTableColumnProps } from './gantt-basic-data';
import './gantt-basic.css';
const { isDark } = currentTheme();
const plugins = [GanttPlugin];
function GanttBasic() {
const project = useMemo(() => ganttConfig, []);
const source = useMemo(() => tasks.map((task) => ({ ...task })), []);
const hiddenColumns = useMemo(() => [...initiallyHiddenColumnProps], []);
const columns = useMemo(
() => taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop)),
[],
);
return (
<section className="gantt-basic-demo">
<RevoGrid
className="gantt-basic-grid"
theme={isDark() ? 'darkCompact' : 'compact'}
hideAttribution
canMoveColumns
plugins={plugins}
source={source}
columns={columns}
hideColumns={hiddenColumns}
gantt={project}
ganttDependencies={dependencies}
ganttCalendars={calendars}
/>
</section>
);
}
export default GanttBasic;
// src/components/gantt/GanttBasicAngular.ts
import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';
import { calendars as basicCalendars, dependencies as basicDependencies, ganttConfig as basicGanttConfig, initiallyHiddenColumnProps, tasks as basicTasks, taskTableColumnProps } from './gantt-basic-data';
@Component({
selector: 'gantt-basic-grid',
standalone: true,
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
imports: [RevoGrid],
template: `
<section class="gantt-basic-demo">
<revo-grid
class="gantt-basic-grid"
[theme]="theme"
[hideAttribution]="true"
[canMoveColumns]="true"
[plugins]="plugins"
[source]="tasks"
[columns]="columns"
[hideColumns]="hiddenColumns"
[gantt]="ganttConfig"
[ganttDependencies]="dependencies"
[ganttCalendars]="calendars"
></revo-grid>
</section>
`,
styleUrls: ['./gantt-basic.css'],
encapsulation: ViewEncapsulation.None,
})
export class GanttBasicGridComponent {
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
plugins = [GanttPlugin];
ganttConfig = basicGanttConfig;
calendars = basicCalendars;
tasks = basicTasks.map((task) => ({ ...task }));
dependencies = basicDependencies;
hiddenColumns = [...initiallyHiddenColumnProps];
columns = taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop));
}
Task hierarchy is represented by parentId.
type: 'task': normal scheduled item.type: 'summary': rollup row; dates and progress come from children.type: 'milestone': zero duration (startDate === endDate,duration: 0).
Scheduling-relevant source fields include duration, durationUnit, durationIsElapsed, taskMode, constraintType, constraintDate, deadlineDate, effortMode, workHours, workUnit, percentDone, actualStartDate, actualFinishDate, remainingDuration, splitRanges, priority, and canLevel. See Duration Units and Conversion for mixed units and text input.
duration, remainingDuration, and workHours are stored in canonical hours. durationUnit is retained display/edit metadata for duration spans, while workUnit independently controls Work. Text quantities are converted to hours on input and converted from hours only when rendered.
Source row contract: GanttTaskSourceRow in packages/gantt/src/gantt/grid/gantt-plugin.types.ts. Internal task entities use manuallyScheduled, duration, and progressPercent in packages/gantt/src/gantt/core/domain.ts.