rays-javascript-extension/project/gantt-chart.js

245 lines
6.8 KiB
JavaScript

class RyzGanttChart {
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;
// a.initialiseComponents();
}
// initialiseComponents() {
// const a = this;
// }
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;
}
}