diff --git a/Documentation.md b/Documentation.md
index dee8ead5..60a57f53 100644
--- a/Documentation.md
+++ b/Documentation.md
@@ -238,6 +238,15 @@ After adding or removing tasks a call to "g.Draw()" must be made to redraw the c
This method will clear al the tasks from the list and after that you have to call "g.Draw()".
+## Drawing custom elements on the chart ##
+
+If you want to draw something custom in addition to task bars you may find following useful:
+
+1. Put your drawing call in `afterDraw` callback. It's called in the end of each `Draw` call so view will mostly stay in sync. There you can get task row elements with `g.getList()[...].getChildRow()` and add HTML you need.
+2. Use `g.chartRowDateToX(date)` to get X coordinate for a date.
+3. If custom drawings go beyond min/max task dates - extend chart range with `vMinDate`/`vMaxDate` chart options.
+4. See example in demo with *Custom elements* flag.
+
# Options #
You can set Options as an object, following the example:
@@ -285,6 +294,8 @@ The following options take a single numeric parameter; a value of 1 will enable
|_setEventsChange():_ |Controls events when a task row is cliked. Pass a function to execute ex.: `{ taskname: function(task, event, cell, column){ console.log(task, event, cell, column); } }`|
|_setAdditionalHeaders:_ |Set object with headers values for additional columns . ex : `{ category: { title: 'Category' }` }|
|_setResources():_ |Set the list of possible resources, must be an array of objects, ex: `[{ id: 1, name: 'Mario' } , { id: 2, name: 'Henrique' }]`|
+|_setMinDate():_ |Set minimum date further than minimum task date. It doesn't trim any task if it starts before this minimum date, but can extend the chart to the left. This may be useful if you want to draw some custom elements on the chart or want to fix the time range regardless of the content|
+|_setMaxDate():_ |Similar to _setMinDate()_|
|_setEditable():_ |Set with true if you want to edit values in the data table, will show inputs instead of texts|
|_setDebug():_ |Set with true if you want to see debug in console|
diff --git a/docs/demo.html b/docs/demo.html
index 9f8a24c0..e61da6f0 100644
--- a/docs/demo.html
+++ b/docs/demo.html
@@ -18,6 +18,19 @@
.row {
width: 100%
}
+
+ .space {
+ display: inline-block;
+ width: 30px;
+ }
+
+ .deadline-line {
+ position: absolute;
+ top: 0;
+ width: 1px;
+ height: 22px;
+ background: #777777;
+ }
@@ -157,6 +170,19 @@
Hide and Show Properties
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/fixes/data.json b/docs/fixes/data.json
index 66ef2974..3d0bdf8a 100644
--- a/docs/fixes/data.json
+++ b/docs/fixes/data.json
@@ -72,8 +72,8 @@
"pDepend": "",
"pCaption": "",
"pNotes": "",
- "pDuration": "3 days"
-
+ "pDuration": "3 days",
+ "deadline": "2018-07-01"
},
{
"pID": 122,
@@ -92,7 +92,8 @@
"pOpen": 1,
"pDepend": 121,
"pCaption": "",
- "pNotes": ""
+ "pNotes": "",
+ "deadline": "2018-07-10"
},
{
"pID": 123,
@@ -130,7 +131,8 @@
"pDepend": "123SS",
"pCaption": "This is a caption",
"pNotes": null,
- "pCost": 34
+ "pCost": 34,
+ "deadline": "2018-07-05"
},
{
"pID": 2,
@@ -353,6 +355,7 @@
"pOpen": 1,
"pDepend": "333",
"pCaption": "",
- "pNotes": ""
+ "pNotes": "",
+ "deadline": "2019-02-01"
}
]
\ No newline at end of file
diff --git a/docs/index.js b/docs/index.js
index 5c1a2e99..140aff06 100644
--- a/docs/index.js
+++ b/docs/index.js
@@ -41,6 +41,9 @@ function start(e) {
vShowTaskInfoLink = document.querySelector('#vShowTaskInfoLink:checked') ? 1 : 0;
vShowEndWeekDate = document.querySelector('#vShowEndWeekDate:checked') ? 1 : 0;
+ vMinDate = document.querySelector('#vMinDate').value;
+ vMaxDate = document.querySelector('#vMaxDate').value;
+
vAdditionalHeaders = {
category: {
title: 'Category'
@@ -68,6 +71,8 @@ function start(e) {
vShowPlanStartDate,
vShowPlanEndDate,
vAdditionalHeaders,
+ vMinDate,
+ vMaxDate,
vEvents: {
taskname: console.log,
res: console.log,
@@ -79,7 +84,12 @@ function start(e) {
planend: console.log,
cost: console.log,
beforeDraw: ()=>console.log('before draw listener'),
- afterDraw: ()=>console.log('before after listener')
+ afterDraw: ()=> {
+ console.log('before after listener');
+ if (document.querySelector("#customElements:checked")) {
+ drawCustomElements(g);
+ }
+ }
},
vEventsChange: {
taskname: editValue.bind(this, jsonObj),
@@ -169,4 +179,19 @@ function editValue(list, task, event, cell, column) {
found[column] = event ? event.target.value : '';
}
}
+
+function drawCustomElements(g) {
+ for (const item of g.getList()) {
+ if (item.getDataObject().deadline) {
+ const x = g.chartRowDateToX(new Date(item.getDataObject().deadline));
+ const td = item.getChildRow().querySelector('td');
+ td.style.position = 'relative';
+ const div = document.createElement('div');
+ div.style.left = `${x}px`;
+ div.classList.add('deadline-line');
+ td.appendChild(div);
+ }
+ }
+}
+
start('pt')
\ No newline at end of file
diff --git a/docs/jsgantt.js b/docs/jsgantt.js
index ac0992e9..df57156f 100644
--- a/docs/jsgantt.js
+++ b/docs/jsgantt.js
@@ -1,4 +1,4 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSGantt = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o vDate.getTime())
@@ -3468,6 +3477,17 @@ exports.changeFormat = function (pFormat, ganttObj) {
else
alert('Chart undefined');
};
+exports.coerceDate = function (date) {
+ if (date instanceof Date) {
+ return date;
+ }
+ else {
+ var temp = new Date(date);
+ if (temp instanceof Date && !isNaN(temp.valueOf())) {
+ return temp;
+ }
+ }
+};
exports.parseDateStr = function (pDateStr, pFormatStr) {
var vDate = new Date();
var vDateParts = pDateStr.split(/[^0-9]/);
@@ -4176,4 +4196,4 @@ exports.getXMLTask = function (pID, pIdx) {
};
},{"./task":8,"./utils":9}]},{},[1])(1)
-});
\ No newline at end of file
+});
diff --git a/src/draw.ts b/src/draw.ts
index 1ca6d046..61a2371b 100644
--- a/src/draw.ts
+++ b/src/draw.ts
@@ -5,7 +5,7 @@ import {
} from "./events";
import {
parseDateFormatStr, formatDateStr, getOffset, parseDateStr, getZoomFactor,
- getScrollPositions, getMaxDate, getMinDate
+ getScrollPositions, getMaxDate, getMinDate, coerceDate
} from './utils';
import { createTaskInfo, AddTaskItem, AddTaskItemObject, RemoveTaskItem, processRows, ClearTasks } from './task';
import { includeGetSet } from './options';
@@ -121,6 +121,8 @@ export const GanttChart = function (pDiv, pFormat) {
this.vTimer = 20;
this.vTooltipDelay = 1500;
this.vTooltipTemplate = null;
+ this.vMinDate = null;
+ this.vMaxDate = null;
this.includeGetSet = includeGetSet.bind(this);
this.includeGetSet();
@@ -165,8 +167,6 @@ export const GanttChart = function (pDiv, pFormat) {
-
-
this.clearDependencies = function () {
let parent = this.getLines();
while (parent.hasChildNodes()) parent.removeChild(parent.firstChild);
@@ -347,8 +347,8 @@ export const GanttChart = function (pDiv, pFormat) {
this.vProcessNeeded = false;
// get overall min/max dates plus padding
- vMinDate = getMinDate(this.vTaskList, this.vFormat);
- vMaxDate = getMaxDate(this.vTaskList, this.vFormat);
+ vMinDate = getMinDate(this.vTaskList, this.vFormat, this.getMinDate() && coerceDate(this.getMinDate()));
+ vMaxDate = getMaxDate(this.vTaskList, this.vFormat, this.getMaxDate() && coerceDate(this.getMaxDate()));
// Calculate chart width variables.
if (this.vFormat == 'day') vColWidth = this.vDayColWidth;
@@ -1031,6 +1031,11 @@ export const GanttChart = function (pDiv, pFormat) {
const ad = new Date();
console.log('after draw', ad, (ad.getTime() - bd.getTime()));
}
+
+ this.chartRowDateToX = function (date) {
+ return getOffset(vMinDate, date, vColWidth, this.vFormat);
+ }
+
if (this.vEvents && this.vEvents.afterDraw) {
this.vEvents.afterDraw()
}
diff --git a/src/options.ts b/src/options.ts
index d96e706c..18678204 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -105,6 +105,8 @@ export const includeGetSet = function () {
this.setTimer = function (pVal) { this.vTimer = pVal * 1; };
this.setTooltipDelay = function (pVal) { this.vTooltipDelay = pVal * 1; };
this.setTooltipTemplate = function (pVal) { this.vTooltipTemplate = pVal; };
+ this.setMinDate = function (pVal) { this.vMinDate = pVal; };
+ this.setMaxDate = function (pVal) { this.vMaxDate = pVal; };
this.addLang = function (pLang, pVals) {
if (!this.vLangs[pLang]) {
this.vLangs[pLang] = new Object();
@@ -176,6 +178,8 @@ export const includeGetSet = function () {
this.getChartTable = function () { return this.vChartTable; };
this.getLines = function () { return this.vLines; };
this.getTimer = function () { return this.vTimer; };
+ this.getMinDate = function () { return this.vMinDate; };
+ this.getMaxDate = function () { return this.vMaxDate; };
this.getTooltipDelay = function () { return this.vTooltipDelay; };
this.getList = function () { return this.vTaskList; };
this.getEventsClickCell = function () { return this.vEvents; };
diff --git a/src/utils.ts b/src/utils.ts
index a7c35c7b..6408bda7 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -22,14 +22,14 @@ export const internalPropertiesLang = {
'pPlanEnd': 'planenddate'
};
-export const getMinDate = function (pList, pFormat) {
+export const getMinDate = function (pList, pFormat, pMinDate) {
let vDate = new Date();
- if (pList.length <= 0) return vDate;
+ if (pList.length <= 0) return pMinDate || vDate;
- vDate.setTime(pList[0].getStart().getTime());
+ vDate.setTime((pMinDate && pMinDate.getTime()) || pList[0].getStart().getTime());
- // Parse all Task End dates to find min
+ // Parse all Task Start dates to find min
for (let i = 0; i < pList.length; i++) {
if (pList[i].getStart().getTime() < vDate.getTime()) vDate.setTime(pList[i].getStart().getTime());
}
@@ -68,12 +68,12 @@ export const getMinDate = function (pList, pFormat) {
return (vDate);
};
-export const getMaxDate = function (pList, pFormat) {
+export const getMaxDate = function (pList, pFormat, pMaxDate) {
let vDate = new Date();
- if (pList.length <= 0) return vDate;
+ if (pList.length <= 0) return pMaxDate || vDate;
- vDate.setTime(pList[0].getEnd().getTime());
+ vDate.setTime((pMaxDate && pMaxDate.getTime()) || pList[0].getEnd().getTime());
// Parse all Task End dates to find max
for (let i = 0; i < pList.length; i++) {
@@ -130,6 +130,17 @@ export const changeFormat = function (pFormat, ganttObj) {
else alert('Chart undefined');
};
+export const coerceDate = function (date) {
+ if (date instanceof Date) {
+ return date;
+ } else {
+ const temp = new Date(date);
+ if (temp instanceof Date && !isNaN(temp.valueOf())) {
+ return temp;
+ }
+ }
+}
+
export const parseDateStr = function (pDateStr, pFormatStr) {
let vDate = new Date();
let vDateParts = pDateStr.split(/[^0-9]/);