ng2-charts
Version:
Reactive, responsive, beautiful charts for Angular2 based on Chart.js
1,512 lines (1,504 loc) • 56.1 kB
JavaScript
import { Injectable, ɵɵdefineInjectable, EventEmitter, Directive, ElementRef, Input, Output, NgModule } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { cloneDeep } from 'lodash-es';
import { pluginService, Chart as Chart$1 } from 'chart.js';
/**
* @fileoverview added by tsickle
* Generated from: lib/default-colors.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const defaultColors = [
[255, 99, 132],
[54, 162, 235],
[255, 206, 86],
[231, 233, 237],
[75, 192, 192],
[151, 187, 205],
[220, 220, 220],
[247, 70, 74],
[70, 191, 189],
[253, 180, 92],
[148, 159, 177],
[77, 83, 96]
];
/**
* @fileoverview added by tsickle
* Generated from: lib/get-colors.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Generate colors by chart type
* @param {?} chartType
* @param {?} index
* @param {?} count
* @return {?}
*/
function getColors(chartType, index, count) {
if (chartType === 'pie' || chartType === 'doughnut') {
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));
}
if (chartType === 'bubble') {
return formatPieColors(generateColors(count));
}
if (chartType === 'scatter') {
return formatPieColors(generateColors(count));
}
throw new Error(`getColors - Unsupported chart type ${chartType}`);
}
/**
* @param {?} colour
* @param {?} alpha
* @return {?}
*/
function rgba(colour, alpha) {
return 'rgba(' + colour.concat(alpha).join(',') + ')';
}
/**
* @param {?} min
* @param {?} max
* @return {?}
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* @param {?} colors
* @return {?}
*/
function formatLineColor(colors) {
return {
backgroundColor: rgba(colors, 0.4),
borderColor: rgba(colors, 1),
pointBackgroundColor: rgba(colors, 1),
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: rgba(colors, 0.8)
};
}
/**
* @param {?} colors
* @return {?}
*/
function formatBarColor(colors) {
return {
backgroundColor: rgba(colors, 0.6),
borderColor: rgba(colors, 1),
hoverBackgroundColor: rgba(colors, 0.8),
hoverBorderColor: rgba(colors, 1)
};
}
/**
* @param {?} colors
* @return {?}
*/
function formatPieColors(colors) {
return {
backgroundColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 0.6))),
borderColor: colors.map((/**
* @return {?}
*/
() => '#fff')),
pointBackgroundColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 1))),
pointBorderColor: colors.map((/**
* @return {?}
*/
() => '#fff')),
pointHoverBackgroundColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 1))),
pointHoverBorderColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 1)))
};
}
/**
* @param {?} colors
* @return {?}
*/
function formatPolarAreaColors(colors) {
return {
backgroundColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 0.6))),
borderColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 1))),
hoverBackgroundColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 0.8))),
hoverBorderColor: colors.map((/**
* @param {?} color
* @return {?}
*/
(color) => rgba(color, 1)))
};
}
/**
* @return {?}
*/
function getRandomColor() {
return [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
}
/**
* Generate colors for line|bar charts
* @param {?} index
* @return {?}
*/
function generateColor(index) {
return defaultColors[index] || getRandomColor();
}
/**
* Generate colors for pie|doughnut charts
* @param {?} count
* @return {?}
*/
function generateColors(count) {
/** @type {?} */
const colorsArr = new Array(count);
for (let i = 0; i < count; i++) {
colorsArr[i] = defaultColors[i] || getRandomColor();
}
return colorsArr;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/theme.service.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class ThemeService {
constructor() {
this.pColorschemesOptions = {};
this.colorschemesOptions = new BehaviorSubject({});
}
/**
* @param {?} options
* @return {?}
*/
setColorschemesOptions(options) {
this.pColorschemesOptions = options;
this.colorschemesOptions.next(options);
}
/**
* @return {?}
*/
getColorschemesOptions() {
return this.pColorschemesOptions;
}
}
ThemeService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
ThemeService.ctorParameters = () => [];
/** @nocollapse */ ThemeService.ɵprov = ɵɵdefineInjectable({ factory: function ThemeService_Factory() { return new ThemeService(); }, token: ThemeService, providedIn: "root" });
if (false) {
/**
* @type {?}
* @private
*/
ThemeService.prototype.pColorschemesOptions;
/** @type {?} */
ThemeService.prototype.colorschemesOptions;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/base-chart.directive.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function OldState() { }
if (false) {
/** @type {?} */
OldState.prototype.dataExists;
/** @type {?} */
OldState.prototype.dataLength;
/** @type {?} */
OldState.prototype.datasetsExists;
/** @type {?} */
OldState.prototype.datasetsLength;
/** @type {?} */
OldState.prototype.datasetsDataObjects;
/** @type {?} */
OldState.prototype.datasetsDataLengths;
/** @type {?} */
OldState.prototype.colorsExists;
/** @type {?} */
OldState.prototype.colors;
/** @type {?} */
OldState.prototype.labelsExist;
/** @type {?} */
OldState.prototype.labels;
/** @type {?} */
OldState.prototype.legendExists;
/** @type {?} */
OldState.prototype.legend;
}
/** @enum {number} */
const UpdateType = {
Default: 0,
Update: 1,
Refresh: 2,
};
UpdateType[UpdateType.Default] = 'Default';
UpdateType[UpdateType.Update] = 'Update';
UpdateType[UpdateType.Refresh] = 'Refresh';
class BaseChartDirective {
/**
* @param {?} element
* @param {?} themeService
*/
constructor(element, themeService) {
this.element = element;
this.themeService = themeService;
this.options = {};
this.chartClick = new EventEmitter();
this.chartHover = new EventEmitter();
this.old = {
dataExists: false,
dataLength: 0,
datasetsExists: false,
datasetsLength: 0,
datasetsDataObjects: [],
datasetsDataLengths: [],
colorsExists: false,
colors: [],
labelsExist: false,
labels: [],
legendExists: false,
legend: {},
};
this.subs = [];
}
/**
* Register a plugin.
* @param {?} plugin
* @return {?}
*/
static registerPlugin(plugin) {
pluginService.register(plugin);
}
/**
* @param {?} plugin
* @return {?}
*/
static unregisterPlugin(plugin) {
pluginService.unregister(plugin);
}
/**
* @return {?}
*/
ngOnInit() {
this.ctx = this.element.nativeElement.getContext('2d');
this.refresh();
this.subs.push(this.themeService.colorschemesOptions.subscribe((/**
* @param {?} r
* @return {?}
*/
r => this.themeChanged(r))));
}
/**
* @private
* @param {?} options
* @return {?}
*/
themeChanged(options) {
this.refresh();
}
/**
* @return {?}
*/
ngDoCheck() {
if (!this.chart) {
return;
}
/** @type {?} */
let updateRequired = UpdateType.Default;
/** @type {?} */
const wantUpdate = (/**
* @param {?} x
* @return {?}
*/
(x) => {
updateRequired = x > updateRequired ? x : updateRequired;
});
if (!!this.data !== this.old.dataExists) {
this.propagateDataToDatasets(this.data);
this.old.dataExists = !!this.data;
wantUpdate(UpdateType.Update);
}
if (this.data && this.data.length !== this.old.dataLength) {
this.old.dataLength = this.data && this.data.length || 0;
wantUpdate(UpdateType.Update);
}
if (!!this.datasets !== this.old.datasetsExists) {
this.old.datasetsExists = !!this.datasets;
wantUpdate(UpdateType.Update);
}
if (this.datasets && this.datasets.length !== this.old.datasetsLength) {
this.old.datasetsLength = this.datasets && this.datasets.length || 0;
wantUpdate(UpdateType.Update);
}
if (this.datasets && this.datasets.filter((/**
* @param {?} x
* @param {?} i
* @return {?}
*/
(x, i) => x.data !== this.old.datasetsDataObjects[i])).length) {
this.old.datasetsDataObjects = this.datasets.map((/**
* @param {?} x
* @return {?}
*/
x => x.data));
wantUpdate(UpdateType.Update);
}
if (this.datasets && this.datasets.filter((/**
* @param {?} x
* @param {?} i
* @return {?}
*/
(x, i) => x.data.length !== this.old.datasetsDataLengths[i])).length) {
this.old.datasetsDataLengths = this.datasets.map((/**
* @param {?} x
* @return {?}
*/
x => x.data.length));
wantUpdate(UpdateType.Update);
}
if (!!this.colors !== this.old.colorsExists) {
this.old.colorsExists = !!this.colors;
this.updateColors();
wantUpdate(UpdateType.Update);
}
// This smells of inefficiency, might need to revisit this
if (this.colors && this.colors.filter((/**
* @param {?} x
* @param {?} i
* @return {?}
*/
(x, i) => !this.colorsEqual(x, this.old.colors[i]))).length) {
this.old.colors = this.colors.map((/**
* @param {?} x
* @return {?}
*/
x => this.copyColor(x)));
this.updateColors();
wantUpdate(UpdateType.Update);
}
if (!!this.labels !== this.old.labelsExist) {
this.old.labelsExist = !!this.labels;
wantUpdate(UpdateType.Update);
}
if (this.labels && this.labels.filter((/**
* @param {?} x
* @param {?} i
* @return {?}
*/
(x, i) => !this.labelsEqual(x, this.old.labels[i]))).length) {
this.old.labels = this.labels.map((/**
* @param {?} x
* @return {?}
*/
x => this.copyLabel(x)));
wantUpdate(UpdateType.Update);
}
if (!!this.options.legend !== this.old.legendExists) {
this.old.legendExists = !!this.options.legend;
wantUpdate(UpdateType.Refresh);
}
if (this.options.legend && this.options.legend.position !== this.old.legend.position) {
this.old.legend.position = this.options.legend.position;
wantUpdate(UpdateType.Refresh);
}
switch ((/** @type {?} */ (updateRequired))) {
case UpdateType.Default:
break;
case UpdateType.Update:
this.update();
break;
case UpdateType.Refresh:
this.refresh();
break;
}
}
/**
* @param {?} a
* @return {?}
*/
copyLabel(a) {
if (Array.isArray(a)) {
return [...a];
}
return a;
}
/**
* @param {?} a
* @param {?} b
* @return {?}
*/
labelsEqual(a, b) {
return Array.isArray(a) === Array.isArray(b)
&& (Array.isArray(a) || a === b)
&& (!Array.isArray(a) || a.length === b.length)
&& (!Array.isArray(a) || a.filter((/**
* @param {?} x
* @param {?} i
* @return {?}
*/
(x, i) => x !== b[i])).length === 0);
}
/**
* @param {?} a
* @return {?}
*/
copyColor(a) {
return {
backgroundColor: a.backgroundColor,
borderWidth: a.borderWidth,
borderColor: a.borderColor,
borderCapStyle: a.borderCapStyle,
borderDash: a.borderDash,
borderDashOffset: a.borderDashOffset,
borderJoinStyle: a.borderJoinStyle,
pointBorderColor: a.pointBorderColor,
pointBackgroundColor: a.pointBackgroundColor,
pointBorderWidth: a.pointBorderWidth,
pointRadius: a.pointRadius,
pointHoverRadius: a.pointHoverRadius,
pointHitRadius: a.pointHitRadius,
pointHoverBackgroundColor: a.pointHoverBackgroundColor,
pointHoverBorderColor: a.pointHoverBorderColor,
pointHoverBorderWidth: a.pointHoverBorderWidth,
pointStyle: a.pointStyle,
hoverBackgroundColor: a.hoverBackgroundColor,
hoverBorderColor: a.hoverBorderColor,
hoverBorderWidth: a.hoverBorderWidth,
};
}
/**
* @param {?} a
* @param {?} b
* @return {?}
*/
colorsEqual(a, b) {
if (!a !== !b) {
return false;
}
return !a ||
(a.backgroundColor === b.backgroundColor)
&& (a.borderWidth === b.borderWidth)
&& (a.borderColor === b.borderColor)
&& (a.borderCapStyle === b.borderCapStyle)
&& (a.borderDash === b.borderDash)
&& (a.borderDashOffset === b.borderDashOffset)
&& (a.borderJoinStyle === b.borderJoinStyle)
&& (a.pointBorderColor === b.pointBorderColor)
&& (a.pointBackgroundColor === b.pointBackgroundColor)
&& (a.pointBorderWidth === b.pointBorderWidth)
&& (a.pointRadius === b.pointRadius)
&& (a.pointHoverRadius === b.pointHoverRadius)
&& (a.pointHitRadius === b.pointHitRadius)
&& (a.pointHoverBackgroundColor === b.pointHoverBackgroundColor)
&& (a.pointHoverBorderColor === b.pointHoverBorderColor)
&& (a.pointHoverBorderWidth === b.pointHoverBorderWidth)
&& (a.pointStyle === b.pointStyle)
&& (a.hoverBackgroundColor === b.hoverBackgroundColor)
&& (a.hoverBorderColor === b.hoverBorderColor)
&& (a.hoverBorderWidth === b.hoverBorderWidth);
}
/**
* @return {?}
*/
updateColors() {
this.datasets.forEach((/**
* @param {?} elm
* @param {?} index
* @return {?}
*/
(elm, index) => {
if (this.colors && this.colors[index]) {
Object.assign(elm, this.colors[index]);
}
else {
Object.assign(elm, getColors(this.chartType, index, elm.data.length), Object.assign({}, elm));
}
}));
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
/** @type {?} */
let updateRequired = UpdateType.Default;
/** @type {?} */
const wantUpdate = (/**
* @param {?} x
* @return {?}
*/
(x) => {
updateRequired = x > updateRequired ? x : updateRequired;
});
// Check if the changes are in the data or datasets or labels or legend
if (changes.hasOwnProperty('data') && changes.data.currentValue) {
this.propagateDataToDatasets(changes.data.currentValue);
wantUpdate(UpdateType.Update);
}
if (changes.hasOwnProperty('datasets') && changes.datasets.currentValue) {
this.propagateDatasetsToData(changes.datasets.currentValue);
wantUpdate(UpdateType.Update);
}
if (changes.hasOwnProperty('labels')) {
if (this.chart) {
this.chart.data.labels = changes.labels.currentValue;
}
wantUpdate(UpdateType.Update);
}
if (changes.hasOwnProperty('legend')) {
if (this.chart) {
this.chart.config.options.legend.display = changes.legend.currentValue;
this.chart.generateLegend();
}
wantUpdate(UpdateType.Update);
}
if (changes.hasOwnProperty('options')) {
wantUpdate(UpdateType.Refresh);
}
switch ((/** @type {?} */ (updateRequired))) {
case UpdateType.Update:
this.update();
break;
case UpdateType.Refresh:
case UpdateType.Default:
this.refresh();
break;
}
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this.chart) {
this.chart.destroy();
this.chart = void 0;
}
this.subs.forEach((/**
* @param {?} x
* @return {?}
*/
x => x.unsubscribe()));
}
/**
* @param {?=} duration
* @return {?}
*/
update(duration) {
if (this.chart) {
return this.chart.update(duration);
}
}
/**
* @param {?} index
* @param {?} hidden
* @return {?}
*/
hideDataset(index, hidden) {
this.chart.getDatasetMeta(index).hidden = hidden;
this.chart.update();
}
/**
* @param {?} index
* @return {?}
*/
isDatasetHidden(index) {
return this.chart.getDatasetMeta(index).hidden;
}
/**
* @return {?}
*/
toBase64Image() {
return this.chart.toBase64Image();
}
/**
* @return {?}
*/
getChartConfiguration() {
/** @type {?} */
const datasets = this.getDatasets();
/** @type {?} */
const options = Object.assign({}, this.options);
if (this.legend === false) {
options.legend = { display: false };
}
// hook for onHover and onClick events
options.hover = options.hover || {};
if (!options.hover.onHover) {
options.hover.onHover = (/**
* @param {?} event
* @param {?} active
* @return {?}
*/
(event, active) => {
if (active && !active.length) {
return;
}
this.chartHover.emit({ event, active });
});
}
if (!options.onClick) {
options.onClick = (/**
* @param {?=} event
* @param {?=} active
* @return {?}
*/
(event, active) => {
this.chartClick.emit({ event, active });
});
}
/** @type {?} */
const mergedOptions = this.smartMerge(options, this.themeService.getColorschemesOptions());
return {
type: this.chartType,
data: {
labels: this.labels || [],
datasets
},
plugins: this.plugins,
options: mergedOptions,
};
}
/**
* @param {?} ctx
* @return {?}
*/
getChartBuilder(ctx /*, data:any[], options:any*/) {
/** @type {?} */
const chartConfig = this.getChartConfiguration();
return new Chart$1(ctx, chartConfig);
}
/**
* @param {?} options
* @param {?} overrides
* @param {?=} level
* @return {?}
*/
smartMerge(options, overrides, level = 0) {
if (level === 0) {
options = cloneDeep(options);
}
/** @type {?} */
const keysToUpdate = Object.keys(overrides);
keysToUpdate.forEach((/**
* @param {?} key
* @return {?}
*/
key => {
if (Array.isArray(overrides[key])) {
/** @type {?} */
const arrayElements = options[key];
if (arrayElements) {
arrayElements.forEach((/**
* @param {?} r
* @return {?}
*/
r => {
this.smartMerge(r, overrides[key][0], level + 1);
}));
}
}
else if (typeof (overrides[key]) === 'object') {
if (!(key in options)) {
options[key] = {};
}
this.smartMerge(options[key], overrides[key], level + 1);
}
else {
options[key] = overrides[key];
}
}));
if (level === 0) {
return options;
}
}
/**
* @private
* @param {?} label
* @return {?}
*/
isMultiLineLabel(label) {
return Array.isArray(label);
}
/**
* @private
* @param {?} label
* @return {?}
*/
joinLabel(label) {
if (!label) {
return null;
}
if (this.isMultiLineLabel(label)) {
return label.join(' ');
}
else {
return label;
}
}
/**
* @private
* @param {?} datasets
* @return {?}
*/
propagateDatasetsToData(datasets) {
this.data = this.datasets.map((/**
* @param {?} r
* @return {?}
*/
r => r.data));
if (this.chart) {
this.chart.data.datasets = datasets;
}
this.updateColors();
}
/**
* @private
* @param {?} newDataValues
* @return {?}
*/
propagateDataToDatasets(newDataValues) {
if (this.isMultiDataSet(newDataValues)) {
if (this.datasets && newDataValues.length === this.datasets.length) {
this.datasets.forEach((/**
* @param {?} dataset
* @param {?} i
* @return {?}
*/
(dataset, i) => {
dataset.data = newDataValues[i];
}));
}
else {
this.datasets = newDataValues.map((/**
* @param {?} data
* @param {?} index
* @return {?}
*/
(data, index) => {
return { data, label: this.joinLabel(this.labels[index]) || `Label ${index}` };
}));
if (this.chart) {
this.chart.data.datasets = this.datasets;
}
}
}
else {
if (!this.datasets) {
this.datasets = [{ data: newDataValues }];
if (this.chart) {
this.chart.data.datasets = this.datasets;
}
}
else {
this.datasets[0].data = newDataValues;
this.datasets.splice(1); // Remove all elements but the first
}
}
this.updateColors();
}
/**
* @private
* @param {?} data
* @return {?}
*/
isMultiDataSet(data) {
return Array.isArray(data[0]);
}
/**
* @private
* @return {?}
*/
getDatasets() {
if (!this.datasets && !this.data) {
throw new Error(`ng-charts configuration error, data or datasets field are required to render chart ${this.chartType}`);
}
// If `datasets` is defined, use it over the `data` property.
if (this.datasets) {
this.propagateDatasetsToData(this.datasets);
return this.datasets;
}
if (this.data) {
this.propagateDataToDatasets(this.data);
return this.datasets;
}
}
/**
* @private
* @return {?}
*/
refresh() {
// if (this.options && this.options.responsive) {
// setTimeout(() => this.refresh(), 50);
// }
// todo: remove this line, it is producing flickering
if (this.chart) {
this.chart.destroy();
this.chart = void 0;
}
if (this.ctx) {
this.chart = this.getChartBuilder(this.ctx /*, data, this.options*/);
}
}
}
BaseChartDirective.decorators = [
{ type: Directive, args: [{
// tslint:disable-next-line:directive-selector
selector: 'canvas[baseChart]',
exportAs: 'base-chart'
},] }
];
/** @nocollapse */
BaseChartDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: ThemeService }
];
BaseChartDirective.propDecorators = {
data: [{ type: Input }],
datasets: [{ type: Input }],
labels: [{ type: Input }],
options: [{ type: Input }],
chartType: [{ type: Input }],
colors: [{ type: Input }],
legend: [{ type: Input }],
plugins: [{ type: Input }],
chartClick: [{ type: Output }],
chartHover: [{ type: Output }]
};
if (false) {
/** @type {?} */
BaseChartDirective.prototype.data;
/** @type {?} */
BaseChartDirective.prototype.datasets;
/** @type {?} */
BaseChartDirective.prototype.labels;
/** @type {?} */
BaseChartDirective.prototype.options;
/** @type {?} */
BaseChartDirective.prototype.chartType;
/** @type {?} */
BaseChartDirective.prototype.colors;
/** @type {?} */
BaseChartDirective.prototype.legend;
/** @type {?} */
BaseChartDirective.prototype.plugins;
/** @type {?} */
BaseChartDirective.prototype.chartClick;
/** @type {?} */
BaseChartDirective.prototype.chartHover;
/** @type {?} */
BaseChartDirective.prototype.ctx;
/** @type {?} */
BaseChartDirective.prototype.chart;
/**
* @type {?}
* @private
*/
BaseChartDirective.prototype.old;
/**
* @type {?}
* @private
*/
BaseChartDirective.prototype.subs;
/**
* @type {?}
* @private
*/
BaseChartDirective.prototype.element;
/**
* @type {?}
* @private
*/
BaseChartDirective.prototype.themeService;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/charts.module.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class ChartsModule {
}
ChartsModule.decorators = [
{ type: NgModule, args: [{
declarations: [
BaseChartDirective
],
imports: [],
exports: [
BaseChartDirective
]
},] }
];
/**
* @fileoverview added by tsickle
* Generated from: lib/color.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function Color() { }
if (false) {
/** @type {?|undefined} */
Color.prototype.backgroundColor;
/** @type {?|undefined} */
Color.prototype.borderWidth;
/** @type {?|undefined} */
Color.prototype.borderColor;
/** @type {?|undefined} */
Color.prototype.borderCapStyle;
/** @type {?|undefined} */
Color.prototype.borderDash;
/** @type {?|undefined} */
Color.prototype.borderDashOffset;
/** @type {?|undefined} */
Color.prototype.borderJoinStyle;
/** @type {?|undefined} */
Color.prototype.pointBorderColor;
/** @type {?|undefined} */
Color.prototype.pointBackgroundColor;
/** @type {?|undefined} */
Color.prototype.pointBorderWidth;
/** @type {?|undefined} */
Color.prototype.pointRadius;
/** @type {?|undefined} */
Color.prototype.pointHoverRadius;
/** @type {?|undefined} */
Color.prototype.pointHitRadius;
/** @type {?|undefined} */
Color.prototype.pointHoverBackgroundColor;
/** @type {?|undefined} */
Color.prototype.pointHoverBorderColor;
/** @type {?|undefined} */
Color.prototype.pointHoverBorderWidth;
/** @type {?|undefined} */
Color.prototype.pointStyle;
/** @type {?|undefined} */
Color.prototype.hoverBackgroundColor;
/** @type {?|undefined} */
Color.prototype.hoverBorderColor;
/** @type {?|undefined} */
Color.prototype.hoverBorderWidth;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/colors.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function Colors() { }
if (false) {
/** @type {?|undefined} */
Colors.prototype.data;
/** @type {?|undefined} */
Colors.prototype.label;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/monkey-patch-chart-js-legend.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
// tslint:disable:variable-name
// tslint:disable:no-var-keyword
// tslint:disable:prefer-const
// tslint:disable:only-arrow-functions
// tslint:disable:one-variable-per-declaration
// tslint:disable:object-literal-shorthand
// tslint:disable:space-before-function-paren
/**
* @return {?}
*/
function monkeyPatchChartJsLegend() {
if (typeof Chart === 'undefined') {
console.log('Chart not defined (guessing this is a universal build, and I don\'t know why this happens -- Aviad)');
return;
}
/** @type {?} */
const plugins = Chart.plugins.getAll();
/** @type {?} */
const legend = plugins.filter((/**
* @param {?} p
* @return {?}
*/
p => p.id === 'legend'))[0];
legend._element.prototype.fit = fit;
legend._element.prototype.draw = draw;
/** @type {?} */
const helpers = Chart.helpers;
/** @type {?} */
const defaults = Chart.defaults;
/** @type {?} */
const valueOrDefault = helpers.valueOrDefault;
/**
* @param {?} labelOpts
* @param {?} fontSize
* @return {?}
*/
function getBoxWidth(labelOpts, fontSize) {
return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ?
fontSize :
labelOpts.boxWidth;
}
/**
* @return {?}
*/
function fit() {
/** @type {?} */
var me = this;
/** @type {?} */
var opts = me.options;
/** @type {?} */
var labelOpts = opts.labels;
/** @type {?} */
var display = opts.display;
/** @type {?} */
var ctx = me.ctx;
/** @type {?} */
var labelFont = helpers.options._parseFont(labelOpts);
/** @type {?} */
var fontSize = labelFont.size;
// Reset hit boxes
/** @type {?} */
var hitboxes = me.legendHitBoxes = [];
/** @type {?} */
var minSize = me.minSize;
/** @type {?} */
var isHorizontal = me.isHorizontal();
if (isHorizontal) {
minSize.width = me.maxWidth; // fill all the width
minSize.height = display ? 10 : 0;
}
else {
minSize.width = display ? 10 : 0;
minSize.height = me.maxHeight; // fill all the height
}
/** @type {?} */
var getMaxLineWidth = (/**
* @param {?} textLines
* @return {?}
*/
function (textLines) {
return textLines.map((/**
* @param {?} textLine
* @return {?}
*/
function (textLine) {
return ctx.measureText(textLine).width;
})).reduce((/**
* @param {?} acc
* @param {?} v
* @return {?}
*/
function (acc, v) {
return v > acc ? v : acc;
}), 0);
});
// Increase sizes here
if (display) {
ctx.font = labelFont.string;
if (isHorizontal) {
// Labels
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
/** @type {?} */
var lineWidths = me.lineWidths = [0];
/** @type {?} */
var lineHeights = me.lineHeights = [];
/** @type {?} */
var currentLineHeight = 0;
/** @type {?} */
var lineIndex = 0;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
helpers.each(me.legendItems, (/**
* @param {?} legendItem
* @param {?} i
* @return {?}
*/
function (legendItem, i) {
/** @type {?} */
var width;
/** @type {?} */
var height;
if (helpers.isArray(legendItem.text)) {
width = getMaxLineWidth(legendItem.text);
height = fontSize * legendItem.text.length + labelOpts.padding;
}
else {
width = ctx.measureText(legendItem.text).width;
height = fontSize + labelOpts.padding;
}
width += getBoxWidth(labelOpts, fontSize) + (fontSize / 2);
if (lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
lineHeights.push(currentLineHeight);
currentLineHeight = 0;
lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
lineIndex++;
}
legendItem.lineOrColumnIndex = lineIndex;
if (height > currentLineHeight) {
currentLineHeight = height;
}
// Store the hitbox width and height here. Final position will be updated in `draw`
hitboxes[i] = {
left: 0,
top: 0,
width: width,
height: height,
};
lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
}));
lineHeights.push(currentLineHeight);
minSize.height += lineHeights.reduce((/**
* @param {?} acc
* @param {?} v
* @return {?}
*/
function (acc, v) {
return acc + v;
}), 0);
}
else {
/** @type {?} */
var vPadding = labelOpts.padding;
/** @type {?} */
var columnWidths = me.columnWidths = [];
/** @type {?} */
var columnHeights = me.columnHeights = [];
/** @type {?} */
var totalWidth = labelOpts.padding;
/** @type {?} */
var currentColWidth = 0;
/** @type {?} */
var currentColHeight = 0;
/** @type {?} */
var columnIndex = 0;
helpers.each(me.legendItems, (/**
* @param {?} legendItem
* @param {?} i
* @return {?}
*/
function (legendItem, i) {
/** @type {?} */
var itemWidth;
/** @type {?} */
var height;
if (helpers.isArray(legendItem.text)) {
itemWidth = getMaxLineWidth(legendItem.text);
height = fontSize * legendItem.text.length;
}
else {
itemWidth = ctx.measureText(legendItem.text).width;
height = fontSize;
}
itemWidth += getBoxWidth(labelOpts, fontSize) + (fontSize / 2);
// If too tall, go to new column
if (currentColHeight + fontSize + 2 * vPadding > minSize.height) {
totalWidth += currentColWidth + labelOpts.padding;
columnWidths.push(currentColWidth); // previous column width
columnHeights.push(currentColHeight);
currentColWidth = 0;
currentColHeight = 0;
columnIndex++;
}
legendItem.lineOrColumnIndex = columnIndex;
// Get max width
currentColWidth = Math.max(currentColWidth, itemWidth);
currentColHeight += height + vPadding;
// Store the hitbox width and height here. Final position will be updated in `draw`
hitboxes[i] = {
left: 0,
top: 0,
width: itemWidth,
height: height
};
}));
totalWidth += currentColWidth;
columnWidths.push(currentColWidth);
columnHeights.push(currentColHeight);
minSize.width += totalWidth;
}
}
me.width = minSize.width;
me.height = minSize.height;
}
/**
* @return {?}
*/
function draw() {
/** @type {?} */
var me = this;
/** @type {?} */
var opts = me.options;
/** @type {?} */
var labelOpts = opts.labels;
/** @type {?} */
var globalDefaults = defaults.global;
/** @type {?} */
var defaultColor = globalDefaults.defaultColor;
/** @type {?} */
var lineDefault = globalDefaults.elements.line;
/** @type {?} */
var legendHeight = me.height;
/** @type {?} */
var columnHeights = me.columnHeights;
/** @type {?} */
var columnWidths = me.columnWidths;
/** @type {?} */
var legendWidth = me.width;
/** @type {?} */
var lineWidths = me.lineWidths;
/** @type {?} */
var lineHeights = me.lineHeights;
if (opts.display) {
/** @type {?} */
var ctx = me.ctx;
/** @type {?} */
var fontColor = valueOrDefault(labelOpts.fontColor, globalDefaults.defaultFontColor);
/** @type {?} */
var labelFont = helpers.options._parseFont(labelOpts);
/** @type {?} */
var fontSize = labelFont.size;
/** @type {?} */
var cursor;
// Canvas setup
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.lineWidth = 0.5;
ctx.strokeStyle = fontColor; // for strikethrough effect
ctx.fillStyle = fontColor; // render in correct colour
ctx.font = labelFont.string;
/** @type {?} */
var boxWidth = getBoxWidth(labelOpts, fontSize);
/** @type {?} */
var hitboxes = me.legendHitBoxes;
// current position
/** @type {?} */
var drawLegendBox = (/**
* @param {?} x
* @param {?} y
* @param {?} legendItem
* @return {?}
*/
function (x, y, legendItem) {
if (isNaN(boxWidth) || boxWidth <= 0) {
return;
}
// Set the ctx for the box
ctx.save();
/** @type {?} */
var lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);
ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);
if (ctx.setLineDash) {
// IE 9 and 10 do not support line dash
ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
}
if (opts.labels && opts.labels.usePointStyle) {
// Recalculate x and y for drawPoint() because its expecting
// x and y to be center of figure (instead of top left)
/** @type {?} */
var radius = boxWidth * Math.SQRT2 / 2;
/** @type {?} */
var centerX = x + boxWidth / 2;
/** @type {?} */
var centerY = y + fontSize / 2;
// Draw pointStyle as legend symbol
helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
}
else {
// Draw box as legend symbol
if (lineWidth !== 0) {
ctx.strokeRect(x, y, boxWidth, fontSize);
}
ctx.fillRect(x, y, boxWidth, fontSize);
}
ctx.restore();
});
/** @type {?} */
var drawStrikeThrough = (/**
* @param {?} x
* @param {?} y
* @param {?} w
* @return {?}
*/
function (x, y, w) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(x, y);
ctx.lineTo(x + w, y);
ctx.stroke();
});
/** @type {?} */
var drawCrossOver = (/**
* @param {?} x
* @param {?} y
* @param {?} w
* @param {?} h
* @return {?}
*/
function (x, y, w, h) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(x, y);
ctx.lineTo(x + w, y + h);
ctx.moveTo(x, y + h);
ctx.lineTo(x + w, y);
ctx.stroke();
});
/** @type {?} */
var fillText = (/**
* @param {?} x
* @param {?} y
* @param {?} legendItem
* @param {?} textWidth
* @return {?}
*/
function (x, y, legendItem, textWidth) {
/** @type {?} */
var halfFontSize = fontSize / 2;
/** @type {?} */
var xLeft = boxWidth + halfFontSize + x;
/** @type {?} */
var yMiddle = y + halfFontSize;
if (helpers.isArray(legendItem.text)) {
helpers.each(legendItem.text, (/**
* @param {?} textLine
* @param {?} index
* @return {?}
*/
function (textLine, index) {
/** @type {?} */
var lineOffset = index * fontSize;
ctx.fillText(textLine, xLeft, yMiddle + lineOffset);
}));
}
else {
ctx.fillText(legendItem.text, xLeft, yMiddle);
}
if (legendItem.hidden) {
if (helpers.isArray(legendItem.text)) {
drawCrossOver(xLeft, yMiddle, textWidth, (legendItem.text.length - 1) * (fontSize - 1));
}
else {
drawStrikeThrough(xLeft, yMiddle, textWidth);
}
}
});
/** @type {?} */
var alignmentOffset = (/**
* @param {?} dimension
* @param {?} blockSize
* @return {?}
*/
function (dimension, blockSize) {
switch (opts.align) {
case 'start':
return labelOpts.padding;
case 'end':
return dimension - blockSize;
default: // center
return (dimension - blockSize + labelOpts.padding) / 2;
}
});
// Horizontal
/** @type {?} */
var isHorizontal = me.isHorizontal();
if (isHorizontal) {
cursor = {
x: me.left + alignmentOffset(legendWidth, lineWidths[0]),
y: me.top + labelOpts.padding,
line: 0
};
}
else {
cursor = {
x: me.left + labelOpts.padding,
y: me.top + alignmentOffset(legendHeight, columnHeights[0]),
line: 0
};
}
helpers.each(me.legendItems, (/**
* @param {?} legendItem
* @param {?} i
* @return {?}
*/
function (legendItem, i) {
/** @type {?} */
var textWidth;
/** @type {?} */
var height;
/** @type {?} */
var boxTopOffset;
if (legendItem.lineOrColumnIndex > cursor.line) {
if (isHorizontal) {
cursor.y += lineHeights[cursor.line];
cursor.line = legendItem.lineOrColumnIndex;
cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]);
}
else {
cursor.x += columnWidths[cursor.line] + labelOpts.padding;
cursor.line = legendItem.lineOrColumnIndex;
cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]);
}
}
if (helpers.isArray(legendItem.text)) {
textWidth = legendItem.text.map((/**
* @param {?} textLine
* @return {?}
*/
function (textLine) {
return ctx.measureText(textLine).width;
})).reduce((/**
* @param {?} acc
* @param {?} v
* @return {?}
*/
function (acc, v) {
return v > acc ? v : acc;
}), 0);
boxTo