Compare commits
	
		
			No commits in common. "06f0a9b4acb162ebcb60b34855e6fe817ba79d8b" and "0b43e4ecd792c696960a5f6dc0b506fa792d2f8f" have entirely different histories.
		
	
	
		
			06f0a9b4ac
			...
			0b43e4ecd7
		
	
		
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "LiteRyzJS/Project", | ||||
|   "version": "0.2.1.114", | ||||
|   "version": "0.2.0.508", | ||||
|   "devDependencies": { | ||||
|     "css-loader": "^7.1.2", | ||||
|     "sass": "^1.77.8", | ||||
|  | ||||
| @ -1,204 +0,0 @@ | ||||
| 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; | ||||
| @ -1,80 +0,0 @@ | ||||
| import Canvas from '../references/canvas.js'; | ||||
| 
 | ||||
| 
 | ||||
| class FlourishCanvas extends Canvas { | ||||
|   Options = null; | ||||
|   Debug = false; | ||||
| 
 | ||||
|   XPos = -1 | ||||
| 
 | ||||
| 
 | ||||
|   constructor(el) { | ||||
|     super(el); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   get HeaderHeight() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1]; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   Load() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.el.addEventListener('mousemove', function (e) { | ||||
|       // Hottracking
 | ||||
|       if (a.Options.EnableHotTracking) { | ||||
|         const point = { X: e.offsetX, Y: e.offsetY }; | ||||
| 
 | ||||
|         if (a.Debug) { | ||||
|           console.log(point); | ||||
|         } | ||||
| 
 | ||||
|         a.XPos = point.X; | ||||
| 
 | ||||
|         a.Invalidate(); | ||||
|       } | ||||
| 
 | ||||
|     }); | ||||
| 
 | ||||
|     a.el.addEventListener('mouseout', function (e) { | ||||
|       a.XPos = -1; | ||||
| 
 | ||||
|       a.Invalidate(); | ||||
|     }); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   Invalidate() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.Options == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     a.Clear(); | ||||
| 
 | ||||
|     if (a.XPos < 0) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     a.#drawVerticalLine(a.XPos); | ||||
|   } | ||||
| 
 | ||||
|   #drawVerticalLine(x) { | ||||
|     const a = this; | ||||
|     const h = this.Height - a.HeaderHeight; | ||||
| 
 | ||||
|     if (a.Options.HotTrackLine.Width > 1) { | ||||
|       x += Math.half(a.Options.HotTrackLine.Width); | ||||
|     } | ||||
| 
 | ||||
|     a.DrawVerticalLine(x, a.HeaderHeight, h, a.Options.HotTrackLine.Colour, { LineWidth: a.Options.HotTrackLine.Width }); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export default FlourishCanvas; | ||||
| @ -1,157 +0,0 @@ | ||||
| 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,58 +0,0 @@ | ||||
| class GanttChartOptions { | ||||
|   DayWidth = 24; | ||||
|   HeaderRow = { | ||||
|     Height: [ 20, 20 ] | ||||
|   }; | ||||
|   Row = { | ||||
|     Height: 28, | ||||
|     BackColour: "rgba(235, 235, 235, 0.3)", | ||||
|     Task: { | ||||
|       Height: 14, | ||||
|       BorderColour: "#555555", | ||||
|       FillColour: "#D8EEDB" | ||||
|     }, | ||||
|     OrphanTask: { | ||||
|       Height: 14, | ||||
|       BorderColour: "#555555", | ||||
|       FillColour: "#9CC2E6" | ||||
|     }, | ||||
|     ChildTask: { | ||||
|       Height: 14, | ||||
|       BorderColour: "#555555", | ||||
|       FillColour: "#FFF5C1" | ||||
|     }, | ||||
|     CollatedTask: { | ||||
|       Height: 6, | ||||
|       BorderColour: "#555555", | ||||
|       FillColour: "#555555" | ||||
|     } | ||||
|   }; | ||||
|   Column = { | ||||
|     SatColour: "rgba(233, 237, 239, 0.8)", | ||||
|     SunColour: "rgba(233, 237, 239, 0.9)" | ||||
|   }; | ||||
|   Line = { | ||||
|     Colour: "#555555", | ||||
|     Width: 1, | ||||
|     ArrowSize: 5 | ||||
|   }; | ||||
|   HotTrackLine = { | ||||
|     Colour: "#D04437", | ||||
|     Width: 1 | ||||
|   }; | ||||
|   DateFont = "7pt sans-serif"; | ||||
|   DateForeColour = "#636363"; | ||||
|   BorderWidth = 1; | ||||
|   BorderColour = "#B8B8B8"; | ||||
|   BorderDashPattern = [1, 1]; | ||||
|   EnableHotTracking = true; | ||||
|   MinimumRowCount = 6; | ||||
|   ShowDateLines = true; | ||||
|   ShowStartDayOfWeekLine = true; | ||||
|   ShowRowLines = true; | ||||
|   ShowRowStripes = true; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export default GanttChartOptions; | ||||
| @ -1,29 +1,20 @@ | ||||
| import GanttChartOptions from './gantt-chart-options.js'; | ||||
| 
 | ||||
| import CanvasContainer from '../references/canvas-container.js'; | ||||
| import BackgroundCanvas from './background-canvas.js'; | ||||
| import ForegroundCanvas from './foreground-canvas.js'; | ||||
| import FlourishCanvas from './flourish-canvas.js'; | ||||
| import Canvas from '../references/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.Options = Object.assign(new GanttChartOptions(), options); | ||||
|     a.CanvasContainer = new CanvasContainer(el); | ||||
|     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.#initialiseComponents(); | ||||
|   } | ||||
| @ -31,102 +22,136 @@ class GanttChart { | ||||
|   #initialiseComponents() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     // Add background canvas layer
 | ||||
|     const layer1 = a.CanvasContainer.AddLayer(); | ||||
|     if (!a.Canvas.el.classList.contains("literyzjs-project")) { | ||||
|       a.Canvas.el.classList.add("literyzjs-project"); | ||||
|     } | ||||
| 
 | ||||
|     const backCanvasLayer = new BackgroundCanvas(layer1); | ||||
|     backCanvasLayer.Options = a.Options; | ||||
|     if (a.Canvas.FlowContainer.classList.contains("border")) { | ||||
|       a.Canvas.FlowContainer.classList.remove("border"); | ||||
|     } | ||||
| 
 | ||||
|     a.CanvasContainer.Layer.push(backCanvasLayer); | ||||
|     if (!a.Canvas.FlowContainer.classList.contains("gantt-chart")) { | ||||
|       a.Canvas.FlowContainer.classList.add("gantt-chart"); | ||||
|     } | ||||
| 
 | ||||
|     // Add foreground canvas layer
 | ||||
|     const layer2 = a.CanvasContainer.AddLayer(); | ||||
| 
 | ||||
|     const foreCanvasLayer = new ForegroundCanvas(layer2); | ||||
|     foreCanvasLayer.Options = a.Options; | ||||
| 
 | ||||
|     a.CanvasContainer.Layer.push(foreCanvasLayer); | ||||
| 
 | ||||
|     // Add flourish canvas layer
 | ||||
|     const layer3 = a.CanvasContainer.AddLayer(); | ||||
| 
 | ||||
|     const flourishCanvasLayer = new FlourishCanvas(layer3); | ||||
|     flourishCanvasLayer.Options = a.Options; | ||||
| 
 | ||||
|     a.CanvasContainer.Layer.push(flourishCanvasLayer); | ||||
| 
 | ||||
|     // Invalidate every canvas
 | ||||
|     a.CanvasContainer.Invalidate(); | ||||
| 
 | ||||
|     Document.removeClass(a.CanvasContainer.FlowContainer, "border"); | ||||
|     Document.addClass(a.CanvasContainer.FlowContainer, "gantt-chart"); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   get DefaultOptions() { | ||||
|     return { | ||||
|       DayWidth: 24, | ||||
|       HeaderRow: { | ||||
|         Height: [ 20, 21 ] | ||||
|       }, | ||||
|       Row: { | ||||
|         Height: 28, | ||||
|         BackColour: "rgba(235, 235, 235, 0.3)", | ||||
|         Task: { | ||||
|           Height: 14, | ||||
|           BorderColour: "#555555", | ||||
|           FillColour: "#D8EEDB" | ||||
|         }, | ||||
|         OrphanTask: { | ||||
|           Height: 14, | ||||
|           BorderColour: "#555555", | ||||
|           FillColour: "#9CC2E6" | ||||
|         }, | ||||
|         ChildTask: { | ||||
|           Height: 14, | ||||
|           BorderColour: "#555555", | ||||
|           FillColour: "#FFF5C1" | ||||
|         }, | ||||
|         CollatedTask: { | ||||
|           Height: 6, | ||||
|           BorderColour: "#555555", | ||||
|           FillColour: "#555555" | ||||
|         } | ||||
|       }, | ||||
|       Column: { | ||||
|         SatColour: "rgba(233, 237, 239, 0.8)", | ||||
|         SunColour: "rgba(233, 237, 239, 0.9)", | ||||
|       }, | ||||
|       Line: { | ||||
|         Colour: "#555555", | ||||
|         Width: 1, | ||||
|         ArrowSize: 5 | ||||
|       }, | ||||
|       DateFont: "7pt sans-serif", | ||||
|       DateForeColour: "#636363", | ||||
|       BorderWidth: 1, | ||||
|       BorderColour: "#B8B8B8", | ||||
|       BorderDashPattern: [1, 1], | ||||
|       MinimumRowCount: 6, | ||||
|       ShowDateLines: true, | ||||
|       ShowStartDayOfWeekLine: true, | ||||
|       ShowRowLines: true, | ||||
|       ShowRowStripes: true | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   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.CanvasContainer.Clear(); | ||||
|     a.Canvas.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.CanvasContainer.Layer[2].Load(); | ||||
| 
 | ||||
|     a.Invalidate(); | ||||
|   } | ||||
| 
 | ||||
|   Invalidate() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.CanvasContainer.Clear(); | ||||
|     a.Canvas.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.CanvasContainer.Size = { | ||||
|       W: width, | ||||
|       H: height | ||||
|     }; | ||||
|     a.Canvas.Size = { W: width, H: height }; | ||||
| 
 | ||||
|     a.CanvasContainer.Invalidate(); | ||||
|     a.Canvas.Invalidate(); | ||||
| 
 | ||||
|     a.#drawChartHeader(); | ||||
|     a.#drawColumnLayout(); | ||||
|     a.#drawRowLayout(); | ||||
|     a.#drawTasks(); | ||||
|     a.#drawConnectorLines(); | ||||
|   } | ||||
| 
 | ||||
|   Load(project) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.CanvasContainer.Clear(); | ||||
|     a.Canvas.Clear(); | ||||
| 
 | ||||
|     if (project == null) { | ||||
|       return; | ||||
| @ -137,14 +162,244 @@ 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.CanvasContainer.Layer[2].Load(); | ||||
| 
 | ||||
|     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 = false; | ||||
|     a.Debug = true; | ||||
|     a.Project = _options; | ||||
|     a.Tasks = []; | ||||
|   } | ||||
|  | ||||
| @ -117,25 +117,17 @@ | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|   .gantt-chart { | ||||
|     border-color: #B8B8B8; | ||||
|     border-style: solid; | ||||
|     border-width: 1px; | ||||
|     overflow: hidden; | ||||
|     scrollbar-width: thin; | ||||
|     scrollbar-color: #CDCDCD #F0F0F0; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| .gantt-chart { | ||||
|   border-color: #B8B8B8; | ||||
|   border-style: solid; | ||||
|   border-width: 1px; | ||||
|   overflow: hidden; | ||||
|   scrollbar-width: thin; | ||||
|   scrollbar-color: #CDCDCD #F0F0F0; | ||||
| 
 | ||||
| 
 | ||||
|   &.scroll-x { | ||||
|   .gantt-chart.scroll-x { | ||||
|     overflow-x: scroll !important; | ||||
|   } | ||||
| 
 | ||||
|   canvas { | ||||
|     display: block; | ||||
|     position: relative; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } | ||||
| @ -1,229 +0,0 @@ | ||||
| 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(); | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     let offsetTop = 0; | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|       offsetTop += h; | ||||
| 
 | ||||
|       a.Layer[i].el.style.top = "-" + offsetTop + "px"; | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export default CanvasContainer; | ||||
| @ -1,10 +1,19 @@ | ||||
| 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(); | ||||
|   } | ||||
| @ -16,30 +25,48 @@ class Canvas { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     a.Clear(); | ||||
|     a.el.innerHTML = "<div class='border'><canvas></canvas></div>"; | ||||
| 
 | ||||
|     a.Invalidate(); | ||||
|     a.Clear(); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   get FlowContainer() { | ||||
|     return this.el.getElementsByTagName("div")[0]; | ||||
|   } | ||||
| 
 | ||||
|   get CanvasContainer() { | ||||
|     return this.FlowContainer.getElementsByTagName("canvas")[0]; | ||||
|   } | ||||
| 
 | ||||
|   get CanvasContext() { | ||||
|     return this.el.getContext("2d"); | ||||
|     return this.CanvasContainer.getContext("2d"); | ||||
|   } | ||||
| 
 | ||||
|   get Height() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.el == null) { | ||||
|     if (a.CanvasContainer == null) { | ||||
|       return 0; | ||||
|     } | ||||
| 
 | ||||
|     return Document.getHeight(a.el); | ||||
|     return a.#getHeight(a.CanvasContainer); | ||||
|   } | ||||
| 
 | ||||
|   set Height(value) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.el != null) { | ||||
|       a.el.style.height = value + "px"; | ||||
|     // 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) { | ||||
| @ -50,18 +77,26 @@ class Canvas { | ||||
|   get Width() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.el == null) { | ||||
|     if (a.CanvasContainer == null) { | ||||
|       return 0; | ||||
|     } | ||||
| 
 | ||||
|     return Document.getWidth(a.el); | ||||
|     return a.#getWidth(a.CanvasContainer); | ||||
|   } | ||||
| 
 | ||||
|   set Width(value) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.el != null) { | ||||
|       a.el.style.width = value + "px"; | ||||
|     // 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) { | ||||
| @ -81,6 +116,17 @@ 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; | ||||
| 
 | ||||
| @ -420,6 +466,23 @@ 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; | ||||
| 
 | ||||
| @ -437,6 +500,30 @@ 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