Gantt-elastic - Javascript Gantt Chart (editable, responsive)
Javascript Gantt Chart for vue, jquery, vanilla js and other frameworks
Keywords: [ gantt, javascript gantt, gantt chart,js gantt,vue gantt,project manager,gantt project manager,responsive gantt ]
Gantt-elastic demo here
Gantt-Group
本组件基于gantt-elastic进行二次封装,甘特图一行可以显示多个任务
Gantt-Group is a vue component but it could be used in other frameworks or even with jQuery (vue is kind of elastic and lightweight framework).
⭐
If you want more activity in this repo give it a Full documentation soon (for now take a look at examples folder)
For now you can look at WIKI There is also full example vue project at https://github.com/neuronetio/vue-gantt-elastic
Installation
npm install --save gantt-group
Group Usage
//原有gantt-elastic属性不变,新增一个type属性:group
//The gantt-elastic attribute will remain unchanged. Add a new type attribute: group
//sample task code,使用dayjs处理时间
tasks: [
{
id: 5,
label: '202204',
start: dayjs('2022-05-08').valueOf(),
end:dayjs('2022-9-10').valueOf(),
percent: 25,
type: 'group',
tasks: [
{
id: 6,
label: 'V0',
start: dayjs('2022-06-16').valueOf(),
end:dayjs('2022-06-24').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#C0E2D9',
stroke: '#C0E2D9',
},
},
},
{
id: 7,
label: 'V1',
start: dayjs('2022-07-01').valueOf(),
end:dayjs('2022-07-16').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#FEE4D5',
stroke: '#FEE4D5',
},
},
},
{
id: 8,
label: 'V2',
start: dayjs('2022-08-02').valueOf(),
end:dayjs('2022-08-16').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#C6EBFE',
stroke: '#C6EBFE',
},
},
},
{
id: 9,
label: 'V3',
start: dayjs('2022-08-24').valueOf(),
end:dayjs('2022-09-16').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#E7E7E7',
stroke: '#E7E7E7',
},
},
},
],
},
{
id: 6,
label: '202205',
start: dayjs('2022-04-08').valueOf(),
end:dayjs('2022-9-30').valueOf(),
percent: 25,
type: 'group',
tasks: [
{
id: 7,
label: 'V0',
start: dayjs('2022-06-01').valueOf(),
end:dayjs('2022-06-16').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#C0E2D9',
stroke: '#C0E2D9',
},
},
},
{
id: 8,
label: 'V1',
start: dayjs('2022-06-18').valueOf(),
end:dayjs('2022-07-28').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#FEE4D5',
stroke: '#FEE4D5',
},
},
},
{
id: 9,
label: 'V2',
start: dayjs('2022-08-01').valueOf(),
end:dayjs('2022-08-10').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#C6EBFE',
stroke: '#C6EBFE',
},
},
},
{
id: 10,
label: 'V3',
start: dayjs('2022-08-21').valueOf(),
end:dayjs('2022-09-06').valueOf(),
percent: 50,
type: 'task',
style: {
base: {
fill: '#E7E7E7',
stroke: '#E7E7E7',
},
},
},
],
},
],
Usage
<!DOCTYPE html>
<html charset="utf-8">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>GanttElastic editor demo</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs"></script>
<script src="https://unpkg.com/gantt-elastic/dist/GanttElastic.umd.js"></script>
<script src="https://unpkg.com/gantt-elastic-header/dist/Header.umd.js"></script>
</head>
<body>
<div style="width:100%;height:100%">
<div id="app" v-if="!destroy">
<gantt-elastic :tasks="tasks" :options="options" :dynamic-style="dynamicStyle">
<gantt-header slot="header"></gantt-header>
<gantt-footer slot="footer"></gantt-footer>
</gantt-elastic>
</div>
</div>
<script>
// just helper to get current dates
function getDate(hours) {
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentMonth = currentDate.getMonth();
const currentDay = currentDate.getDate();
const timeStamp = new Date(currentYear, currentMonth, currentDay, 0, 0, 0).getTime();
return new Date(timeStamp + hours * 60 * 60 * 1000).getTime();
}
let tasks = [
{
id: 1,
label: 'Make some noise',
user: '<a href="https://www.google.com/search?q=John+Doe" target="_blank" style="color:#0077c0;">John Doe</a>',
start: getDate(-24 * 5),
duration: 15 * 24 * 60 * 60 * 1000,
progress: 85,
type: 'project',
//collapsed: true,
},
{
id: 2,
label: 'With great power comes great responsibility',
user: '<a href="https://www.google.com/search?q=Peter+Parker" target="_blank" style="color:#0077c0;">Peter Parker</a>',
parentId: 1,
start: getDate(-24 * 4),
duration: 4 * 24 * 60 * 60 * 1000,
progress: 50,
type: 'milestone',
collapsed: true,
style: {
base: {
fill: '#1EBC61',
stroke: '#0EAC51',
},
/*'tree-row-bar': {
fill: '#1EBC61',
stroke: '#0EAC51'
},
'tree-row-bar-polygon': {
stroke: '#0EAC51'
}*/
},
},
{
id: 3,
label: 'Courage is being scared to death, but saddling up anyway.',
user: '<a href="https://www.google.com/search?q=John+Wayne" target="_blank" style="color:#0077c0;">John Wayne</a>',
parentId: 2,
start: getDate(-24 * 3),
duration: 2 * 24 * 60 * 60 * 1000,
progress: 100,
type: 'task',
},
{
id: 4,
label: 'Put that toy AWAY!',
user: '<a href="https://www.google.com/search?q=Clark+Kent" target="_blank" style="color:#0077c0;">Clark Kent</a>',
start: getDate(-24 * 2),
duration: 2 * 24 * 60 * 60 * 1000,
progress: 50,
type: 'task',
dependentOn: [3],
},
];
let options = {
maxRows: 100,
maxHeight: 300,
title: {
label: 'Your project title as html (link or whatever...)',
html: false,
},
row: {
height: 24,
},
calendar: {
hour: {
display: false,
},
},
chart: {
progress: {
bar: false,
},
expander: {
display: true,
},
},
taskList: {
expander: {
straight: false,
},
columns: [
{
id: 1,
label: 'ID',
value: 'id',
width: 40,
},
{
id: 2,
label: 'Description',
value: 'label',
width: 200,
expander: true,
html: true,
events: {
click({ data, column }) {
alert('description clicked!\n' + data.label);
},
},
},
{
id: 3,
label: 'Assigned to',
value: 'user',
width: 130,
html: true,
},
{
id: 3,
label: 'Start',
value: (task) => dayjs(task.start).format('YYYY-MM-DD'),
width: 78,
},
{
id: 4,
label: 'Type',
value: 'type',
width: 68,
},
{
id: 5,
label: '%',
value: 'progress',
width: 35,
style: {
'task-list-header-label': {
'text-align': 'center',
width: '100%',
},
'task-list-item-value-container': {
'text-align': 'center',
width: '100%',
},
},
},
],
},
/*locale:{
name: 'pl', // name String
weekdays: 'Poniedziałek_Wtorek_Środa_Czwartek_Piątek_Sobota_Niedziela'.split('_'), // weekdays Array
weekdaysShort: 'Pon_Wto_Śro_Czw_Pią_Sob_Nie'.split('_'), // OPTIONAL, short weekdays Array, use first three letters if not provided
weekdaysMin: 'Pn_Wt_Śr_Cz_Pt_So_Ni'.split('_'), // OPTIONAL, min weekdays Array, use first two letters if not provided
months: 'Styczeń_Luty_Marzec_Kwiecień_Maj_Czerwiec_Lipiec_Sierpień_Wrzesień_Październik_Listopad_Grudzień'.split('_'), // months Array
monthsShort: 'Sty_Lut_Mar_Kwi_Maj_Cze_Lip_Sie_Wrz_Paź_Lis_Gru'.split('_'), // OPTIONAL, short months Array, use first three letters if not provided
ordinal: n => `${n}`, // ordinal Function (number) => return number + output
relativeTime: { // relative time format strings, keep %s %d as the same
future: 'za %s', // e.g. in 2 hours, %s been replaced with 2hours
past: '%s temu',
s: 'kilka sekund',
m: 'minutę',
mm: '%d minut',
h: 'godzinę',
hh: '%d godzin', // e.g. 2 hours, %d been replaced with 2
d: 'dzień',
dd: '%d dni',
M: 'miesiąc',
MM: '%d miesięcy',
y: 'rok',
yy: '%d lat'
}
}*/
};
// create instance
const app = new Vue({
components: {
'gantt-header': Header,
'gantt-elastic': GanttElastic,
'gantt-footer': {
template: `<span>this is a footer</span>`,
},
},
data: {
tasks: tasks.map((task) => Object.assign({}, task)),
options,
dynamicStyle: {
'task-list-header-label': {
'font-weight': 'bold',
},
},
destroy: false,
},
});
// gantt state which will be updated in realtime
let ganttState, ganttInstance;
// listen to 'gantt-elastic.ready' or 'gantt-elastic.mounted' event
// to get the gantt state for real time modification
app.$on('gantt-elastic-ready', (ganttElasticInstance) => {
ganttInstance = ganttElasticInstance;
ganttInstance.$on('tasks-changed', (tasks) => {
app.tasks = tasks;
});
ganttInstance.$on('options-changed', (options) => {
app.options = options;
});
ganttInstance.$on('dynamic-style-changed', (style) => {
app.dynamicStyle = style;
});
ganttInstance.$on('chart-task-mouseenter', ({ data, event }) => {
console.log('task mouse enter', { data, event });
});
ganttInstance.$on('updated', () => {
//console.log('gantt view was updated');
});
ganttInstance.$on('destroyed', () => {
//console.log('gantt was destroyed');
});
ganttInstance.$on('times-timeZoom-updated', () => {
console.log('time zoom changed');
});
ganttInstance.$on('taskList-task-click', ({ event, data, column }) => {
console.log('task list clicked! (task)', { data, column });
});
});
// mount gantt to DOM
app.$mount('#app');
</script>
</body>
</html>
gantt-elastic as vue component
Take a look at the vue.html
inside examples folder file to see how you could add gantt-elastic inside <script>
tag along with the Vue framework
Demo project: https://github.com/neuronetio/vue-gantt-elastic
You can also import gantt-elastic as compiled js component in commonjs or umd format (examples folder) or just grab GanttElastic.vue from src directory and add to your existing vue project.
import Vue from 'vue';
import GanttElastic from "gantt-elastic";
import Header from "gantt-elastic-header"; // if you want standard header (npm i -s gantt-elastic-header)
new Vue({
el:'#gantt',
template:`<gantt-elastic :tasks="tasks" :options="options">
<gantt-elastic-header slot="header"></gantt-elastic-header>
<gantt-elastic-footer slot="footer"></gantt-elastic-footer>
</gantt-elastic>`,
components: {
ganttElasticHeader: {template:`<span>your header</span>`}, // or Header
ganttElastic: GanttElastic
ganttElasticFooter: {template:`<span>your footer</span>`},
},
data() {
return {
tasks: tasks,
options: options
};
}
});
or
import Vue from 'vue';
import App from './App.vue'; // your app that uses gantt-elastic from 'gantt-elastic/src/GanttElastic.vue'
new Vue({
el: '#app',
render: (h) => h(App),
});
For webpack usage (pure javascript, inside other frameworks or Vue App/Component)
Take a look at this demo project: https://github.com/neuronetio/gantt-elastic-webpack for other bundlers use umd or commonjs from dist folder.
import GanttElastic from 'gantt-elastic/dist/GantElastic.umd.js';
import GanttElastic from 'gantt-elastic/dist/GantElastic.common.js'; // same as import GanttElastic from 'gantt-elastic';
import GanttElastic from 'gantt-elastic/src/GantElastic.vue'; // if you want vue component directly without compilation - look above
// and the same with require
const GanttElastic = require('gantt-elastic/dist/GantElastic.common.js');
uglifyjs
If you are using uglifyjs in your project be sure to have es6 compatible version like uglify-es
Licensce
MIT