@@ -22,10 +22,19 @@ import {
22
22
23
23
import merge from 'lodash-es/merge' ;
24
24
25
- import { Chart , ChartConfiguration , ChartType , DefaultDataPoint , registerables } from 'chart.js' ;
25
+ import {
26
+ Chart as ChartJS ,
27
+ ChartConfiguration ,
28
+ ChartData ,
29
+ ChartOptions ,
30
+ ChartType ,
31
+ InteractionItem ,
32
+ Plugin ,
33
+ registerables
34
+ } from 'chart.js' ;
26
35
import { customTooltips as cuiCustomTooltips } from '@coreui/chartjs' ;
27
36
28
- Chart . register ( ...registerables ) ;
37
+ ChartJS . register ( ...registerables ) ;
29
38
30
39
let nextId = 0 ;
31
40
@@ -39,26 +48,70 @@ let nextId = 0;
39
48
// eslint-disable-next-line @angular-eslint/no-host-metadata-property
40
49
// host: { ngSkipHydration: 'true' }
41
50
} )
42
- export class ChartjsComponent < TType extends ChartType = ChartType , TData = DefaultDataPoint < TType > , TLabel = unknown > implements AfterViewInit , OnDestroy , OnChanges {
43
-
44
- @Input ( ) customTooltips = true ;
45
- @Input ( ) data ?: ChartConfiguration < TType , TData , TLabel > [ 'data' ] ;
46
-
51
+ export class ChartjsComponent implements AfterViewInit , OnDestroy , OnChanges {
52
+
53
+ /**
54
+ * Enables custom html based tooltips instead of standard tooltips.
55
+ * @type boolean
56
+ * @default true
57
+ */
58
+ @Input ( { transform : booleanAttribute } ) customTooltips : boolean = true ;
59
+
60
+ /**
61
+ * The data object that is passed into the Chart.js chart (more info).
62
+ */
63
+ @Input ( ) data ?: ChartData ;
64
+
65
+ /**
66
+ * Height attribute applied to the rendered canvas.
67
+ * @type number | undefined
68
+ * @default 150
69
+ */
47
70
@HostBinding ( 'style.height.px' )
48
- @Input ( { transform : ( value : string | number ) => numberAttribute ( value , undefined ) } ) height ?: string | number ;
49
-
50
- @Input ( ) id = `c-chartjs-${ nextId ++ } ` ;
51
- @Input ( ) options ?: ChartConfiguration < TType , TData , TLabel > [ 'options' ] ;
52
- @Input ( ) plugins : ChartConfiguration < TType , TData , TLabel > [ 'plugins' ] = [ ] ;
53
-
54
- @Input ( { transform : booleanAttribute } ) redraw : string | boolean = false ;
55
-
56
- @Input ( ) type : ChartConfiguration < TType , TData , TLabel > [ 'type' ] = 'bar' as TType ;
57
-
71
+ @Input ( { transform : ( value : string | number ) => numberAttribute ( value , undefined ) } ) height ?: number ;
72
+
73
+ /**
74
+ * ID attribute applied to the rendered canvas.
75
+ * @type string
76
+ */
77
+ @Input ( ) id : string = `c-chartjs-${ nextId ++ } ` ;
78
+
79
+ /**
80
+ * The options object that is passed into the Chart.js chart.
81
+ */
82
+ @Input ( ) options ?: ChartOptions = { } ;
83
+
84
+ /**
85
+ * The plugins array that is passed into the Chart.js chart
86
+ */
87
+ @Input ( ) plugins : Plugin [ ] = [ ] ;
88
+
89
+ /**
90
+ * If true, will tear down and redraw chart on all updates.
91
+ * @type boolean
92
+ * @default false
93
+ */
94
+ @Input ( { transform : booleanAttribute } ) redraw : boolean = false ;
95
+
96
+ /**
97
+ * Chart.js chart type.
98
+ * @type {'line' | 'bar' | 'radar' | 'doughnut' | 'polarArea' | 'bubble' | 'pie' | 'scatter' }
99
+ */
100
+ @Input ( ) type : ChartType = 'bar' ;
101
+
102
+ /**
103
+ * Width attribute applied to the rendered canvas.
104
+ * @type number | undefined
105
+ * @default 300
106
+ */
58
107
@HostBinding ( 'style.width.px' )
59
- @Input ( { transform : ( value : string | number ) => numberAttribute ( value , undefined ) } ) width ?: string | number ;
108
+ @Input ( { transform : ( value : string | number ) => numberAttribute ( value , undefined ) } ) width ?: number ;
60
109
61
- @Input ( ) wrapper = true ;
110
+ /**
111
+ * Put the chart into the wrapper div element.
112
+ * @default true
113
+ */
114
+ @Input ( { transform : booleanAttribute } ) wrapper = true ;
62
115
63
116
@Output ( ) readonly getDatasetAtEvent = new EventEmitter < any > ( ) ;
64
117
@Output ( ) readonly getElementAtEvent = new EventEmitter < any > ( ) ;
@@ -68,7 +121,7 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
68
121
69
122
@ViewChild ( 'canvasElement' ) canvasElement ! : ElementRef ;
70
123
71
- chart ! : Chart < TType , TData , TLabel > ;
124
+ chart ! : ChartJS ;
72
125
ctx ! : CanvasRenderingContext2D ;
73
126
74
127
@HostBinding ( 'class' )
@@ -79,10 +132,9 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
79
132
}
80
133
81
134
constructor (
82
- private elementRef : ElementRef ,
83
- private ngZone : NgZone ,
84
- private renderer : Renderer2 ,
85
- private changeDetectorRef : ChangeDetectorRef
135
+ private readonly ngZone : NgZone ,
136
+ private readonly renderer : Renderer2 ,
137
+ private readonly changeDetectorRef : ChangeDetectorRef
86
138
) {
87
139
// todo: verify afterRender / afterNextRender for chartjs (spec fails with 17.0.10)
88
140
afterRender ( ( ) => {
@@ -110,13 +162,13 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
110
162
return ;
111
163
}
112
164
113
- const datasetAtEvent = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect : true } , false ) ;
165
+ const datasetAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect : true } , false ) ;
114
166
this . getDatasetAtEvent . emit ( datasetAtEvent ) ;
115
167
116
- const elementAtEvent = this . chart . getElementsAtEventForMode ( $event , 'nearest' , { intersect : true } , false ) ;
168
+ const elementAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'nearest' , { intersect : true } , false ) ;
117
169
this . getElementAtEvent . emit ( elementAtEvent ) ;
118
170
119
- const elementsAtEvent = this . chart . getElementsAtEventForMode ( $event , 'index' , { intersect : true } , false ) ;
171
+ const elementsAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'index' , { intersect : true } , false ) ;
120
172
this . getElementsAtEvent . emit ( elementsAtEvent ) ;
121
173
}
122
174
@@ -126,15 +178,15 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
126
178
}
127
179
128
180
public chartRender ( ) {
129
- if ( ! this . canvasElement ?. nativeElement || ! this . ctx ) {
181
+ if ( ! this . canvasElement ?. nativeElement || ! this . ctx || this . chart ) {
130
182
return ;
131
183
}
132
184
133
185
this . ngZone . runOutsideAngular ( ( ) => {
134
186
const config = this . chartConfig ( ) ;
135
187
if ( config ) {
136
- setTimeout ( ( ) => {
137
- this . chart = new Chart ( this . ctx , config ) ;
188
+ this . chart = new ChartJS ( this . ctx , config ) ;
189
+ this . ngZone . run ( ( ) => {
138
190
this . renderer . setStyle ( this . canvasElement . nativeElement , 'display' , 'block' ) ;
139
191
this . changeDetectorRef . markForCheck ( ) ;
140
192
this . chartRef . emit ( this . chart ) ;
@@ -150,13 +202,11 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
150
202
151
203
if ( this . redraw ) {
152
204
this . chartDestroy ( ) ;
153
- setTimeout ( ( ) => {
154
- this . chartRender ( ) ;
155
- } ) ;
205
+ this . chartRender ( ) ;
156
206
return ;
157
207
}
158
208
159
- const config = this . chartConfig ( ) ;
209
+ const config : ChartConfiguration = this . chartConfig ( ) ;
160
210
161
211
if ( this . options ) {
162
212
Object . assign ( this . chart . options ?? { } , config . options ?? { } ) ;
@@ -180,7 +230,9 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
180
230
setTimeout ( ( ) => {
181
231
this . ngZone . runOutsideAngular ( ( ) => {
182
232
this . chart ?. update ( ) ;
183
- this . changeDetectorRef . markForCheck ( ) ;
233
+ this . ngZone . run ( ( ) => {
234
+ this . changeDetectorRef . markForCheck ( ) ;
235
+ } ) ;
184
236
} ) ;
185
237
} ) ;
186
238
}
@@ -189,18 +241,18 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
189
241
return this . chart ?. toBase64Image ( ) ;
190
242
}
191
243
192
- private chartDataConfig ( ) : ChartConfiguration < TType , TData , TLabel > [ 'data' ] {
244
+ private chartDataConfig ( ) : ChartData {
193
245
return {
194
246
labels : this . data ?. labels ?? [ ] ,
195
247
datasets : this . data ?. datasets ?? [ ]
196
248
} ;
197
249
}
198
250
199
- private chartOptions ( ) : ChartConfiguration < TType , TData , TLabel > [ 'options' ] {
200
- return this . options ;
251
+ private chartOptions ( ) : ChartOptions {
252
+ return this . options ?? { } ;
201
253
}
202
254
203
- private chartConfig ( ) : ChartConfiguration < TType , TData , TLabel > {
255
+ private chartConfig ( ) : ChartConfiguration {
204
256
this . chartCustomTooltips ( ) ;
205
257
return {
206
258
data : this . chartDataConfig ( ) ,
@@ -213,9 +265,7 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
213
265
private chartCustomTooltips ( ) {
214
266
if ( this . customTooltips ) {
215
267
const options = this . options ;
216
- // @ts -ignore
217
268
const plugins = this . options ?. plugins ;
218
- // @ts -ignore
219
269
const tooltip = this . options ?. plugins ?. tooltip ;
220
270
this . options = merge ( {
221
271
...options ,
0 commit comments