Simple library for creating gantt chart combined with task grid.
- highly customizable
- contains two resizable parts: task grid and Gantt chart
- resizable grid columns with drag'n'drop reordering support
- 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
- four out-of-box supported languages: English, Ukrainian, Russian, Japanese
- 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
- editing task dates and progress percentages by resizing the chart bars
- written completely in Typescript
- shadow DOM mode can be used to make sure it won't mess with your styling/layout
- light codebase: only one dependency (lightweight Day.js is used to work with dates)
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
<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!
⚠️ if you use the shadow DOM mode for the chart, including the stylesheet is not needed, the styles bundled inside the main file will be used.
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.
you can instantly switch chart language
chart.locale = locale; // "en" | "uk" | "ru" | "ja" or any custom locale you provided in chart options
you can instantly switch chart timeline scale
chart.chartScale = scale; // "day" | "week" | "month" | "year"
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 task rows programmatically
chart.selectedTasks = [{id: "taskIdString"}];
get selected tasks
const selectedTasks = chart.selectedTasks;
you can customize chart in two ways:
- edit or override styles in the styles file (if shadow DOM is not used)
- provide custom options to 'TsGantt' class constructor
preffered way to customize styling is to change css variable values. It'll work for both regular DOM and shadow DOM modes
: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-scrollbar-track-color: #eeeeee; /* webkit browsers only */
--tsg-scrollbar-thumb-color: #b0b0b0; /* webkit browsers only */
--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 */
}
you can apply your custom options by passing options object as second parameter to 'TsGantt' constructor
const options = new TsGanttOptions({
multilineSelection: false,
enableChartEdit = true,
// 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
useShadowDom = false; // render chart using shadow DOM
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
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 are 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"]
localeDurationFormatters: {[key: string]: (duration: number) => string}; // duration formatter function for each locale
// Data columns setup.
// there are default 8 columns: "Name", "Progress", "Start date planned", "End date planned",
// "Start date actual", "End date actual", "Duration planned", "Duration actual".
// You can remove the columns or add your own ones, but you need too make sure to edit all of the following arrays respectively:
// you should provide a width, an alignment, a value getter, and localized headers.
// !!!the length of each column-related array should be equal to the columns count!!!
columnsMinWidthPx: number[]; // array of numeric values, one for each of the columns. 0 to disable column
columnsContentAlign: ("start" | "center" | "end")[]; // array of values, one for each of the columns
// 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 string value getters for each locale
// column header locales should be provided for all the locales intended for use
localeHeaders: {[key: string]: string[]}; // array of string values for each locale
//
taskComparer: (taskA: TsGanttTask, taskB: TsGanttTask) => number; // you can provide here your custom task comparer
enableChartEdit = false; // (experimental) allows making changes to tasks by dragging chart bar handles
// all the task models including the changed ones can be returned to your code using the TsGantt.tasks property getter
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
const options = new tsGantt.TsGanttOptions();
options.columnsMinWidthPx.push(100);
options.columnsContentAlign.push("center");
// the "customColumnKey" below is the property of the model object
// which contains the value needed to be shown
options.columnValueGetters.push(task => (task["customColumnKey"] ?? ""));
options.localeHeaders.en.push("User column");
const ganttChart = new tsGantt.TsGantt("#gantt-container", options);
add optional multiple row selectionadded in 0.2.0make grid columns resizableadded in 0.2.2add callbacks on chart events (on row click/double click, selection change)added in 0.3.0remove the hardcoded column number, allow adding custom columnsadded in 0.4.0move chart to shadow DOMadded as an option in 0.5.0allow drag'n'drop grid column reorderingadded in 0.6.0add optional possibility to move/resize chart barsadded in 0.7.0- implement applying changes to the parent task when changing dates for its child
- add event firing on task change
- add optional tooltips on bar hover
- increase code coverage
- optimize task change detection
- add row virtualization (move grid to custom table implementation)