iamferraz-gantt-chart
Version:
Gantt Chart Component using Echarts library
1,059 lines (1,049 loc) • 43.5 kB
JavaScript
import { EventEmitter, Component, ViewChild, Input, Output, HostListener, NgModule } from '@angular/core';
import { format, time, graphic } from 'echarts/core';
import { NgxEchartsModule } from 'ngx-echarts';
class TaskModel {
}
class DateManipulator {
static datediff(first, second) {
// Take the difference between the dates and divide by milliseconds per day.
// Round to nearest whole number to deal with DST.
return Math.round((second - first) / (1000 * 60 * 60 * 24));
}
static daysLeft(baseDate, translation) {
//get days left based on today
var left = this.datediff(baseDate, new Date().getTime());
if (left < 0) {
return (-left) + " " + (translation ? translation.TO_END : "TO_END");
}
else {
return left + " " + (translation ? translation.DELAYED : "DELAYED");
}
}
}
class TaskDataManipulator {
constructor(colours, enableGroup) {
this.COLOURS = colours;
this._enableGroup = enableGroup;
}
mapData(taskData) {
//Im changing the item object to array... this is why the encode is filled with indexed
var _groupData = this.mapGroups(taskData);
var mappedData = [];
for (let index = 0; index < taskData.length; index++) {
let item = taskData[index];
//filling the group information
// here I get the taskID gorupped by mapGroups functions and compare the position of taskid with the array present in the groupped. If the current taskid is in the end of array I dont need to draw the group
let isToDrawGroup = 0;
let groupInfo = _groupData[item.groupName];
if (groupInfo != undefined && groupInfo.tasks.length > 1) {
if (groupInfo.tasks.indexOf(item.taskId) < groupInfo.tasks.length - 1)
isToDrawGroup = 1;
}
let color = "";
if (this._enableGroup == false) {
color = this.getColorHex(index);
}
else {
color = groupInfo.color;
}
let index_attributes = [index, item.taskName, item.start, item.end, item.taskId, item.donePercentage, item.owner, item.image, item.groupName, isToDrawGroup, color];
mappedData.push(index_attributes);
}
return mappedData;
}
mapZebra(taskData) {
var mappedData = [];
for (let index = 0; index < taskData.length; index++) {
let item = taskData[index];
let index_attributes = [index, this.getMinDate(taskData), this.getMaxDate(taskData), item.taskId];
mappedData.push(index_attributes);
}
return mappedData;
}
getMinDate(taskData) {
let minDate = new Date(8640000000000000);
for (let index = 0; index < taskData.length; index++) {
let item = taskData[index];
if (item.start < minDate) {
minDate = item.start;
}
}
return new Date(minDate);
}
getMaxDate(taskData) {
let maxDate = new Date(-8640000000000000);
for (let index = 0; index < taskData.length; index++) {
let item = taskData[index];
if (item.end > maxDate) {
maxDate = item.end;
}
}
return new Date(maxDate);
}
mapGroups(taskData) {
/**
* return a hash
* {
* "groupName1" => { color: "#222", tasks: [taskId1, taskId2, ..., taskIdN]}
* "groupName2" => { color: "#222", tasks: [taskId1, taskId2, ..., taskIdN]}
* }
*/
if (this._enableGroup == false) {
return {};
}
let countColor = 0;
let mappedGroups = {};
//Im creating a map of groups => taskId
for (let i = 0; i < taskData.length; i++) {
if (mappedGroups[taskData[i].groupName] == undefined) {
mappedGroups[taskData[i].groupName] = {};
mappedGroups[taskData[i].groupName].color = this.getColorHex(countColor); //this.getRandomHexColor()
mappedGroups[taskData[i].groupName].tasks = [taskData[i].taskId];
countColor = countColor + 1;
}
else
mappedGroups[taskData[i].groupName].tasks.push(taskData[i].taskId);
}
return mappedGroups;
}
compareTasks(a, b) {
let dateComp = 0;
if (a.start > b.start)
dateComp = -1;
if (b.start > a.start)
dateComp = 1;
let groupOrderComp = 0;
if (a.groupOrder > b.groupOrder)
groupOrderComp = -1;
if (b.groupOrder > a.groupOrder)
groupOrderComp = 1;
let taskNameComp = 0;
if (a.taskName > b.taskName)
taskNameComp = -1;
if (b.taskName > a.taskName)
taskNameComp = 1;
return groupOrderComp || taskNameComp || dateComp;
}
getTaskById(taskData, id) {
for (let i = 0; i < taskData.length; i++) {
if (taskData[i].taskId == id) {
return taskData[i];
}
}
return null;
}
getTaskByIdInMappedData(mappedData, id) {
for (let i = 0; i < mappedData.length; i++) {
if (mappedData[i][4] == id) {
return mappedData[i];
}
}
return null;
}
randomInt(min, max) {
return min + Math.floor((max - min) * Math.random());
}
getRandomHexColor() {
//var randomColor = Math.floor(Math.random()*16777215).toString(16);
//return "#" + randomColor;
return this.COLOURS[this.randomInt(0, this.COLOURS.length)];
}
getColorHex(index) {
if (index >= this.COLOURS.length)
index = 0;
return this.COLOURS[index];
}
}
class GanttRenderers {
constructor(taskData, mappedData, colours, dateFormat, heightRatio, translation, enableGroup = true, darkTheme = false) {
//normal|dark
this.arrowColors = ["#000", "#fff"];
this.zebraColor = [["#f2f2f2", "#e6e6e6"], ["#212529", "#2C3034"]];
this._taskData = taskData;
this._mappedData = mappedData;
this.taskDataManipulator = new TaskDataManipulator(colours, enableGroup);
this.DATE_FORMAT = dateFormat;
this.HEIGHT_RATIO = heightRatio;
this._darkTheme = darkTheme;
this._enableGroup = enableGroup;
this._translation = translation;
}
renderGanttItem(params, api) {
var index = api.value(0);
var taskName = api.value(1);
var timeStart = api.coord([api.value(2), index]);
var timeEnd = api.coord([api.value(3), index]);
var taskId = api.value(4);
var donePercentage = api.value(5);
var groupColor = api.value(10);
var barLength = timeEnd[0] - timeStart[0];
// Get the heigth corresponds to length 1 on y axis.
var barHeight = api.size([0, 1])[1] * this.HEIGHT_RATIO;
var x = timeStart[0];
var y = (timeStart[1] - barHeight) - (barHeight / 3);
var taskNameWidth = format.getTextRect(taskName).width;
var text = (barLength > taskNameWidth + 40 && x + barLength >= 180)
? taskName : '';
var rectNormal = this.clipRectByRect(params, {
x: x, y: y, width: (barLength), height: barHeight
});
var rectText = this.clipRectByRect(params, {
x: x, y: y, width: (barLength), height: barHeight
});
var rectPercent = this.clipRectByRect(params, {
x: x, y: y, width: (barLength) * donePercentage / 100, height: 3
});
return {
type: 'group',
children: [{
type: 'rect',
ignore: !rectNormal,
shape: rectNormal,
style: api.style({
fill: groupColor
})
}, {
type: 'rect',
ignore: !rectText,
shape: rectText,
style: api.style({
fill: 'transparent',
stroke: 'transparent',
text: text,
textFill: '#fff'
})
}, {
type: 'rect',
ignore: !rectPercent,
shape: rectPercent,
style: api.style({
fill: 'rgba(214, 40, 40, 1)',
stroke: 'transparent',
})
}]
};
}
renderAxisLabelItem(params, api) {
//console.log("renderAxisLabelItem", api.value(0), api.value(1), api);
//console.log("api.coord([0, api.value(0)])", api.coord([0, api.value(0)]))
var index = api.value(0);
var taskName = api.value(1);
var taskId = api.value(4);
var donePercentage = api.value(5);
var start = api.value(2);
var end = api.value(3);
var owner = api.value(6);
var image = api.value(7);
var groupName = api.value(8);
var isToDrawGroup = api.value(9);
var groupColor = api.value(10);
//console.log(taskId, groupName, isToDrawGroup, groupColor)
var daysToEnd = DateManipulator.daysLeft(end, this._translation);
var y = api.coord([0, index])[1];
var barHeight = api.size([0, 1])[1];
if (donePercentage == 100) {
daysToEnd = (this._translation ? this._translation.FINISHED : "FINISHED");
}
let groupedElement = {
type: 'group',
silent: true,
position: [
10,
y
],
children: [{
type: 'rect',
shape: { x: 0, y: (params.coordSys.y - 2 * barHeight) + (barHeight / 6), width: 210, height: 46 },
style: {
fill: groupColor,
}
}, {
type: 'image',
//left: 'center', // Position at the center horizontally.
//bottom: '10%', // Position beyond the bottom boundary 10%.
style: {
image: image,
x: 5,
y: (params.coordSys.y - 2 * barHeight) + (barHeight / 3),
width: 25,
height: 25
}
}, {
type: 'text',
style: {
x: 35,
y: (params.coordSys.y - 2 * barHeight) + (barHeight * 0.5),
text: taskName,
textVerticalAlign: 'bottom',
textAlign: 'left',
textFill: '#000'
}
}, {
type: 'text',
style: {
x: 35,
y: (params.coordSys.y - 2 * barHeight) + (barHeight * 0.7),
textVerticalAlign: 'bottom',
textAlign: 'left',
text: daysToEnd,
textFill: '#000',
fontSize: 9,
}
}]
};
if (this._enableGroup) {
if (isToDrawGroup == 1) { // group agrupator (Vertical rectangle)
groupedElement.children.push({
type: 'rect',
shape: { x: 105, y: (params.coordSys.y - 2 * barHeight) - (barHeight / 3), width: 10, height: 46 },
style: {
fill: groupColor,
}
});
}
else {
groupedElement.children.push({
type: 'text',
style: {
x: -10,
y: (params.coordSys.y - 2 * barHeight) + (barHeight / 9),
text: groupName,
textVerticalAlign: 'bottom',
textAlign: 'left',
textFill: this._darkTheme ? '#fff' : '#000'
}
});
}
}
return groupedElement;
}
renderArrowsItem(params, api) {
var index = api.value(0);
var taskName = api.value(1);
var timeStart = api.coord([api.value(2), index]);
var timeEnd = api.coord([api.value(3), index]);
var taskId = api.value(4);
var barLength = timeEnd[0] - timeStart[0];
// Get the heigth corresponds to length 1 on y axis.
var barHeight = api.size([0, 1])[1] * this.HEIGHT_RATIO;
var x = timeStart[0];
var y = (timeStart[1] - barHeight) - (barHeight / 3);
//the api.value only suports numeric and string values to get... to get taskDependencies I need to get from my real data variable
var currentData = this._taskData[params.dataIndex];
var taskDependencies = currentData.taskDependencies;
let links = [];
let dependencies = taskDependencies;
for (let j = 0; j < dependencies.length; j++) {
let taskFather = this.taskDataManipulator.getTaskByIdInMappedData(this._mappedData, dependencies[j]);
if (taskFather == null)
continue;
//console.log("dependencies", taskName, taskFather)
var indexFather = taskFather[0]; //index
var timeStartFather = api.coord([taskFather[2], indexFather]);
var timeEndFather = api.coord([taskFather[3], indexFather]);
var barLengthFather = timeEndFather[0] - timeStartFather[0];
// Get the heigth corresponds to length 1 on y axis.
var barHeightFather = api.size([0, 1])[1] * this.HEIGHT_RATIO;
var xFather = timeStartFather[0];
var yFather = (timeStartFather[1] - barHeightFather) - (barHeightFather / 3);
let color = this._darkTheme ? this.arrowColors[1] : this.arrowColors[0];
let arrow = {};
//condition to draw the arrow correctly when a dependent task is exactly below another task
if (x < xFather + barLengthFather / 2) {
if (y > yFather) {
arrow = {
type: 'polygon',
shape: {
points: [[xFather + barLengthFather / 2 - 5, (y) - 10], [xFather + barLengthFather / 2 + 5, (y) - 10], [xFather + barLengthFather / 2, (y)]]
},
style: api.style({
fill: color,
})
};
}
else {
arrow = {
type: 'polygon',
shape: {
points: [[xFather + barLengthFather / 2 - 5, (y + barHeightFather + 10)], [xFather + barLengthFather / 2 + 5, (y + barHeightFather + 10)], [xFather + barLengthFather / 2, (y + barHeightFather)]]
},
style: api.style({
fill: color,
})
};
}
}
else {
//draw normaly
arrow = {
type: 'polygon',
shape: {
points: [[x - 5, (y + barHeight / 2) - 5], [x - 5, (y + barHeight / 2) + 5], [x + 5, (y + barHeight / 2)]]
},
style: api.style({
fill: color,
})
};
}
let verticalLine = {
type: 'line',
shape: {
x1: xFather + barLengthFather / 2,
y1: yFather + barHeightFather,
x2: xFather + barLengthFather / 2,
y2: y + barHeightFather / 2
},
style: api.style({
fill: color,
stroke: color
})
};
let horizontalLine = {
type: 'line',
shape: {
x1: xFather + barLengthFather / 2,
y1: y + barHeightFather / 2,
x2: x,
y2: y + barHeightFather / 2
},
style: api.style({
fill: color,
stroke: color
})
};
links.push({
type: 'group',
children: [verticalLine, horizontalLine, arrow]
});
}
return {
type: 'group',
children: links
};
}
renderArrowsItem2(params, api) {
var index = api.value(0);
var taskName = api.value(1);
var timeStart = api.coord([api.value(2), index]);
var timeEnd = api.coord([api.value(3), index]);
var taskId = api.value(4);
var barLength = timeEnd[0] - timeStart[0];
// Get the heigth corresponds to length 1 on y axis.
var barHeight = api.size([0, 1])[1] * this.HEIGHT_RATIO;
var x = timeStart[0];
var y = timeStart[1] - barHeight;
//the api.value only suports numeric and string values to get... to get taskDependencies I need to get from my real data variable
var currentData = this._taskData[params.dataIndex];
var taskDependencies = currentData.taskDependencies;
let links = [];
let dependencies = taskDependencies;
for (let j = 0; j < dependencies.length; j++) {
let taskFather = this.taskDataManipulator.getTaskByIdInMappedData(this._mappedData, dependencies[j]);
if (taskFather == null)
continue;
//console.log("dependencies", taskName, taskFather)
var indexFather = taskFather[0]; //index
var timeStartFather = api.coord([taskFather[2], indexFather]);
var timeEndFather = api.coord([taskFather[3], indexFather]);
var barLengthFather = timeEndFather[0] - timeStartFather[0];
// Get the heigth corresponds to length 1 on y axis.
var barHeightFather = api.size([0, 1])[1] * this.HEIGHT_RATIO;
var xFather = timeStartFather[0];
var yFather = timeStartFather[1] - barHeightFather;
links.push({
type: 'group',
children: [
{
type: 'line',
shape: {
x1: xFather + barLengthFather,
y1: yFather + barHeightFather / 2,
x2: x,
y2: yFather + barHeightFather / 2
},
style: api.style({
fill: "#000",
stroke: "#000"
})
}, {
type: 'line',
shape: {
x1: x,
y1: yFather + barHeightFather / 2,
x2: x - 10,
y2: y + barHeight / 2
},
style: api.style({
fill: "#000",
stroke: "#000"
})
}, {
type: 'polygon',
shape: {
points: [[x - 5, (y + barHeight / 2) - 5], [x - 5, (y + barHeight / 2) + 10], [x + 5, (y + barHeight / 2)],]
},
style: api.style({
fill: "#000",
stroke: "#000"
})
}
]
});
}
return {
type: 'group',
children: links
};
}
renderZebra(params, api) {
var index = api.value(0);
var timeToday = api.coord([new Date(), index]);
var timeStart = api.coord([api.value(1), index]);
var timeEnd = api.coord([api.value(2), index]);
//if time start > timeToday we need to fix the bar lenght and x position
var barLength = timeEnd[0] - (timeStart[0] > timeToday[0] ? timeToday[0] : timeStart[0]);
// Get the heigth corresponds to length 1 on y axis.
var barHeight = api.size([0, 1])[1];
var x = timeStart[0] > timeToday[0] ? timeToday[0] : timeStart[0];
var y = timeStart[1] - barHeight;
//console.log("=======>",x, y)
var rectNormal = this.clipRectByRect(params, {
x: x, y: y, width: barLength, height: barHeight
});
return {
type: 'group',
silent: true,
children: [{
type: 'rect',
ignore: !rectNormal,
shape: rectNormal,
style: api.style({
fill: this._darkTheme ? (index % 2 == 0 ? this.zebraColor[1][0] : this.zebraColor[1][1]) : (index % 2 == 0 ? this.zebraColor[0][0] : this.zebraColor[0][1])
})
}]
};
}
renderToday(params, api) {
var today = api.coord([api.value(0), 0]);
var barHeight = api.size([0, 1])[1];
var x = today[0];
var y = barHeight;
var y_end = barHeight * 1000;
let todayText = time.format((new Date()), this.DATE_FORMAT, false);
var todayTextWidth = format.getTextRect(todayText).width;
return {
type: 'group',
silent: true,
children: [{
type: 'text',
style: {
x: x - todayTextWidth / 2,
y: y,
text: todayText,
textVerticalAlign: 'bottom',
textAlign: 'left',
textFill: '#a14b27'
}
}, {
type: 'line',
shape: {
x1: x,
y1: y,
x2: x,
y2: y_end
},
style: api.style({
fill: "#a14b27",
stroke: "#a14b27"
})
}]
};
}
clipRectByRect(params, rect) {
return graphic.clipRectByRect(rect, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
});
}
}
class GanttComponent {
constructor() {
this.taskData = [];
this.taskDataChange = new EventEmitter();
//this.dataChange.emit(this.size);
this.editClicked = new EventEmitter();
this.taskClicked = new EventEmitter();
/**
* The scroll will stop to work... its a bug that I cant figure it out :(
*/
this.enableDataZoom = false;
this.enableDarkTheme = false;
this.enableGroup = true;
this.chartTitle = "";
this.dateFormat = "{MM}/{dd}/{yyyy}";
this.colours = ["#F94144", "#F3722C", "#F8961E", "#F9844A", "#F9C74F", "#90BE6D", "#43AA8B", "#4D908E", "#577590", "#277DA1"];
this.heightRatio = 0.6;
this.loading = false;
this.height = 300;
/**
* To replace the strings
*/
this.translation = {
DONE: "done",
TO_END: "days to finish",
DELAYED: "delayed",
FINISHED: "completed",
JANUARY: "Jan",
FEBRUARY: "Fev",
MARCH: "Mar",
APRIL: "Apr",
MAY: "May",
JUNE: "Jun",
JULY: "Jul",
AUGUST: "Aug",
SEPTEMBER: "Sep",
OCTOBER: "Oct",
NOVEMBER: "Nov",
DECEMBER: "Dec"
};
/**
* Variable to control chart
*/
this.ganttWidth = 700;
this.ganttHeight = 500;
this.taskDataManipulator = new TaskDataManipulator(this.colours, this.enableGroup);
this.taskData = this.taskData.sort(this.taskDataManipulator.compareTasks);
//after sort we map to maintain the order
this.mappedData = this.taskDataManipulator.mapData(this.taskData);
this.zebraData = this.taskDataManipulator.mapZebra(this.taskData);
this.todayData = [new Date()];
}
getTitleOption() {
if (this.chartTitle === "")
return {};
return {
text: this.chartTitle,
textStyle: {
color: this.enableDarkTheme ? '#fff' : '#000'
},
left: 'center'
};
}
getGridOption() {
return {
show: true,
top: 70,
bottom: 20,
left: 225,
right: 20,
//height: '1000px',
backgroundColor: '#fff',
borderWidth: 0
};
}
getTooltipOption() {
let DATE_FORMAT = this.dateFormat;
let translation = this.translation;
return {
confine: true,
appendToBody: true,
trigger: 'item',
formatter: function (info) {
//removing tooltip from the lines
if (info != undefined && info.seriesIndex != 2) {
return "";
}
//console.log("info", info)
var value = info.value;
var taskName = value[1];
var start = time.format((new Date(value[2])), DATE_FORMAT, false);
var end = time.format((new Date(value[3])), DATE_FORMAT, false);
var donePercentage = value[5];
return [
'<div class="tooltip-title">' + format.encodeHTML(taskName) + '</div>',
start + ' - ',
end + '<br>', donePercentage + '% ' + (translation ? translation.DONE : "DONE")
].join('');
}
};
}
resetZoomAction() {
this.echartsInstance.dispatchAction({
type: 'dataZoom',
start: 0,
end: 100
});
}
editAction() {
this.editClicked.emit(true);
}
getToolboxOption() {
return {
left: 20,
top: 0,
itemSize: 20,
feature: {
myEditor: {
show: true,
title: 'Edit',
icon: 'path://M990.55 380.08 q11.69 0 19.88 8.19 q7.02 7.01 7.02 18.71 l0 480.65 q-1.17 43.27 -29.83 71.93 q-28.65 28.65 -71.92 29.82 l-813.96 0 q-43.27 -1.17 -72.5 -30.41 q-28.07 -28.07 -29.24 -71.34 l0 -785.89 q1.17 -43.27 29.24 -72.5 q29.23 -29.24 72.5 -29.24 l522.76 0 q11.7 0 18.71 7.02 q8.19 8.18 8.19 18.71 q0 11.69 -7.6 19.29 q-7.6 7.61 -19.3 7.61 l-518.08 0 q-22.22 1.17 -37.42 16.37 q-15.2 15.2 -15.2 37.42 l0 775.37 q0 23.39 15.2 38.59 q15.2 15.2 37.42 15.2 l804.6 0 q22.22 0 37.43 -15.2 q15.2 -15.2 16.37 -38.59 l0 -474.81 q0 -11.7 7.02 -18.71 q8.18 -8.19 18.71 -8.19 l0 0 ZM493.52 723.91 l-170.74 -170.75 l509.89 -509.89 q23.39 -23.39 56.13 -21.05 q32.75 1.17 59.65 26.9 l47.94 47.95 q25.73 26.89 27.49 59.64 q1.75 32.75 -21.64 57.3 l-508.72 509.9 l0 0 ZM870.09 80.69 l-56.13 56.14 l94.72 95.9 l56.14 -57.31 q8.19 -9.35 8.19 -21.05 q-1.17 -12.86 -10.53 -22.22 l-47.95 -49.12 q-10.52 -9.35 -23.39 -9.35 q-11.69 -1.17 -21.05 7.01 l0 0 ZM867.75 272.49 l-93.56 -95.9 l-380.08 380.08 l94.73 94.73 l378.91 -378.91 l0 0 ZM322.78 553.16 l38.59 39.77 l-33.92 125.13 l125.14 -33.92 l38.59 38.6 l-191.79 52.62 q-5.85 1.17 -12.28 0 q-6.44 -1.17 -11.11 -5.84 q-4.68 -4.68 -5.85 -11.7 q-2.34 -5.85 0 -11.69 l52.63 -192.97 l0 0 Z',
onclick: this.editAction.bind(this)
},
myZoomMinus: this.enableDataZoom ? {
show: true,
title: 'Reset Zoom',
icon: 'path://M10,1.344c-4.781,0-8.656,3.875-8.656,8.656c0,4.781,3.875,8.656,8.656,8.656c4.781,0,8.656-3.875,8.656-8.656C18.656,5.219,14.781,1.344,10,1.344z M10,17.903c-4.365,0-7.904-3.538-7.904-7.903S5.635,2.096,10,2.096S17.903,5.635,17.903,10S14.365,17.903,10,17.903z M13.388,9.624H6.613c-0.208,0-0.376,0.168-0.376,0.376s0.168,0.376,0.376,0.376h6.775c0.207,0,0.377-0.168,0.377-0.376S13.595,9.624,13.388,9.624z',
onclick: this.resetZoomAction.bind(this)
} : {},
saveAsImage: {
show: true,
icon: 'path://M6.523,7.683c0.96,0,1.738-0.778,1.738-1.738c0-0.96-0.778-1.738-1.738-1.738c-0.96,0-1.738,0.778-1.738,1.738 C4.785,6.904,5.563,7.683,6.523,7.683z M5.944,5.365h1.159v1.159H5.944V5.365z M18.113,0.729H1.888 c-0.64,0-1.159,0.519-1.159,1.159v16.224c0,0.64,0.519,1.159,1.159,1.159h16.225c0.639,0,1.158-0.52,1.158-1.159V1.889 C19.271,1.249,18.752,0.729,18.113,0.729z M18.113,17.532c0,0.321-0.262,0.58-0.58,0.58H2.467c-0.32,0-0.579-0.259-0.579-0.58 V2.468c0-0.32,0.259-0.579,0.579-0.579h15.066c0.318,0,0.58,0.259,0.58,0.579V17.532z M15.91,7.85l-4.842,5.385l-3.502-2.488 c-0.127-0.127-0.296-0.18-0.463-0.17c-0.167-0.009-0.336,0.043-0.463,0.17l-3.425,4.584c-0.237,0.236-0.237,0.619,0,0.856 c0.236,0.236,0.62,0.236,0.856,0l3.152-4.22l3.491,2.481c0.123,0.123,0.284,0.179,0.446,0.174c0.16,0.005,0.32-0.051,0.443-0.174 l5.162-5.743c0.238-0.236,0.238-0.619,0-0.856C16.529,7.614,16.146,7.614,15.91,7.85z'
}
}
};
}
getXAxisOption() {
return {
type: 'time',
position: 'top',
splitLine: {
lineStyle: {
color: ['#E9EDFF']
}
},
axisLine: {
show: false
},
axisTick: {
lineStyle: {
color: '#929ABA'
}
},
axisLabel: {
color: '#929ABA',
inside: false,
align: 'center',
formatter: this.formatLabelDate.bind(this)
}
};
}
formatLabelDate(value, index) {
let valueDate = new Date(value);
let dayToday = valueDate.getDate();
let monthToday = valueDate.getMonth();
if (this.isFirstDay(dayToday, monthToday)) {
return this.getMonthName(monthToday);
}
return dayToday + "";
/*let DATE_FORMAT = this.dateFormat
return echarts.time.format(
value,
DATE_FORMAT,
false
);*/
}
/**
*
* @param dayToday day reference to check if is the last day of the month
* @param month (0-11) month reference to check if the day passed is the last day of the month.
* @returns true if day is the last day of the month. False otherwise
*/
getLastDayMonth(dayToday, month) {
//var month = 0; // January
var d = new Date(new Date().getFullYear(), month + 1, 0).getDate();
return d == dayToday;
}
isFirstDay(dayToday, month) {
return dayToday == 1;
}
getMonthName(month) {
switch (month) {
case 0:
return this.translation ? this.translation.JANUARY : "Jan";
case 1:
return this.translation ? this.translation.FEBRUARY : "Fev";
case 2:
return this.translation ? this.translation.MARCH : "Mar";
case 3:
return this.translation ? this.translation.APRIL : "Apr";
case 4:
return this.translation ? this.translation.MAY : "May";
case 5:
return this.translation ? this.translation.JUNE : "Jun";
case 6:
return this.translation ? this.translation.JULY : "Jul";
case 7:
return this.translation ? this.translation.AUGUST : "Aug";
case 8:
return this.translation ? this.translation.SEPTEMBER : "Sep";
case 9:
return this.translation ? this.translation.OCTOBER : "Oct";
case 10:
return this.translation ? this.translation.NOVEMBER : "Nov";
case 11:
return this.translation ? this.translation.DECEMBER : "Dec";
}
return "";
}
getYAxisOption() {
return {
axisTick: { show: false },
splitLine: { show: false },
axisLine: { show: false },
axisLabel: { show: false },
min: 0,
max: this.taskData.length
};
}
getSerieZebra() {
var _zebraDataDimensions = [
{ name: 'index', type: 'number' },
{ name: 'start', type: 'time' },
{ name: 'end', type: 'time' },
{ name: 'taskId', type: 'number' }
];
return {
id: 'zebra',
type: 'custom',
renderItem: this.renderers.renderZebra.bind(this.renderers),
dimensions: _zebraDataDimensions,
encode: {
x: -1,
y: 3,
},
data: this.zebraData //Im changing the item object to array... this is why the encode is filled with indexed
};
}
getSerieArrow(taskDataDimensions) {
return {
id: 'arrow',
type: 'custom',
clip: true,
silent: true,
itemStyle: {
borderType: 'dashed'
},
renderItem: this.renderers.renderArrowsItem.bind(this.renderers),
dimensions: taskDataDimensions,
tooltip: null,
encode: {
x: -1,
y: 4,
},
data: this.mappedData //Im changing the item object to array... this is why the encode is filled with indexed
};
}
getSerieGantt(taskDataDimensions) {
return {
id: 'taskData',
type: 'custom',
itemStyle: {},
renderItem: this.renderers.renderGanttItem.bind(this.renderers),
dimensions: taskDataDimensions,
encode: {
x: [1, 2, 3, 4],
y: 4,
tooltip: [0, 1, 2]
},
data: this.mappedData //Im changing the item object to array... this is why the encode is filled with indexed
};
}
getSerieAxisY(taskDataDimensions) {
return {
type: 'custom',
renderItem: this.renderers.renderAxisLabelItem.bind(this.renderers),
dimensions: taskDataDimensions,
encode: {
x: -1,
y: 4,
tooltip: [0, 1, 2]
},
data: this.mappedData //Im changing the item object to array... this is why the encode is filled with indexed
};
}
getSerieToday() {
return {
id: 'today',
type: 'custom',
renderItem: this.renderers.renderToday.bind(this.renderers),
dimensions: [{ name: 'today', type: 'time' }],
encode: {
x: 0,
y: -1,
},
data: this.todayData
};
}
getDataZoom() {
if (this.enableDataZoom == false) {
return [];
}
return [{
type: 'slider',
xAxisIndex: 0,
filterMode: 'weakFilter',
height: 30,
bottom: 0,
start: 0,
end: 30,
showDetail: false
}, {
type: 'inside',
id: 'insideX',
xAxisIndex: 0,
filterMode: 'weakFilter',
start: 0,
end: 30,
zoomOnMouseWheel: false,
moveOnMouseMove: false,
moveOnMouseWheel: true,
preventDefaultMouseMove: false,
preventDefaultMouseWheel: false
}];
}
getSeries() {
var taskDataDimensions = [
{ name: 'index', type: 'number' },
{ name: 'taskName', type: 'ordinal' },
{ name: 'start', type: 'time' },
{ name: 'end', type: 'time' },
{ name: 'taskId', type: 'number' },
{ name: 'donePercentage', type: 'number' },
{ name: 'owner', type: 'ordinal' },
{ name: 'image', type: 'ordinal' },
{ name: 'groupName', type: 'ordinal' },
{ name: 'isToDrawGroup', type: 'number' },
{ name: 'groupColor', type: 'ordinal' },
];
return [this.getSerieZebra(),
this.getSerieArrow(taskDataDimensions),
this.getSerieGantt(taskDataDimensions),
this.getSerieAxisY(taskDataDimensions),
this.getSerieToday()];
}
setChartOptions() {
this.chartOptions = {
backgroundColor: "transparent",
tooltip: this.getTooltipOption(),
animation: false,
toolbox: this.getToolboxOption(),
title: this.getTitleOption(),
dataZoom: this.getDataZoom(),
grid: this.getGridOption(),
xAxis: this.getXAxisOption(),
yAxis: this.getYAxisOption(),
series: this.getSeries()
};
/*if(this.echartsInstance){
this.echartsInstance.setOption(this.chartOptions)
}*/
}
ngOnInit() {
this.setChartOptions();
}
ngAfterViewInit() {
//import * as echarts from 'echarts';
//@ViewChild('gantt')
//public gantt: ElementRef | undefined;
//public ganttEchart: any;
//this.ganttEchart = echarts.init(this.gantt!.nativeElement);
}
ngOnChanges(changes) {
if (this.echartsInstance) {
this.echartsInstance.clear();
}
this.taskDataManipulator = new TaskDataManipulator(this.colours, this.enableGroup);
this.taskData = this.taskData.sort(this.taskDataManipulator.compareTasks);
//after sort we map to maintain the order
this.mappedData = this.taskDataManipulator.mapData(this.taskData);
this.zebraData = this.taskDataManipulator.mapZebra(this.taskData);
this.todayData = [new Date()];
this.renderers = new GanttRenderers(this.taskData, this.mappedData, this.colours, this.dateFormat, this.heightRatio, this.translation, this.enableGroup, this.enableDarkTheme);
this.setChartOptions();
}
ngAfterContentChecked() {
if (this.wrapper == undefined)
return;
this.ganttWidth = this.wrapper.nativeElement.offsetWidth;
this.ganttHeight = this.wrapper.nativeElement.offsetHeight;
var chartHeight = this.taskData.length * 80;
this.ganttHeight = chartHeight < 300 ? 300 : chartHeight;
}
onChartInit(ec) {
this.echartsInstance = ec;
this.echartsInstance.resize();
}
onTaskClicked(params) {
if (params != undefined) {
/*let task:TaskModel = new TaskModel()
task.taskName = params.value[1]
task.start = params.value[2]
task.end = params.value[3]
task.taskId = params.value[4]
task.donePercentage = params.value[5]
task.owner = params.value[6]
task.image = params.value[7]
task.groupName = params.value[8]*/
//re-mapping [index, item.taskName, item.start, item.end, item.taskId, item.donePercentage, item.owner, item.image, item.groupName, isToDrawGroup, color] into taskmodel
let task = this.taskDataManipulator.getTaskById(this.taskData, params.value[4]);
if (this.taskClicked != undefined)
this.taskClicked.emit(task);
}
}
resizeChart() {
if (this.echartsInstance) {
this.echartsInstance.resize();
}
}
sizeChange(event) {
this.resizeChart();
}
}
GanttComponent.decorators = [
{ type: Component, args: [{
selector: 'iamferraz-gantt',
template: "<div class=\"wrapper\" [style.width.%]=\"100\" [style.height.%]=\"100\" #wrapper>\n <div echarts class=\"gantt-chart\" [options]=\"chartOptions\" [loading]=\"loading\" (chartInit)=\"onChartInit($event)\" (chartClick)=\"onTaskClicked($event)\" [style.width.px]=\"ganttWidth\" [style.height.px]=\"ganttHeight\" #gantt></div>\n</div>",
styles: [".wrapper{display:inline-block;width:100%;overflow-y:auto;overflow-x:hidden}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-track{background:#f1f1f1}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#555}"]
},] }
];
GanttComponent.ctorParameters = () => [];
GanttComponent.propDecorators = {
wrapper: [{ type: ViewChild, args: ['wrapper',] }],
gantt: [{ type: ViewChild, args: ['gantt',] }],
taskData: [{ type: Input }],
taskDataChange: [{ type: Output }],
editClicked: [{ type: Output }],
taskClicked: [{ type: Output }],
enableDataZoom: [{ type: Input }],
enableDarkTheme: [{ type: Input }],
enableGroup: [{ type: Input }],
chartTitle: [{ type: Input }],
dateFormat: [{ type: Input }],
colours: [{ type: Input }],
heightRatio: [{ type: Input }],
loading: [{ type: Input }],
height: [{ type: Input }],
translation: [{ type: Input }],
sizeChange: [{ type: HostListener, args: ['window:resize', ['$event'],] }]
};
class GanttChartModule {
}
GanttChartModule.decorators = [
{ type: NgModule, args: [{
declarations: [GanttComponent],
imports: [
NgxEchartsModule
],
exports: [GanttComponent]
},] }
];
/*
* Public API Surface of gantt-chart
*/
/**
* Generated bundle index. Do not edit.
*/
export { GanttChartModule, GanttComponent, TaskModel };
//# sourceMappingURL=iamferraz-gantt-chart.js.map