UNPKG

@coreui/angular-chartjs

Version:

Angular wrapper component for Chart.js

250 lines (244 loc) 13.6 kB
import * as i0 from '@angular/core'; import { inject, NgZone, Renderer2, ChangeDetectorRef, input, booleanAttribute, numberAttribute, linkedSignal, output, viewChild, computed, afterRenderEffect, untracked, ChangeDetectionStrategy, Component, NgModule } from '@angular/core'; import merge from 'lodash-es/merge'; import { Chart, registerables } from 'chart.js'; import { customTooltips } from '@coreui/chartjs'; Chart.register(...registerables); let nextId = 0; class ChartjsComponent { // static ngAcceptInputType_redraw; ngZone = inject(NgZone); renderer = inject(Renderer2); changeDetectorRef = inject(ChangeDetectorRef); /** * Enables custom html based tooltips instead of standard tooltips. * @return boolean * @default true */ customTooltips = input(true, { ...(ngDevMode ? { debugName: "customTooltips" } : {}), transform: booleanAttribute }); /** * The data object that is passed into the Chart.js chart (more info). */ data = input(...(ngDevMode ? [undefined, { debugName: "data" }] : [])); /** * A fallback when the canvas cannot be rendered. Can be used for accessible chart descriptions. */ // @Input() fallbackContent?: TemplateRef<any>; /** * Height attribute applied to the rendered canvas. * @return number | undefined * @default null */ height = input(null, { ...(ngDevMode ? { debugName: "height" } : {}), transform: (value) => numberAttribute(value, undefined) }); /** * ID attribute applied to the rendered canvas. * @return string */ idInput = input(`c-chartjs-${nextId++}`, { ...(ngDevMode ? { debugName: "idInput" } : {}), alias: 'id' }); get id() { return this.idInput(); } /** * The options object that is passed into the Chart.js chart. */ optionsInput = input({}, { ...(ngDevMode ? { debugName: "optionsInput" } : {}), alias: 'options' }); options = linkedSignal(this.optionsInput, ...(ngDevMode ? [{ debugName: "options" }] : [])); /** * The plugins array that is passed into the Chart.js chart */ plugins = input([], ...(ngDevMode ? [{ debugName: "plugins" }] : [])); /** * If true, will tear down and redraw chart on all updates. * @return boolean * @default false */ redraw = input(false, { ...(ngDevMode ? { debugName: "redraw" } : {}), transform: booleanAttribute }); /** * Chart.js chart type. * @return {'line' | 'bar' | 'radar' | 'doughnut' | 'polarArea' | 'bubble' | 'pie' | 'scatter'} */ type = input('bar', ...(ngDevMode ? [{ debugName: "type" }] : [])); /** * Width attribute applied to the rendered canvas. * @return number | undefined * @default null */ width = input(null, { ...(ngDevMode ? { debugName: "width" } : {}), transform: (value) => numberAttribute(value, undefined) }); /** * Put the chart into the wrapper div element. * @default true */ wrapper = input(true, { ...(ngDevMode ? { debugName: "wrapper" } : {}), transform: booleanAttribute }); getDatasetAtEvent = output(); getElementAtEvent = output(); getElementsAtEvent = output(); chartRef = output(); canvasElement = viewChild.required('canvasElement'); chart; ctx; hostClasses = computed(() => { return { 'chart-wrapper': this.wrapper() }; }, ...(ngDevMode ? [{ debugName: "hostClasses" }] : [])); constructor() { afterRenderEffect({ read: () => { const canvasElement = this.canvasElement(); this.ctx = canvasElement?.nativeElement?.getContext('2d'); this.chartRender(); } }); } ngOnChanges(changes) { if (changes['data'] && !changes['data'].firstChange) { this.chartUpdate(); } } ngOnDestroy() { this.chartDestroy(); } handleClick($event) { if (!this.chart) { return; } const datasetAtEvent = this.chart.getElementsAtEventForMode($event, 'dataset', { intersect: true }, false); this.getDatasetAtEvent.emit(datasetAtEvent); const elementAtEvent = this.chart.getElementsAtEventForMode($event, 'nearest', { intersect: true }, false); this.getElementAtEvent.emit(elementAtEvent); const elementsAtEvent = this.chart.getElementsAtEventForMode($event, 'index', { intersect: true }, false); this.getElementsAtEvent.emit(elementsAtEvent); } chartDestroy() { this.chart?.destroy(); this.chartRef.emit(undefined); } chartRender() { const canvasElement = this.canvasElement(); if (!canvasElement?.nativeElement || !this.ctx || this.chart) { return; } this.ngZone.runOutsideAngular(() => { const config = this.chartConfig(); if (config) { this.chart = new Chart(this.ctx, config); this.ngZone.run(() => { this.renderer.setStyle(canvasElement.nativeElement, 'display', 'block'); this.changeDetectorRef.markForCheck(); this.chartRef.emit(this.chart); }); } }); } chartUpdate() { if (!this.chart) { return; } if (this.redraw()) { this.chartDestroy(); this.chartRender(); return; } const config = this.chartConfig(); if (this.options()) { Object.assign(this.chart.options ?? {}, config.options ?? {}); } if (!this.chart.config.data) { this.chart.config.data = { ...config.data }; this.chartUpdateOutsideAngular(); } if (this.chart) { Object.assign(this.chart.config.options ?? {}, config.options ?? {}); Object.assign(this.chart.config.plugins ?? [], config.plugins ?? []); Object.assign(this.chart.config.data, config.data); } this.chartUpdateOutsideAngular(); } chartUpdateOutsideAngular() { setTimeout(() => { this.ngZone.runOutsideAngular(() => { this.chart?.update(); this.ngZone.run(() => { this.changeDetectorRef.markForCheck(); }); }); }); } chartToBase64Image() { return this.chart?.toBase64Image(); } chartDataConfig = computed(() => { const { labels, datasets } = { ...this.data() }; return { labels: labels ?? [], datasets: datasets ?? [] }; }, ...(ngDevMode ? [{ debugName: "chartDataConfig" }] : [])); chartOptions = computed(() => this.options() ?? {}, ...(ngDevMode ? [{ debugName: "chartOptions" }] : [])); chartConfig = computed(() => { this.chartCustomTooltips(); return { data: this.chartDataConfig(), options: this.chartOptions(), plugins: this.plugins(), type: this.type() }; }, ...(ngDevMode ? [{ debugName: "chartConfig" }] : [])); chartCustomTooltips() { if (this.customTooltips()) { const options = this.options(); const plugins = options?.plugins; const tooltip = options?.plugins?.tooltip; untracked(() => { this.options.set(merge({ ...options, plugins: { ...plugins, tooltip: { ...tooltip, enabled: false, mode: 'index', position: 'nearest', external: customTooltips } } })); }); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartjsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: ChartjsComponent, isStandalone: true, selector: "c-chart", inputs: { customTooltips: { classPropertyName: "customTooltips", publicName: "customTooltips", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, idInput: { classPropertyName: "idInput", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, optionsInput: { classPropertyName: "optionsInput", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, redraw: { classPropertyName: "redraw", publicName: "redraw", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, wrapper: { classPropertyName: "wrapper", publicName: "wrapper", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { getDatasetAtEvent: "getDatasetAtEvent", getElementAtEvent: "getElementAtEvent", getElementsAtEvent: "getElementsAtEvent", chartRef: "chartRef" }, host: { properties: { "class": "hostClasses()", "style.height.px": "height()", "style.width.px": "width()" } }, viewQueries: [{ propertyName: "canvasElement", first: true, predicate: ["canvasElement"], descendants: true, isSignal: true }], exportAs: ["cChart"], usesOnChanges: true, ngImport: i0, template: "<canvas\n #canvasElement\n (click)=\"handleClick($event)\"\n [height]=\"height() || null\"\n [id]=\"id\"\n [width]=\"width() || null\"\n role=\"img\"\n style=\"display: none;\"\n>\n <ng-content />\n <!-- <ng-container *ngTemplateOutlet=\"fallbackContent\"/>-->\n</canvas>\n", styles: [":host.chart-wrapper{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartjsComponent, decorators: [{ type: Component, args: [{ selector: 'c-chart', exportAs: 'cChart', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()', '[style.height.px]': 'height()', '[style.width.px]': 'width()' }, template: "<canvas\n #canvasElement\n (click)=\"handleClick($event)\"\n [height]=\"height() || null\"\n [id]=\"id\"\n [width]=\"width() || null\"\n role=\"img\"\n style=\"display: none;\"\n>\n <ng-content />\n <!-- <ng-container *ngTemplateOutlet=\"fallbackContent\"/>-->\n</canvas>\n", styles: [":host.chart-wrapper{display:block}\n"] }] }], ctorParameters: () => [], propDecorators: { customTooltips: [{ type: i0.Input, args: [{ isSignal: true, alias: "customTooltips", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], idInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], optionsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], redraw: [{ type: i0.Input, args: [{ isSignal: true, alias: "redraw", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], wrapper: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrapper", required: false }] }], getDatasetAtEvent: [{ type: i0.Output, args: ["getDatasetAtEvent"] }], getElementAtEvent: [{ type: i0.Output, args: ["getElementAtEvent"] }], getElementsAtEvent: [{ type: i0.Output, args: ["getElementsAtEvent"] }], chartRef: [{ type: i0.Output, args: ["chartRef"] }], canvasElement: [{ type: i0.ViewChild, args: ['canvasElement', { isSignal: true }] }] } }); class ChartjsModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartjsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ChartjsModule, imports: [ChartjsComponent], exports: [ChartjsComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartjsModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartjsModule, decorators: [{ type: NgModule, args: [{ imports: [ ChartjsComponent ], exports: [ ChartjsComponent ] }] }] }); /* * Public API Surface of coreui-angular-chartjs */ /** * Generated bundle index. Do not edit. */ export { ChartjsComponent, ChartjsModule }; //# sourceMappingURL=coreui-angular-chartjs.mjs.map