ts-gantt

simple gantt chart written using typescript

Usage no npm install needed!

<script type="module">
  import tsGantt from 'https://cdn.skypack.dev/ts-gantt';
</script>

README

TsGantt

Npm Build Status Codecov License

Simple library for creating gantt chart combined with task grid.

Current features

  • highly customizable
  • contains two resizable parts: task grid and Gantt chart
  • resizable grid columns
  • tree-like structure with expandable and selectable rows
  • support for tasks with two date pairs (planned and actual)
  • single or multiple row selection mode
  • localization support
    • three out-of-box supported languages: English, Ukrainian, Russian
    • custom locales support
    • instant locale switching
  • configurable chart
    • four available chart scales: day, week, month, year
    • three available chart display modes: planned dates, actual dates, both
    • instant chart scale and display mode switching
  • written completely in Typescript
  • light codebase: only one dependency (lightweight Day.js is used to work with dates)

Getting started

Install and initialize

With npm

npm install ts-gantt
import { TsGantt } from "ts-gantt";

const chart = new TsGantt("#container-selector");

include stylesheet ('ts-gantt/dist/styles.min.css') in any suitable way

Or using CDN

<link rel="stylesheet" href="https://unpkg.com/ts-gantt/dist/styles.min.css">
<script src="https://unpkg.com/ts-gantt/dist/ts-gantt.umd.min.js"></script>
const chart = new tsGantt.TsGantt("#container-selector");

⚠️for chart to function properly its container element must have relative, absolute or fixed position!

Set your task list

your tasks must implement following interface

interface TsGanttTaskModel {
  id: string; // to avoid incorrect behaviour please use unique ids within array
  parentId: string | null | undefined; // use if you need tree-like structure

  name: string;  
  progress: number; // percentage from 0 to 100. higher or lower values will be truncated

  datePlannedStart: Date | null | undefined;
  datePlannedEnd: Date | null | undefined;  
  dateActualStart: Date | null | undefined;
  dateActualEnd: Date | null | undefined;
  
  localizedNames: {[key: string]: string} | null | undefined; // eg {"en": "Name", "uk": "Ім'я", "ru": "Имя"}
}

to pass your task array to chart use 'tasks' property setter

chart.tasks = yourTaskArray;

task are updated in the same way. you should just pass actual task array when any change happens. change detection will find tasks that have been changed/added/removed and will replace/add/remove them in chart.

Switch modes

Language

you can instantly switch chart language

chart.locale = locale; // "en" | "uk" | "ru" | "ja" or any custom locale you provided in chart options

Timeline scale

you can instantly switch chart timeline scale

chart.chartScale = scale; // "day" | "week" | "month" | "year"

Display mode (chart bars)

you can instantly switch chart bar display mode

chart.chartDisplayMode = mode; // "planned" | "actual" | "both"

"planned" - show only planned dates bar on timeline
"actual" - show only actual dates bar on timeline

Select tasks

select task rows programmatically

chart.selectedTasks = [{id: "taskIdString"}];

get selected tasks

const selectedTasks = chart.selectedTasks;

Customize chart

you can customize chart in two ways:

  • edit or override styles in styles.css file
  • provide custom options to 'TsGantt' class constructor

Css

preffered way to customize styling is to change css variable values

:root {
  --tsg-table-min-width: 100px;
  --tsg-chart-min-width: 100px;
  --tsg-nesting-indent: 20px; /* indent width per nesting level */
  
  --tsg-background-color: white;
  --tsg-foreground-color: black;
  --tsg-separator-color: rgb(80, 80, 80); /* color of movable vertical line between parts */
  --tsg-header-color: rgb(210, 210, 210); /* header background color */
  --tsg-border-color: rgb(190, 190, 190);
  --tsg-symbol-color: rgb(80, 80, 80); /* color of row special symbols */
  --tsg-selection-color: rgb(230, 230, 230); /* background color of selected row */

  --tsg-not-started-fg-color: dimgray; /* color of task row text depending on task state */
  --tsg-in-progress-fg-color: black;
  --tsg-overdue-fg-color: darkred;
  --tsg-completed-fg-color: darkgreen;
  --tsg-completed-late-fg-color: sienna;

  --tsg-today-line-color: orangered; /* color of vertical line on chart that represents today */  
  --tsg-chart-bar-color-1: skyblue; /* chart bars colors */
  --tsg-chart-bar-color-2: lightcoral;
  --tsg-chart-bar-accent-1: darkcyan;
  --tsg-chart-bar-accent-2: darkred;  
  
  --tsg-font-family: 'Calibri', sans-serif;
  --tsg-font-size: 14px;
  --tsg-line-height: 16px;
  --tsg-max-cell-text-lines: 2; /* max lines of multiline text */
}

Options

you can apply your custom options by passing options object as second parameter to 'TsGantt' constructor

const options = new TsGanttOptions({
    multilineSelection: false,
    // other options you want to change
});
// or you can use assignment expressions (come in handy for getters and formatters that refence options object itself)
options.columnValueGetters[0] = task => 
    task.localizedNames && task.localizedNames[options.locale] || task.name; // value getter implementation for first column

// esm chart init with options
const chart = new TsGantt("#container-selector", options); 
// umd chart init with options
const chart = new tsGantt.TsGantt("#container-selector", options);

// ⚠️chart class in not designed to allow changes in options instance after the chart initialization.
// such changes can lead to unpredictable behavior.
// to change locale, scale and display mode use appropriate TsGantt instance methods.
// if it's very necessary to change other options after chart init then you should destroy old chart instance and create new one.
this.chart.destroy();
this.chart = new TsGantt("#container-selector", options);
ℹ️ complete list of 'TsGanttOptions' class properties you can use

   // some default values ommited for brevity. you can always see them in 'TsGanttOptions' source code
   
  multilineSelection = true; // allow multiple rows to be selected at the same time
  useCtrlKeyForMultilineSelection = false; // enable using ctrl key to select multiple rows

  drawTodayLine = true; // draw a vertical line on chart that represents today  
  highlightRowsDependingOnTaskState = true; // change row text color depending on task state

  // columns order: "Name", "Progress", "Start date planned", "End date planned",
  // "Start date actual", "End date actual", "Duration planned", "Duration actual"
  columnsMinWidthPx: number[]; // array of 8 values, one for each of 8 columns. 0 to disable column
  columnsContentAlign: ("start" | "center" | "end")[]; // array of 8 values, one for each of 8 columns.

  separatorWidthPx = 5; // vertical central line width
  headerHeightPx = 90; // lower values are not recommended, but you can still try
  rowHeightPx = 40; // lower values are not recommended, but you can still try
  borderWidthPx = 1;
  barStrokeWidthPx = 2;
  barMarginPx = 2;
  barCornerRadiusPx = 6;
  
  // special row symbols. you can also use some HTML code
  rowSymbols: TsGanttRowSymbols = {childless: "◆", collapsed: "⬘", expanded: "⬙"};

  chartShowProgress = true; // indicating progress percentage on chart bar using different color
  chartDisplayMode: "planned" | "actual" | "both";
  chartScale: "day" | "week" | "month" | "year";
  
  // optimal spare space on timeline edges in days
  chartDateOffsetDays: {[key: string]: number} = {"day": 14, "week": 60, "month": 240, "year": 730};
  // minimal spare space on timeline edges in days
  // chart timeline is redrawn only when trespassing minimal distance to chart edge to nearest bar
  chartDateOffsetDaysMin: {[key: string]: number} = {"day": 7, "week": 30, "month": 120, "year": 365};
  // width of 1 day on timeline. not recommended to use lower values than default
  chartDayWidthPx: {[key: string]: number} = {"day": 60, "week": 20, "month": 3, "year": 1};

  locale = "en"; // default locale
  localeDecimalSeparator: {[key: string]: string} = {en: ".", uk: ",", ru: ",", ja: "."};
  // you can provide any format strings that supported by dayjs
  localeDateFormat: {[key: string]: string} = {en: "MM/DD/YYYY", uk: "DD.MM.YYYY", ru: "DD.MM.YYYY", ja: "YYYY/MM/DD"};
  localeFirstWeekDay: {[key: string]: number} = {en: 0, uk: 1, ru: 1, ja: 0}; // Sunday is 0
  localeDateMonths: {[key: string]: string[]}; // array of 12 string values for each locale. eg ["January", "February", ...etc]  
  localeDateDays: {[key: string]: string[]}; // array of 7 string values for each locale. eg ["Sunday", "Monday", ...etc]
  localeDateDaysShort: {[key: string]: string[]}; // array of 7 string values for each locale. eg ["Su", "Mo", ...etc]
  
  localeDateScale: {[key: string]: string[]}; // array of 3 string values for each locale. eg ["Weeks", "Months", "Years"]
  localeHeaders: {[key: string]: string[]}; // array of 8 string values for each locale
  localeDurationFormatters: {[key: string]: (duration: number) => string}; // duration formatter function for each locale

  // default column value getters return localized values by taking into account all the properties assigned above
  // but you can provide your own ones if you need more complex output 
  // returned value is assigned to cell's innerHTML property. so you can use html tags
  columnValueGetters: ((a: TsGanttTask) => string)[]; // array of 8 string value getters for each locale
  
  taskComparer: (taskA: TsGanttTask, taskB: TsGanttTask) => number; // you can provide here your custom task comparer

Event callbacks

you can pass callbacks for chart row events using TsGantt properties shown below

onRowClickCb: (model: TsGanttTaskModel, event: MouseEvent) => void;
onRowDoubleClickCb: (model: TsGanttTaskModel, event: MouseEvent) => void;
onRowContextMenuCb: (model: TsGanttTaskModel, event: MouseEvent) => void;
onSelectionChangeCb: (models: TsGanttTaskModel[]) => void;

context menu implementation is not provided, but you can implement your own using callback

TODO list

  • add optional multiple row selection added in 0.2.0
  • make grid columns resizable added in 0.2.2
  • add callbacks on chart events (on row click/double click, selection change) added in 0.3.0
  • allow grid column reorder
  • add optional possibility to move/resize chart bars
  • add tooltips on bar hover
  • increase code coverage
  • optimize task change detection
  • add row virtualization (move grid to custom table implementation)