UNPKG

ts-gantt

Version:

simple gantt chart written using typescript

306 lines (264 loc) 14.5 kB
# TsGantt <p align="left"> <a href="https://www.npmjs.com/package/ts-gantt"><img src="https://img.shields.io/npm/v/ts-gantt" alt="Npm"></a> <a href="https://circleci.com/gh/yermolim/ts-gantt"><img src="https://circleci.com/gh/yermolim/ts-gantt.svg?style=shield" alt="Build Status"></a> <a href="https://codecov.io/gh/yermolim/ts-gantt"><img src="https://img.shields.io/codecov/c/github/yermolim/ts-gantt/master.svg?style=flat-round" alt="Codecov"></a> <a href="https://github.com/yermolim/ts-gantt/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-round" alt="License"></a> <br> </p> Simple library for creating gantt chart combined with task grid. ![](gantt-demo.gif) ### Current features <ul> <li>highly customizable</li> <li>contains two resizable parts: task grid and Gantt chart</li> <li>resizable grid columns with drag'n'drop reordering support</li> <li>tree-like structure with expandable and selectable rows</li> <li>support for tasks with two date pairs (planned and actual)</li> <li>single or multiple row selection mode</li> <li>localization support <ul> <li>four out-of-box supported languages: English, Ukrainian, Russian, Japanese</li> <li>custom locales support</li> <li>instant locale switching</li> </ul> </li> <li>configurable chart <ul> <li>four available chart scales: day, week, month, year</li> <li>three available chart display modes: planned dates, actual dates, both</li> <li>instant chart scale and display mode switching</li> </ul> </li> <li>editing task dates and progress percentages by resizing the chart bars</li> <li>written completely in Typescript</li> <li>shadow DOM mode can be used to make sure it won't mess with your styling/layout</li> <li>light codebase: only one dependency (lightweight <a href="https://github.com/iamkun/dayjs">Day.js<a> is used to work with dates)</li> </ul> ## Getting started ### Install and initialize #### With npm ``` npm install ts-gantt ``` ```javascript 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 ```html <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> ``` ```javascript 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 ```javascript 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 ```javascript 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 ```javascript 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 ```javascript chart.chartScale = scale; // "day" | "week" | "month" | "year" ``` #### Display mode (chart bars) you can instantly switch chart bar display mode ```javascript chart.chartDisplayMode = mode; // "planned" | "actual" | "both" ``` "planned" - show only planned dates bar on timeline <br/> "actual" - show only actual dates bar on timeline ### Select tasks select task rows programmatically ```javascript chart.selectedTasks = [{id: "taskIdString"}]; ``` get selected tasks ```javascript const selectedTasks = chart.selectedTasks; ``` ### Customize chart you can customize chart in two ways: <ul> <li>edit or override styles in the styles file (if shadow DOM is not used)</li> <li>provide custom options to 'TsGantt' class constructor</li> </ul> #### Css preffered way to customize styling is to change css variable values. It'll work for both regular DOM and shadow DOM modes ```css :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 ```javascript 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); ``` <details><summary>ℹ️ complete list of 'TsGanttOptions' class properties you can use</summary> <p> ```javascript // 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 ``` </p> </details> ### Event callbacks you can pass callbacks for chart row events using TsGantt properties shown below ```javascript 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 ```javascript 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 <ul> <li><del>add optional multiple row selection</del> added in 0.2.0</li> <li><del>make grid columns resizable</del> added in 0.2.2</li> <li><del>add callbacks on chart events (on row click/double click, selection change)</del> added in 0.3.0</li> <li><del>remove the hardcoded column number, allow adding custom columns</del> added in 0.4.0</li> <li><del>move chart to shadow DOM</del> added as an option in 0.5.0</li> <li><del>allow drag'n'drop grid column reordering</del> added in 0.6.0</li> <li><del>add optional possibility to move/resize chart bars</del> added in 0.7.0</li> <li>implement applying changes to the parent task when changing dates for its child</li> <li>add event firing on task change</li> <li>add optional tooltips on bar hover</li> <li>increase code coverage</li> <li>optimize task change detection</li> <li>add row virtualization (move grid to custom table implementation)</li> </ul>