chartjs-plugin-trendline 2.1.7 → 2.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- /*! For license information please see chartjs-plugin-trendline.min.js.LICENSE.txt */
2
- (()=>{var e={339:(e,t)=>{const i={id:"chartjs-plugin-trendline",afterDatasetsDraw:e=>{const t=e.ctx,{xScale:i,yScale:a}=n(e);e.data.datasets.forEach(((n,l)=>{const o=n.alwaysShowTrendline||e.isDatasetVisible(l);if(n.trendlineLinear&&o&&n.data.length>1){const o=e.getDatasetMeta(l);s(o,t,n,i,a)}})),t.setLineDash([])},beforeInit:e=>{e.data.datasets.forEach((t=>{if(t.trendlineLinear&&t.trendlineLinear.label){const i=t.trendlineLinear.label,n=e.legend.options.labels.generateLabels;e.legend.options.labels.generateLabels=function(e){const s=n(e),a=t.trendlineLinear.legend;return a&&!1!==a.display&&s.push({text:a.text||i+" (Trendline)",strokeStyle:a.color||t.borderColor||"rgba(169,169,169, .6)",fillStyle:a.fillStyle||"transparent",lineCap:a.lineCap||"butt",lineDash:a.lineDash||[],lineWidth:a.width||1}),s}}}))}},n=e=>{let t,i;for(const n of Object.values(e.scales))if(n.isHorizontal()?t=n:i=n,t&&i)break;return{xScale:t,yScale:i}},s=(e,t,i,n,s)=>{const x=i.yAxisID||"y",h=e.controller.chart.scales[x]||s,u=i.borderColor||"rgba(169,169,169, .6)",{colorMin:y=u,colorMax:f=u,width:m=i.borderWidth||3,lineStyle:p="solid",fillColor:b=!1}=i.trendlineLinear||{},{color:w=u,text:g="Trendline",display:L=!0,displayValue:F=!0,offset:S=10,percentage:v=!1}=i.trendlineLinear.label||{},{family:C="'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:D=12}=i.trendlineLinear.label?.font||{},T=e.controller.chart.options,N="object"==typeof T.parsing?T.parsing:void 0,P=i.trendlineLinear?.xAxisKey||N?.xAxisKey||"x",A=i.trendlineLinear?.yAxisKey||N?.yAxisKey||"y";let M=new c,V=i.data.findIndex((e=>null!=e)),k=(i.data.length,"object"==typeof i.data[V]);i.data.forEach(((e,t)=>{if(null!=e)if(["time","timeseries"].includes(n.options.type)){let i=null!=e[P]?e[P]:e.t;void 0!==i?M.add(new Date(i).getTime(),e[A]):M.add(t,e)}else k?isNaN(e.x)||isNaN(e.y)?isNaN(e.x)?isNaN(e.y)||M.add(t,e.y):M.add(t,e.x):M.add(e.x,e.y):M.add(t,e)}));let j,E,W=n.getPixelForValue(M.minx),I=h.getPixelForValue(M.f(M.minx));if(i.trendlineLinear.projection&&M.scale()<0){let e=M.fo();e<M.minx&&(e=M.maxx),j=n.getPixelForValue(e),E=h.getPixelForValue(M.f(e))}else j=n.getPixelForValue(M.maxx),E=h.getPixelForValue(M.f(M.maxx));const K=e.controller.chart.chartArea.bottom,$=e.controller.chart.width;if(isFinite(W)&&isFinite(I)&&isFinite(j)&&isFinite(E)){l({x1:W,y1:I,x2:j,y2:E,drawBottom:K,chartWidth:$}),t.lineWidth=m,o(t,p),r({ctx:t,x1:W,y1:I,x2:j,y2:E,colorMin:y,colorMax:f}),b&&d(t,W,I,j,E,K,b);const e=Math.atan2(E-I,j-W),n=(I-E)/(j-W);if(i.trendlineLinear.label&&!1!==L){const i=F?`${g} (Slope: ${v?(100*n).toFixed(2)+"%":n.toFixed(2)})`:g;a(t,i,W,I,j,E,e,w,C,D,S)}}},a=(e,t,i,n,s,a,l,o,r,d,c)=>{e.font=`${d}px ${r}`,e.fillStyle=o;const x=e.measureText(t).width,h=(i+s)/2,u=(n+a)/2;e.save(),e.translate(h,u),e.rotate(l);const y=-x/2,f=c;e.fillText(t,y,f),e.restore()},l=({x1:e,y1:t,x2:i,y2:n,drawBottom:s,chartWidth:a})=>{if(t>s){t=s}else if(n>s){let e=n-s,l=n-t;n=s,i=a-(i-(a-a*(e/l)))}},o=(e,t)=>{switch(t){case"dotted":e.setLineDash([2,2]);break;case"dashed":e.setLineDash([8,3]);break;case"dashdot":e.setLineDash([8,3,2,3]);break;default:e.setLineDash([])}},r=({ctx:e,x1:t,y1:i,x2:n,y2:s,colorMin:a,colorMax:l})=>{if(isFinite(t)&&isFinite(i)&&isFinite(n)&&isFinite(s)){e.beginPath(),e.moveTo(t,i),e.lineTo(n,s);try{let o=e.createLinearGradient(t,i,n,s);o.addColorStop(0,a),o.addColorStop(1,l),e.strokeStyle=o}catch(t){console.warn("Gradient creation failed, using solid color:",t),e.strokeStyle=a}e.stroke(),e.closePath()}else console.warn("Cannot draw trendline: coordinates contain non-finite values",{x1:t,y1:i,x2:n,y2:s})},d=(e,t,i,n,s,a,l)=>{isFinite(t)&&isFinite(i)&&isFinite(n)&&isFinite(s)&&isFinite(a)?(e.beginPath(),e.moveTo(t,i),e.lineTo(n,s),e.lineTo(n,a),e.lineTo(t,a),e.lineTo(t,i),e.closePath(),e.fillStyle=l,e.fill()):console.warn("Cannot fill below trendline: coordinates contain non-finite values",{x1:t,y1:i,x2:n,y2:s,drawBottom:a})};class c{constructor(){this.count=0,this.sumx=0,this.sumy=0,this.sumx2=0,this.sumxy=0,this.minx=Number.MAX_VALUE,this.maxx=Number.MIN_VALUE}add(e,t){this.sumx+=e,this.sumy+=t,this.sumx2+=e*e,this.sumxy+=e*t,e<this.minx&&(this.minx=e),e>this.maxx&&(this.maxx=e),this.count++}slope(){const e=this.count*this.sumx2-this.sumx*this.sumx;return(this.count*this.sumxy-this.sumx*this.sumy)/e}intercept(){return(this.sumy-this.slope()*this.sumx)/this.count}f(e){return this.slope()*e+this.intercept()}fo(){return-this.intercept()/this.slope()}scale(){return this.slope()}}"undefined"!=typeof window&&window.Chart&&(window.Chart.hasOwnProperty("register")?window.Chart.register(i):window.Chart.plugins.register(i));try{e.exports=i}catch(e){}}},t={};!function i(n){var s=t[n];if(void 0!==s)return s.exports;var a=t[n]={exports:{}};return e[n](a,a.exports,i),a.exports}(339)})();
1
+ /*! For license information please see chartjs-plugin-trendline.min.js.LICENSE.txt */
2
+ (()=>{var e={339:(e,t)=>{const i={id:"chartjs-plugin-trendline",afterDatasetsDraw:e=>{const t=e.ctx,{xScale:i,yScale:s}=n(e);e.data.datasets.map(((e,t)=>({dataset:e,index:t}))).filter((e=>e.dataset.trendlineLinear)).sort(((e,t)=>{const i=e.dataset.order??0,n=t.dataset.order??0;return 0===i&&0!==n?1:0===n&&0!==i?-1:i-n})).forEach((({dataset:n,index:r})=>{if((n.alwaysShowTrendline||e.isDatasetVisible(r))&&n.data.length>1){const o=e.getDatasetMeta(r);a(o,t,n,i,s)}})),t.setLineDash([])},beforeInit:e=>{e.data.datasets.forEach((t=>{if(t.trendlineLinear&&t.trendlineLinear.label){const i=t.trendlineLinear.label,n=e.legend.options.labels.generateLabels;e.legend.options.labels.generateLabels=function(e){const a=n(e),s=t.trendlineLinear.legend;return s&&!1!==s.display&&a.push({text:s.text||i+" (Trendline)",strokeStyle:s.color||t.borderColor||"rgba(169,169,169, .6)",fillStyle:s.fillStyle||"transparent",lineCap:s.lineCap||"butt",lineDash:s.lineDash||[],lineWidth:s.width||1}),a}}}))}},n=e=>{let t,i;for(const n of Object.values(e.scales))if(n.isHorizontal()?t=n:i=n,t&&i)break;return{xScale:t,yScale:i}},a=(e,t,i,n,a)=>{const x=i.yAxisID||"y",h=e.controller.chart.scales[x]||a,u=i.borderColor||"rgba(169,169,169, .6)",{colorMin:y=u,colorMax:f=u,width:m=i.borderWidth||3,lineStyle:p="solid",fillColor:b=!1}=i.trendlineLinear||{},{color:w=u,text:g="Trendline",display:F=!0,displayValue:L=!0,offset:S=10,percentage:v=!1}=i.trendlineLinear.label||{},{family:C="'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:D=12}=i.trendlineLinear.label?.font||{},T=e.controller.chart.options,N="object"==typeof T.parsing?T.parsing:void 0,P=i.trendlineLinear?.xAxisKey||N?.xAxisKey||"x",A=i.trendlineLinear?.yAxisKey||N?.yAxisKey||"y";let M=new c,V=i.data.findIndex((e=>null!=e)),k=i.data.length-1,j=e.data[V]?.[P],E=e.data[k]?.[P],W="object"==typeof i.data[V];i.data.forEach(((e,t)=>{if(null!=e)if(["time","timeseries"].includes(n.options.type)){let i=null!=e[P]?e[P]:e.t;void 0!==i?M.add(new Date(i).getTime(),e[A]):M.add(t,e)}else W?isNaN(e.x)||isNaN(e.y)?isNaN(e.x)?isNaN(e.y)||M.add(t,e.y):M.add(t,e.x):M.add(e.x,e.y):M.add(t,e)}));let I,K,$=isFinite(j)?j:n.getPixelForValue(M.minx),B=h.getPixelForValue(M.f(M.minx));if(i.trendlineLinear.projection&&M.scale()<0){let e=M.fo();e<M.minx&&(e=M.maxx),I=n.getPixelForValue(e),K=h.getPixelForValue(M.f(e))}else I=isFinite(E)?E:n.getPixelForValue(M.maxx),K=h.getPixelForValue(M.f(M.maxx));const H=e.controller.chart.chartArea.bottom,z=e.controller.chart.width;if(isFinite($)&&isFinite(B)&&isFinite(I)&&isFinite(K)){r({x1:$,y1:B,x2:I,y2:K,drawBottom:H,chartWidth:z}),t.lineWidth=m,o(t,p),l({ctx:t,x1:$,y1:B,x2:I,y2:K,colorMin:y,colorMax:f}),b&&d(t,$,B,I,K,H,b);const e=Math.atan2(K-B,I-$),n=(B-K)/(I-$);if(i.trendlineLinear.label&&!1!==F){const i=L?`${g} (Slope: ${v?(100*n).toFixed(2)+"%":n.toFixed(2)})`:g;s(t,i,$,B,I,K,e,w,C,D,S)}}},s=(e,t,i,n,a,s,r,o,l,d,c)=>{e.font=`${d}px ${l}`,e.fillStyle=o;const x=e.measureText(t).width,h=(i+a)/2,u=(n+s)/2;e.save(),e.translate(h,u),e.rotate(r);const y=-x/2,f=c;e.fillText(t,y,f),e.restore()},r=({x1:e,y1:t,x2:i,y2:n,drawBottom:a,chartWidth:s})=>{if(t>a){t=a}else if(n>a){let e=n-a,r=n-t;n=a,i=s-(i-(s-s*(e/r)))}},o=(e,t)=>{switch(t){case"dotted":e.setLineDash([2,2]);break;case"dashed":e.setLineDash([8,3]);break;case"dashdot":e.setLineDash([8,3,2,3]);break;default:e.setLineDash([])}},l=({ctx:e,x1:t,y1:i,x2:n,y2:a,colorMin:s,colorMax:r})=>{if(isFinite(t)&&isFinite(i)&&isFinite(n)&&isFinite(a)){e.beginPath(),e.moveTo(t,i),e.lineTo(n,a);try{let o=e.createLinearGradient(t,i,n,a);o.addColorStop(0,s),o.addColorStop(1,r),e.strokeStyle=o}catch(t){console.warn("Gradient creation failed, using solid color:",t),e.strokeStyle=s}e.stroke(),e.closePath()}else console.warn("Cannot draw trendline: coordinates contain non-finite values",{x1:t,y1:i,x2:n,y2:a})},d=(e,t,i,n,a,s,r)=>{isFinite(t)&&isFinite(i)&&isFinite(n)&&isFinite(a)&&isFinite(s)?(e.beginPath(),e.moveTo(t,i),e.lineTo(n,a),e.lineTo(n,s),e.lineTo(t,s),e.lineTo(t,i),e.closePath(),e.fillStyle=r,e.fill()):console.warn("Cannot fill below trendline: coordinates contain non-finite values",{x1:t,y1:i,x2:n,y2:a,drawBottom:s})};class c{constructor(){this.count=0,this.sumx=0,this.sumy=0,this.sumx2=0,this.sumxy=0,this.minx=Number.MAX_VALUE,this.maxx=Number.MIN_VALUE}add(e,t){this.sumx+=e,this.sumy+=t,this.sumx2+=e*e,this.sumxy+=e*t,e<this.minx&&(this.minx=e),e>this.maxx&&(this.maxx=e),this.count++}slope(){const e=this.count*this.sumx2-this.sumx*this.sumx;return(this.count*this.sumxy-this.sumx*this.sumy)/e}intercept(){return(this.sumy-this.slope()*this.sumx)/this.count}f(e){return this.slope()*e+this.intercept()}fo(){return-this.intercept()/this.slope()}scale(){return this.slope()}}"undefined"!=typeof window&&window.Chart&&(window.Chart.hasOwnProperty("register")?window.Chart.register(i):window.Chart.plugins.register(i));try{e.exports=i}catch(e){}}},t={};!function i(n){var a=t[n];if(void 0!==a)return a.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,i),s.exports}(339)})();
@@ -1,11 +1,11 @@
1
- /*!
2
- * chartjs-plugin-trendline.js
3
- * Version: 2.1.7
4
- *
5
- * Copyright 2025 Marcus Alsterfjord
6
- * Released under the MIT license
7
- * https://github.com/Makanz/chartjs-plugin-trendline/blob/master/README.md
8
- *
9
- * Modified by @vesal: accept xy-data from scatter,
10
- * Modified by @Megaemce: add label and basic legend to trendline, add JSDoc,
11
- */
1
+ /*!
2
+ * chartjs-plugin-trendline.js
3
+ * Version: 2.1.9
4
+ *
5
+ * Copyright 2025 Marcus Alsterfjord
6
+ * Released under the MIT license
7
+ * https://github.com/Makanz/chartjs-plugin-trendline/blob/master/README.md
8
+ *
9
+ * Modified by @vesal: accept xy-data from scatter,
10
+ * Modified by @Megaemce: add label and basic legend to trendline, add JSDoc,
11
+ */
@@ -0,0 +1,102 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Chart.js Example</title>
7
+ <!-- Include Chart.js and the Trendline plugin -->
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
10
+ <script src="../src/chartjs-plugin-trendline.js"></script>
11
+ </head>
12
+ <body>
13
+
14
+ <!-- Canvas element for rendering the chart -->
15
+ <canvas id="chartjs" width="400" height="200"></canvas>
16
+
17
+ <script type="text/javascript">
18
+ // Define colors
19
+ const barColor1 = '#258cda'; // Blue color for incidents
20
+ const borderColor1 = '#258cda'; // Same as backgroundColor for incidents
21
+ const barColor2 = '#cccccc'; // Gray color for outage
22
+ const borderColor2 = '#808080'; // Darker gray for borderColor of outage
23
+
24
+ // Get the context of the canvas element we want to select
25
+ const ctx = document.getElementById("chartjs").getContext('2d');
26
+
27
+ const chartDynamics = [
28
+ {
29
+ datepost: "2025-03-01T00:00:00",
30
+ quantity: 75,
31
+ },
32
+ {
33
+ datepost: "2025-03-02T00:00:00",
34
+ quantity: 64,
35
+ },
36
+ {
37
+ datepost: "2025-03-03T00:00:00",
38
+ quantity: 52,
39
+ },
40
+ {
41
+ datepost: "2025-03-04T00:00:00",
42
+ quantity: 23,
43
+ },
44
+ {
45
+ datepost: "2025-03-05T00:00:00",
46
+ quantity: 44,
47
+ },
48
+ {
49
+ datepost: "2025-03-06T00:00:00",
50
+ quantity: 38,
51
+ },
52
+ {
53
+ datepost: "2025-03-07T00:00:00",
54
+ quantity: 44,
55
+ },
56
+ {
57
+ datepost: "2025-03-08T00:00:00",
58
+ quantity: 41,
59
+ },
60
+ {
61
+ datepost: "2025-03-09T00:00:00",
62
+ quantity: 78,
63
+ },
64
+ {
65
+ datepost: "2025-03-10T00:00:00",
66
+ quantity: 31,
67
+ },
68
+ ];
69
+
70
+ // Create a new Chart instance
71
+ const myChart1 = new Chart(ctx, {
72
+ type: "line",
73
+ data: {
74
+ labels: chartDynamics.map((o) => o.datepost),
75
+ datasets: [
76
+ {
77
+ label: "total",
78
+ data: chartDynamics.map((o) => o.quantity),
79
+ borderWidth: 1,
80
+ pointStyle: false,
81
+ trendlineLinear: { lineStyle: "dotted", width: 2 },
82
+ },
83
+ ],
84
+ },
85
+ options: {
86
+ maintainAspectRatio: false,
87
+ scales: {
88
+ x: { type: "time", time: { tooltipFormat: "d", minUnit: "day" } },
89
+ },
90
+ interaction: { mode: "nearest", intersect: false },
91
+ plugins: {
92
+ legend: { display: false },
93
+ autocolors: { enabled: false },
94
+ datalabels: { display: false },
95
+ },
96
+ },
97
+ });
98
+
99
+ </script>
100
+
101
+ </body>
102
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chartjs-plugin-trendline",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
4
4
  "description": "Trendline for Chart.js",
5
5
  "main": "src/chartjs-plugin-trendline.js",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * chartjs-plugin-trendline.js
3
- * Version: 2.1.7
3
+ * Version: 2.1.9
4
4
  *
5
5
  * Copyright 2025 Marcus Alsterfjord
6
6
  * Released under the MIT license
@@ -25,16 +25,27 @@ const pluginTrendlineLinear = {
25
25
  const ctx = chartInstance.ctx;
26
26
  const { xScale, yScale } = getScales(chartInstance);
27
27
 
28
- chartInstance.data.datasets.forEach((dataset, index) => {
28
+ const sortedDatasets = chartInstance.data.datasets
29
+ .map((dataset, index) => ({ dataset, index }))
30
+ .filter((entry) => entry.dataset.trendlineLinear)
31
+ .sort((a, b) => {
32
+ const orderA = a.dataset.order ?? 0;
33
+ const orderB = b.dataset.order ?? 0;
34
+
35
+ // Push 0-order datasets to the end (they draw last / on top)
36
+ if (orderA === 0 && orderB !== 0) return 1;
37
+ if (orderB === 0 && orderA !== 0) return -1;
38
+
39
+ // Otherwise, draw lower order first
40
+ return orderA - orderB;
41
+ });
42
+
43
+ sortedDatasets.forEach(({ dataset, index }) => {
29
44
  const showTrendline =
30
45
  dataset.alwaysShowTrendline ||
31
46
  chartInstance.isDatasetVisible(index);
32
47
 
33
- if (
34
- dataset.trendlineLinear &&
35
- showTrendline &&
36
- dataset.data.length > 1
37
- ) {
48
+ if (showTrendline && dataset.data.length > 1) {
38
49
  const datasetMeta = chartInstance.getDatasetMeta(index);
39
50
  addFitter(datasetMeta, ctx, dataset, xScale, yScale);
40
51
  }
@@ -148,6 +159,8 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
148
159
  (d) => d !== undefined && d !== null
149
160
  );
150
161
  let lastIndex = dataset.data.length - 1;
162
+ let startPos = datasetMeta.data[firstIndex]?.[xAxisKey];
163
+ let endPos = datasetMeta.data[lastIndex]?.[xAxisKey];
151
164
  let xy = typeof dataset.data[firstIndex] === 'object';
152
165
 
153
166
  // Collect data points for the fitter
@@ -175,7 +188,9 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
175
188
  });
176
189
 
177
190
  // Calculate the pixel coordinates for the trendline
178
- let x1 = xScale.getPixelForValue(fitter.minx);
191
+ let x1 = isFinite(startPos)
192
+ ? startPos
193
+ : xScale.getPixelForValue(fitter.minx);
179
194
  let y1 = yScaleToUse.getPixelForValue(fitter.f(fitter.minx));
180
195
  let x2, y2;
181
196
 
@@ -186,13 +201,10 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
186
201
  x2 = xScale.getPixelForValue(x2value);
187
202
  y2 = yScaleToUse.getPixelForValue(fitter.f(x2value));
188
203
  } else {
189
- x2 = xScale.getPixelForValue(fitter.maxx);
204
+ x2 = isFinite(endPos) ? endPos : xScale.getPixelForValue(fitter.maxx);
190
205
  y2 = yScaleToUse.getPixelForValue(fitter.f(fitter.maxx));
191
206
  }
192
207
 
193
- // Do not use startPos and endPos directly, as they may be undefined
194
- // This was causing the vertical line issue
195
-
196
208
  const drawBottom = datasetMeta.controller.chart.chartArea.bottom;
197
209
  const chartWidth = datasetMeta.controller.chart.width;
198
210
 
@@ -1,113 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Chart.js Example</title>
7
- <!-- Include Chart.js and the Trendline plugin -->
8
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
- <script src="../src/chartjs-plugin-trendline.js"></script>
10
- </head>
11
- <body>
12
-
13
- <!-- Canvas element for rendering the chart -->
14
- <canvas id="chartjs" width="400" height="200"></canvas>
15
-
16
- <script type="text/javascript">
17
- // Define colors
18
- const barColor1 = '#258cda'; // Blue color for incidents
19
- const borderColor1 = '#258cda'; // Same as backgroundColor for incidents
20
- const barColor2 = '#cccccc'; // Gray color for outage
21
- const borderColor2 = '#808080'; // Darker gray for borderColor of outage
22
-
23
- // Get the context of the canvas element we want to select
24
- const ctx = document.getElementById("chartjs").getContext('2d');
25
-
26
- // Create a new Chart instance
27
- const myChart1 = new Chart(ctx, {
28
- type: 'bar', // Specify chart type (e.g., bar)
29
-
30
- data: {
31
- labels: ["July 2024","August 2024","September 2024","October 2024","November 2024","December 2024","January 2025","February 2025","March 2025","April 2025","May 2025","June 2025"], // Example labels for x-axis
32
- datasets: [
33
- {
34
- label: '# of Incidents',
35
- yAxisID: 'y1',
36
- fill: false,
37
- backgroundColor: barColor1,
38
- borderColor: borderColor1,
39
- borderWidth: 3,
40
- tension: 0.3,
41
- data: [7,6,13,6,4,3,4,4,0,0,0,0], // Example data for incidents
42
- trendlineLinear: {
43
- lineStyle: 'dotted',
44
- width: 2
45
- },
46
- },
47
- {
48
- label: 'Total Outage',
49
- yAxisID: 'y2',
50
- fill: true,
51
- backgroundColor: barColor2,
52
- borderColor: borderColor2,
53
- borderWidth: 3,
54
- tension: 0.3,
55
- data: [112,201,316,16,16,13,11,81,20,300,250,200], // Example data for outage in hours
56
- trendlineLinear: {
57
- lineStyle: 'dotted',
58
- width: 2
59
- },
60
- }
61
- ],
62
- },
63
-
64
- options: {
65
- plugins: {
66
- legend: {
67
- position: 'bottom', // Position of the legend
68
- },
69
- },
70
- tooltips: {
71
- displayColors: true, // Display color in tooltip
72
- mode: 'index', // Tooltip mode
73
- },
74
- scales: {
75
- x: { // X-axis configuration
76
- ticks: {
77
- autoSkip: true,
78
- maxTicksLimit: 12,
79
- maxRotation: 45,
80
- minRotation: 45,
81
- },
82
- },
83
- y1: { // Left Y-axis for incidents
84
- ticks: {
85
- beginAtZero: true,
86
- },
87
- title: {
88
- display: true,
89
- text: 'Incidents',
90
- },
91
- type: 'linear',
92
- position: 'left',
93
- },
94
- y2: { // Right Y-axis for outage in hours
95
- ticks: {
96
- beginAtZero: true,
97
- },
98
- title: {
99
- display: true,
100
- text: 'Outage in Hours',
101
- },
102
- type: 'linear',
103
- position: 'right',
104
- }
105
- },
106
- responsive: true, // Make the chart responsive
107
- maintainAspectRatio: true, // Maintain aspect ratio
108
- }
109
- });
110
- </script>
111
-
112
- </body>
113
- </html>