UNPKG

ng-chartjs

Version:
606 lines (596 loc) 23.2 kB
import * as i0 from '@angular/core'; import { Injectable, EventEmitter, Directive, Input, Output, NgModule } from '@angular/core'; import Chart$1 from 'chart.js/auto'; import { Chart } from 'chart.js'; class StoreService { constructor() { this._chartInstances = []; this._chartId = []; } addChart(id, chart) { for (let i = 0; i < this._chartId.length; i++) { if (id === this._chartId[i]) { return; } } this._chartId.push(id); this._chartInstances.push(chart); } removeChart(id) { for (let i = 0; i < this._chartId.length; i++) { if (id === this._chartId[i]) { this._chartId.splice(i, 1); this._chartInstances.splice(i, 1); // delete chart instance. } } } getChart(id) { for (let i = 0; i < this._chartId.length; i++) { if (id === this._chartId[i]) { return this._chartInstances[i]; } } return null; } } StoreService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: StoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); StoreService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: StoreService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: StoreService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class NgChartjsCustomPluginConfig { constructor() { this.plugins = []; } } NgChartjsCustomPluginConfig.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsCustomPluginConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); NgChartjsCustomPluginConfig.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsCustomPluginConfig, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsCustomPluginConfig, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class NgChartjsService { constructor(storeService, pluginConfig) { this.storeService = storeService; this.pluginConfig = pluginConfig; if (pluginConfig.plugins.length !== 0) { Chart.register(...pluginConfig.plugins); } } // get chart instance by id getChart(id) { return this.storeService.getChart(id); } } NgChartjsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsService, deps: [{ token: StoreService }, { token: NgChartjsCustomPluginConfig }], target: i0.ɵɵFactoryTarget.Injectable }); NgChartjsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: StoreService }, { type: NgChartjsCustomPluginConfig }]; } }); /** * 深复制一个json对象 * @source 需要深复制的对象 */ function deepCopyJson(source) { if (!source || typeof source !== 'object') { return source; } const newObj = source.constructor === Array ? [] : {}; for (const key in source) { if (typeof source[key] === 'object') { // @ts-ignore newObj[key] = deepCopyJson(source[key]); } else { // @ts-ignore newObj[key] = source[key]; } } return newObj; } /** * 合并json对象,遇到相同元素级属性,以source为准 * @source 被合并的json对象 * @dest json对象,将此json的属性递归赋值给source */ function mergeJson(source, dest) { if (!dest) { return source; } source = source || {}; for (const key of Object.keys(dest)) { if (source[key] === undefined) { source[key] = deepCopyJson(dest[key]); continue; } // 冲突了,如果是Object,看看有么有不冲突的属性 // 不是Object 则以main为主,忽略即可。故不需要else if (isJson(dest[key])) { // arguments.callee 递归调用,并且与函数名解耦 mergeJson(source[key], dest[key]); } } return source; } /** * 是否是json对象 * @target 需要被判断的类型 */ function isJson(target) { if (target === null || target === undefined) { return false; } return typeof target === 'object' && target.constructor === Object; } // Default colors const DefaultColors = [ [63, 81, 181], [0, 150, 136], [255, 152, 0], [233, 30, 99], [156, 39, 176], [0, 188, 212], [3, 169, 244], [96, 125, 139], [255, 193, 7], [37, 155, 36], [205, 220, 57], [86, 119, 252] // Blue ]; function rgba(colour, alpha) { return 'rgba(' + colour.concat(alpha).join(',') + ')'; } function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function formatLineColor(colors) { return { backgroundColor: rgba(colors, 0.35), borderColor: rgba(colors, 1), pointBackgroundColor: rgba(colors, 1), pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: rgba(colors, 0.95) }; } function formatBarColor(colors) { return { backgroundColor: rgba(colors, 0.75), borderColor: rgba(colors, 1), hoverBackgroundColor: rgba(colors, 1), hoverBorderColor: rgba(colors, 1) }; } function formatPieColors(colors) { return { backgroundColor: colors.map((color) => rgba(color, 0.76)), borderColor: colors.map(() => '#fff'), pointBackgroundColor: colors.map((color) => rgba(color, 1)), pointBorderColor: colors.map(() => '#fff'), pointHoverBackgroundColor: colors.map((color) => rgba(color, 1)), pointHoverBorderColor: colors.map((color) => rgba(color, 1)) }; } function formatPolarAreaColors(colors) { return { backgroundColor: colors.map((color) => rgba(color, 0.75)), borderColor: colors.map((color) => rgba(color, 1)), hoverBackgroundColor: colors.map((color) => rgba(color, 1)), hoverBorderColor: colors.map((color) => rgba(color, 1)) }; } function getRandomColor() { return [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)]; } /** * @method Generate colors for line|bar charts * @param index he index of the default color array. eg. 0, 1 * @return number[] * @author vincent 2019-01-22 * @version 0.0.0 * @example * @log 1. vincent,2019-01-22,done */ function generateColor(index) { return DefaultColors[index] || getRandomColor(); } /** * @method Generate colors for pie|doughnut charts * @param count Length of data. eg. datasets.data.length * @return number[][] * @author vincent 2019-01-22 * @version 0.0.0 * @example * @log 1. vincent,2019-01-22,done */ function generateColors(count) { const colorsArr = new Array(count); for (let i = 0; i < count; i++) { colorsArr[i] = DefaultColors[i] || getRandomColor(); } return colorsArr; } /** * @method Generate colors by chart type * @param chartType The type of chart you are using. eg. line,bar * @param index The index of the default color array. eg. DefaultColors[index] * @param count Length of data. eg. datasets.data.length * @return Color or number[] * @author vincent 2019-01-22 * @version 0.0.0 * @example * @log 1. vincent,2019-01-22,done */ function getColors(chartType, index, count) { if (chartType === 'pie' || chartType === 'doughnut' || chartType === 'bubble' || chartType === 'scatter') { return formatPieColors(generateColors(count)); } if (chartType === 'polarArea') { return formatPolarAreaColors(generateColors(count)); } if (chartType === 'line' || chartType === 'radar') { return formatLineColor(generateColor(index)); } if (chartType === 'bar' || chartType === 'horizontalBar') { return formatBarColor(generateColor(index)); } return generateColor(index); } /* tslint:disable-next-line */ class NgChartjsDirective { constructor(element, ngChartjsService, storeService, zone) { this.ngChartjsService = ngChartjsService; this.storeService = storeService; this.zone = zone; // x轴标签。这对图表来说是必要的:线,条和雷达。并且只是图表的标签(悬停):polarArea,pie和doughnut this.labels = []; this.noZone = true; // disable angular NgZone // @ts-ignore this.id = null; // chart instance id // 鼠标点击图表所有的区域 this.chartClick = new EventEmitter(); // 鼠标悬浮在标签或者活跃的点上面时 this.chartHover = new EventEmitter(); this.initFlag = false; this.hasChanges = false; this.element = element; // 获取指令所在canvas元素 } ngOnInit() { this.ctx = this.element.nativeElement.getContext('2d'); // 获取元素的ctx this.initFlag = true; // 是否初始化了的标志 if (this.data || this.datasets) { // 判断data和datasets有一个有数据就刷新 if (this.noZone) { this.zone.runOutsideAngular(() => { this.refresh(); }); } else { this.refresh(); } } } ngOnChanges(changes) { // TODO: 插件变化刷新,开放刷新按钮 if (this.initFlag) { // Check if the changes are in the data or datasets if (changes.hasOwnProperty('data') || changes.hasOwnProperty('datasets')) { if (changes.data) { this.updateChartData(changes.data.currentValue); } else { this.updateChartData(changes.datasets.currentValue); } this.hasChanges = true; } if (changes.hasOwnProperty('labels')) { this.chart.data.labels = changes.labels.currentValue; this.hasChanges = true; } if (changes.hasOwnProperty('legend')) { if (changes.legend.currentValue !== changes.legend.previousValue) { // @ts-ignore this.chart.options.plugins.legend.display = changes.legend.currentValue; this.hasChanges = true; } } if (changes.hasOwnProperty('adding')) { this.addData_(changes.adding.currentValue.labels, changes.adding.currentValue.data); this.hasChanges = true; } if (changes.hasOwnProperty('removing')) { if (changes.removing.currentValue.orientation === 'oldest' || changes.removing.currentValue.orientation === 'latest') { this.removeData_(changes.removing.currentValue.orientation); this.hasChanges = true; } } if (changes.hasOwnProperty('chartType')) { this.refresh(); } if (changes.hasOwnProperty('resetOption')) { const resetOption = deepCopyJson(changes.resetOption.currentValue); this.chart.options = mergeJson(resetOption, this.chart.options); this.hasChanges = true; } if (this.hasChanges) { this.chart.update(); this.hasChanges = false; } // change chart id if (changes.hasOwnProperty('id')) { this.removeChart(changes.id.previousValue); this.addChart(changes.id.currentValue); } } } ngOnDestroy() { if (this.chart) { this.chart.destroy(); // @ts-ignore this.chart = void 0; this.removeChart(this.id); } } // update chartjs update() { this.chart.update(); } // Dynamic add data addData(labels, data) { this.addData_(labels, data); this.update(); } // Dynamic remove data, orientation is 'ildest' or 'latest' removeData(orientation) { this.removeData_(orientation); this.update(); } refresh() { this.ngOnDestroy(); this.chart = this.getChartBuilder(this.ctx /*, data, this.options*/); this.addChart(this.id); } removeChart(id) { if (this.element.nativeElement.hasAttribute('id')) { this.storeService.removeChart(this.element.nativeElement.id); return; } if (id !== null && id !== undefined) { this.storeService.removeChart(id); // delete chart instance. } } addChart(id) { if (this.element.nativeElement.hasAttribute('id')) { this.storeService.addChart(this.element.nativeElement.id, this.chart); return; } if (id !== null && id !== undefined) { this.storeService.addChart(id, this.chart); } } updateChartData(newDataValues) { if (Array.isArray(newDataValues[0].data)) { // @ts-ignore // this.chart.data.datasets.forEach((dataset: ChartDataset, i: number) => { // dataset.data = newDataValues[i].data; // if (newDataValues[i].label) { // dataset.label = newDataValues[i].label; // } // }); // @ts-ignore this.chart.data.datasets = newDataValues; } else { // @ts-ignore this.chart.data.datasets[0].data = newDataValues; } // update colors // @ts-ignore this.chart.data.datasets = this.updateColors(this.chart.data.datasets); } getChartBuilder(ctx /*, data:Array<any>, options:any*/) { const datasets = this.getDatasets(); let options = this.options || {}; options = Object.assign({}, this.options); // 深复制options mergeJson(options, { plugins: { legend: { display: this.legend } } }); // hock for onHover and onClick events options.hover = options.hover || {}; if (!options.onHover) { options.onHover = (event, active) => { if (active && !active.length) { return; } this.chartHover.emit({ event, active }); }; } if (!options.onClick) { options.onClick = (event, active) => { this.chartClick.emit({ event, active }); }; } const inlinePlugins = this.inlinePlugins || []; const opts = { type: this.chartType, data: { labels: this.labels, datasets: datasets // TODO: 后续更改这个属性名字,否则警告 }, options: options, plugins: inlinePlugins }; return new Chart$1(ctx, opts); } // 获取 chart.js的datasets数据 getDatasets() { // @ts-ignore let datasets = void 0; // in case if datasets is not provided, but data is present if (!this.datasets || !this.datasets.length && (this.data && this.data.length)) { if (Array.isArray(this.data[0])) { datasets = this.data.map((data, index) => { return { data, label: this.labels[index] || `Label ${index}` }; }); } else { datasets = [{ data: this.data, label: `Label 0` }]; } } datasets = this.updateColors(datasets); // update colors if (!datasets) { throw new Error(`ng-chartjs configuration error, data or datasets field are required to render char ${this.chartType}`); } return datasets; } // update dataset colors updateColors(datasets) { if (this.datasets && this.datasets.length || (datasets && datasets.length)) { // fix elm type, pre type is number datasets = (this.datasets || datasets).map((elm, index) => { const newElm = Object.assign({}, elm); if (this.colors && this.colors.length) { Object.assign(newElm, this.colors[index]); } else { // @ts-ignore Object.assign(newElm, getColors(this.chartType, index, newElm.data.length)); } return newElm; }); } return datasets; } addData_(labels, data) { if (labels.length === 0 || data.length === 0) { return; } // update labels // @ts-ignore labels.forEach((label) => { this.chart.data.labels.push(label); }); // @ts-ignore this.chart.data.datasets.forEach((dataset, index) => { if (data[index]) { for (let i = 0; i < data[index].length; i++) { // @ts-ignore dataset.data.push(data[index][i]); } } else { console.log('The added data does not match the original data'); return; } }); } removeData_(orientation) { // fix: support to oldest feature if (orientation === 'latest') { // @ts-ignore this.chart.data.labels.pop(); // @ts-ignore this.chart.data.datasets.forEach((dataset) => { // @ts-ignore dataset.data.pop(); }); } else if (orientation === 'oldest') { // @ts-ignore this.chart.data.labels.shift(); // @ts-ignore this.chart.data.datasets.forEach((dataset) => { // @ts-ignore dataset.data.shift(); }); } } } NgChartjsDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsDirective, deps: [{ token: i0.ElementRef }, { token: NgChartjsService }, { token: StoreService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); NgChartjsDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: NgChartjsDirective, selector: "canvas[ngChartjs]", inputs: { data: "data", datasets: "datasets", labels: "labels", options: "options", inlinePlugins: "inlinePlugins", chartType: "chartType", colors: "colors", legend: "legend", adding: "adding", removing: "removing", resetOption: "resetOption", noZone: "noZone", id: "id" }, outputs: { chartClick: "chartClick", chartHover: "chartHover" }, exportAs: ["ngChartjs"], usesOnChanges: true, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsDirective, decorators: [{ type: Directive, args: [{ selector: 'canvas[ngChartjs]', exportAs: 'ngChartjs' }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: NgChartjsService }, { type: StoreService }, { type: i0.NgZone }]; }, propDecorators: { data: [{ type: Input }], datasets: [{ type: Input }], labels: [{ type: Input }], options: [{ type: Input }], inlinePlugins: [{ type: Input }], chartType: [{ type: Input }], colors: [{ type: Input }], legend: [{ type: Input }], adding: [{ type: Input }], removing: [{ type: Input }], resetOption: [{ type: Input }], noZone: [{ type: Input }], id: [{ type: Input }], chartClick: [{ type: Output }], chartHover: [{ type: Output }] } }); class NgChartjsModule { /** * Register a plugin. * @param plugins */ static registerPlugin(plugins = []) { const config = new NgChartjsCustomPluginConfig(); config.plugins = plugins; return { ngModule: NgChartjsModule, providers: [ { provide: NgChartjsCustomPluginConfig, useValue: config } ] }; } } NgChartjsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NgChartjsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsModule, declarations: [NgChartjsDirective], exports: [NgChartjsDirective] }); NgChartjsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsModule, providers: [NgChartjsService] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgChartjsModule, decorators: [{ type: NgModule, args: [{ imports: [], declarations: [NgChartjsDirective], exports: [NgChartjsDirective], providers: [NgChartjsService] }] }] }); /* * Public API Surface of ng-chartjs */ /** * Generated bundle index. Do not edit. */ export { DefaultColors, NgChartjsDirective, NgChartjsModule, NgChartjsService, deepCopyJson, formatBarColor, formatLineColor, formatPieColors, formatPolarAreaColors, generateColor, generateColors, getColors, getRandomColor, getRandomInt, isJson, mergeJson, rgba }; //# sourceMappingURL=ng-chartjs.mjs.map