UNPKG

iamferraz-gantt-chart

Version:

Gantt Chart Component using Echarts library

1,059 lines (1,049 loc) 43.5 kB
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