Skip to content

Tasks, Summaries, And Milestones

Source code
TypeScriptts
// 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();
}
Datats
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 },
];
Vuevue
<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>
Reacttsx
// 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;
Angularts
// 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.