@@ -22,10 +22,19 @@ import {
2222
2323import merge from 'lodash-es/merge' ;
2424
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' ;
2635import { customTooltips as cuiCustomTooltips } from '@coreui/chartjs' ;
2736
28- Chart . register ( ...registerables ) ;
37+ ChartJS . register ( ...registerables ) ;
2938
3039let nextId = 0 ;
3140
@@ -39,26 +48,70 @@ let nextId = 0;
3948// eslint-disable-next-line @angular-eslint/no-host-metadata-property
4049// host: { ngSkipHydration: 'true' }
4150} )
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+ */
4770 @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+ */
58107 @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 ;
60109
61- @Input ( ) wrapper = true ;
110+ /**
111+ * Put the chart into the wrapper div element.
112+ *@default true
113+ */
114+ @Input ( { transform :booleanAttribute } ) wrapper = true ;
62115
63116 @Output ( ) readonly getDatasetAtEvent = new EventEmitter < any > ( ) ;
64117 @Output ( ) readonly getElementAtEvent = new EventEmitter < any > ( ) ;
@@ -68,7 +121,7 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
68121
69122 @ViewChild ( 'canvasElement' ) canvasElement ! :ElementRef ;
70123
71- chart ! :Chart < TType , TData , TLabel > ;
124+ chart ! :ChartJS ;
72125ctx ! :CanvasRenderingContext2D ;
73126
74127 @HostBinding ( 'class' )
@@ -79,10 +132,9 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
79132}
80133
81134constructor (
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
86138) {
87139// todo: verify afterRender / afterNextRender for chartjs (spec fails with 17.0.10)
88140afterRender ( ( ) => {
@@ -110,13 +162,13 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
110162return ;
111163}
112164
113- const datasetAtEvent = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect :true } , false ) ;
165+ const datasetAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect :true } , false ) ;
114166this . getDatasetAtEvent . emit ( datasetAtEvent ) ;
115167
116- const elementAtEvent = this . chart . getElementsAtEventForMode ( $event , 'nearest' , { intersect :true } , false ) ;
168+ const elementAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'nearest' , { intersect :true } , false ) ;
117169this . getElementAtEvent . emit ( elementAtEvent ) ;
118170
119- const elementsAtEvent = this . chart . getElementsAtEventForMode ( $event , 'index' , { intersect :true } , false ) ;
171+ const elementsAtEvent : InteractionItem [ ] = this . chart . getElementsAtEventForMode ( $event , 'index' , { intersect :true } , false ) ;
120172this . getElementsAtEvent . emit ( elementsAtEvent ) ;
121173}
122174
@@ -126,15 +178,15 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
126178}
127179
128180public chartRender ( ) {
129- if ( ! this . canvasElement ?. nativeElement || ! this . ctx ) {
181+ if ( ! this . canvasElement ?. nativeElement || ! this . ctx || this . chart ) {
130182return ;
131183}
132184
133185this . ngZone . runOutsideAngular ( ( ) => {
134186const config = this . chartConfig ( ) ;
135187if ( config ) {
136- setTimeout ( ( ) => {
137- this . chart = new Chart ( this . ctx , config ) ;
188+ this . chart = new ChartJS ( this . ctx , config ) ;
189+ this . ngZone . run ( ( ) => {
138190this . renderer . setStyle ( this . canvasElement . nativeElement , 'display' , 'block' ) ;
139191this . changeDetectorRef . markForCheck ( ) ;
140192this . chartRef . emit ( this . chart ) ;
@@ -150,13 +202,11 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
150202
151203if ( this . redraw ) {
152204this . chartDestroy ( ) ;
153- setTimeout ( ( ) => {
154- this . chartRender ( ) ;
155- } ) ;
205+ this . chartRender ( ) ;
156206return ;
157207}
158208
159- const config = this . chartConfig ( ) ;
209+ const config : ChartConfiguration = this . chartConfig ( ) ;
160210
161211if ( this . options ) {
162212Object . assign ( this . chart . options ?? { } , config . options ?? { } ) ;
@@ -180,7 +230,9 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
180230setTimeout ( ( ) => {
181231this . ngZone . runOutsideAngular ( ( ) => {
182232this . chart ?. update ( ) ;
183- this . changeDetectorRef . markForCheck ( ) ;
233+ this . ngZone . run ( ( ) => {
234+ this . changeDetectorRef . markForCheck ( ) ;
235+ } ) ;
184236} ) ;
185237} ) ;
186238}
@@ -189,18 +241,18 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
189241return this . chart ?. toBase64Image ( ) ;
190242}
191243
192- private chartDataConfig ( ) :ChartConfiguration < TType , TData , TLabel > [ 'data' ] {
244+ private chartDataConfig ( ) :ChartData {
193245return {
194246labels :this . data ?. labels ?? [ ] ,
195247datasets :this . data ?. datasets ?? [ ]
196248} ;
197249}
198250
199- private chartOptions ( ) :ChartConfiguration < TType , TData , TLabel > [ 'options' ] {
200- return this . options ;
251+ private chartOptions ( ) :ChartOptions {
252+ return this . options ?? { } ;
201253}
202254
203- private chartConfig ( ) :ChartConfiguration < TType , TData , TLabel > {
255+ private chartConfig ( ) :ChartConfiguration {
204256this . chartCustomTooltips ( ) ;
205257return {
206258data :this . chartDataConfig ( ) ,
@@ -213,9 +265,7 @@ export class ChartjsComponent<TType extends ChartType = ChartType, TData = Defau
213265private chartCustomTooltips ( ) {
214266if ( this . customTooltips ) {
215267const options = this . options ;
216- //@ts -ignore
217268const plugins = this . options ?. plugins ;
218- //@ts -ignore
219269const tooltip = this . options ?. plugins ?. tooltip ;
220270this . options = merge ( {
221271 ...options ,