chartjs-plugin-trendline 3.2.0 → 3.2.3

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.
Files changed (43) hide show
  1. package/.github/copilot-instructions.md +40 -40
  2. package/.github/workflows/release.yml +64 -61
  3. package/.github/workflows/tests.yml +26 -26
  4. package/.prettierrc +5 -5
  5. package/CLAUDE.md +44 -44
  6. package/GEMINI.md +40 -40
  7. package/LICENSE +21 -21
  8. package/MIGRATION.md +126 -126
  9. package/README.md +166 -166
  10. package/babel.config.js +3 -3
  11. package/changelog.md +39 -39
  12. package/dist/chartjs-plugin-trendline.cjs +884 -885
  13. package/dist/chartjs-plugin-trendline.esm.js +882 -883
  14. package/dist/chartjs-plugin-trendline.js +890 -891
  15. package/dist/chartjs-plugin-trendline.min.js +8 -8
  16. package/dist/chartjs-plugin-trendline.min.js.map +1 -1
  17. package/example/barChart.html +165 -165
  18. package/example/barChartWithNullValues.html +168 -168
  19. package/example/barChart_label.html +174 -174
  20. package/example/exponentialChart.html +244 -244
  21. package/example/lineChart.html +210 -210
  22. package/example/lineChartProjection.html +261 -261
  23. package/example/lineChartTypeTime.html +190 -190
  24. package/example/scatterChart.html +136 -136
  25. package/example/scatterProjection.html +141 -141
  26. package/example/test-null-handling.html +59 -59
  27. package/index.html +215 -215
  28. package/jest.config.js +4 -4
  29. package/package.json +45 -40
  30. package/rollup.config.js +54 -54
  31. package/src/components/label.js +56 -56
  32. package/src/components/label.test.js +129 -129
  33. package/src/components/trendline.js +375 -375
  34. package/src/components/trendline.test.js +789 -789
  35. package/src/core/plugin.js +78 -79
  36. package/src/core/plugin.test.js +307 -0
  37. package/src/index.js +12 -12
  38. package/src/utils/drawing.js +125 -125
  39. package/src/utils/drawing.test.js +308 -308
  40. package/src/utils/exponentialFitter.js +146 -146
  41. package/src/utils/exponentialFitter.test.js +362 -362
  42. package/src/utils/lineFitter.js +86 -86
  43. package/src/utils/lineFitter.test.js +340 -340
@@ -1,262 +1,262 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
8
- <title>Trendline Projection & Offset Example</title>
9
- <script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js"></script>
10
- <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline/dist/chartjs-plugin-trendline.min.js"></script>
11
- <style>
12
- body {
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
- margin: 0;
15
- padding: 40px 20px;
16
- background-color: #f8f9fa;
17
- color: #333;
18
- line-height: 1.6;
19
- }
20
- .container {
21
- max-width: 1000px;
22
- margin: 0 auto;
23
- }
24
- h1 {
25
- text-align: center;
26
- color: #2c3e50;
27
- margin-bottom: 10px;
28
- font-size: 2.5rem;
29
- }
30
- .subtitle {
31
- text-align: center;
32
- color: #7f8c8d;
33
- margin-bottom: 40px;
34
- font-size: 1.1rem;
35
- }
36
- .chart-container {
37
- background: white;
38
- padding: 30px;
39
- border-radius: 8px;
40
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
41
- margin-bottom: 30px;
42
- }
43
- .chart-wrapper {
44
- position: relative;
45
- height: 500px;
46
- }
47
- .info-section {
48
- background: white;
49
- padding: 25px;
50
- border-radius: 8px;
51
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
52
- margin-bottom: 20px;
53
- }
54
- .info-section h3 {
55
- margin-top: 0;
56
- color: #2c3e50;
57
- border-bottom: 2px solid #3498db;
58
- padding-bottom: 10px;
59
- }
60
- .nav-links {
61
- text-align: center;
62
- margin-top: 30px;
63
- }
64
- .nav-links a {
65
- display: inline-block;
66
- margin: 0 10px;
67
- padding: 10px 20px;
68
- background: #3498db;
69
- color: white;
70
- text-decoration: none;
71
- border-radius: 4px;
72
- font-weight: 500;
73
- transition: background-color 0.2s;
74
- }
75
- .nav-links a:hover {
76
- background: #2980b9;
77
- }
78
- code {
79
- background: #f8f9fa;
80
- padding: 2px 6px;
81
- border-radius: 3px;
82
- font-family: 'Monaco', 'Menlo', monospace;
83
- }
84
- </style>
85
- <script>
86
- document.addEventListener("DOMContentLoaded", function (event) {
87
- new Chart(document.getElementById("line-chart"), {
88
- type: 'line',
89
- data: {
90
- datasets: [
91
- {
92
- // Dataset 1: Original data, demonstrates basic projection.
93
- // Observe: Trendline extends to the edges of the chart area.
94
- data: [{ x: 0, y: 30 }, { x: 5, y: 25 }, { x: 10, y: 23 }, { x: 25, y: 23 }, { x: 35, y: 17 }, { x: 45, y: 17 }],
95
- label: "Dataset 1 (Projected)",
96
- borderColor: "#3e95cd",
97
- backgroundColor: "rgba(62,149,205,0.1)",
98
- fill: false,
99
- trendlineLinear: {
100
- colorMin: "#3e95cd", // Should match borderColor or be distinct
101
- lineStyle: "dotted", // "line" was not a valid Chart.js option, "solid" or "dotted", "dashed"
102
- width: 2,
103
- projection: true,
104
- label: {
105
- display: true,
106
- text: "Trend D1 (Proj)",
107
- displayValue: true,
108
- }
109
- }
110
- },
111
- {
112
- // Dataset 2: Clustered data, non-projected.
113
- // Observe: Trendline only spans the range of its data points (approx x=15 to x=35).
114
- // This contrasts with projected lines that extend to chart boundaries.
115
- data: [{ x: 15, y: 10 }, { x: 20, y: 12 }, { x: 25, y: 9 }, { x: 30, y: 11 }, { x: 35, y: 8 }],
116
- label: "Dataset 2 (Non-Projected, Clustered)",
117
- borderColor: "#FF6384",
118
- backgroundColor: "rgba(255,99,132,0.1)",
119
- fill: false,
120
- trendlineLinear: {
121
- colorMin: "#FF6384",
122
- lineStyle: "dashed",
123
- width: 2,
124
- projection: false, // Explicitly false
125
- label: {
126
- display: true,
127
- text: "Trend D2 (No Proj)",
128
- displayValue: true,
129
- }
130
- }
131
- },
132
- {
133
- // Dataset 3: Projected, with positive trendoffset and null data points.
134
- // Observe: Trendline calculation starts after skipping the first 2 data points (due to trendoffset: 2).
135
- // Null values (e.g., at x=65) are correctly ignored by the fitter. The line projects to chart edges.
136
- data: [{ x: 50, y: 10 }, { x: 55, y: 12 }, { x: 60, y: 15 }, { x: 70, y: 18 }, { x: 75, y: 22 }, {x: 65, y: null}],
137
- label: "Dataset 3 (Projected, Offset+, Nulls)",
138
- borderColor: "#4BC0C0",
139
- backgroundColor: "rgba(75,192,192,0.1)",
140
- fill: false,
141
- trendlineLinear: {
142
- colorMin: "#4BC0C0",
143
- lineStyle: "solid",
144
- width: 2,
145
- projection: true,
146
- trendoffset: 2, // Skips first 2 data points from calculation {50,y:10}, {55,y:12}
147
- label: {
148
- display: true,
149
- text: "Trend D3 (Proj, Offset+)",
150
- displayValue: true,
151
- }
152
- }
153
- },
154
- {
155
- // Dataset 4: Projected, with negative trendoffset.
156
- // Observe: Trendline calculation excludes the last 2 data points from the set.
157
- // The line projects to chart edges based on the remaining points.
158
- data: [{ x: 0, y: 5 }, { x: 10, y: 8 }, { x: 20, y: 6}, { x: 30, y: 9 }, { x: 40, y: 7 }, { x: 50, y: 10 } ],
159
- label: "Dataset 4 (Projected, Offset-)",
160
- borderColor: "#FF9F40",
161
- backgroundColor: "rgba(255,159,64,0.1)",
162
- fill: false,
163
- trendlineLinear: {
164
- colorMin: "#FF9F40",
165
- lineStyle: "dotted",
166
- width: 2,
167
- projection: true,
168
- trendoffset: -2, // Excludes last 2 data points {40,y:7}, {50,y:10}
169
- label: {
170
- display: true,
171
- text: "Trend D4 (Proj, Offset-)",
172
- displayValue: true,
173
- }
174
- }
175
- },
176
- // Dummy dataset to ensure chart scales to desired max, if other data doesn't reach it.
177
- // { data: [{ x: 80, y: 35 }], label: "ScaleHelper", hidden: true}
178
- ]
179
- },
180
- options: {
181
- plugins: {
182
- title: {
183
- display: true,
184
- text: 'Trendline Projection, Offset, and Data Handling Demo',
185
- font: { size: 18 }
186
- },
187
- legend: {
188
- position: 'top',
189
- },
190
- tooltip: {
191
- mode: 'index',
192
- intersect: false,
193
- }
194
- },
195
- maintainAspectRatio: false,
196
- responsive: true,
197
- scales: {
198
- x: {
199
- type: 'linear',
200
- position: 'bottom',
201
- title: { text: 'X Value / Days', display: true },
202
- min: -5, // Adjusted min to give space
203
- max: 80, // Adjusted max to give space for projection
204
- },
205
- y: {
206
- type: 'linear',
207
- position: 'left',
208
- title: { text: 'Y Value / Count', display: true },
209
- min: 0, // Adjusted min
210
- max: 35, // Adjusted max
211
- }
212
- }
213
- }
214
- });
215
- });
216
- </script>
217
- </head>
218
-
219
- <body>
220
- <div class="container">
221
- <h1>Line Chart Projection</h1>
222
- <p class="subtitle">Trendline projection, data offset, and null value handling</p>
223
-
224
- <div class="chart-container">
225
- <div class="chart-wrapper">
226
- <canvas id="line-chart"></canvas>
227
- </div>
228
- </div>
229
-
230
- <div class="info-section">
231
- <h3>Observations Guide</h3>
232
- <ul>
233
- <li><strong>Dataset 1 (Blue, Dotted Trend):</strong> Shows a standard projected trendline extending to the chart's X-axis boundaries.</li>
234
- <li><strong>Dataset 2 (Red, Dashed Trend):</strong> Demonstrates a <strong>non-projected</strong> trendline. Notice how it only covers the span of its own data points (approx. x=15 to x=35).</li>
235
- <li><strong>Dataset 3 (Teal, Solid Trend):</strong>
236
- <ul>
237
- <li>Uses <code>projection: true</code>.</li>
238
- <li><code>trendoffset: 2</code> means the first two data points ( (50,10) and (55,12) ) are excluded from the trendline calculation.</li>
239
- <li>Contains a <code>null</code> data point (originally at x=65) which is correctly ignored by the trendline fitter.</li>
240
- </ul>
241
- </li>
242
- <li><strong>Dataset 4 (Orange, Dotted Trend):</strong>
243
- <ul>
244
- <li>Uses <code>projection: true</code>.</li>
245
- <li><code>trendoffset: -2</code> means the last two data points ( (40,7) and (50,10) ) are excluded from the trendline calculation.</li>
246
- </ul>
247
- </li>
248
- <li>All trendlines should be clipped correctly within the chart area boundaries, even if their mathematical extension would go beyond.</li>
249
- <li>The slope displayed in the labels should consistently reflect the underlying data used for fitting, regardless of projection.</li>
250
- </ul>
251
- </div>
252
-
253
- <div class="nav-links">
254
- <a href="../index.html">← Back to Examples</a>
255
- <a href="lineChart.html">← Line Chart</a>
256
- <a href="lineChartTypeTime.html">Time Series Line Chart →</a>
257
- </div>
258
- </div>
259
-
260
- </body>
261
-
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
8
+ <title>Trendline Projection & Offset Example</title>
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline/dist/chartjs-plugin-trendline.min.js"></script>
11
+ <style>
12
+ body {
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
+ margin: 0;
15
+ padding: 40px 20px;
16
+ background-color: #f8f9fa;
17
+ color: #333;
18
+ line-height: 1.6;
19
+ }
20
+ .container {
21
+ max-width: 1000px;
22
+ margin: 0 auto;
23
+ }
24
+ h1 {
25
+ text-align: center;
26
+ color: #2c3e50;
27
+ margin-bottom: 10px;
28
+ font-size: 2.5rem;
29
+ }
30
+ .subtitle {
31
+ text-align: center;
32
+ color: #7f8c8d;
33
+ margin-bottom: 40px;
34
+ font-size: 1.1rem;
35
+ }
36
+ .chart-container {
37
+ background: white;
38
+ padding: 30px;
39
+ border-radius: 8px;
40
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
41
+ margin-bottom: 30px;
42
+ }
43
+ .chart-wrapper {
44
+ position: relative;
45
+ height: 500px;
46
+ }
47
+ .info-section {
48
+ background: white;
49
+ padding: 25px;
50
+ border-radius: 8px;
51
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
52
+ margin-bottom: 20px;
53
+ }
54
+ .info-section h3 {
55
+ margin-top: 0;
56
+ color: #2c3e50;
57
+ border-bottom: 2px solid #3498db;
58
+ padding-bottom: 10px;
59
+ }
60
+ .nav-links {
61
+ text-align: center;
62
+ margin-top: 30px;
63
+ }
64
+ .nav-links a {
65
+ display: inline-block;
66
+ margin: 0 10px;
67
+ padding: 10px 20px;
68
+ background: #3498db;
69
+ color: white;
70
+ text-decoration: none;
71
+ border-radius: 4px;
72
+ font-weight: 500;
73
+ transition: background-color 0.2s;
74
+ }
75
+ .nav-links a:hover {
76
+ background: #2980b9;
77
+ }
78
+ code {
79
+ background: #f8f9fa;
80
+ padding: 2px 6px;
81
+ border-radius: 3px;
82
+ font-family: 'Monaco', 'Menlo', monospace;
83
+ }
84
+ </style>
85
+ <script>
86
+ document.addEventListener("DOMContentLoaded", function (event) {
87
+ new Chart(document.getElementById("line-chart"), {
88
+ type: 'line',
89
+ data: {
90
+ datasets: [
91
+ {
92
+ // Dataset 1: Original data, demonstrates basic projection.
93
+ // Observe: Trendline extends to the edges of the chart area.
94
+ data: [{ x: 0, y: 30 }, { x: 5, y: 25 }, { x: 10, y: 23 }, { x: 25, y: 23 }, { x: 35, y: 17 }, { x: 45, y: 17 }],
95
+ label: "Dataset 1 (Projected)",
96
+ borderColor: "#3e95cd",
97
+ backgroundColor: "rgba(62,149,205,0.1)",
98
+ fill: false,
99
+ trendlineLinear: {
100
+ colorMin: "#3e95cd", // Should match borderColor or be distinct
101
+ lineStyle: "dotted", // "line" was not a valid Chart.js option, "solid" or "dotted", "dashed"
102
+ width: 2,
103
+ projection: true,
104
+ label: {
105
+ display: true,
106
+ text: "Trend D1 (Proj)",
107
+ displayValue: true,
108
+ }
109
+ }
110
+ },
111
+ {
112
+ // Dataset 2: Clustered data, non-projected.
113
+ // Observe: Trendline only spans the range of its data points (approx x=15 to x=35).
114
+ // This contrasts with projected lines that extend to chart boundaries.
115
+ data: [{ x: 15, y: 10 }, { x: 20, y: 12 }, { x: 25, y: 9 }, { x: 30, y: 11 }, { x: 35, y: 8 }],
116
+ label: "Dataset 2 (Non-Projected, Clustered)",
117
+ borderColor: "#FF6384",
118
+ backgroundColor: "rgba(255,99,132,0.1)",
119
+ fill: false,
120
+ trendlineLinear: {
121
+ colorMin: "#FF6384",
122
+ lineStyle: "dashed",
123
+ width: 2,
124
+ projection: false, // Explicitly false
125
+ label: {
126
+ display: true,
127
+ text: "Trend D2 (No Proj)",
128
+ displayValue: true,
129
+ }
130
+ }
131
+ },
132
+ {
133
+ // Dataset 3: Projected, with positive trendoffset and null data points.
134
+ // Observe: Trendline calculation starts after skipping the first 2 data points (due to trendoffset: 2).
135
+ // Null values (e.g., at x=65) are correctly ignored by the fitter. The line projects to chart edges.
136
+ data: [{ x: 50, y: 10 }, { x: 55, y: 12 }, { x: 60, y: 15 }, { x: 70, y: 18 }, { x: 75, y: 22 }, {x: 65, y: null}],
137
+ label: "Dataset 3 (Projected, Offset+, Nulls)",
138
+ borderColor: "#4BC0C0",
139
+ backgroundColor: "rgba(75,192,192,0.1)",
140
+ fill: false,
141
+ trendlineLinear: {
142
+ colorMin: "#4BC0C0",
143
+ lineStyle: "solid",
144
+ width: 2,
145
+ projection: true,
146
+ trendoffset: 2, // Skips first 2 data points from calculation {50,y:10}, {55,y:12}
147
+ label: {
148
+ display: true,
149
+ text: "Trend D3 (Proj, Offset+)",
150
+ displayValue: true,
151
+ }
152
+ }
153
+ },
154
+ {
155
+ // Dataset 4: Projected, with negative trendoffset.
156
+ // Observe: Trendline calculation excludes the last 2 data points from the set.
157
+ // The line projects to chart edges based on the remaining points.
158
+ data: [{ x: 0, y: 5 }, { x: 10, y: 8 }, { x: 20, y: 6}, { x: 30, y: 9 }, { x: 40, y: 7 }, { x: 50, y: 10 } ],
159
+ label: "Dataset 4 (Projected, Offset-)",
160
+ borderColor: "#FF9F40",
161
+ backgroundColor: "rgba(255,159,64,0.1)",
162
+ fill: false,
163
+ trendlineLinear: {
164
+ colorMin: "#FF9F40",
165
+ lineStyle: "dotted",
166
+ width: 2,
167
+ projection: true,
168
+ trendoffset: -2, // Excludes last 2 data points {40,y:7}, {50,y:10}
169
+ label: {
170
+ display: true,
171
+ text: "Trend D4 (Proj, Offset-)",
172
+ displayValue: true,
173
+ }
174
+ }
175
+ },
176
+ // Dummy dataset to ensure chart scales to desired max, if other data doesn't reach it.
177
+ // { data: [{ x: 80, y: 35 }], label: "ScaleHelper", hidden: true}
178
+ ]
179
+ },
180
+ options: {
181
+ plugins: {
182
+ title: {
183
+ display: true,
184
+ text: 'Trendline Projection, Offset, and Data Handling Demo',
185
+ font: { size: 18 }
186
+ },
187
+ legend: {
188
+ position: 'top',
189
+ },
190
+ tooltip: {
191
+ mode: 'index',
192
+ intersect: false,
193
+ }
194
+ },
195
+ maintainAspectRatio: false,
196
+ responsive: true,
197
+ scales: {
198
+ x: {
199
+ type: 'linear',
200
+ position: 'bottom',
201
+ title: { text: 'X Value / Days', display: true },
202
+ min: -5, // Adjusted min to give space
203
+ max: 80, // Adjusted max to give space for projection
204
+ },
205
+ y: {
206
+ type: 'linear',
207
+ position: 'left',
208
+ title: { text: 'Y Value / Count', display: true },
209
+ min: 0, // Adjusted min
210
+ max: 35, // Adjusted max
211
+ }
212
+ }
213
+ }
214
+ });
215
+ });
216
+ </script>
217
+ </head>
218
+
219
+ <body>
220
+ <div class="container">
221
+ <h1>Line Chart Projection</h1>
222
+ <p class="subtitle">Trendline projection, data offset, and null value handling</p>
223
+
224
+ <div class="chart-container">
225
+ <div class="chart-wrapper">
226
+ <canvas id="line-chart"></canvas>
227
+ </div>
228
+ </div>
229
+
230
+ <div class="info-section">
231
+ <h3>Observations Guide</h3>
232
+ <ul>
233
+ <li><strong>Dataset 1 (Blue, Dotted Trend):</strong> Shows a standard projected trendline extending to the chart's X-axis boundaries.</li>
234
+ <li><strong>Dataset 2 (Red, Dashed Trend):</strong> Demonstrates a <strong>non-projected</strong> trendline. Notice how it only covers the span of its own data points (approx. x=15 to x=35).</li>
235
+ <li><strong>Dataset 3 (Teal, Solid Trend):</strong>
236
+ <ul>
237
+ <li>Uses <code>projection: true</code>.</li>
238
+ <li><code>trendoffset: 2</code> means the first two data points ( (50,10) and (55,12) ) are excluded from the trendline calculation.</li>
239
+ <li>Contains a <code>null</code> data point (originally at x=65) which is correctly ignored by the trendline fitter.</li>
240
+ </ul>
241
+ </li>
242
+ <li><strong>Dataset 4 (Orange, Dotted Trend):</strong>
243
+ <ul>
244
+ <li>Uses <code>projection: true</code>.</li>
245
+ <li><code>trendoffset: -2</code> means the last two data points ( (40,7) and (50,10) ) are excluded from the trendline calculation.</li>
246
+ </ul>
247
+ </li>
248
+ <li>All trendlines should be clipped correctly within the chart area boundaries, even if their mathematical extension would go beyond.</li>
249
+ <li>The slope displayed in the labels should consistently reflect the underlying data used for fitting, regardless of projection.</li>
250
+ </ul>
251
+ </div>
252
+
253
+ <div class="nav-links">
254
+ <a href="../index.html">← Back to Examples</a>
255
+ <a href="lineChart.html">← Line Chart</a>
256
+ <a href="lineChartTypeTime.html">Time Series Line Chart →</a>
257
+ </div>
258
+ </div>
259
+
260
+ </body>
261
+
262
262
  </html>