chartjs-plugin-trendline 2.1.7 → 2.1.8

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
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)})();
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: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)){l({x1:$,y1:B,x2:I,y2:K,drawBottom:H,chartWidth:z}),t.lineWidth=m,o(t,p),r({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;a(t,i,$,B,I,K,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,6 +1,6 @@
1
1
  /*!
2
2
  * chartjs-plugin-trendline.js
3
- * Version: 2.1.7
3
+ * Version: 2.1.8
4
4
  *
5
5
  * Copyright 2025 Marcus Alsterfjord
6
6
  * Released under the MIT license
@@ -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.8",
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.8
4
4
  *
5
5
  * Copyright 2025 Marcus Alsterfjord
6
6
  * Released under the MIT license
@@ -148,6 +148,8 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
148
148
  (d) => d !== undefined && d !== null
149
149
  );
150
150
  let lastIndex = dataset.data.length - 1;
151
+ let startPos = datasetMeta.data[firstIndex]?.[xAxisKey];
152
+ let endPos = datasetMeta.data[lastIndex]?.[xAxisKey];
151
153
  let xy = typeof dataset.data[firstIndex] === 'object';
152
154
 
153
155
  // Collect data points for the fitter
@@ -175,7 +177,9 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
175
177
  });
176
178
 
177
179
  // Calculate the pixel coordinates for the trendline
178
- let x1 = xScale.getPixelForValue(fitter.minx);
180
+ let x1 = isFinite(startPos)
181
+ ? startPos
182
+ : xScale.getPixelForValue(fitter.minx);
179
183
  let y1 = yScaleToUse.getPixelForValue(fitter.f(fitter.minx));
180
184
  let x2, y2;
181
185
 
@@ -186,13 +190,10 @@ const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
186
190
  x2 = xScale.getPixelForValue(x2value);
187
191
  y2 = yScaleToUse.getPixelForValue(fitter.f(x2value));
188
192
  } else {
189
- x2 = xScale.getPixelForValue(fitter.maxx);
193
+ x2 = isFinite(endPos) ? endPos : xScale.getPixelForValue(fitter.maxx);
190
194
  y2 = yScaleToUse.getPixelForValue(fitter.f(fitter.maxx));
191
195
  }
192
196
 
193
- // Do not use startPos and endPos directly, as they may be undefined
194
- // This was causing the vertical line issue
195
-
196
197
  const drawBottom = datasetMeta.controller.chart.chartArea.bottom;
197
198
  const chartWidth = datasetMeta.controller.chart.width;
198
199
 
@@ -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>