diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index 9c89658357..21eb869af0 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -1,4 +1,5 @@ import { AccountConfigModule } from '@abp/ng.account/config'; +import { ChartModule } from '@abp/ng.components/chart.js'; import { CoreModule } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; import { IdentityConfigModule } from '@abp/ng.identity/config'; @@ -31,6 +32,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider'; TenantManagementConfigModule.forRoot(), SettingManagementConfigModule.forRoot(), ThemeBasicModule.forRoot(), + ChartModule.forRoot() ], providers: [APP_ROUTE_PROVIDER], declarations: [AppComponent], diff --git a/npm/ng-packs/packages/components/chart.js/ng-package.json b/npm/ng-packs/packages/components/chart.js/ng-package.json new file mode 100644 index 0000000000..cc98fd4e36 --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/packages/components/chart.js", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/components/chart.js/src/chart.component.html b/npm/ng-packs/packages/components/chart.js/src/chart.component.html new file mode 100644 index 0000000000..211614c90d --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/src/chart.component.html @@ -0,0 +1,11 @@ +
+ +
diff --git a/npm/ng-packs/packages/components/chart.js/src/chart.component.ts b/npm/ng-packs/packages/components/chart.js/src/chart.component.ts new file mode 100644 index 0000000000..499b0b5e33 --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/src/chart.component.ts @@ -0,0 +1,141 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + Input, + OnDestroy, + Output +} from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { chartJsLoaded$ } from './widget-utils'; +declare const Chart: any; + +@Component({ + selector: 'abp-chart', + templateUrl: './chart.component.html', +}) +export class ChartComponent implements AfterViewInit, OnDestroy { + @Input() type: string; + + @Input() options: any = {}; + + @Input() plugins: any[] = []; + + @Input() width: string; + + @Input() height: string; + + @Input() responsive = true; + + // eslint-disable-next-line @angular-eslint/no-output-on-prefix + @Output() readonly onDataSelect: EventEmitter = new EventEmitter(); + + @Output() readonly initialized = new BehaviorSubject(this); + + private _initialized: boolean; + + _data: any; + + chart: any; + + constructor(public el: ElementRef, private cdRef: ChangeDetectorRef) {} + + @Input() get data(): any { + return this._data; + } + + set data(val: any) { + this._data = val; + this.reinit(); + } + + get canvas() { + return this.el.nativeElement.children[0].children[0]; + } + + get base64Image() { + return this.chart.toBase64Image(); + } + + ngAfterViewInit() { + chartJsLoaded$.subscribe(() => { + this.testChartJs(); + + this.initChart(); + this._initialized = true; + }); + } + + testChartJs() { + try { + Chart; + } catch (error) { + throw new Error(`Chart is not found. Import the Chart from app.module like shown below: + import('chart.js'); + `); + } + } + + onCanvasClick = event => { + if (this.chart) { + const element = this.chart.getElementAtEvent(event); + const dataset = this.chart.getDatasetAtEvent(event); + if (element && element.length && dataset) { + this.onDataSelect.emit({ + originalEvent: event, + element: element[0], + dataset, + }); + } + } + }; + + initChart = () => { + const opts = this.options || {}; + opts.responsive = this.responsive; + + // allows chart to resize in responsive mode + if (opts.responsive && (this.height || this.width)) { + opts.maintainAspectRatio = false; + } + + this.chart = new Chart(this.canvas, { + type: this.type, + data: this.data, + options: this.options, + plugins: this.plugins, + }); + + this.cdRef.detectChanges(); + }; + + generateLegend = () => { + if (this.chart) { + return this.chart.generateLegend(); + } + }; + + refresh = () => { + if (this.chart) { + this.chart.update(); + this.cdRef.detectChanges(); + } + }; + + reinit = () => { + if (this.chart) { + this.chart.destroy(); + this.initChart(); + } + }; + + ngOnDestroy() { + if (this.chart) { + this.chart.destroy(); + this._initialized = false; + this.chart = null; + } + } +} diff --git a/npm/ng-packs/packages/components/chart.js/src/chart.module.ts b/npm/ng-packs/packages/components/chart.js/src/chart.module.ts new file mode 100644 index 0000000000..47e422174c --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/src/chart.module.ts @@ -0,0 +1,29 @@ +import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; +import { ChartComponent } from './chart.component'; + +declare const Chart: any + +@NgModule({ + imports: [], + exports: [], + declarations: [ChartComponent], + providers: [], +}) +export class ChartModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: ChartModule, + providers: [{ + provide: APP_INITIALIZER, + multi: true, + useFactory: (injector: Injector) => () => { + import('chart.js/auto').then((module) => { + console.dir(module) + }) + return Promise.resolve() + }, + deps: [Injector] + }] + } + } +} diff --git a/npm/ng-packs/packages/components/chart.js/src/public-api.ts b/npm/ng-packs/packages/components/chart.js/src/public-api.ts new file mode 100644 index 0000000000..399f89f358 --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/src/public-api.ts @@ -0,0 +1,4 @@ +export * from './chart.component'; +export * from './chart.module'; +export * from './widget-utils'; + diff --git a/npm/ng-packs/packages/components/chart.js/src/widget-utils.ts b/npm/ng-packs/packages/components/chart.js/src/widget-utils.ts new file mode 100644 index 0000000000..dfe81c973c --- /dev/null +++ b/npm/ng-packs/packages/components/chart.js/src/widget-utils.ts @@ -0,0 +1,16 @@ +import { ReplaySubject } from 'rxjs'; + +export function getRandomBackgroundColor(count) { + const colors = []; + + for (let i = 0; i < count; i++) { + const r = ((i + 5) * (i + 5) * 474) % 255; + const g = ((i + 5) * (i + 5) * 1600) % 255; + const b = ((i + 5) * (i + 5) * 84065) % 255; + colors.push('rgba(' + r + ', ' + g + ', ' + b + ', 0.7)'); + } + + return colors; +} + +export const chartJsLoaded$ = new ReplaySubject(1); diff --git a/npm/ng-packs/tsconfig.base.json b/npm/ng-packs/tsconfig.base.json index 415fcd2ce5..674468c130 100644 --- a/npm/ng-packs/tsconfig.base.json +++ b/npm/ng-packs/tsconfig.base.json @@ -17,6 +17,7 @@ "@abp/ng.account.core": ["packages/account-core/src/public-api.ts"], "@abp/ng.account/config": ["packages/account/config/src/public-api.ts"], "@abp/ng.components": ["packages/components/src/public-api.ts"], + "@abp/ng.components/chart.js": ["packages/components/chart.js/src/public-api.ts"], "@abp/ng.components/page": ["packages/components/page/src/public-api.ts"], "@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"], "@abp/ng.core": ["packages/core/src/public-api.ts"], diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index dbc51cc020..0af1a56c06 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -4945,6 +4945,11 @@ chart.js@^2.9.3: chartjs-color "^2.1.0" moment "^2.10.2" +chart.js@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.5.1.tgz#73e24d23a4134a70ccdb5e79a917f156b6f3644a" + integrity sha512-m5kzt72I1WQ9LILwQC4syla/LD/N413RYv2Dx2nnTkRS9iv/ey1xLTt0DnPc/eWV4zI+BgEgDYBIzbQhZHc/PQ== + chartjs-color-string@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"