Added multi-layer canvas container
This commit is contained in:
parent
62c1917b80
commit
d05d22afe7
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "LiteRyzJS/Project",
|
||||
"version": "0.2.0.508",
|
||||
"version": "0.2.1.114",
|
||||
"devDependencies": {
|
||||
"css-loader": "^7.1.2",
|
||||
"sass": "^1.77.8",
|
||||
|
204
src/project/background-canvas.js
Normal file
204
src/project/background-canvas.js
Normal file
@ -0,0 +1,204 @@
|
||||
import Canvas from '../references/canvas.js';
|
||||
|
||||
|
||||
class BackgroundCanvas extends Canvas {
|
||||
Options = null;
|
||||
Debug = false;
|
||||
|
||||
StartDate = new Date();
|
||||
Duration = 30;
|
||||
StartOfWeek = 1;
|
||||
RowCount = 8;
|
||||
|
||||
|
||||
constructor(el) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
|
||||
get HeaderRow1Rectangle() {
|
||||
return {
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: this.Width,
|
||||
H: (this.Options.HeaderRow.Height[0] - this.Options.BorderWidth)
|
||||
};
|
||||
}
|
||||
|
||||
get HeaderRow2Rectangle() {
|
||||
const a = this;
|
||||
|
||||
return {
|
||||
X: 0,
|
||||
Y: this.Options.HeaderRow.Height[0],
|
||||
W: this.Width,
|
||||
H: (this.Options.HeaderRow.Height[1] - this.Options.BorderWidth)
|
||||
};
|
||||
}
|
||||
|
||||
get HeaderHeight() {
|
||||
const a = this;
|
||||
|
||||
return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
||||
}
|
||||
|
||||
|
||||
Load(startDate, duration, startOfWeek, rowCount) {
|
||||
const a = this;
|
||||
|
||||
a.StartDate = new Date(startDate);
|
||||
a.Duration = duration;
|
||||
a.StartOfWeek = startOfWeek; // 1 = Monday
|
||||
a.RowCount = rowCount;
|
||||
}
|
||||
|
||||
Invalidate() {
|
||||
const a = this;
|
||||
|
||||
if (a.Options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.StartDate == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.#drawChartHeader();
|
||||
a.#drawColumnLayout();
|
||||
a.#drawRowLayout();
|
||||
}
|
||||
|
||||
#drawChartHeader() {
|
||||
const a = this;
|
||||
|
||||
const displayDays = a.Duration + 2;
|
||||
|
||||
if (a.Debug) {
|
||||
a.DrawRectangle(a.HeaderRow1Rectangle, "red", {});
|
||||
a.DrawRectangle(a.HeaderRow2Rectangle, "orange", {});
|
||||
}
|
||||
|
||||
let startDate = new Date(a.StartDate);
|
||||
startDate.addDays(-1);
|
||||
|
||||
// Draw horizontal line under months
|
||||
a.#drawHorizontalLine(a.Options.HeaderRow.Height[0]);
|
||||
|
||||
// Draw vertical lines for dates
|
||||
for (let i=1; i<displayDays; i++) {
|
||||
a.DrawVerticalLine((a.Options.DayWidth * i), (a.Options.HeaderRow.Height[0] + a.Options.BorderWidth), (a.Options.HeaderRow.Height[1] - a.Options.BorderWidth), a.Options.BorderColour, {});
|
||||
}
|
||||
|
||||
// Draw horizontal line under dates
|
||||
a.#drawHorizontalLine(a.HeaderHeight);
|
||||
|
||||
// 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.MeasureText(a.Options.DateFont, "#");
|
||||
const monthPoint = {
|
||||
X: (x + 2),
|
||||
Y: Math.half(a.Options.HeaderRow.Height[0] - size.H)
|
||||
};
|
||||
|
||||
a.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[0],
|
||||
W: a.Options.DayWidth - a.Options.BorderWidth,
|
||||
H: (a.Options.HeaderRow.Height[1] - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
a.FillText(dateRectangle, date.getDate(), a.Options.DateFont, a.Options.DateForeColour);
|
||||
|
||||
if (a.Debug) {
|
||||
a.DrawRectangle(dateRectangle, "red", {});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawColumnLayout() {
|
||||
const a = this;
|
||||
|
||||
const height = a.Height;
|
||||
const displayDays = a.Duration + 2;
|
||||
|
||||
let startDate = new Date(a.StartDate);
|
||||
startDate.addDays(-1);
|
||||
|
||||
// Write dates
|
||||
for (let i=0; i<displayDays; i++) {
|
||||
const date = Date.addDays(startDate, i);
|
||||
const x = (a.Options.DayWidth * i);
|
||||
|
||||
const dateRectangle = {
|
||||
X: x,
|
||||
Y: a.HeaderHeight,
|
||||
W: a.Options.DayWidth - a.Options.BorderWidth,
|
||||
H: height - a.Options.BorderWidth
|
||||
};
|
||||
|
||||
// Fill background for Saturday and Sunday
|
||||
if (date.getDay() == 6) {
|
||||
a.DrawRectangle(dateRectangle, a.Options.Column.SatColour, { FillColour: a.Options.Column.SatColour });
|
||||
} else if (date.getDay() == 0) {
|
||||
a.DrawRectangle(dateRectangle, a.Options.Column.SunColour, { FillColour: a.Options.Column.SunColour });
|
||||
}
|
||||
|
||||
// Draw vertical date lines
|
||||
if (a.StartOfWeek == date.getDay()) {
|
||||
if (a.Options.ShowStartDayOfWeekLine) {
|
||||
a.DrawVerticalLine(dateRectangle.X, a.HeaderHeight, dateRectangle.H, a.Options.BorderColour, {});
|
||||
}
|
||||
} else {
|
||||
if (a.Options.ShowDateLines) {
|
||||
a.DrawVerticalLine(dateRectangle.X, a.HeaderHeight, dateRectangle.H, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawRowLayout() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.RowCount; i++) {
|
||||
const rowRectangle = {
|
||||
X: 0,
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i)),
|
||||
W: a.Width,
|
||||
H: (a.Options.Row.Height - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
if (a.Options.ShowRowStripes) {
|
||||
if ((i % 2) == 1) {
|
||||
a.DrawRectangle(rowRectangle, a.Options.Row.BackColour, { FillColour: a.Options.Row.BackColour });
|
||||
}
|
||||
}
|
||||
|
||||
if (a.Options.ShowRowLines) {
|
||||
a.DrawHorizontalLine(0, (rowRectangle.Y + a.Options.Row.Height), rowRectangle.W, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawHorizontalLine(y) {
|
||||
const a = this;
|
||||
const width = a.Width;
|
||||
|
||||
a.DrawHorizontalLine(0, y, width, this.Options.BorderColour);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default BackgroundCanvas;
|
157
src/project/foreground-canvas.js
Normal file
157
src/project/foreground-canvas.js
Normal file
@ -0,0 +1,157 @@
|
||||
import Canvas from '../references/canvas.js';
|
||||
|
||||
|
||||
class ForegroundCanvas extends Canvas {
|
||||
Options = null;
|
||||
Debug = false;
|
||||
|
||||
StartDate = new Date();
|
||||
Tasks = [];
|
||||
|
||||
|
||||
constructor(el) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
|
||||
get HeaderHeight() {
|
||||
const a = this;
|
||||
|
||||
return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
||||
}
|
||||
|
||||
|
||||
Load(startDate, tasks) {
|
||||
const a = this;
|
||||
|
||||
a.StartDate = new Date(startDate);
|
||||
a.Tasks = tasks;
|
||||
}
|
||||
|
||||
Invalidate() {
|
||||
const a = this;
|
||||
|
||||
if (a.Options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.StartDate == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.#drawTasks();
|
||||
a.#drawConnectorLines();
|
||||
}
|
||||
|
||||
|
||||
#drawTasks() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.Tasks.length; i++) {
|
||||
let style = a.Options.Row.Task;
|
||||
|
||||
if (a.Tasks[i].IsCollated == true) {
|
||||
style = a.Options.Row.CollatedTask;
|
||||
} else if (a.Tasks[i].CollatedTaskID != null) {
|
||||
style = a.Options.Row.ChildTask;
|
||||
} else if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
style = a.Options.Row.OrphanTask;
|
||||
}
|
||||
|
||||
const rectangle = a.#getTaskRectangle(a.Tasks[i], style);
|
||||
|
||||
if (a.Tasks[i].Duration <= 0) {
|
||||
a.DrawDiamond(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
} else {
|
||||
a.DrawRectangle(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#drawConnectorLines() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.Tasks.length; i++) {
|
||||
if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const style = ((a.Tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
||||
const rectangle = a.#getTaskRectangle(a.Tasks[i], style);
|
||||
const paddingX = (a.Options.Row.CollatedTask.Height + a.Options.BorderWidth);
|
||||
const offsetX = (rectangle.X + paddingX);
|
||||
|
||||
const predecessorTask = a.Tasks.first("ID", a.Tasks[i].PredecessorTaskID);
|
||||
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: offsetX, Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
||||
|
||||
points.push({ X: offsetX, Y: (rectangle.Y - paddingX) });
|
||||
points.push({ X: offsetX, Y: rectangle.Y });
|
||||
|
||||
a.DrawLines(points, a.Options.Line.Colour, { LineWidth: a.Options.Line.Width });
|
||||
|
||||
const arrowRectangle = {
|
||||
X: (offsetX - Math.half(a.Options.Line.ArrowSize)),
|
||||
Y: (rectangle.Y - a.Options.Line.ArrowSize),
|
||||
W: a.Options.Line.ArrowSize,
|
||||
H: a.Options.Line.ArrowSize
|
||||
}
|
||||
|
||||
a.DrawArrowS(arrowRectangle, a.Options.Line.Colour, { FillColour: a.Options.Line.Colour });
|
||||
}
|
||||
}
|
||||
|
||||
#getTaskRectangle(task, style) {
|
||||
const a = this;
|
||||
const i = (task.Order - 1);
|
||||
|
||||
const rowRectangle = {
|
||||
X: 0,
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i)),
|
||||
W: a.Width,
|
||||
H: (a.Options.Row.Height - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
if (a.Debug) {
|
||||
a.DrawRectangle(rowRectangle, "red", {});
|
||||
}
|
||||
|
||||
if (task.Duration <= 0) {
|
||||
let result = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + Math.half(rowRectangle.H - style.Height)),
|
||||
W: style.Height,
|
||||
H: style.Height
|
||||
};
|
||||
|
||||
result.X += Math.half(a.Options.DayWidth - (style.Height + a.Options.BorderWidth));
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + Math.half(rowRectangle.H - style.Height)),
|
||||
W: (a.Options.DayWidth * task.Duration),
|
||||
H: style.Height
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default ForegroundCanvas;
|
@ -1,20 +1,26 @@
|
||||
import Canvas from '../references/canvas.js';
|
||||
import CanvasContainer from '../references/canvas-container.js';
|
||||
import BackgroundCanvas from './background-canvas.js';
|
||||
import ForegroundCanvas from './foreground-canvas.js';
|
||||
|
||||
import './project.scss';
|
||||
|
||||
|
||||
class GanttChart {
|
||||
Options = null;
|
||||
CanvasContainer = null;
|
||||
|
||||
#debug = false;
|
||||
Tasks = [];
|
||||
StartDate = null;
|
||||
Duration = 30;
|
||||
StartOfWeek = 1;
|
||||
|
||||
|
||||
constructor(el, options) {
|
||||
const a = this;
|
||||
|
||||
a.Canvas = new Canvas(el);
|
||||
a.Options = Object.assign(a.DefaultOptions, options);
|
||||
|
||||
a.Debug = false;
|
||||
a.Tasks = [];
|
||||
a.StartDate = null;
|
||||
a.Duration = 30;
|
||||
a.StartOfWeek = 1;
|
||||
a.CanvasContainer = new CanvasContainer(el);
|
||||
|
||||
a.#initialiseComponents();
|
||||
}
|
||||
@ -22,17 +28,26 @@ class GanttChart {
|
||||
#initialiseComponents() {
|
||||
const a = this;
|
||||
|
||||
if (!a.Canvas.el.classList.contains("literyzjs-project")) {
|
||||
a.Canvas.el.classList.add("literyzjs-project");
|
||||
}
|
||||
// Add background canvas layer
|
||||
const layer1 = a.CanvasContainer.AddLayer();
|
||||
|
||||
if (a.Canvas.FlowContainer.classList.contains("border")) {
|
||||
a.Canvas.FlowContainer.classList.remove("border");
|
||||
}
|
||||
const backCanvasLayer = new BackgroundCanvas(layer1);
|
||||
backCanvasLayer.Options = a.Options;
|
||||
|
||||
if (!a.Canvas.FlowContainer.classList.contains("gantt-chart")) {
|
||||
a.Canvas.FlowContainer.classList.add("gantt-chart");
|
||||
}
|
||||
a.CanvasContainer.Layer.push(backCanvasLayer);
|
||||
|
||||
// Add foreground canvas layer
|
||||
const layer2 = a.CanvasContainer.AddLayer();
|
||||
|
||||
const foreCanvasLayer = new ForegroundCanvas(layer2);
|
||||
foreCanvasLayer.Options = a.Options;
|
||||
|
||||
a.CanvasContainer.Layer.push(foreCanvasLayer);
|
||||
|
||||
a.CanvasContainer.Invalidate();
|
||||
|
||||
Document.removeClass(a.CanvasContainer.FlowContainer, "border");
|
||||
Document.addClass(a.CanvasContainer.FlowContainer, "gantt-chart");
|
||||
|
||||
}
|
||||
|
||||
@ -41,7 +56,7 @@ class GanttChart {
|
||||
return {
|
||||
DayWidth: 24,
|
||||
HeaderRow: {
|
||||
Height: [ 20, 21 ]
|
||||
Height: [ 20, 20 ]
|
||||
},
|
||||
Row: {
|
||||
Height: 28,
|
||||
@ -89,69 +104,66 @@ class GanttChart {
|
||||
};
|
||||
}
|
||||
|
||||
get HeaderRow1Rectangle() {
|
||||
return {
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: this.Canvas.Width,
|
||||
H: (this.Options.HeaderRow.Height[0] - this.Options.BorderWidth)
|
||||
};
|
||||
}
|
||||
|
||||
get HeaderRow2Rectangle() {
|
||||
const a = this;
|
||||
|
||||
return {
|
||||
X: 0,
|
||||
Y: this.Options.HeaderRow.Height[0],
|
||||
W: this.Canvas.Width,
|
||||
H: (this.Options.HeaderRow.Height[1] - this.Options.BorderWidth)
|
||||
};
|
||||
}
|
||||
|
||||
get HeaderHeight() {
|
||||
const a = this;
|
||||
|
||||
return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
||||
}
|
||||
|
||||
get Debug() {
|
||||
return this.#debug;
|
||||
}
|
||||
|
||||
set Debug(value) {
|
||||
const a = this;
|
||||
|
||||
a.#debug = value;
|
||||
|
||||
a.CanvasContainer.Layer.forEach(e => {
|
||||
if (typeof(e.Debug) == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
e.Debug = value;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
Clear() {
|
||||
const a = this;
|
||||
|
||||
a.Canvas.Clear();
|
||||
a.CanvasContainer.Clear();
|
||||
|
||||
a.StartDate = new Date();
|
||||
a.Duration = 30;
|
||||
a.StartOfWeek = 1; // 1 = Monday
|
||||
a.Tasks = [];
|
||||
|
||||
// Invalidate canvas (background, foreground)
|
||||
a.CanvasContainer.Layer[0].Load(new Date(), a.Duration, a.StartOfWeek, a.Tasks.length);
|
||||
a.CanvasContainer.Layer[1].Load(new Date(), a.Tasks);
|
||||
|
||||
a.Invalidate();
|
||||
}
|
||||
|
||||
Invalidate() {
|
||||
const a = this;
|
||||
|
||||
a.Canvas.Clear();
|
||||
a.CanvasContainer.Clear();
|
||||
|
||||
const width = ((a.Duration + 2) * a.Options.DayWidth);
|
||||
const height = (Math.max(a.Tasks.length, a.Options.MinimumRowCount) * a.Options.Row.Height) + a.HeaderHeight;
|
||||
|
||||
a.Canvas.Size = { W: width, H: height };
|
||||
a.CanvasContainer.Size = { W: width, H: height };
|
||||
|
||||
a.Canvas.Invalidate();
|
||||
|
||||
a.#drawChartHeader();
|
||||
a.#drawColumnLayout();
|
||||
a.#drawRowLayout();
|
||||
a.#drawTasks();
|
||||
a.#drawConnectorLines();
|
||||
a.CanvasContainer.Invalidate();
|
||||
}
|
||||
|
||||
Load(project) {
|
||||
const a = this;
|
||||
|
||||
a.Canvas.Clear();
|
||||
a.CanvasContainer.Clear();
|
||||
|
||||
if (project == null) {
|
||||
return;
|
||||
@ -162,244 +174,13 @@ class GanttChart {
|
||||
a.StartOfWeek = project.Project.StartOfWeek;
|
||||
a.Tasks = project.ExportTasks();
|
||||
|
||||
// Invalidate canvas (background, foreground)
|
||||
a.CanvasContainer.Layer[0].Load(new Date(project.StartDate), project.Duration, project.Project.StartOfWeek, a.Tasks.length);
|
||||
a.CanvasContainer.Layer[1].Load(new Date(project.StartDate), a.Tasks);
|
||||
|
||||
a.Invalidate();
|
||||
}
|
||||
|
||||
#drawChartHeader() {
|
||||
const a = this;
|
||||
|
||||
const displayDays = a.Duration + 2;
|
||||
|
||||
if (a.Debug) {
|
||||
a.Canvas.DrawRectangle(a.HeaderRow1Rectangle, "red", {});
|
||||
a.Canvas.DrawRectangle(a.HeaderRow2Rectangle, "orange", {});
|
||||
}
|
||||
|
||||
let startDate = new Date(a.StartDate);
|
||||
startDate.addDays(-1);
|
||||
|
||||
// Draw horizontal line under months
|
||||
a.#drawHorizontalLine(a.Options.HeaderRow.Height[0]);
|
||||
|
||||
// Draw vertical lines for dates
|
||||
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), a.Options.BorderColour, {});
|
||||
}
|
||||
|
||||
// Draw horizontal line under dates
|
||||
a.#drawHorizontalLine(a.HeaderHeight);
|
||||
|
||||
// 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[0],
|
||||
W: a.Options.DayWidth - a.Options.BorderWidth,
|
||||
H: (a.Options.HeaderRow.Height[1] - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
a.Canvas.FillText(dateRectangle, date.getDate(), a.Options.DateFont, a.Options.DateForeColour);
|
||||
|
||||
if (a.Debug) {
|
||||
a.Canvas.DrawRectangle(dateRectangle, "red", {});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawColumnLayout() {
|
||||
const a = this;
|
||||
|
||||
const height = a.Canvas.Height;
|
||||
const displayDays = a.Duration + 2;
|
||||
|
||||
let startDate = new Date(a.StartDate);
|
||||
startDate.addDays(-1);
|
||||
|
||||
// Write dates
|
||||
for (let i=0; i<displayDays; i++) {
|
||||
const date = Date.addDays(startDate, i);
|
||||
const x = (a.Options.DayWidth * i);
|
||||
|
||||
const dateRectangle = {
|
||||
X: x,
|
||||
Y: a.HeaderHeight,
|
||||
W: a.Options.DayWidth - a.Options.BorderWidth,
|
||||
H: height - a.Options.BorderWidth
|
||||
};
|
||||
|
||||
// Fill background for Saturday and Sunday
|
||||
if (date.getDay() == 6) {
|
||||
a.Canvas.DrawRectangle(dateRectangle, a.Options.Column.SatColour, { FillColour: a.Options.Column.SatColour });
|
||||
} else if (date.getDay() == 0) {
|
||||
a.Canvas.DrawRectangle(dateRectangle, a.Options.Column.SunColour, { FillColour: a.Options.Column.SunColour });
|
||||
}
|
||||
|
||||
// Draw vertical date lines
|
||||
if (a.StartOfWeek == date.getDay()) {
|
||||
if (a.Options.ShowStartDayOfWeekLine) {
|
||||
a.Canvas.DrawVerticalLine(dateRectangle.X, a.HeaderHeight, dateRectangle.H, a.Options.BorderColour, {});
|
||||
}
|
||||
} else {
|
||||
if (a.Options.ShowDateLines) {
|
||||
a.Canvas.DrawVerticalLine(dateRectangle.X, a.HeaderHeight, dateRectangle.H, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawRowLayout() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.Tasks.length; i++) {
|
||||
const rowRectangle = {
|
||||
X: 0,
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i)),
|
||||
W: a.Canvas.Width,
|
||||
H: (a.Options.Row.Height - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
if (a.Options.ShowRowStripes) {
|
||||
if ((i % 2) == 1) {
|
||||
a.Canvas.DrawRectangle(rowRectangle, a.Options.Row.BackColour, { FillColour: a.Options.Row.BackColour });
|
||||
}
|
||||
}
|
||||
|
||||
if (a.Options.ShowRowLines) {
|
||||
a.Canvas.DrawHorizontalLine(0, (rowRectangle.Y + a.Options.Row.Height), rowRectangle.W, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#drawTasks() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.Tasks.length; i++) {
|
||||
let style = a.Options.Row.Task;
|
||||
|
||||
if (a.Tasks[i].IsCollated == true) {
|
||||
style = a.Options.Row.CollatedTask;
|
||||
} else if (a.Tasks[i].CollatedTaskID != null) {
|
||||
style = a.Options.Row.ChildTask;
|
||||
} else if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
style = a.Options.Row.OrphanTask;
|
||||
}
|
||||
|
||||
const rectangle = a.#getTaskRectangle(a.Tasks[i], style);
|
||||
|
||||
if (a.Tasks[i].Duration <= 0) {
|
||||
a.Canvas.DrawDiamond(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
} else {
|
||||
a.Canvas.DrawRectangle(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#drawConnectorLines() {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<a.Tasks.length; i++) {
|
||||
if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a.Tasks[i].PredecessorTaskID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const style = ((a.Tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
||||
const rectangle = a.#getTaskRectangle(a.Tasks[i], style);
|
||||
const paddingX = (a.Options.Row.CollatedTask.Height + a.Options.BorderWidth);
|
||||
const offsetX = (rectangle.X + paddingX);
|
||||
|
||||
const predecessorTask = a.Tasks.first("ID", a.Tasks[i].PredecessorTaskID);
|
||||
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: offsetX, Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
||||
|
||||
points.push({ X: offsetX, Y: (rectangle.Y - paddingX) });
|
||||
points.push({ X: offsetX, Y: rectangle.Y });
|
||||
|
||||
a.Canvas.DrawLines(points, a.Options.Line.Colour, { LineWidth: a.Options.Line.Width });
|
||||
|
||||
const arrowRectangle = {
|
||||
X: (offsetX - 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 });
|
||||
}
|
||||
}
|
||||
|
||||
#drawHorizontalLine(y) {
|
||||
const a = this;
|
||||
const width = a.Canvas.Width;
|
||||
|
||||
a.Canvas.DrawHorizontalLine(0, y, width, this.Options.BorderColour);
|
||||
}
|
||||
|
||||
#getTaskRectangle(task, style) {
|
||||
const a = this;
|
||||
const i = (task.Order - 1);
|
||||
|
||||
const rowRectangle = {
|
||||
X: 0,
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i)),
|
||||
W: a.Canvas.Width,
|
||||
H: (a.Options.Row.Height - a.Options.BorderWidth)
|
||||
};
|
||||
|
||||
if (a.Debug) {
|
||||
a.Canvas.DrawRectangle(rowRectangle, "red", {});
|
||||
}
|
||||
|
||||
if (task.Duration <= 0) {
|
||||
let result = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + Math.half(rowRectangle.H - style.Height)),
|
||||
W: style.Height,
|
||||
H: style.Height
|
||||
};
|
||||
|
||||
result.X += Math.half(a.Options.DayWidth - (style.Height + a.Options.BorderWidth));
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + Math.half(rowRectangle.H - style.Height)),
|
||||
W: (a.Options.DayWidth * task.Duration),
|
||||
H: style.Height
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ class Project {
|
||||
const a = this;
|
||||
const _options = Object.assign(a.DefaultOptions, options);
|
||||
|
||||
a.Debug = true;
|
||||
a.Debug = false;
|
||||
a.Project = _options;
|
||||
a.Tasks = [];
|
||||
}
|
||||
|
@ -117,17 +117,25 @@
|
||||
}
|
||||
|
||||
|
||||
.gantt-chart {
|
||||
border-color: #B8B8B8;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
overflow: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #CDCDCD #F0F0F0;
|
||||
}
|
||||
}
|
||||
|
||||
.gantt-chart.scroll-x {
|
||||
|
||||
.gantt-chart {
|
||||
border-color: #B8B8B8;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
overflow: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #CDCDCD #F0F0F0;
|
||||
|
||||
|
||||
&.scroll-x {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
|
225
src/references/canvas-container.js
Normal file
225
src/references/canvas-container.js
Normal file
@ -0,0 +1,225 @@
|
||||
class CanvasContainer {
|
||||
|
||||
constructor(el) {
|
||||
const a = this;
|
||||
|
||||
a.el = el;
|
||||
|
||||
a.Layer = [];
|
||||
a.BorderWidth = 2;
|
||||
a.ScrollbarWidth = 11;
|
||||
a.Padding = {
|
||||
Top: 0,
|
||||
Right: 0,
|
||||
Bottom: 0,
|
||||
Left: 0
|
||||
};
|
||||
a.EnableDock = true;
|
||||
|
||||
a.#initialiseComponents();
|
||||
}
|
||||
|
||||
#initialiseComponents() {
|
||||
const a = this;
|
||||
|
||||
a.ClearLayers();
|
||||
|
||||
// a.Invalidate();
|
||||
// a.Clear();
|
||||
}
|
||||
|
||||
|
||||
get FlowContainer() {
|
||||
return this.el.getElementsByTagName("div")[0];
|
||||
}
|
||||
|
||||
get Height() {
|
||||
const a = this;
|
||||
|
||||
if (a.Layer.length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Document.getHeight(a.Layer[0].el);
|
||||
}
|
||||
|
||||
set Height(value) {
|
||||
const a = this;
|
||||
|
||||
// if (a.el != null) {
|
||||
// a.el.style.height = value + "px";
|
||||
// }
|
||||
|
||||
if (a.FlowContainer != null) {
|
||||
a.FlowContainer.style.height = value + "px";
|
||||
}
|
||||
|
||||
// if (a.CanvasContainer != null) {
|
||||
// a.CanvasContainer.style.height = value + "px";
|
||||
// }
|
||||
|
||||
// if (a.CanvasContext != null) {
|
||||
// a.CanvasContext.canvas.height = value;
|
||||
// }
|
||||
|
||||
a.Layer.forEach((e, i) => {
|
||||
e.Height = value;
|
||||
});
|
||||
}
|
||||
|
||||
get Width() {
|
||||
const a = this;
|
||||
|
||||
if (a.Layer.length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Document.getWidth(a.Layer[0].el);
|
||||
}
|
||||
|
||||
set Width(value) {
|
||||
const a = this;
|
||||
|
||||
// if (a.el != null) {
|
||||
// a.el.style.width = value + "px";
|
||||
// }
|
||||
|
||||
if (a.FlowContainer != null) {
|
||||
a.FlowContainer.style.width = value + "px";
|
||||
}
|
||||
|
||||
// if (a.CanvasContainer != null) {
|
||||
// a.CanvasContainer.style.width = value + "px";
|
||||
// }
|
||||
|
||||
// if (a.CanvasContext != null) {
|
||||
// a.CanvasContext.canvas.width = value;
|
||||
// }
|
||||
|
||||
a.Layer.forEach((e, i) => {
|
||||
e.Width = value;
|
||||
});
|
||||
}
|
||||
|
||||
get Size() {
|
||||
return {
|
||||
W: this.Width,
|
||||
H: this.Height
|
||||
};
|
||||
}
|
||||
|
||||
set Size(value) {
|
||||
this.Width = value.W;
|
||||
this.Height = value.H;
|
||||
}
|
||||
|
||||
get ClientRectangle() {
|
||||
const a = this;
|
||||
|
||||
return {
|
||||
X: a.Padding.Left,
|
||||
Y: a.Padding.Top,
|
||||
W: (a.Width - (a.Padding.Left + a.Padding.Right)),
|
||||
H: (a.Height - (a.Padding.Top + a.Padding.Bottom))
|
||||
};
|
||||
}
|
||||
|
||||
get Rectangle() {
|
||||
const a = this;
|
||||
|
||||
return {
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: a.Width,
|
||||
H: a.Height
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Clear() {
|
||||
const a = this;
|
||||
|
||||
// if (a.CanvasContext == null) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// a.CanvasContext.clearRect(0, 0, a.CanvasContext.canvas.width, a.CanvasContext.canvas.height);
|
||||
|
||||
a.Layer.forEach((e, i) => {
|
||||
e.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
ClearLayers() {
|
||||
const a = this;
|
||||
|
||||
if (a.el != null) {
|
||||
a.el.innerHTML = "<div class='border'></div>";
|
||||
}
|
||||
|
||||
a.Layer = [];
|
||||
|
||||
a.Invalidate();
|
||||
}
|
||||
|
||||
AddLayer() {
|
||||
const a = this;
|
||||
|
||||
if (a.el == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const divList = a.el.getElementsByTagName("div");
|
||||
if (divList.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add another canvas element
|
||||
Document.appendHtml(divList[0], "canvas");
|
||||
|
||||
const canvasList = a.el.getElementsByTagName("canvas");
|
||||
const result = canvasList[(canvasList.length - 1)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Invalidate() {
|
||||
const a = this;
|
||||
|
||||
if (a.EnableDock) {
|
||||
a.FlowContainer.style.width = "100%";
|
||||
}
|
||||
|
||||
const w = (a.Layer.length <= 0 ? 0 : Document.getWidth(a.Layer[0].el));
|
||||
const h = (a.Layer.length <= 0 ? 0 : Document.getHeight(a.Layer[0].el));
|
||||
|
||||
if (w > (Document.getWidth(a.FlowContainer) + (a.BorderWidth * 2))) {
|
||||
a.FlowContainer.classList.add("scroll-x");
|
||||
a.FlowContainer.style.height = (h + a.ScrollbarWidth) + "px";
|
||||
} else {
|
||||
a.FlowContainer.classList.remove("scroll-x");
|
||||
a.FlowContainer.style.height = h + "px";
|
||||
}
|
||||
|
||||
a.Layer.forEach(e => {
|
||||
if (typeof(e.Invalidate) != "undefined") {
|
||||
e.Invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
a.Layer.forEach((e, i) => {
|
||||
if (i <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Correct positon because of position-relative to keep container overflow working
|
||||
const h = Document.getHeight(a.Layer[(i - 1)].el);
|
||||
|
||||
a.Layer[i].el.style.top = "-" + h + "px";
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default CanvasContainer;
|
@ -1,19 +1,10 @@
|
||||
class Canvas {
|
||||
|
||||
constructor(el) {
|
||||
const a = this;
|
||||
|
||||
a.el = el;
|
||||
|
||||
a.BorderWidth = 2;
|
||||
a.ScrollbarWidth = 11;
|
||||
a.Padding = {
|
||||
Top: 0,
|
||||
Right: 0,
|
||||
Bottom: 0,
|
||||
Left: 0
|
||||
};
|
||||
a.EnableDock = true;
|
||||
|
||||
a.#initialiseComponents();
|
||||
}
|
||||
@ -25,48 +16,30 @@ class Canvas {
|
||||
return;
|
||||
}
|
||||
|
||||
a.el.innerHTML = "<div class='border'><canvas></canvas></div>";
|
||||
|
||||
a.Invalidate();
|
||||
a.Clear();
|
||||
a.Invalidate();
|
||||
}
|
||||
|
||||
|
||||
get FlowContainer() {
|
||||
return this.el.getElementsByTagName("div")[0];
|
||||
}
|
||||
|
||||
get CanvasContainer() {
|
||||
return this.FlowContainer.getElementsByTagName("canvas")[0];
|
||||
}
|
||||
|
||||
get CanvasContext() {
|
||||
return this.CanvasContainer.getContext("2d");
|
||||
return this.el.getContext("2d");
|
||||
}
|
||||
|
||||
get Height() {
|
||||
const a = this;
|
||||
|
||||
if (a.CanvasContainer == null) {
|
||||
if (a.el == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return a.#getHeight(a.CanvasContainer);
|
||||
return Document.getHeight(a.el);
|
||||
}
|
||||
|
||||
set Height(value) {
|
||||
const a = this;
|
||||
|
||||
// if (a.el != null) {
|
||||
// a.el.style.height = value + "px";
|
||||
// }
|
||||
|
||||
if (a.FlowContainer != null) {
|
||||
a.FlowContainer.style.height = value + "px";
|
||||
}
|
||||
|
||||
if (a.CanvasContainer != null) {
|
||||
a.CanvasContainer.style.height = value + "px";
|
||||
if (a.el != null) {
|
||||
a.el.style.height = value + "px";
|
||||
}
|
||||
|
||||
if (a.CanvasContext != null) {
|
||||
@ -77,26 +50,18 @@ class Canvas {
|
||||
get Width() {
|
||||
const a = this;
|
||||
|
||||
if (a.CanvasContainer == null) {
|
||||
if (a.el == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return a.#getWidth(a.CanvasContainer);
|
||||
return Document.getWidth(a.el);
|
||||
}
|
||||
|
||||
set Width(value) {
|
||||
const a = this;
|
||||
|
||||
// if (a.el != null) {
|
||||
// a.el.style.width = value + "px";
|
||||
// }
|
||||
|
||||
if (a.FlowContainer != null) {
|
||||
a.FlowContainer.style.width = value + "px";
|
||||
}
|
||||
|
||||
if (a.CanvasContainer != null) {
|
||||
a.CanvasContainer.style.width = value + "px";
|
||||
if (a.el != null) {
|
||||
a.el.style.width = value + "px";
|
||||
}
|
||||
|
||||
if (a.CanvasContext != null) {
|
||||
@ -116,17 +81,6 @@ class Canvas {
|
||||
this.Height = value.H;
|
||||
}
|
||||
|
||||
get ClientRectangle() {
|
||||
const a = this;
|
||||
|
||||
return {
|
||||
X: a.Padding.Left,
|
||||
Y: a.Padding.Top,
|
||||
W: (a.Width - (a.Padding.Left + a.Padding.Right)),
|
||||
H: (a.Height - (a.Padding.Top + a.Padding.Bottom))
|
||||
};
|
||||
}
|
||||
|
||||
get Rectangle() {
|
||||
const a = this;
|
||||
|
||||
@ -466,23 +420,6 @@ class Canvas {
|
||||
a.CanvasContext.fillText(text, x, y);
|
||||
}
|
||||
|
||||
Invalidate() {
|
||||
const a = this;
|
||||
|
||||
if (a.EnableDock) {
|
||||
a.FlowContainer.style.width = "100%";
|
||||
}
|
||||
|
||||
if (a.#getWidth(a.CanvasContainer) > (a.#getWidth(a.FlowContainer) + (a.BorderWidth * 2))) {
|
||||
a.FlowContainer.classList.add("scroll-x");
|
||||
a.FlowContainer.style.height = (a.#getHeight(a.CanvasContainer) + a.ScrollbarWidth) + "px";
|
||||
} else {
|
||||
a.FlowContainer.classList.remove("scroll-x");
|
||||
a.FlowContainer.style.height = a.#getHeight(a.CanvasContainer) + "px";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MeasureText(font, value) {
|
||||
const a = this;
|
||||
|
||||
@ -500,30 +437,6 @@ class Canvas {
|
||||
};
|
||||
}
|
||||
|
||||
#getWidth(el) {
|
||||
if (el == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (typeof(el) == "undefined") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (el.offsetWidth || el.innerWidth || el.clientWidth);
|
||||
}
|
||||
|
||||
#getHeight(el) {
|
||||
if (el == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (typeof(el) == "undefined") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (el.offsetHeight || el.innerHeight || el.clientHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user