ng-chartjs
Version:
This is a Angular chart.js library.
606 lines (596 loc) • 23.2 kB
JavaScript
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