Compare commits
6 Commits
master
...
release/0.
Author | SHA1 | Date | |
---|---|---|---|
|
993a2eef96 | ||
|
c4434e1b2b | ||
|
1d9c6bd146 | ||
|
e3ad9348b5 | ||
|
ba85af796e | ||
|
005da263fa |
@ -1,80 +0,0 @@
|
|||||||
.ryz-project {
|
|
||||||
border-spacing: 0px;
|
|
||||||
border-collapse: separate;
|
|
||||||
cursor: default;
|
|
||||||
padding: 0px 0px 30px 0px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ryz-project .b {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.ryz-project .c {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.ryz-project .i {
|
|
||||||
display: inline-block;
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ryz-project thead tr {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 0.8em;
|
|
||||||
user-select: none;
|
|
||||||
height: 40px;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
.ryz-project thead tr th {
|
|
||||||
background-color: #E1E1E1;
|
|
||||||
border-color: #B8B8B8;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0px 0px 1px 1px;
|
|
||||||
color: #3E7138;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 6px 10px 5px 10px;
|
|
||||||
min-width: 20px;
|
|
||||||
}
|
|
||||||
.ryz-project thead tr th:first-child {
|
|
||||||
background-color: inherit;
|
|
||||||
border-left-width: 0px;
|
|
||||||
}
|
|
||||||
.ryz-project thead tr th:last-child {
|
|
||||||
border-right-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ryz-project tbody tr td {
|
|
||||||
border-color: #B8B8B8;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0px 0px 1px 0px;
|
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 5px 5px 5px 5px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.ryz-project tbody tr td:first-child {
|
|
||||||
border-right-width: 1px;
|
|
||||||
color: #7E7E7E;
|
|
||||||
font-size: 0.8em;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.ryz-project tbody tr td:last-child {
|
|
||||||
border-right-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ryz-project tbody tr:hover td:first-child {
|
|
||||||
background-color: #D1F2C7;
|
|
||||||
color: #3E7138;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.border {
|
|
||||||
border-color: #B8B8B8;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
}
|
|
@ -1,178 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en-GB">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="content-type" content="text/html" charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="description" content="" />
|
|
||||||
<meta name="keyword" content="" />
|
|
||||||
|
|
||||||
<script src="dist/extensions.js"></script>
|
|
||||||
<!-- <script src="build/ryzproj.min.js"></script> -->
|
|
||||||
|
|
||||||
<script src="dist/project.js"></script>
|
|
||||||
<!-- <script src="project/task-grid.js"></script> -->
|
|
||||||
<!-- <script src="project/gantt-chart.js"></script> -->
|
|
||||||
<link href="demo-project.css" rel="stylesheet" />
|
|
||||||
|
|
||||||
<title></title>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
font-size: 10pt;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
/* display: flex; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:after {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column {
|
|
||||||
/* flex: 50%; */
|
|
||||||
float: left;
|
|
||||||
/* width: 50%; */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="column" id="taskGrid1" style="width:800px">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="column" style="padding-left: 3px; width: calc(100% - 800px - 3px);">
|
|
||||||
|
|
||||||
<!-- <div style="width:100%; height: 100%; overflow: scroll;"> -->
|
|
||||||
<div id="ganttChart1"></div>
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
Document.ready(async function() {
|
|
||||||
var project1 = new LiteRyzJS.Project({ Name: "New Project 1" });
|
|
||||||
var taskGrid1 = null;
|
|
||||||
var ganttChart1 = null;
|
|
||||||
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 1,
|
|
||||||
Name: "Task A",
|
|
||||||
StartDelay: 0,
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 2,
|
|
||||||
Name: "Task B",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: 1,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 5,
|
|
||||||
Name: "Task B1",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: 2,
|
|
||||||
IsCollated: true,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 6,
|
|
||||||
Name: "Task B11",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: 5
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 7,
|
|
||||||
Name: "Task B12",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: 5,
|
|
||||||
Progress: 50
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 8,
|
|
||||||
Name: "Task E",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: true,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 9,
|
|
||||||
Name: "Task E1",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: 8
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 3,
|
|
||||||
Name: "Task C",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: 8,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.AddTask({
|
|
||||||
ID: 4,
|
|
||||||
Name: "Task D",
|
|
||||||
StartDelay: Math.randomN(0, 5),
|
|
||||||
Duration: Math.randomN(0, 28),
|
|
||||||
PredecessorTaskID: 3,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null
|
|
||||||
});
|
|
||||||
|
|
||||||
project1.Invalidate();
|
|
||||||
console.log(new Date(project1.StartDate).toLocaleDateString() + " - " + new Date(project1.FinishDate).toLocaleDateString() + " [" + project1.Duration + "]");
|
|
||||||
|
|
||||||
const taskData = project1.ExportTasks();
|
|
||||||
|
|
||||||
console.log(project1.Tasks);
|
|
||||||
console.log(taskData);
|
|
||||||
|
|
||||||
if (taskGrid1 == null) taskGrid1 = new LiteRyzJS.ProjectTaskGrid(document.getElementById("taskGrid1"));
|
|
||||||
taskGrid1.Render(taskData);
|
|
||||||
|
|
||||||
if (ganttChart1 == null) ganttChart1 = new LiteRyzJS.GanttChart(document.getElementById("ganttChart1"), {});
|
|
||||||
ganttChart1.Load(project1);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,2 +0,0 @@
|
|||||||
npm install --save-dev webpack webpack-cli
|
|
||||||
npm run build
|
|
@ -1,11 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "LiteRyzJS",
|
"name": "LiteRyzJS",
|
||||||
"version": "0.1.0.154",
|
"version": "0.1.0.424",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"css-loader": "^7.1.2",
|
||||||
|
"sass": "^1.77.8",
|
||||||
|
"sass-loader": "^16.0.0",
|
||||||
|
"style-loader": "^4.0.0",
|
||||||
"webpack": "^5.93.0",
|
"webpack": "^5.93.0",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack"
|
"build": "webpack",
|
||||||
|
"watch": "webpack --watch"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Check is array empty.
|
||||||
|
* @param {Array} value Array.
|
||||||
|
* @returns Boolean True, if array is empty or not an array type.
|
||||||
|
*/
|
||||||
Array.isEmpty = function(value) {
|
Array.isEmpty = function(value) {
|
||||||
const dataType = Object.getDataType(value);
|
const dataType = Object.getDataType(value);
|
||||||
if (dataType != "array") {
|
if (dataType != "array") {
|
||||||
@ -7,6 +12,12 @@ Array.isEmpty = function(value) {
|
|||||||
return (value.length <= 0);
|
return (value.length <= 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a linear/one-dimension array of objects from an object's property.
|
||||||
|
* @param {*} sourceArray Source array.
|
||||||
|
* @param {*} propName Property name.
|
||||||
|
* @param {*} destArray Destination array.
|
||||||
|
*/
|
||||||
Array.toFlatten = function (sourceArray, propName, destArray) {
|
Array.toFlatten = function (sourceArray, propName, destArray) {
|
||||||
for (let i=0; i<sourceArray.length; i++) {
|
for (let i=0; i<sourceArray.length; i++) {
|
||||||
destArray.push(sourceArray[i]);
|
destArray.push(sourceArray[i]);
|
||||||
@ -19,27 +30,85 @@ Array.toFlatten = function (sourceArray, propName, destArray) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add elements from an array into this array.
|
||||||
|
* @param {Array} array Array.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.addRange = function (array) {
|
Array.prototype.addRange = function (array) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
array.forEach(e => {
|
||||||
this.push(array[i]);
|
this.push(e);
|
||||||
}
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
Array.prototype.any = function (propName, value) {
|
/**
|
||||||
return (this.count(propName, value) > 0);
|
* Check all match property value.
|
||||||
|
* @param {String} propName Property name.
|
||||||
|
* @param {Object} value Property value.
|
||||||
|
* @returns Boolean True, if all objects of property name match value.
|
||||||
|
*/
|
||||||
|
Array.prototype.all = function (propName, value) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (propName == null) {
|
||||||
|
if (this[i] != value){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof(this[i][propName]) != "undefined") {
|
||||||
|
if (this[i][propName] != value){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check any match property value.
|
||||||
|
* @param {String} propName Property name.
|
||||||
|
* @param {Object} value Property value.
|
||||||
|
* @returns Boolean True, if any objects of property name match value.
|
||||||
|
*/
|
||||||
|
Array.prototype.any = function (propName, value) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (propName == null) {
|
||||||
|
if (this[i] == value){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof(this[i][propName]) != "undefined") {
|
||||||
|
if (this[i][propName] == value){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create deep copy of array.
|
||||||
|
* @returns Array
|
||||||
|
*/
|
||||||
Array.prototype.copy = function () {
|
Array.prototype.copy = function () {
|
||||||
return JSON.parse(JSON.stringify(this));
|
return JSON.parse(JSON.stringify(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of objects in array with the property value.
|
||||||
|
* @param {*} propName Property name.
|
||||||
|
* @param {*} value Property value.
|
||||||
|
* @returns Integer Number of matches.
|
||||||
|
*/
|
||||||
Array.prototype.count = function (propName, value) {
|
Array.prototype.count = function (propName, value) {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
|
|
||||||
@ -60,6 +129,11 @@ Array.prototype.count = function (propName, value) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of objects in array with many matches.
|
||||||
|
* @param {Object} filters Array of matches [{propName, value}].
|
||||||
|
* @returns Integer Number of matches.
|
||||||
|
*/
|
||||||
Array.prototype.countMany = function (...filters) {
|
Array.prototype.countMany = function (...filters) {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
|
|
||||||
@ -70,6 +144,12 @@ Array.prototype.countMany = function (...filters) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty array of a given length with a given default value.
|
||||||
|
* @param {int} length Length of array.
|
||||||
|
* @param {object} value Default value.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.create = function (length, value) {
|
Array.prototype.create = function (length, value) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -80,6 +160,19 @@ Array.prototype.create = function (length, value) {
|
|||||||
return result
|
return result
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Array.prototype.distinct = function () {
|
||||||
|
this = this.filter((obj, index, self) =>
|
||||||
|
index === self.findIndex((x) => JSON.stringify(x) === JSON.stringify(obj))
|
||||||
|
);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this array of objects to a linear/one-dimensional array using a property.
|
||||||
|
* @param {*} propName Property name.
|
||||||
|
* @returns Array Array.
|
||||||
|
*/
|
||||||
Array.prototype.flatten = function (propName) {
|
Array.prototype.flatten = function (propName) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -88,6 +181,29 @@ Array.prototype.flatten = function (propName) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get element at index.
|
||||||
|
* @param {Object} index Index of element.
|
||||||
|
* @returns Object Element.
|
||||||
|
*/
|
||||||
|
Array.prototype.get = function (index) {
|
||||||
|
if (index < 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= this.length) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return this[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find index of first occurrence of value in property.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @param {object} value Property value.
|
||||||
|
* @returns Index of element.
|
||||||
|
*/
|
||||||
Array.prototype.index = function (propName, value) {
|
Array.prototype.index = function (propName, value) {
|
||||||
const result = this.indexes(propName, value);
|
const result = this.indexes(propName, value);
|
||||||
if (result.length <= 0) {
|
if (result.length <= 0) {
|
||||||
@ -97,6 +213,12 @@ Array.prototype.index = function (propName, value) {
|
|||||||
return result[0];
|
return result[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find index of occurrences of value in property in array.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @param {object} value Property value.
|
||||||
|
* @returns Array of indexes.
|
||||||
|
*/
|
||||||
Array.prototype.indexes = function (propName, value) {
|
Array.prototype.indexes = function (propName, value) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -117,6 +239,12 @@ Array.prototype.indexes = function (propName, value) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an element into an array at index position.
|
||||||
|
* @param {int} index Array index.
|
||||||
|
* @param {object} item Array element.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.insert = function(index, item) {
|
Array.prototype.insert = function(index, item) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
this.splice(0, 0, item);
|
this.splice(0, 0, item);
|
||||||
@ -129,6 +257,11 @@ Array.prototype.insert = function(index, item) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates an array if value is not null or whitespace using a separator.
|
||||||
|
* @param {string} separator Separator text.
|
||||||
|
* @returns string.
|
||||||
|
*/
|
||||||
Array.prototype.joinIfNotNullOrWhitespace = function (separator) {
|
Array.prototype.joinIfNotNullOrWhitespace = function (separator) {
|
||||||
const a = this;
|
const a = this;
|
||||||
|
|
||||||
@ -149,6 +282,12 @@ Array.prototype.joinIfNotNullOrWhitespace = function (separator) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns first element that matches.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @param {string} value Property value.
|
||||||
|
* @returns Array element.
|
||||||
|
*/
|
||||||
Array.prototype.first = function (propName, value) {
|
Array.prototype.first = function (propName, value) {
|
||||||
for (let i = 0; i < this.length; i++) {
|
for (let i = 0; i < this.length; i++) {
|
||||||
if (typeof(this[i][propName]) == "undefined") {
|
if (typeof(this[i][propName]) == "undefined") {
|
||||||
@ -163,6 +302,11 @@ Array.prototype.first = function (propName, value) {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses an array tree following a property (by name), performing the defined function.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @param {function} func Function to perform at each element.
|
||||||
|
*/
|
||||||
Array.prototype.forEachTree = function (propName, func) {
|
Array.prototype.forEachTree = function (propName, func) {
|
||||||
for (let i=0; i<this.length; i++) {
|
for (let i=0; i<this.length; i++) {
|
||||||
if (func(this[i]) === false) {
|
if (func(this[i]) === false) {
|
||||||
@ -179,6 +323,11 @@ Array.prototype.forEachTree = function (propName, func) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort an array alphanumerically using the named property (ascending).
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.orderBy = function (propName) {
|
Array.prototype.orderBy = function (propName) {
|
||||||
this.sort(function(a, b) {
|
this.sort(function(a, b) {
|
||||||
if (a[propName] < b[propName]) {
|
if (a[propName] < b[propName]) {
|
||||||
@ -193,6 +342,11 @@ Array.prototype.orderBy = function (propName) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort an array alphanumerically using the named property (descending).
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.orderByDesc = function (propName) {
|
Array.prototype.orderByDesc = function (propName) {
|
||||||
this.sort(function(a, b) {
|
this.sort(function(a, b) {
|
||||||
if (a[propName] < b[propName]) {
|
if (a[propName] < b[propName]) {
|
||||||
@ -207,6 +361,11 @@ Array.prototype.orderByDesc = function (propName) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an element from an array.
|
||||||
|
* @param {object} element Element.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.remove = function (element) {
|
Array.prototype.remove = function (element) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -223,6 +382,11 @@ Array.prototype.remove = function (element) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove element at index position.
|
||||||
|
* @param {int} index Element index.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.removeAt = function(index) {
|
Array.prototype.removeAt = function(index) {
|
||||||
if ((index < 0) || (index >= this.length)) {
|
if ((index < 0) || (index >= this.length)) {
|
||||||
return this;
|
return this;
|
||||||
@ -233,6 +397,11 @@ Array.prototype.removeAt = function(index) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove elements in an array.
|
||||||
|
* @param {Array} array Array of elements to be removed.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.removeRange = function (array) {
|
Array.prototype.removeRange = function (array) {
|
||||||
for (let i=0; i<array.length; i++) {
|
for (let i=0; i<array.length; i++) {
|
||||||
this.remove(array[i]);
|
this.remove(array[i]);
|
||||||
@ -242,8 +411,32 @@ Array.prototype.removeRange = function (array) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find elements where a property equals a value.
|
* Returns array where each element's property does not equal value.
|
||||||
* @returns {Array} Of elements found.
|
* @param {string} propName Property name.
|
||||||
|
* @param {*} value Property value.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
|
Array.prototype.notSelect = function (propName, value) {
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (typeof(this[i][propName]) == "undefined") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this[i][propName] != value){
|
||||||
|
result.push(this[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns array where each element's property equals value.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @param {*} value Property value.
|
||||||
|
* @returns Array.
|
||||||
*/
|
*/
|
||||||
Array.prototype.select = function (propName, value) {
|
Array.prototype.select = function (propName, value) {
|
||||||
let result = [];
|
let result = [];
|
||||||
@ -261,6 +454,11 @@ Array.prototype.select = function (propName, value) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns array where each element matches a set of property values.
|
||||||
|
* @param {...any} filters Property-value pairs.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.selectMany = function (...filters) {
|
Array.prototype.selectMany = function (...filters) {
|
||||||
let result = this;
|
let result = this;
|
||||||
|
|
||||||
@ -271,6 +469,11 @@ Array.prototype.selectMany = function (...filters) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return array of element from array of object by property name.
|
||||||
|
* @param {string} propName Property name.
|
||||||
|
* @returns Array.
|
||||||
|
*/
|
||||||
Array.prototype.toList = function (propName) {
|
Array.prototype.toList = function (propName) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -285,6 +488,11 @@ Array.prototype.toList = function (propName) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort this object property using property name.
|
||||||
|
* @param {string} childPropName Property name.
|
||||||
|
* @param {string} sortPropName Sort by property name.
|
||||||
|
*/
|
||||||
Array.prototype.sortTree = function (childPropName, sortPropName) {
|
Array.prototype.sortTree = function (childPropName, sortPropName) {
|
||||||
this.orderBy(sortPropName);
|
this.orderBy(sortPropName);
|
||||||
|
|
||||||
|
@ -6,4 +6,51 @@ Document.ready = async function(fn) {
|
|||||||
fn();
|
fn();
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Document.getWidth = function(el) {
|
||||||
|
if (el == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(el) == "undefined") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (el.offsetWidth || el.innerWidth || el.clientWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document.getHeight = function(el) {
|
||||||
|
if (el == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(el) == "undefined") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (el.offsetHeight || el.innerHeight || el.clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document.appendHtml = function(el, html) {
|
||||||
|
const newEl = document.createElement(html);
|
||||||
|
|
||||||
|
el.appendChild(newEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document.addClass = function(el, className) {
|
||||||
|
if (el.classList.contains(className)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document.removeClass = function(el, className) {
|
||||||
|
if (!el.classList.contains(className)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.classList.remove(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
// src/project.js
|
|
||||||
import GanttChart from './project/gantt-chart.js';
|
|
||||||
import Project from './project/project.js';
|
|
||||||
import ProjectTaskGrid from './project/task-grid.js';
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
|
||||||
GanttChart,
|
|
||||||
Project,
|
|
||||||
ProjectTaskGrid
|
|
||||||
};
|
|
@ -1,244 +0,0 @@
|
|||||||
import Canvas from '../graphics/canvas.js';
|
|
||||||
|
|
||||||
|
|
||||||
class GanttChart {
|
|
||||||
constructor(el, options) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Canvas = new Canvas(el);
|
|
||||||
a.Options = Object.assign(a.DefaultOptions, options);
|
|
||||||
|
|
||||||
a.Debug = false;
|
|
||||||
a.Project = null;
|
|
||||||
|
|
||||||
a.StartDate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get DefaultOptions() {
|
|
||||||
return {
|
|
||||||
DayWidth: 24,
|
|
||||||
HeaderRow: {
|
|
||||||
Height: [ 21, 21 ]
|
|
||||||
},
|
|
||||||
Row: {
|
|
||||||
Height: 28,
|
|
||||||
Task: {
|
|
||||||
Height: 13,
|
|
||||||
PaddingTop: 6,
|
|
||||||
BorderColour: "#555555",
|
|
||||||
FillColour: "#9CC2E6"
|
|
||||||
},
|
|
||||||
CollatedTask: {
|
|
||||||
Height: 3,
|
|
||||||
PaddingTop: 11,
|
|
||||||
BorderColour: "#555555",
|
|
||||||
FillColour: "#555555"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Line: {
|
|
||||||
Margin: 5,
|
|
||||||
Colour: "#555555",
|
|
||||||
Width: 1,
|
|
||||||
ArrowSize: 5
|
|
||||||
},
|
|
||||||
DateFont: "7pt sans-serif",
|
|
||||||
DateForeColour: "#636363",
|
|
||||||
BorderWidth: 1,
|
|
||||||
BorderColour: "#B8B8B8",
|
|
||||||
BorderDashPattern: [1, 3],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get HeaderHeight() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
Invalidate() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Canvas.Clear();
|
|
||||||
|
|
||||||
if (a.Project == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tasks = a.Project.ExportTasks();
|
|
||||||
const width = ((a.Project.Duration + 2) * a.Options.DayWidth);
|
|
||||||
const height = (tasks.length * a.Options.Row.Height) + a.HeaderHeight;
|
|
||||||
|
|
||||||
a.AutoSize = false;
|
|
||||||
a.ClientWidth = width;
|
|
||||||
a.ClientHeight = height;
|
|
||||||
|
|
||||||
a.Canvas.Invalidate();
|
|
||||||
|
|
||||||
a.drawChartLabel(a.Project);
|
|
||||||
a.drawTasks(tasks);
|
|
||||||
a.drawLines(tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
Load(project) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Canvas.Clear();
|
|
||||||
|
|
||||||
if (project == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Project = project;
|
|
||||||
a.StartDate = new Date(project.StartDate);
|
|
||||||
|
|
||||||
a.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
drawChartLabel(project) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
const width = a.Canvas.ClientWidth;
|
|
||||||
const height = a.Canvas.ClientHeight;
|
|
||||||
const displayDays = project.Duration + 2;
|
|
||||||
|
|
||||||
let startDate = new Date(a.StartDate);
|
|
||||||
startDate.addDays(-1);
|
|
||||||
|
|
||||||
// Draw vertical lines
|
|
||||||
for (let i=1; i<displayDays; i++) {
|
|
||||||
a.Canvas.DrawVerticalLine((a.Options.DayWidth * i), (a.Options.HeaderRow.Height[0] + a.Options.BorderWidth), (a.Options.HeaderRow.Height[1] - (a.Options.BorderWidth * 2)), a.Options.BorderColour, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Canvas.DrawHorizontalLine(0, a.Options.HeaderRow.Height[0], width, a.Options.BorderColour);
|
|
||||||
a.Canvas.DrawHorizontalLine(0, (a.HeaderHeight - a.Options.BorderWidth), width, a.Options.BorderColour);
|
|
||||||
|
|
||||||
// Write dates
|
|
||||||
for (let i=0; i<displayDays; i++) {
|
|
||||||
const date = Date.addDays(startDate, i);
|
|
||||||
const x = (a.Options.DayWidth * i);
|
|
||||||
|
|
||||||
// Draw month
|
|
||||||
if (date.getDate() == 1) {
|
|
||||||
const size = a.Canvas.MeasureText(a.Options.DateFont, "#");
|
|
||||||
const monthPoint = {
|
|
||||||
X: (x + 2),
|
|
||||||
Y: Math.half(a.Options.HeaderRow.Height[0] - size.H)
|
|
||||||
};
|
|
||||||
|
|
||||||
a.Canvas.DrawText(monthPoint.X, monthPoint.Y, date.toCString("MMMM"), a.Options.DateFont, a.Options.DateForeColour);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw day
|
|
||||||
const dateRectangle = {
|
|
||||||
X: x,
|
|
||||||
Y: a.Options.HeaderRow.Height[1],
|
|
||||||
W: a.Options.DayWidth - a.Options.BorderWidth,
|
|
||||||
H: (a.Options.HeaderRow.Height[1] - (a.Options.BorderWidth * 2))
|
|
||||||
};
|
|
||||||
|
|
||||||
a.Canvas.FillText(dateRectangle, date.getDate(), a.Options.DateFont, a.Options.DateForeColour);
|
|
||||||
|
|
||||||
if (a.Debug) a.Canvas.DrawRectangle(dateRectangle, "red", {});
|
|
||||||
|
|
||||||
// Draw day-of-week guideline
|
|
||||||
const guidelineHeight = height - a.Options.BorderWidth;
|
|
||||||
|
|
||||||
if (project.Project.StartOfWeek == date.getDay()) {
|
|
||||||
// Draw start-of-the-week guideline
|
|
||||||
a.Canvas.DrawVerticalLine(x, a.HeaderHeight, guidelineHeight, a.Options.BorderColour, {});
|
|
||||||
} else {
|
|
||||||
a.Canvas.DrawVerticalLine(x, a.HeaderHeight, guidelineHeight, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drawTasks(tasks) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
for (let i=0; i<tasks.length; i++) {
|
|
||||||
const style = ((tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
|
||||||
const rectangle = a.getTaskRectangle(tasks[i], style);
|
|
||||||
|
|
||||||
if (tasks[i].Duration <= 0) {
|
|
||||||
a.Canvas.DrawDiamond(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
|
||||||
} else {
|
|
||||||
a.Canvas.DrawRectangle(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drawLines(tasks) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
for (let i=0; i<tasks.length; i++) {
|
|
||||||
if (tasks[i].PredecessorTaskID == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tasks[i].PredecessorTaskNo == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const style = ((tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
|
||||||
const rectangle = a.getTaskRectangle(tasks[i], style);
|
|
||||||
|
|
||||||
const predecessorTask = tasks.first("Order", tasks[i].PredecessorTaskNo);
|
|
||||||
if (predecessorTask == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const predecessorStyle = ((predecessorTask.IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
|
||||||
const predecessorRectangle = a.getTaskRectangle(predecessorTask, predecessorStyle);
|
|
||||||
|
|
||||||
let points = [];
|
|
||||||
points.push({ X: (predecessorRectangle.X + predecessorRectangle.W), Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
|
||||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
|
||||||
|
|
||||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: (rectangle.Y - a.Options.Line.Margin) });
|
|
||||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: rectangle.Y });
|
|
||||||
|
|
||||||
a.Canvas.DrawLines(points, a.Options.Line.Colour, { LineWidth: a.Options.Line.Width });
|
|
||||||
|
|
||||||
const arrowRectangle = {
|
|
||||||
X: (rectangle.X + a.Options.Line.Margin - Math.half(a.Options.Line.ArrowSize)),
|
|
||||||
Y: (rectangle.Y - a.Options.Line.ArrowSize),
|
|
||||||
W: a.Options.Line.ArrowSize,
|
|
||||||
H: a.Options.Line.ArrowSize
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Canvas.DrawArrowS(arrowRectangle, a.Options.Line.Colour, { FillColour: a.Options.Line.Colour });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getTaskRectangle(task, style) {
|
|
||||||
const a = this;
|
|
||||||
const i = (task.Order - 1);
|
|
||||||
|
|
||||||
let rectangle = {};
|
|
||||||
|
|
||||||
if (task.Duration <= 0) {
|
|
||||||
rectangle = {
|
|
||||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
|
||||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + style.PaddingTop),
|
|
||||||
W: style.Height,
|
|
||||||
H: style.Height
|
|
||||||
};
|
|
||||||
|
|
||||||
rectangle.X -= Math.half(rectangle.W);
|
|
||||||
} else {
|
|
||||||
rectangle = {
|
|
||||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
|
||||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + style.PaddingTop),
|
|
||||||
W: (a.Options.DayWidth * task.Duration),
|
|
||||||
H: style.Height
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return rectangle;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default GanttChart;
|
|
@ -1,428 +0,0 @@
|
|||||||
class Project {
|
|
||||||
constructor(options) {
|
|
||||||
const a = this;
|
|
||||||
const _options = Object.assign(a.newProject, options);
|
|
||||||
|
|
||||||
a.Project = _options;
|
|
||||||
a.Tasks = [];
|
|
||||||
|
|
||||||
a.initialiseComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
initialiseComponents() {
|
|
||||||
const a = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get NewTask() {
|
|
||||||
return {
|
|
||||||
// Order: null,
|
|
||||||
ID: null,
|
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
Tag: null,
|
|
||||||
// StartDate: null, // new Date(),
|
|
||||||
// FinishDate: null, // new Date(),
|
|
||||||
StartDelay: 0, // Days
|
|
||||||
Duration: 1, // Days
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null,
|
|
||||||
// CalcWorkHours: 0,
|
|
||||||
ActuWorkHours: null,
|
|
||||||
Progress: 0,
|
|
||||||
Resources: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get StartDate() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
return new Date(a.Project.StartDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
get FinishDate() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let result = new Date(a.Project.StartDate);
|
|
||||||
|
|
||||||
a.ExportTasks().forEach(e => {
|
|
||||||
if (e.FinishDate == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = Date.max(result, e.FinishDate);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Duration() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
return Date.diffDays(a.StartDate, a.FinishDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get newProject() {
|
|
||||||
return {
|
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
StartDate: Date.today(),
|
|
||||||
Tag: null,
|
|
||||||
StartOfWeek: 1, // Monday
|
|
||||||
WorkHours: [0, 7.5, 7.5, 7.5, 7.5, 7.5, 0] // 0 = Sunday
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get newTaskNode() {
|
|
||||||
return {
|
|
||||||
Order: null,
|
|
||||||
ID: null,
|
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
Tag: null,
|
|
||||||
StartDate: null, // new Date(),
|
|
||||||
FinishDate: null, // new Date(),
|
|
||||||
StartDelay: 0, // Days
|
|
||||||
Duration: 1, // Days
|
|
||||||
PredecessorTaskID: null,
|
|
||||||
IsCollated: false,
|
|
||||||
CollatedTaskID: null,
|
|
||||||
CalcWorkHours: 0,
|
|
||||||
ActuWorkHours: null,
|
|
||||||
Progress: 0,
|
|
||||||
Resources: [],
|
|
||||||
Level: 0,
|
|
||||||
PredecessorTaskNo: null,
|
|
||||||
Tasks: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AddTask(task) {
|
|
||||||
const a = this;
|
|
||||||
const newTask = Object.assign(a.NewTask, task);
|
|
||||||
const newTaskNode = Object.assign(a.newTaskNode, newTask);
|
|
||||||
|
|
||||||
if ((newTaskNode.PredecessorTaskID == null) && (newTaskNode.CollatedTaskID == null)) {
|
|
||||||
a.Tasks.push(newTaskNode);
|
|
||||||
} else if (newTaskNode.PredecessorTaskID != null) {
|
|
||||||
const node = a.FindTask(newTaskNode.PredecessorTaskID);
|
|
||||||
|
|
||||||
if (node != null) {
|
|
||||||
node.Tasks.push(newTaskNode);
|
|
||||||
} else {
|
|
||||||
a.log("Task not found (" + newTaskNode.PredecessorTaskID + ")");
|
|
||||||
}
|
|
||||||
} else if (newTaskNode.CollatedTaskID != null) {
|
|
||||||
const node = a.FindTask(newTaskNode.CollatedTaskID);
|
|
||||||
|
|
||||||
if (node != null) {
|
|
||||||
node.Tasks.push(newTaskNode);
|
|
||||||
} else {
|
|
||||||
a.log("Task not found (" + newTaskNode.CollatedTaskID + ")");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
a.log("Task not found (" + newTaskNode.ID + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearTasks() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Tasks = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportTasks() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let result = a.Tasks.copy().flatten("Tasks");
|
|
||||||
for (var i=0; i<result.length; i++) {
|
|
||||||
result[i].Order = (i + 1);
|
|
||||||
|
|
||||||
delete result[i].Tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FindTask(id) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
a.Tasks.forEachTree("Tasks", function(e) {
|
|
||||||
if (e.ID == id) {
|
|
||||||
result = e;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get node type.
|
|
||||||
* @returns {int} enum
|
|
||||||
*
|
|
||||||
* 0 = orphan
|
|
||||||
* 1 = task no-parent/root
|
|
||||||
* 2 = task no-parent/root, collated/group
|
|
||||||
* 3 = task with parent
|
|
||||||
* 4 = task with parent, collated/group
|
|
||||||
* 5 = sub-task
|
|
||||||
* 6 = sub-task, collated/group
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
GetNodeType(node) {
|
|
||||||
if (node == null) {
|
|
||||||
// 0 = orphan
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.PredecessorTaskID == null) && (node.CollatedTaskID == null) && (node.IsCollated == false)) {
|
|
||||||
// 1 = task no-parent/root
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.PredecessorTaskID == null) && (node.CollatedTaskID == null) && (node.IsCollated == true)) {
|
|
||||||
// 2 = task no-parent/root, collated/group
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.PredecessorTaskID != null) && (node.CollatedTaskID == null) && (node.IsCollated == false)) {
|
|
||||||
// 3 = task with parent
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.PredecessorTaskID != null) && (node.CollatedTaskID == null) && (node.IsCollated == true)) {
|
|
||||||
// 4 = task with parent, collated/group
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.CollatedTaskID != null) && (node.IsCollated == false)) {
|
|
||||||
// 5 = sub-task
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((node.CollatedTaskID != null) && (node.IsCollated == true)) {
|
|
||||||
// 6 = sub-task, collated/group
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Invalidate() {
|
|
||||||
const a = this;
|
|
||||||
let isSuccess = false;
|
|
||||||
|
|
||||||
if (!a.Recalculate()) {
|
|
||||||
isSuccess = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Recalculate() {
|
|
||||||
const a = this;
|
|
||||||
let isSuccess = false;
|
|
||||||
|
|
||||||
a.Sort();
|
|
||||||
|
|
||||||
// Get flat references
|
|
||||||
let result = a.Tasks.flatten("Tasks");
|
|
||||||
|
|
||||||
// Reset calculated values
|
|
||||||
for (var i=0; i<result.length; i++) {
|
|
||||||
result[i].Order = (i + 1);
|
|
||||||
result[i].StartDate = null;
|
|
||||||
result[i].FinishDate = null;
|
|
||||||
result[i].CalcWorkHours = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximum 128 rounds
|
|
||||||
for (var x=0; x<128; x++) {
|
|
||||||
let pendingCount = result.copy().countMany(
|
|
||||||
{ propName: "StartDate", value: null },
|
|
||||||
{ propName: "FinishDate", value: null }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
if (pendingCount <= 0) {
|
|
||||||
isSuccess = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.log("Round " + (x + 1));
|
|
||||||
|
|
||||||
for (var i=0; i<result.length; i++) {
|
|
||||||
const nodeType = a.GetNodeType(result[i]);
|
|
||||||
|
|
||||||
switch (nodeType) {
|
|
||||||
case 1: // task no-parent/root
|
|
||||||
result[i].StartDate = Date.addDays(a.Project.StartDate, result[i].StartDelay);
|
|
||||||
result[i].FinishDate = Date.addDays(result[i].StartDate, result[i].Duration);
|
|
||||||
break;
|
|
||||||
case 2: // task no-parent/root, collated/group
|
|
||||||
result[i].StartDate = Date.addDays(a.Project.StartDate, result[i].StartDelay);
|
|
||||||
result[i].Progress = 0;
|
|
||||||
|
|
||||||
// update finish date, if possible
|
|
||||||
a.recalculateCollatedTask(result, i);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 3: // task with parent
|
|
||||||
const node3 = result.first("ID", result[i].PredecessorTaskID);
|
|
||||||
if (node3 != null) {
|
|
||||||
if (node3.FinishDate != null) {
|
|
||||||
result[i].StartDate = Date.addDays(node3.FinishDate, result[i].StartDelay);
|
|
||||||
result[i].FinishDate = Date.addDays(result[i].StartDate, result[i].Duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 4: // task with parent, collated/group
|
|
||||||
const node4 = result.first("ID", result[i].PredecessorTaskID);
|
|
||||||
if (node4 != null) {
|
|
||||||
if (node4.FinishDate != null) {
|
|
||||||
result[i].StartDate = Date.addDays(node4.FinishDate, result[i].StartDelay);
|
|
||||||
result[i].Progress = 0;
|
|
||||||
|
|
||||||
// update finish date, if possible
|
|
||||||
a.recalculateCollatedTask(result, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 5: // sub-task
|
|
||||||
const node5 = result.first("ID", result[i].CollatedTaskID);
|
|
||||||
if (node5 != null) {
|
|
||||||
if (node5.StartDate != null) {
|
|
||||||
result[i].StartDate = Date.addDays(node5.StartDate, result[i].StartDelay);
|
|
||||||
result[i].FinishDate = Date.addDays(result[i].StartDate, result[i].Duration);
|
|
||||||
result[i].Level = (node5.Level + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 6: // sub-task, collated/group
|
|
||||||
const node6 = result.first("ID", result[i].CollatedTaskID);
|
|
||||||
if (node6 != null) {
|
|
||||||
if (node6.StartDate != null) {
|
|
||||||
result[i].StartDate = Date.addDays(node6.StartDate, result[i].StartDelay);
|
|
||||||
result[i].Progress = 0;
|
|
||||||
result[i].Level = (node6.Level + 1);
|
|
||||||
|
|
||||||
// update finish date, if possible
|
|
||||||
a.recalculateCollatedTask(result, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default: // orphan
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate work-hours
|
|
||||||
a.recalculateWorkHours(result);
|
|
||||||
|
|
||||||
// Calculate predecessor
|
|
||||||
a.recalculatePredecessorTaskNo(result);
|
|
||||||
|
|
||||||
return isSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sort() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Tasks.sortTree("Tasks", "StartDelay");
|
|
||||||
}
|
|
||||||
|
|
||||||
log(message) {
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
recalculateWorkHours(array) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
for (var i=0; i<array.length; i++) {
|
|
||||||
if (array[i].StartDate == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array[i].FinishDate == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array[i].Duration <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let workHours = 0;
|
|
||||||
let date = new Date(array[i].StartDate);
|
|
||||||
for (let x=0; x<array[i].Duration; x++) {
|
|
||||||
const d = date.getDay();
|
|
||||||
|
|
||||||
workHours += a.Project.WorkHours[d];
|
|
||||||
}
|
|
||||||
|
|
||||||
array[i].CalcWorkHours = workHours;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recalculatePredecessorTaskNo(array) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
for (var i=0; i<array.length; i++) {
|
|
||||||
if (array[i].PredecessorTaskID == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const predecessor = a.FindTask(array[i].PredecessorTaskID);
|
|
||||||
if (predecessor == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
array[i].PredecessorTaskNo = predecessor.Order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recalculateCollatedTask(array, index) {
|
|
||||||
let node2 = array.select("CollatedTaskID", array[index].ID);
|
|
||||||
if (node2.length <= 0) {
|
|
||||||
// No children
|
|
||||||
array[index].Duration = 0;
|
|
||||||
|
|
||||||
return new Date(array[index].StartDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not ready, calculation pending
|
|
||||||
if (node2.any("FinishDate", null)) {
|
|
||||||
array[index].Duration = 0;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let node2FinishDate = new Date(array[index].StartDate);
|
|
||||||
node2.forEach(e => {
|
|
||||||
if (e.FinishDate > node2FinishDate) {
|
|
||||||
node2FinishDate = e.FinishDate;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
array[index].Progress = Math.average(node2.toList("Progress"));
|
|
||||||
array[index].FinishDate = new Date(node2FinishDate);
|
|
||||||
array[index].Duration = Date.diffDays(array[index].StartDate, array[index].FinishDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default Project;
|
|
@ -1,120 +0,0 @@
|
|||||||
class ProjectTaskGrid {
|
|
||||||
constructor(el) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
a.Container = el;
|
|
||||||
|
|
||||||
a.Columns = [
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Task Name",
|
|
||||||
"Duration",
|
|
||||||
"Start",
|
|
||||||
"Finish",
|
|
||||||
"Predecessor",
|
|
||||||
"Resource Names",
|
|
||||||
null
|
|
||||||
];
|
|
||||||
|
|
||||||
a.initialiseComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
initialiseComponents() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let htmlContent = "";
|
|
||||||
htmlContent += "<table class='ryz-project'>";
|
|
||||||
htmlContent += a.renderTHead();
|
|
||||||
htmlContent += "<tbody>";
|
|
||||||
htmlContent += a.renderPlaceholder();
|
|
||||||
htmlContent += "</tbody>";
|
|
||||||
htmlContent += "</table>";
|
|
||||||
|
|
||||||
a.Container.innerHTML = htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Render(model) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let htmlContent = "";
|
|
||||||
htmlContent += "<table class='ryz-project'>";
|
|
||||||
htmlContent += a.renderTHead();
|
|
||||||
htmlContent += "<tbody>";
|
|
||||||
|
|
||||||
model.forEach(e => {
|
|
||||||
htmlContent += a.renderRow(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
htmlContent += "</tbody>";
|
|
||||||
htmlContent += "</table>";
|
|
||||||
|
|
||||||
a.Container.innerHTML = htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
renderTHead() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let htmlContent = "";
|
|
||||||
htmlContent += "<thead>";
|
|
||||||
htmlContent += "<tr>";
|
|
||||||
|
|
||||||
a.Columns.forEach(e => {
|
|
||||||
htmlContent += "<th>" + (e ?? "") + "</th>";
|
|
||||||
});
|
|
||||||
|
|
||||||
htmlContent += "</tr>";
|
|
||||||
htmlContent += "</thead>";
|
|
||||||
|
|
||||||
return htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPlaceholder() {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let htmlContent = "";
|
|
||||||
htmlContent += "<tr>";
|
|
||||||
htmlContent += "<td></td>";
|
|
||||||
htmlContent += "<td colspan='" + (a.Columns.length - 1) + "' class='c'>Loading...</td>";
|
|
||||||
htmlContent += "</tr>";
|
|
||||||
|
|
||||||
return htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRow(e) {
|
|
||||||
const a = this;
|
|
||||||
|
|
||||||
let htmlContent = "";
|
|
||||||
|
|
||||||
if (e.IsCollated == true) {
|
|
||||||
htmlContent += "<tr class='b'>";
|
|
||||||
} else {
|
|
||||||
htmlContent += "<tr>";
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlContent += "<td class='c'>" + e.Order + "</td>";
|
|
||||||
htmlContent += "<td></td>";
|
|
||||||
htmlContent += "<td>";
|
|
||||||
|
|
||||||
for (let i=0; i<e.Level; i++) {
|
|
||||||
htmlContent += "<span class='i'></span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlContent += e.Name;
|
|
||||||
htmlContent += "</td>";
|
|
||||||
htmlContent += "<td>" + e.Duration + " day" + (parseInt(e.Duration) == 1 ? "" : "s") + "</td>";
|
|
||||||
htmlContent += "<td class='c'>" + new Date(e.StartDate).toLocaleDateString() + "</td>";
|
|
||||||
htmlContent += "<td class='c'>" + new Date(e.FinishDate).toLocaleDateString() + "</td>";
|
|
||||||
htmlContent += "<td class='c'>" + (e.PredecessorTaskNo ?? "") + "</td>";
|
|
||||||
htmlContent += "<td></td>";
|
|
||||||
htmlContent += "<td></td>";
|
|
||||||
htmlContent += "</tr>";
|
|
||||||
|
|
||||||
return htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default ProjectTaskGrid;
|
|
@ -30,11 +30,10 @@ class PrependVersionPlugin {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
extensions: './src/extensions.js',
|
extensions: './src/extensions.js',
|
||||||
graphics: './src/graphics.js',
|
graphics: './src/graphics.js'
|
||||||
project: './src/project.js'
|
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: `[name].min.js`,
|
filename: `[name].dist.js`,
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
library: 'LiteRyzJS',
|
library: 'LiteRyzJS',
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
@ -46,5 +45,17 @@ module.exports = {
|
|||||||
'process.env.VERSION': JSON.stringify(version)
|
'process.env.VERSION': JSON.stringify(version)
|
||||||
}),
|
}),
|
||||||
new PrependVersionPlugin()
|
new PrependVersionPlugin()
|
||||||
]
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
'sass-loader'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user