ts-gantt
TypeScript icon, indicating that this package has built-in type declarations

0.7.0 • Public • Published

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 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)

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!

⚠️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.

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 the styles file (if shadow DOM is not used)
  • provide custom options to 'TsGantt' class constructor

Css

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 */
}

Options

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

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

Adding a custom column to the end of the column list example

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);

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
  • remove the hardcoded column number, allow adding custom columns added in 0.4.0
  • move chart to shadow DOM added as an option in 0.5.0
  • allow drag'n'drop grid column reordering added in 0.6.0
  • add optional possibility to move/resize chart bars added 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)

Package Sidebar

Install

npm i ts-gantt

Weekly Downloads

18

Version

0.7.0

License

MIT

Unpacked Size

7.55 MB

Total Files

12

Last publish

Collaborators

  • yermolim