Gantt

Gantt charts illustrate project schedules. They show the start and finish dates of each task and typically add information such as the completion state of each task and dependencies between tasks.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wjChart from '@grapecity/wijmo.chart'; import * as wjCore from '@grapecity/wijmo'; import { getData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create Gantt chart let theChart = new wjChart.FlexChart('#theChart', { itemsSource: getData(), chartType: 'Bar', bindingX: 'name', tooltip: { content: getTooltipContent }, axisY: { majorGrid: false, minorGrid: true, reversed: true }, itemFormatter: ganttItemFormatter, rendered: ganttChartRendered, series: [ { binding: 'start,end' } ], palette: getRandomPalette() }); // // utilities function getTooltipContent(ht) { let str = wjCore.format('<b>{name}</b>:<br/>{start:d} - {end:d}', { name: ht.x, start: ht.item.start, end: ht.item.end }); // if (ht.item && ht.item.percent != null) { str += wjCore.format('<br/><i>percent complete: {percent}%</i>', ht.item); } // return str; } // // show the percentage complete for each task function ganttItemFormatter(engine, hti, defaultFormatter) { // draw the item as usual defaultFormatter(); // // show percentage done let task = hti.item; // if (wjCore.isNumber(task.percent) && task.percent > 0) { let pct = wjCore.clamp(task.percent, 0, 100) / 100, rc = getTaskRect(hti.series.chart, task).inflate(-8, -8); // engine.fill = pct == 1 ? 'green' : 'gold'; //engine.stroke; engine.drawRect(rc.left, rc.top, rc.width * pct, rc.height); } } // // show the task dependencies function ganttChartRendered(chart, e) { let tasks = chart.collectionView.items; // tasks.forEach(task => { let parents = getTaskParents(task, tasks); // get the parent tasks // parents.forEach(function (parent) { drawConnectingLine(e.engine, chart, task, parent); // draw connector }); }); } // function drawConnectingLine(engine, chart, task, parent) { let rc1 = getTaskRect(chart, parent), // parent rect rc2 = getTaskRect(chart, task), // task rect x1 = rc1.left + rc1.width / 2, // parent x center x2 = rc2.left, // task left y1 = rc1.bottom, // parent bottom y2 = rc2.top + rc2.height / 2; // task y center // // draw connecting line let xs = [x1, x1, x2], ys = [y1, y2, y2]; // engine.drawLines(xs, ys, 'connector', { stroke: 'black' }); // // draw arrow at the end let sz = 5; // xs = [x2 - 2 * sz, x2, x2 - 2 * sz]; ys = [y2 - sz, y2, y2 + sz]; // engine.drawPolygon(xs, ys, 'arrow', { fill: 'black' }); } // function getTaskRect(chart, task) { let x1 = chart.axisX.convert(task.start.valueOf()), x2 = chart.axisX.convert(task.end.valueOf()), index = chart.collectionView.items.indexOf(task), y1 = chart.axisY.convert(index - .35), y2 = chart.axisY.convert(index + .35); // return new wjCore.Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } // function getTaskParents(task, tasks) { let parents = []; // if (task.parent) { task.parent.split(',').forEach(name => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].name === name) { parents.push(tasks[i]); break; } } }); } // return parents; } } // function getRandomPalette() { let palettes = Object.getOwnPropertyNames(wjChart.Palettes) .filter(prop => typeof wjChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjChart.Palettes[palettes[rand]]; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Gantt</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="theChart"></div> </div> </body> </html> // export function getData() { let year = new Date().getFullYear(); // return [ { name: 'Task1', start: new Date(year, 0, 1), end: new Date(year, 2, 31), parent: null, percent: 100 }, { name: 'Task2', start: new Date(year, 3, 1), end: new Date(year, 3, 30), parent: 'Task1', percent: 100 }, { name: 'Task3', start: new Date(year, 4, 1), end: new Date(year, 6, 31), parent: 'Task2', percent: 75 }, { name: 'Task4', start: new Date(year, 3, 1), end: new Date(year, 6, 31), parent: 'Task1', percent: 33 }, { name: 'Task5', start: new Date(year, 7, 1), end: new Date(year, 8, 30), parent: 'Task3,Task4', percent: 0 }, { name: 'Task6', start: new Date(year, 9, 1), end: new Date(year, 11, 31), parent: 'Task1,Task5', percent: 0 }, { name: 'Task7', start: new Date(year, 0, 1), end: new Date(year, 11, 31), parent: null, percent: 50 } ]; } body { margin-bottom: 24pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import { Component, Inject, enableProdMode, NgModule } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjChartModule } from '@grapecity/wijmo.angular2.chart'; import { DataService, TTask } from './app.data'; import * as wjCore from '@grapecity/wijmo'; import * as wjChart from '@grapecity/wijmo.chart'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { data: TTask[]; palette: string[]; // constructor(@Inject(DataService) private dataService: DataService) { this.data = dataService.getData(); this.palette = this._getRandomPalette(); } // getTooltipContent(ht: wjChart.HitTestInfo) { let str = wjCore.format('<b>{name}</b>:<br/>{start:d} - {end:d}', { name: ht.x, start: ht.item.start, end: ht.item.end }); // if (ht.item && ht.item.percent != null) { str += wjCore.format('<br/><i>percent complete: {percent}%</i>', ht.item); } // return str; } // // show the percentage complete for each task ganttItemFormatter = (engine: wjChart.IRenderEngine, ht: wjChart.HitTestInfo, defaultFormatter: Function) => { // draw the item as usual defaultFormatter(); // // show percentage done let task: TTask = ht.item; // if (wjCore.isNumber(task.percent) && task.percent > 0) { let pct = wjCore.clamp(task.percent, 0, 100) / 100, rc = this._getTaskRect(ht.series.chart, task).inflate(-8, -8); // engine.fill = pct == 1 ? 'green' : 'gold';//engine.stroke; engine.drawRect(rc.left, rc.top, rc.width * pct, rc.height); } } // // show the task dependencies ganttChartRendered = (chart: wjChart.FlexChart, e: wjChart.RenderEventArgs) => { let tasks: TTask[] = chart.collectionView.items; // tasks.forEach(task => { // for each task let parents = this._getTaskParents(task, tasks); // get the parent tasks // parents.forEach(parent => { // for each parent this._drawConnectingLine(e.engine, chart, task, parent); // draw connector }); }); } // // private _getRandomPalette() { let palettes = Object.getOwnPropertyNames(wjChart.Palettes) .filter(prop => typeof wjChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjChart.Palettes[palettes[rand]]; } // private _drawConnectingLine(engine: wjChart.IRenderEngine, chart: wjChart.FlexChart, task: TTask, parent: TTask) { let rc1 = this._getTaskRect(chart, parent), // parent rect rc2 = this._getTaskRect(chart, task), // task rect x1 = rc1.left + rc1.width / 2, // parent x center x2 = rc2.left, // task left y1 = rc1.bottom, // parent bottom y2 = rc2.top + rc2.height / 2; // task y center // // draw connecting line let xs = [x1, x1, x2], ys = [y1, y2, y2]; // engine.drawLines(xs, ys, 'connector', { stroke: 'black' }); // // draw arrow at the end let sz = 5; // xs = [x2 - 2 * sz, x2, x2 - 2 * sz]; ys = [y2 - sz, y2, y2 + sz]; // engine.drawPolygon(xs, ys, 'arrow', { fill: 'black' }); } // private _getTaskRect(chart: wjChart.FlexChartCore, task: TTask) { let x1 = chart.axisX.convert(task.start.valueOf()), x2 = chart.axisX.convert(task.end.valueOf()), index = chart.collectionView.items.indexOf(task), y1 = chart.axisY.convert(index - .35), y2 = chart.axisY.convert(index + .35); // return new wjCore.Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } // private _getTaskParents(task: TTask, tasks: TTask[]) { let parents: TTask[] = []; // if (task.parent) { task.parent.split(',').forEach(name => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].name === name) { parents.push(tasks[i]); break; } } }); } // return parents; } } // @NgModule({ imports: [WjChartModule, BrowserModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Gantt</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <wj-flex-chart #chart [itemsSource]="data" chartType="Bar" bindingX="name" [tooltipContent]="getTooltipContent" [itemFormatter]="ganttItemFormatter" (rendered)="ganttChartRendered(chart, $event)" [palette]="palette"> <wj-flex-chart-series binding="start,end"></wj-flex-chart-series> <wj-flex-chart-axis wjProperty="axisY" [majorGrid]="false" [minorGrid]="true" [reversed]="true"> </wj-flex-chart-axis> </wj-flex-chart> </div> import { Injectable } from '@angular/core'; // export type TTask = { name: string; start: Date; end: Date; parent: string; percent: number; }; // @Injectable() export class DataService { getData(): TTask[] { let year = new Date().getFullYear(); // return [ { name: 'Task1', start: new Date(year, 0, 1), end: new Date(year, 2, 31), parent: null, percent: 100 }, { name: 'Task2', start: new Date(year, 3, 1), end: new Date(year, 3, 30), parent: 'Task1', percent: 100 }, { name: 'Task3', start: new Date(year, 4, 1), end: new Date(year, 6, 31), parent: 'Task2', percent: 75 }, { name: 'Task4', start: new Date(year, 3, 1), end: new Date(year, 6, 31), parent: 'Task1', percent: 33 }, { name: 'Task5', start: new Date(year, 7, 1), end: new Date(year, 8, 30), parent: 'Task3,Task4', percent: 0 }, { name: 'Task6', start: new Date(year, 9, 1), end: new Date(year, 11, 31), parent: 'Task1,Task5', percent: 0 }, { name: 'Task7', start: new Date(year, 0, 1), end: new Date(year, 11, 31), parent: null, percent: 50 } ]; } } body { margin-bottom: 24pt; } <template> <div class="container-fluid"> <wj-flex-chart :itemsSource="data" chartType="Bar" bindingX="name" :tooltipContent="getTooltipContent" :itemFormatter="ganttItemFormatter" :rendered="ganttChartRendered" :palette="palette"> <wj-flex-chart-series binding="start,end"></wj-flex-chart-series> <wj-flex-chart-axis wjProperty="axisY" :majorGrid=false :minorGrid=true :reversed=true> </wj-flex-chart-axis> </wj-flex-chart> </div> </template> <script> import '@grapecity/wijmo.styles/wijmo.css'; import 'bootstrap.css'; import Vue from 'vue'; import * as wijmo from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import '@grapecity/wijmo.vue2.chart'; import { getData } from './data'; // new Vue({ el: '#app', data: { data: getData(), palette: (() => { // Get random palette let palettes = Object.getOwnPropertyNames(chart.Palettes) .filter(prop => typeof chart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return chart.Palettes[palettes[rand]]; })() }, methods: { getTooltipContent(ht) { let str = wijmo.format('<b>{name}</b>:<br/>{start:d} - {end:d}', { name: ht.x, start: ht.item.start, end: ht.item.end }); // if (ht.item && ht.item.percent != null) { str += wijmo.format('<br/><i>percent complete: {percent}%</i>', ht.item); } // return str; }, // show the percentage complete for each task ganttItemFormatter(engine, ht, defaultFormatter) { // draw the item as usual defaultFormatter(); // // show percentage done let task = ht.item; // if (wijmo.isNumber(task.percent) && task.percent > 0) { let pct = wijmo.clamp(task.percent, 0, 100) / 100, rc = this.$_getTaskRect(ht.series.chart, task).inflate(-8, -8); // engine.fill = pct == 1 ? 'green' : 'gold';//engine.stroke; engine.drawRect(rc.left, rc.top, rc.width * pct, rc.height); } }, // show the task dependencies ganttChartRendered(chart, e) { let tasks = chart.collectionView.items; // tasks.forEach(task => { // for each task let parents = this.$_getTaskParents(task, tasks); // get the parent tasks // parents.forEach(parent => { // for each parent this.$_drawConnectingLine(e.engine, chart, task, parent); // draw connector }); }); }, $_drawConnectingLine(engine, chart, task, parent) { let rc1 = this.$_getTaskRect(chart, parent), // parent rect rc2 = this.$_getTaskRect(chart, task), // task rect x1 = rc1.left + rc1.width / 2, // parent x center x2 = rc2.left, // task left y1 = rc1.bottom, // parent bottom y2 = rc2.top + rc2.height / 2; // task y center // // draw connecting line let xs = [x1, x1, x2], ys = [y1, y2, y2]; // engine.drawLines(xs, ys, 'connector', { stroke: 'black' }); // // draw arrow at the end let sz = 5; // xs = [x2 - 2 * sz, x2, x2 - 2 * sz]; ys = [y2 - sz, y2, y2 + sz]; // engine.drawPolygon(xs, ys, 'arrow', { fill: 'black' }); }, $_getTaskRect(chart, task) { let x1 = chart.axisX.convert(task.start.valueOf()), x2 = chart.axisX.convert(task.end.valueOf()), index = chart.collectionView.items.indexOf(task), y1 = chart.axisY.convert(index - .35), y2 = chart.axisY.convert(index + .35); // return new wijmo.Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); }, $_getTaskParents(task, tasks) { let parents = []; // if (task.parent) { task.parent.split(',').forEach(name => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].name === name) { parents.push(tasks[i]); break; } } }); } // return parents; } } }); </script> <style> body { margin-bottom: 24pt; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Gantt</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"></div> </body> </html> export function getData() { let year = new Date().getFullYear(); // return [ { name: 'Task1', start: new Date(year, 0, 1), end: new Date(year, 2, 31), parent: null, percent: 100 }, { name: 'Task2', start: new Date(year, 3, 1), end: new Date(year, 3, 30), parent: 'Task1', percent: 100 }, { name: 'Task3', start: new Date(year, 4, 1), end: new Date(year, 6, 31), parent: 'Task2', percent: 75 }, { name: 'Task4', start: new Date(year, 3, 1), end: new Date(year, 6, 31), parent: 'Task1', percent: 33 }, { name: 'Task5', start: new Date(year, 7, 1), end: new Date(year, 8, 30), parent: 'Task3,Task4', percent: 0 }, { name: 'Task6', start: new Date(year, 9, 1), end: new Date(year, 11, 31), parent: 'Task1,Task5', percent: 0 }, { name: 'Task7', start: new Date(year, 0, 1), end: new Date(year, 11, 31), parent: null, percent: 50 } ]; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wijmo from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import * as wjChart from '@grapecity/wijmo.react.chart'; import { getData } from './data'; class App extends React.Component { constructor(props) { super(props); this.chartInitialized = (sender) => { sender.tooltip.content = this.getTooltipContent; }; this.getTooltipContent = (ht) => { let str = wijmo.format('<b>{name}</b>:<br/>{start:d} - {end:d}', { name: ht.x, start: ht.item.start, end: ht.item.end }); // if (ht.item && ht.item.percent != null) { str += wijmo.format('<br/><i>percent complete: {percent}%</i>', ht.item); } // return str; }; // show the percentage complete for each task this.ganttItemFormatter = (engine, ht, defaultFormatter) => { // draw the item as usual defaultFormatter(); // // show percentage done let task = ht.item; // if (wijmo.isNumber(task.percent) && task.percent > 0) { let pct = wijmo.clamp(task.percent, 0, 100) / 100, rc = this.$_getTaskRect(ht.series.chart, task).inflate(-8, -8); // engine.fill = pct == 1 ? 'green' : 'gold'; //engine.stroke; engine.drawRect(rc.left, rc.top, rc.width * pct, rc.height); } }; // show the task dependencies this.ganttChartRendered = (chart, e) => { let tasks = chart.collectionView.items; // tasks.forEach(task => { let parents = this.$_getTaskParents(task, tasks); // get the parent tasks // parents.forEach(parent => { this.$_drawConnectingLine(e.engine, chart, task, parent); // draw connector }); }); }; this.$_drawConnectingLine = (engine, chart, task, parent) => { let rc1 = this.$_getTaskRect(chart, parent), // parent rect rc2 = this.$_getTaskRect(chart, task), // task rect x1 = rc1.left + rc1.width / 2, // parent x center x2 = rc2.left, // task left y1 = rc1.bottom, // parent bottom y2 = rc2.top + rc2.height / 2; // task y center // // draw connecting line let xs = [x1, x1, x2], ys = [y1, y2, y2]; // engine.drawLines(xs, ys, 'connector', { stroke: 'black' }); // // draw arrow at the end let sz = 5; // xs = [x2 - 2 * sz, x2, x2 - 2 * sz]; ys = [y2 - sz, y2, y2 + sz]; // engine.drawPolygon(xs, ys, 'arrow', { fill: 'black' }); }; this.$_getTaskRect = (chart, task) => { let x1 = chart.axisX.convert(task.start.valueOf()), x2 = chart.axisX.convert(task.end.valueOf()), index = chart.collectionView.items.indexOf(task), y1 = chart.axisY.convert(index - .35), y2 = chart.axisY.convert(index + .35); // return new wijmo.Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); }; this.$_getTaskParents = (task, tasks) => { let parents = []; // if (task.parent) { task.parent.split(',').forEach(name => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].name === name) { parents.push(tasks[i]); break; } } }); } // return parents; }; this.state = { data: getData(), palette: (() => { // Get random palette let palettes = Object.getOwnPropertyNames(chart.Palettes) .filter(prop => typeof chart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return chart.Palettes[palettes[rand]]; })() }; } render() { return <div className="container-fluid"> <wjChart.FlexChart itemsSource={this.state.data} itemFormatter={this.ganttItemFormatter} rendered={this.ganttChartRendered} palette={this.state.palette} chartType="Bar" bindingX="name" initialized={this.chartInitialized}> <wjChart.FlexChartSeries binding="start,end"></wjChart.FlexChartSeries> <wjChart.FlexChartAxis wjProperty="axisY" majorGrid={false} minorGrid={true} reversed={true}> </wjChart.FlexChartAxis> </wjChart.FlexChart> </div>; } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Gantt</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> body { margin-bottom: 24pt; } export function getData() { let year = new Date().getFullYear(); // return [ { name: 'Task1', start: new Date(year, 0, 1), end: new Date(year, 2, 31), parent: null, percent: 100 }, { name: 'Task2', start: new Date(year, 3, 1), end: new Date(year, 3, 30), parent: 'Task1', percent: 100 }, { name: 'Task3', start: new Date(year, 4, 1), end: new Date(year, 6, 31), parent: 'Task2', percent: 75 }, { name: 'Task4', start: new Date(year, 3, 1), end: new Date(year, 6, 31), parent: 'Task1', percent: 33 }, { name: 'Task5', start: new Date(year, 7, 1), end: new Date(year, 8, 30), parent: 'Task3,Task4', percent: 0 }, { name: 'Task6', start: new Date(year, 9, 1), end: new Date(year, 11, 31), parent: 'Task1,Task5', percent: 0 }, { name: 'Task7', start: new Date(year, 0, 1), end: new Date(year, 11, 31), parent: null, percent: 50 } ]; }