systelab-charts
Version:
Component to show a Chart
2,102 lines • 87.2 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Input, Output, ViewChild, Component, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import * as ChartJS from 'chart.js';
import { Chart, registerables } from 'chart.js';
import 'chartjs-adapter-date-fns';
import annotationPlugin from 'chartjs-plugin-annotation';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { format } from 'date-fns';
import { FormsModule } from '@angular/forms';
const arrayToObject = (array, keySelector) => {
const result = {};
for (const item of array) {
result[keySelector(item)] = item;
}
return result;
};
class Ticks {
static removeInitialAndFinalTick(value, index, values) {
return index === 0 || index === values.length - 1 ? '' : value;
}
static removeFinalTick(value, index) {
return index === 0 ? '' : value;
}
}
class DecimalFormatService {
constructor() {
/**
* @fieldOf decimalFormat
* @type String
*/
this.prefix = '';
/**
* @fieldOf decimalFormat
* @type String
*/
this.suffix = '';
/**
* @description Grouping size
* @fieldOf decimalFormat
* @type String
*/
this.comma = 0;
/**
* @description Minimum integer digits to be displayed
* @fieldOf decimalFormat
* @type Number
*/
this.minInt = 1;
/**
* @description Minimum fractional digits to be displayed
* @fieldOf decimalFormat
* @type String
*/
this.minFrac = 0;
/**
* @description Maximum fractional digits to be displayed
* @fieldOf decimalFormat
* @type String
*/
this.maxFrac = 0;
}
/**
* @description Formats given value
* @methodOf decimalFormat
* @param numStr
* @param formatStr
* @return Formatted number
* @author Oskan Savli
*/
execute(numStr, formatStr) {
this.configureService(formatStr);
// 1223.06 --> $1,223.06
// remove prefix, suffix and commas
let numberStr = this.formatBack(numStr)
.toLowerCase();
// do not format if not a number
if (isNaN(numberStr) || numberStr.length === 0) {
return numStr;
}
const indexE = numberStr.indexOf('e');
//scientific numbers
if (indexE !== -1) {
if (numberStr.indexOf('e') !== -1) {
return numberStr;
}
}
let negative = false;
// remove sign
if (numberStr.charAt(0) === '-') {
negative = true;
numberStr = numberStr.substring(1);
}
else if (numberStr.charAt(0) === '+') {
numberStr = numberStr.substring(1);
}
const point = numberStr.indexOf('.'); // position of point character
let intStr;
let fracStr = '';
if (point !== -1) {
intStr = numberStr.substring(0, point);
fracStr = numberStr.substring(point + 1);
}
else {
intStr = numberStr;
}
fracStr = fracStr.replace(/[.]/, ''); // remove other point characters
const isPercentage = this.suffix && this.suffix.charAt(0) === '%';
// if percentage, number will be multiplied by 100.
const minInt = this.minInt;
const minFrac = this.minFrac;
const maxFrac = this.maxFrac;
if (isPercentage) {
// copy two digits from frac to int
// with padding cases: '1.' -> 100., '1.1' -> 110., '1.0111' -> 101.11
intStr = intStr + ('00' + (fracStr + '00').substr(0, 2)).substr(-2);
fracStr = fracStr.substr(2);
}
if (fracStr.length > maxFrac) { // round
//case 6143
let num = Number('0.' + fracStr);
num = (maxFrac === 0) ? Math.round(num) : Number(num.toFixed(maxFrac));
// toFixed method has bugs on IE (0.7 --> 0)
fracStr = num.toString(10)
.substr(2);
let c = (num >= 1) ? 1 : 0; //carry
let x;
let strLength = intStr.length - 1;
while (c) { //increment intStr
if (strLength === -1) {
intStr = '1' + intStr;
break;
}
else {
x = intStr.charAt(strLength);
if (x === 9) {
x = '0';
c = 1;
}
else {
x = (++x) + '';
c = 0;
}
intStr = intStr.substring(0, strLength) + x + intStr.substring(strLength + 1, intStr.length);
strLength--;
}
}
}
for (let i = fracStr.length; i < minFrac; i++) { // if minFrac=4 then 1.12 --> 1.1200
fracStr = fracStr + '0';
}
while (fracStr.length > minFrac && fracStr.charAt(fracStr.length - 1) === '0') { // if minInt=4 then 00034 --> 0034)
fracStr = fracStr.substring(0, fracStr.length - 1);
}
for (let i = intStr.length; i < minInt; i++) { // if minInt=4 then 034 --> 0034
intStr = '0' + intStr;
}
while (intStr.length > minInt && intStr.charAt(0) === '0') { // if minInt=4 then 00034 --> 0034)
intStr = intStr.substring(1);
}
let j = 0;
for (let i = intStr.length; i > 0; i--) { // add commas
if (j !== 0 && j % this.comma === 0) {
intStr = intStr.substring(0, i) + ',' + intStr.substring(i);
j = 0;
}
j++;
}
let formattedValue;
if (fracStr.length > 0) {
formattedValue = this.prefix + intStr + '.' + fracStr + this.suffix;
}
else {
formattedValue = this.prefix + intStr + this.suffix;
}
if (negative) {
formattedValue = '-' + formattedValue;
}
return formattedValue;
}
configureService(formatStr) {
// get prefix
for (let i = 0; i < formatStr.length; i++) {
if (formatStr.charAt(i) === '#' || formatStr.charAt(i) === '0') {
this.prefix = formatStr.substring(0, i);
formatStr = formatStr.substring(i);
break;
}
}
// get suffix
this.suffix = formatStr.replace(/[#]|[0]|[,]|[.]/g, '');
// get number as string
const numberStr = formatStr.replace(/[^0#,.]/g, '');
let intStr;
let fracStr = '';
const point = numberStr.indexOf('.');
if (point !== -1) {
intStr = numberStr.substring(0, point);
fracStr = numberStr.substring(point + 1);
}
else {
intStr = numberStr;
}
const commaPos = intStr.lastIndexOf(',');
if (commaPos !== -1) {
this.comma = intStr.length - 1 - commaPos;
}
intStr = intStr.replace(/[,]/g, ''); // remove commas
fracStr = fracStr.replace(/[,]|[.]+/g, '');
this.maxFrac = fracStr.length;
let tmp = intStr.replace(/[^0]/g, ''); // remove all except zero
if (tmp.length > this.minInt) {
this.minInt = tmp.length;
}
tmp = fracStr.replace(/[^0]/g, '');
this.minFrac = tmp.length;
}
/**
* @description Converts formatted value back to non-formatted value
* @methodOf decimalFormat
* @param fNumStr Formatted number
* @return Original number
* @author Oskan Savli
*/
formatBack(fNumStr) {
fNumStr += ''; //ensure it is string
if (!fNumStr) {
return '';
} //do not return undefined or null
if (!isNaN(Number(fNumStr))) {
return this.getNumericString(fNumStr);
}
let fNumberStr = fNumStr;
let negative = false;
if (fNumStr.charAt(0) === '-') {
fNumberStr = fNumberStr.substr(1);
negative = true;
}
const pIndex = fNumberStr.indexOf(this.prefix);
const sIndex = (this.suffix === '') ? fNumberStr.length : fNumberStr.indexOf(this.suffix, this.prefix.length + 1);
if (pIndex === 0 && sIndex > 0) {
// remove suffix
fNumberStr = fNumberStr.substr(0, sIndex);
// remove prefix
fNumberStr = fNumberStr.substr(this.prefix.length);
// remove commas
fNumberStr = fNumberStr.replace(/,/g, '');
if (negative) {
fNumberStr = '-' + fNumberStr;
}
if (!isNaN(Number(fNumberStr))) {
return this.getNumericString(fNumberStr);
}
}
return fNumStr;
}
/**
* @description We shouldn't return strings like 1.000 in formatBack method.
* However, using only Number(str) is not enough, because it omits . in big numbers
* like 23423423423342234.34 => 23423423423342236 . There's a conflict in cases
* 6143 and 6541.
* @methodOf decimalFormat
* @param str Numberic string
* @return Corrected numeric string
* @author Serdar Bicer
*/
getNumericString(str) {
//first convert to number
const num = Number(str);
//check if there is a missing dot
const numStr = num + '';
if (str.indexOf('.') > -1 && numStr.indexOf('.') < 0) {
//check if original string has all zeros after dot or not
for (let i = str.indexOf('.') + 1; i < str.length; i++) {
//if not, this means we lost precision
if (str.charAt(i) !== '0') {
return str;
}
}
return numStr;
}
return str;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DecimalFormatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DecimalFormatService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DecimalFormatService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class ChartTooltipItem {
constructor(title, label, afterLabel, valueInAfterLabel, numberFormat) {
this.title = title;
this.label = label;
this.afterLabel = afterLabel;
this.valueInAfterLabel = valueInAfterLabel;
this.numberFormat = numberFormat;
}
}
class ChartTooltipSettings {
constructor(backgroundColor, borderColor, borderWidth, bodyFontColor, bodyFontSize, titleFontSize, titleFontColor) {
this.backgroundColor = backgroundColor;
this.borderColor = borderColor;
this.borderWidth = borderWidth;
this.bodyFontColor = bodyFontColor;
this.bodyFontSize = bodyFontSize;
this.titleFontSize = titleFontSize;
this.titleFontColor = titleFontColor;
this.bodyFontColor = '#ffffff';
this.borderColor = 'rgba(0,0,0,0)';
this.borderWidth = 0;
this.bodyFontSize = 12;
this.titleFontSize = 12;
this.titleFontColor = '#ffffff';
this.backgroundColor = 'rgba(0,0,0,0.8)';
}
}
class TooltipLegacyService {
constructor(decimalFormatService) {
this.decimalFormatService = decimalFormatService;
}
title(tooltipItems) {
const item = tooltipItems[0].dataset;
if (item.chartTooltipItem) {
const chartTooltipItem = item.chartTooltipItem instanceof Array ?
item.chartTooltipItem[tooltipItems[0].dataIndex] : item.chartTooltipItem;
if (chartTooltipItem.title) {
return chartTooltipItem.title;
}
}
}
tooltipLabel(tooltipItem, tooltipTimeFormatConstant) {
const item = tooltipItem.dataset;
let label = item.label;
// if (!label) {
// label = data.labels[tooltipItem.index];
// }
const val = item.data[tooltipItem.dataIndex];
let rt;
let rtVal;
if (val instanceof Object) {
if (val.t) {
if (val.t instanceof Date) {
const dataValue = '(' + (val.x ? val.x + ',' : '') + val.y + ')';
rt = format(val.t, tooltipTimeFormatConstant) + dataValue;
}
else {
rt = val.t;
}
}
else {
rt = '(' + val.x + ',' + val.y + ')';
}
}
else {
rt = val;
rtVal = val;
}
if (item.chartTooltipItem) {
const chartTooltipItem = item.chartTooltipItem instanceof Array ?
item.chartTooltipItem[tooltipItem.dataIndex] : item.chartTooltipItem;
if (!isNaN(rtVal) && chartTooltipItem.numberFormat) {
rt = this.decimalFormatService.execute(val, chartTooltipItem.numberFormat);
}
if (chartTooltipItem.label) {
label = chartTooltipItem.label;
}
if (!chartTooltipItem.valueInAfterLabel) {
label += ': ' + rt;
}
}
else {
label += ': ' + rt;
}
return label;
}
tooltipAfterLabel(tooltipItem) {
const item = tooltipItem.dataset;
let afterLabel = '';
if (item.chartTooltipItem) {
const chartTooltipItem = item.chartTooltipItem instanceof Array ?
item.chartTooltipItem[tooltipItem.dataIndex] : item.chartTooltipItem;
if (chartTooltipItem.afterLabel) {
afterLabel = chartTooltipItem.afterLabel;
}
if (chartTooltipItem.valueInAfterLabel) {
const val = item.data[tooltipItem.dataIndex];
let rt;
if (val instanceof Object) {
if (val.t) {
rt = val.t;
}
else {
rt = '(' + val.x + ',' + val.y + ')';
}
}
else {
if (!isNaN(val) && chartTooltipItem.numberFormat) {
rt = this.decimalFormatService.execute(val, chartTooltipItem.numberFormat);
}
else {
rt = val;
}
}
afterLabel += ' (' + rt + ')';
}
}
return afterLabel;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipLegacyService, deps: [{ token: DecimalFormatService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipLegacyService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipLegacyService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: DecimalFormatService }] });
Chart.register(...registerables, ChartDataLabels, annotationPlugin);
class ChartItem {
constructor(label, data, borderColor, backgroundColor, fill, showLine, isGradient, borderWidth, chartType, chartTooltipItem, pointRadius, yAxisID, legendType, labelBorderColors, labelBackgroundColors) {
this.label = label;
this.data = data;
this.borderColor = borderColor;
this.backgroundColor = backgroundColor;
this.fill = fill;
this.showLine = showLine;
this.isGradient = isGradient;
this.borderWidth = borderWidth;
this.chartType = chartType;
this.chartTooltipItem = chartTooltipItem;
this.pointRadius = pointRadius;
this.yAxisID = yAxisID;
this.legendType = legendType;
this.labelBorderColors = labelBorderColors;
this.labelBackgroundColors = labelBackgroundColors;
}
}
class Annotation {
constructor(drawTime, type, borderColor, borderWidth, scaleId = 'y') {
this.drawTime = drawTime;
this.type = type;
this.borderColor = borderColor;
this.borderWidth = borderWidth;
this.scaleId = scaleId;
}
}
class ChartLineAnnotation extends Annotation {
constructor(label, value, orientation, drawTime, type, borderDash, borderColor, borderWidth, endValue) {
super(drawTime, type, borderColor, borderWidth);
this.label = label;
this.value = value;
this.orientation = orientation;
this.borderDash = borderDash;
this.endValue = endValue;
}
}
class ChartLine {
constructor(xMinValue, yMinValue, xMaxValue, yMaxValue, borderColor, borderWidth) {
this.xMinValue = xMinValue;
this.yMinValue = yMinValue;
this.xMaxValue = xMaxValue;
this.yMaxValue = yMaxValue;
this.borderColor = borderColor;
this.borderWidth = borderWidth;
}
}
class ChartBoxAnnotation extends Annotation {
constructor(drawTime, xMin, xMax, yMin, yMax, type, backgroundColor, borderColor, borderWidth) {
super(drawTime, type, borderColor, borderWidth);
this.xMin = xMin;
this.xMax = xMax;
this.yMin = yMin;
this.yMax = yMax;
this.backgroundColor = backgroundColor;
}
}
class ChartLabelAnnotation {
constructor(text, position, backgroundColor, fontStyle, fontColor) {
this.text = text;
this.position = position;
this.backgroundColor = backgroundColor;
this.fontStyle = fontStyle;
this.fontColor = fontColor;
}
}
class ChartLabelSettings {
constructor(position, labelColors, chartLabelFont, chartLabelPadding, chartLabelText, formatter) {
this.position = position;
this.labelColors = labelColors;
this.chartLabelFont = chartLabelFont;
this.chartLabelPadding = chartLabelPadding;
this.chartLabelText = chartLabelText;
this.formatter = formatter;
this.position = new ChartLabelPosition();
this.labelColors = new ChartLabelColor();
this.chartLabelFont = new ChartLabelFont();
this.chartLabelPadding = new ChartLabelPadding();
this.chartLabelText = new ChartLabelText();
}
}
class ChartLabelPosition {
constructor(align, anchor, clamp, clip, display, offset, rotation) {
this.align = align;
this.anchor = anchor;
this.clamp = clamp;
this.clip = clip;
this.display = display;
this.offset = offset;
this.rotation = rotation;
}
}
class ChartLabelColor {
constructor(backgroundColor, color, borderColor, borderRadius, borderWidth, opacity) {
this.backgroundColor = backgroundColor;
this.color = color;
this.borderColor = borderColor;
this.borderRadius = borderRadius;
this.borderWidth = borderWidth;
this.opacity = opacity;
}
}
class ChartLabelFont {
constructor(font, family, size, style, weight, lineHeight) {
this.font = font;
this.family = family;
this.size = size;
this.style = style;
this.weight = weight;
this.lineHeight = lineHeight;
}
}
class ChartLabelPadding {
constructor(padding, top, right, bottom, left) {
this.padding = padding;
this.top = top;
this.right = right;
this.bottom = bottom;
this.left = left;
}
}
class ChartLabelText {
constructor(textAlign, textStrokeColor, textShadowBlur, textStrokeWidth, textShadowColor) {
this.textAlign = textAlign;
this.textStrokeColor = textStrokeColor;
this.textShadowBlur = textShadowBlur;
this.textStrokeWidth = textStrokeWidth;
this.textShadowColor = textShadowColor;
}
}
class ChartMultipleYAxisScales {
constructor(id, type, position, stacked = false, ticks, gridLines, scaleLabel) {
this.id = id;
this.type = type;
this.position = position;
this.stacked = stacked;
this.ticks = ticks;
this.gridLines = gridLines;
this.scaleLabel = scaleLabel;
}
getScaleDefinition(callbackFunction) {
const { display, drawBorder } = this.gridLines;
return {
id: this.id,
type: this.type,
position: this.position,
stacked: this.stacked,
max: this.ticks.max,
min: this.ticks.min,
ticks: {
...this.ticks,
...callbackFunction ? {
callback: callbackFunction
} : {}
},
border: {
drawBorder,
},
grid: {
display,
},
title: {
text: this.scaleLabel.labelString,
}
};
}
}
class ChartIntersectionSettings {
constructor(intersect = false, mode = 'index') {
this.intersect = intersect;
this.mode = mode;
}
}
class ChartLegacyComponent {
constructor(appRef, tooltipsService) {
this.appRef = appRef;
this.tooltipsService = tooltipsService;
this.labels = [];
this.data = [];
this.annotations = [];
this.showLegend = true;
this.legendPosition = 'top';
this.intersectionSettings = new ChartIntersectionSettings();
this.isHorizontal = false;
this.xAutoSkip = true;
this.isBackgroundGrid = true;
this.responsive = true;
this.maintainAspectRatio = true;
this.isStacked = false;
this.animationDuration = 1000;
this.timeScale = false;
this.timeUnit = 'day';
this.tooltipTimeFormat = 'd/M/yyyy';
this.customLegend = false;
this.legendWithoutBox = false;
this.hideInitialAndFinalTick = false;
this.hideFinalTick = false;
this.pointStyle = 'circle';
this.canvasBackground = '';
this.itemSelectedChange = new EventEmitter();
this.action = new EventEmitter();
this.chartResized = false;
this.defaultColors = [
[255, 99, 132],
[54, 162, 235],
[255, 206, 86],
[75, 192, 192],
[220, 220, 220],
[247, 70, 74],
[70, 191, 189],
[253, 180, 92],
[148, 159, 177],
[151, 187, 205],
[231, 233, 237],
[77, 83, 96]
];
this.dataset = [];
this._annotations = [];
this.axesVisible = true;
this.yAxisLabelVisible = false;
this.xAxisLabelVisible = false;
Chart.defaults.interaction.intersect = this.intersectionSettings.intersect;
// @ts-ignore
Chart.defaults.interaction.mode = this.intersectionSettings.mode;
console.warn(`❌ The selector <systelab-chart-legacy> is deprecated. Use the new <systelab-chart>
implementation instead. <systelab-chart-legacy> will me removed in next versions.`);
}
get itemSelected() {
return this._itemSelected;
}
set itemSelected(value) {
this._itemSelected = value;
this.itemSelectedChange.emit(this._itemSelected);
}
ngAfterViewInit() {
let cx;
if (!this.tooltipSettings) {
this.tooltipSettings = new ChartTooltipSettings();
}
if (this.canvas.nativeElement) {
cx = this.canvas.nativeElement.getContext('2d');
}
if (this.customLegend) {
this.initCustomLegend();
}
this.setData(cx);
this.setAxisVisibility();
this.addAnnotations();
this.drawChart(cx);
if (this.customLegend && this.data.filter(obj => obj.legendType != null).length === this.data.length) {
this.buildCustomLegend();
}
}
rgba(colour, alpha) {
return 'rgba(' + colour.concat(alpha)
.join(',') + ')';
}
getResizedBase64Image(height, width) {
let base64ImageString;
if (this.chart) {
if (width || height) {
const canvasOffsetHeight = this.chart.canvas.parentElement.offsetHeight;
const canvasOffsetWidth = this.chart.canvas.parentElement.offsetWidth;
const originalAspectRatio = this.maintainAspectRatio;
const originalResponsive = this.responsive;
this.responsive = false;
this.maintainAspectRatio = false;
this.chart.resize();
if (this.doResizeChart(height, width)) {
this.appRef.tick();
this.chart.resize();
base64ImageString = this.chart.toBase64Image();
this.maintainAspectRatio = originalAspectRatio;
this.responsive = originalResponsive;
this.chartResized = false;
this.doResizeChart(canvasOffsetHeight, canvasOffsetWidth);
this.doUpdate();
}
}
else {
base64ImageString = this.chart.toBase64Image();
}
return base64ImageString;
}
return undefined;
}
doResizeChart(height, width) {
const elementToResize = this.chart.canvas.parentElement;
let doResize = false;
if (height) {
doResize = true;
elementToResize.style.height = height + 'px';
}
if (width) {
doResize = true;
elementToResize.style.width = width + 'px';
}
this.chartResized = doResize;
return doResize;
}
doUpdate() {
let cx;
if (this.canvas.nativeElement) {
cx = this.canvas.nativeElement.getContext('2d');
}
this.chart.destroy();
this.setAxisVisibility();
this.dataset = [];
this._annotations = [];
this.setData(cx);
this.addAnnotations();
this.drawChart(cx);
if (this.customLegend && this.data.filter(obj => obj.legendType != null).length === this.data.length) {
this.buildCustomLegend();
}
}
drawLine(chartData, chartLine) {
const scales = chartData.chart.scales;
let cx;
if (this.canvas.nativeElement) {
cx = this.canvas.nativeElement.getContext('2d');
}
let xScale;
let yScale;
Object.keys(scales)
.forEach(k => (k[0] === 'x' && (xScale = scales[k])) || (yScale = scales[k]));
const getXY = (x, y) => ({
x: xScale.getPixelForValue(x, undefined, undefined, true),
y: yScale.getPixelForValue(y)
});
const initPoint = getXY(chartLine.xMinValue, chartLine.yMinValue);
const endPoint = getXY(chartLine.xMaxValue, chartLine.yMaxValue);
cx.beginPath();
cx.lineWidth = chartLine.borderWidth || 1;
cx.moveTo(initPoint.x, initPoint.y);
cx.lineTo(endPoint.x, endPoint.y);
cx.strokeStyle = chartLine.borderColor || 'black';
cx.stroke();
cx.closePath();
cx.restore();
}
initCustomLegend() {
this.showLegend = false;
}
buildCustomLegend() {
let legendItems = [];
if (this.legendPosition === 'top') {
this.topLegend.nativeElement.innerHTML = this.generateLegend();
legendItems = this.topLegend.nativeElement.getElementsByTagName('li');
}
else {
this.bottomLegend.nativeElement.innerHTML = this.generateLegend();
legendItems = this.bottomLegend.nativeElement.getElementsByTagName('li');
}
for (let i = 0; i < legendItems.length; i += 1) {
legendItems[i].addEventListener('click', this.legendClickCallback.bind(this), false);
}
}
generateLegend() {
let listHtml = '<ul>';
this.data.forEach(chartItem => {
listHtml += `
<li>
<span class="${chartItem.legendType}" style="background-color: ${chartItem.backgroundColor !== 'transparent' ? chartItem.backgroundColor : chartItem.borderColor}; border-color: ${chartItem.borderColor}">
</span>
${chartItem.label}
</li>
`;
});
listHtml += '</ul>';
return listHtml;
}
drawChart(cx) {
const tooltipTimeFormatConstant = this.tooltipTimeFormat;
/* Draw the chart */
if (this.canvas.nativeElement) {
const definition = {
type: this.type,
data: {
labels: this.labels,
datasets: this.dataset
},
options: {
animation: {
duration: this.animationDuration,
onComplete: (chartData) => {
if (this.chartLine) {
this.drawLine(chartData, this.chartLine);
}
}
},
responsive: this.responsive,
maintainAspectRatio: this.maintainAspectRatio,
onClick: (evt, item) => {
const e = item[0];
if (e) {
this.itemSelected = e;
this.action.emit();
}
},
elements: {
line: {
tension: this.lineTension
},
point: {
pointStyle: this.pointStyle,
hoverRadius: 8,
}
},
display: true,
scales: this.type !== 'pie' ? this.scales() : {},
plugins: {
annotation: {
events: ['click'],
annotations: arrayToObject(this._annotations, item => item.id),
},
tooltip: {
position: this.type === 'bar' ? 'nearest' : 'average',
callbacks: {
title: (tooltipItem) => this.tooltipsService.title(tooltipItem),
label: (tooltipItem) => this.tooltipsService.tooltipLabel(tooltipItem, tooltipTimeFormatConstant),
afterLabel: (tooltipItem) => this.tooltipsService.tooltipAfterLabel(tooltipItem),
},
backgroundColor: this.tooltipSettings.backgroundColor,
titleFont: {
size: this.tooltipSettings.titleFontSize,
},
titleColor: this.tooltipSettings.titleFontColor,
bodyColor: this.tooltipSettings.bodyFontColor,
bodyFont: {
size: this.tooltipSettings.bodyFontSize,
},
borderColor: this.tooltipSettings.borderColor,
borderWidth: this.tooltipSettings.borderWidth,
},
legend: {
display: this.showLegend,
position: this.legendPosition,
...this.legendWithoutBox ? {
labels: {
boxWidth: 0
}
} : {},
},
// Removed. We need to use htmlLegend. See ChartJS documentation
// legendCallback: (chart) => this.legendCallback(chart),
},
},
plugins: [
{
id: 'customCanvasBackgroundColor',
beforeDraw: (chart, args, options) => {
const { ctx } = chart;
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = options.color || 'transparent';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
}
]
};
if (this.type === 'bar' && this.isHorizontal) {
definition.options.indexAxis = 'y';
}
if (this.type === 'radar') {
definition.options.scale = {
ticks: {
min: this.minValueForRadar,
max: this.maxValueForRadar
}
};
}
if (this.chartLabelSettings) {
definition.options = {
...definition.options,
plugins: {
...definition.options.plugins,
datalabels: {
align: this.chartLabelSettings.position.align,
anchor: this.chartLabelSettings.position.anchor,
backgroundColor: this.chartLabelSettings.labelColors.backgroundColor,
borderColor: this.chartLabelSettings.labelColors.borderColor,
borderRadius: this.chartLabelSettings.labelColors.borderRadius,
borderWidth: this.chartLabelSettings.labelColors.borderWidth,
clamp: this.chartLabelSettings.position.clamp,
clip: this.chartLabelSettings.position.clip,
color: this.chartLabelSettings.labelColors.color,
display: this.chartLabelSettings.position.display,
font: this.initDatalabelsFontProperties(this.chartLabelSettings.chartLabelFont),
formatter: this.chartLabelSettings.formatter,
offset: this.chartLabelSettings.position.offset,
opacity: this.chartLabelSettings.labelColors.opacity,
padding: this.initDatalabelsPaddingProperties(this.chartLabelSettings.chartLabelPadding),
rotation: this.chartLabelSettings.position.rotation,
textAlign: this.chartLabelSettings.chartLabelText.textAlign,
textStrokeColor: this.chartLabelSettings.chartLabelText.textStrokeColor,
textStrokeWidth: this.chartLabelSettings.chartLabelText.textStrokeWidth,
textShadowBlur: this.chartLabelSettings.chartLabelText.textShadowBlur,
textShadowColor: this.chartLabelSettings.chartLabelText.textShadowColor
}
}
};
}
else {
definition.options = {
...definition.options,
plugins: {
...definition.options.plugins,
datalabels: {
display: false,
}
}
};
}
if (this.canvasBackground) {
definition.options = {
...definition.options,
plugins: {
...definition.options.plugins,
customCanvasBackgroundColor: {
color: this.canvasBackground
}
}
};
}
this.chart = new Chart(cx, definition);
}
}
legendCallback(chart) {
const text = [];
text.push('<ul class="' + chart.id + '-legend">');
const data = chart.data;
const dataSets = data.datasets;
if (dataSets.length) {
for (let i = 0; i < dataSets.length; i++) {
text.push('<li>');
if (dataSets[i].legendType) {
if (dataSets[i].borderColor && dataSets[i].backgroundColor) {
if (dataSets[i].backgroundColor === 'transparent') {
text.push(`<span class="${dataSets[i].legendType}" style="background-color: ${dataSets[i].borderColor};
border-color: ${dataSets[i].borderColor}"></span>`);
}
else if (dataSets[i].borderColor === 'transparent') {
text.push(`<span class="${dataSets[i].legendType}" style="background-color: ${dataSets[i].backgroundColor};
border-color: ${dataSets[i].backgroundColor}"></span>`);
}
else {
text.push(`<span class="${dataSets[i].legendType}" style="background-color: ${dataSets[i].backgroundColor};
border-color: ${dataSets[i].borderColor}"></span>`);
}
}
else if (dataSets[i].borderColor) {
text.push(`<span class="${dataSets[i].legendType}" style="border-color: ${dataSets[i].borderColor}"></span>`);
}
else if (dataSets[i].backgroundColor) {
text.push(`<span class="${dataSets[i].legendType}"
style="background-color: ${dataSets[i].backgroundColor}"></span>`);
}
}
text.push(dataSets[i].label);
text.push('</li>');
}
}
text.push('</ul>');
return text.join('');
}
scales() {
const yAxisMultipleArray = this.multipleYAxisScales ?
this.multipleYAxisScales.map(y => y.getScaleDefinition(this.hideInitialAndFinalTick ? Ticks.removeInitialAndFinalTick : this.hideFinalTick ? Ticks.removeFinalTick : null)) : null;
const yAxisMultiple = this.multipleYAxisScales ? arrayToObject(yAxisMultipleArray, i => i.id) : null;
const yAxis = {
stacked: this.isStacked,
min: this.yMinValue,
max: this.yMaxValue,
ticks: {
display: this.axesVisible,
...this.hideInitialAndFinalTick ? {
callback: Ticks.removeInitialAndFinalTick
} : {},
...this.hideFinalTick ? {
callback: Ticks.removeFinalTick
} : {}
},
border: {
drawBorder: this.axesVisible,
},
grid: {
display: this.isBackgroundGrid,
},
title: {
display: this.yAxisLabelVisible,
text: this.yLabelAxis
}
};
const timeScale = this.timeScale ? {
type: 'time',
distribution: 'linear',
time: {
unit: this.timeUnit,
minUnit: 'minute',
}
} : {};
const xAxis = {
stacked: this.isStacked,
min: this.xMinValue,
max: this.xMaxValue,
ticks: {
display: this.axesVisible,
autoSkip: this.xAutoSkip,
...this.hideInitialAndFinalTick ? {
callback: Ticks.removeInitialAndFinalTick
} : {},
...this.hideFinalTick ? {
callback: Ticks.removeFinalTick
} : {}
},
border: {
drawBorder: this.axesVisible,
},
grid: {
display: this.isBackgroundGrid,
},
title: {
display: this.xAxisLabelVisible,
text: this.xLabelAxis
}
};
let axisScales = {
x: {
...timeScale,
...xAxis,
},
y: undefined
};
if (this.multipleYAxisScales) {
axisScales = {
...axisScales,
...yAxisMultiple,
};
delete axisScales.y;
}
else {
axisScales.y = yAxis;
}
return axisScales;
}
initDatalabelsFontProperties(chartLabelText) {
let font;
if (chartLabelText.font) {
font = chartLabelText.font;
}
else {
font = {};
}
if (chartLabelText.family) {
font.family = chartLabelText.family;
}
if (chartLabelText.size) {
font.size = chartLabelText.size;
}
if (chartLabelText.style) {
font.style = chartLabelText.style;
}
if (chartLabelText.weight) {
font.weight = chartLabelText.weight;
}
if (chartLabelText.lineHeight) {
font.lineHeight = chartLabelText.lineHeight;
}
return font;
}
initDatalabelsPaddingProperties(chartLabelPadding) {
let padding;
if (chartLabelPadding.padding) {
padding = chartLabelPadding.padding;
}
else {
padding = {};
}
if (chartLabelPadding.top) {
padding.top = chartLabelPadding.top;
}
if (chartLabelPadding.right) {
padding.right = chartLabelPadding.right;
}
if (chartLabelPadding.bottom) {
padding.bottom = chartLabelPadding.bottom;
}
if (chartLabelPadding.left) {
padding.left = chartLabelPadding.left;
}
return padding;
}
setData(cx) {
let borderColors;
let backgroundColors;
if (this.data) {
let colorNumber = 0;
for (let i = 0; i < this.data.length; i++) {
colorNumber = i;
if (this.data[i].isGradient) {
const gradientStroke = cx.createLinearGradient(500, 0, 100, 0);
gradientStroke.addColorStop(0, this.rgba(this.defaultColors[0], 1));
gradientStroke.addColorStop(1, this.rgba(this.defaultColors[1], 1));
borderColors = gradientStroke;
backgroundColors = gradientStroke;
}
else if ((this.type === 'pie' || this.type === 'doughnut' || this.type === 'polarArea') && !this.data[i].chartType) {
const backgroundColorList = [];
const borderColorList = [];
for (let j = 0; j < this.data[i].data.length; j++) {
if (this.data[i].labelBorderColors && this.data[i].labelBorderColors[j]) {
borderColorList.push(this.rgba(this.data[i].labelBorderColors[j], 1));
}
else {
borderColorList.push(this.rgba(this.defaultColors[colorNumber], 1));
}
if (this.data[i].labelBackgroundColors && this.data[i].labelBackgroundColors[j]) {
backgroundColorList.push(this.rgba(this.data[i].labelBackgroundColors[j], 1));
}
else {
backgroundColorList.push(this.rgba(this.defaultColors[colorNumber], 1));
}
colorNumber++;
if (colorNumber > (this.defaultColors.length - 1)) {
colorNumber = 0;
}
}
borderColors = borderColorList;
backgroundColors = backgroundColorList;
}
else {
if (colorNumber > (this.defaultColors.length - 1)) {
colorNumber = 0;
}
if (!this.data[i].borderColor) {
this.data[i].borderColor = this.rgba(this.defaultColors[colorNumber], 1);
}
if (!this.data[i].backgroundColor) {
if (this.data[i].fill) {
this.data[i].backgroundColor = this.rgba(this.defaultColors[colorNumber], 0.6);
}
else {
this.data[i].backgroundColor = 'transparent';
}
}
borderColors = this.data[i].borderColor;
backgroundColors = this.data[i].backgroundColor;
}
this.dataset.push({
yAxisID: this.data[i].yAxisID,
label: this.data[i].label,
data: this.data[i].data,
borderColor: borderColors,
backgroundColor: backgroundColors,
fill: this.data[i].fill,
type: this.data[i].chartType,
borderWidth: this.data[i].borderWidth,
showLine: this.data[i].showLine,
pointRadius: this.data[i].pointRadius,
chartTooltipItem: this.data[i].chartTooltipItem,
legendType: this.data[i].legendType
});
}
}
}
addAnnotations() {
if (this.annotations) {
for (let i = 0; i < this.annotations.length; i++) {
if (this.annotations[i] instanceof ChartLineAnnotation) {
this.addLineAnnotation(this.annotations[i], this.rgba(this.defaultColors[this.getColorNumber(i)], 1), this.rgba(this.defaultColors[this.getColorNumber(i) + 1], 1));
}
if (this.annotations[i] instanceof ChartBoxAnnotation) {
this.addBoxAnnotation(this.annotations[i], this.rgba(this.defaultColors[this.getColorNumber(i)], 1));
}
}
}
}
addLineAnnotation(lineAnnotation, defaultBorderColor, defaultBackgroundColor) {
if (!lineAnnotation.borderColor) {
lineAnnotation.borderColor = defaultBorderColor;
}
if (!lineAnnotation.borderWidth) {
lineAnnotation.borderWidth = 2;
}
lineAnnotation.endValue = lineAnnotation.endValue ?? lineAnnotation.value;
if (lineAnnotation.label) {
if (!lineAnnotation.label.backgroundColor) {
lineAnnotation.label.backgroundColor = defaultBackgroundColor;
}
if (!lineAnnotation.label.position) {
lineAnnotation.label.position = 'center';
}
if (!lineAnnotation.label.fontColor) {
lineAnnotation.label.fontColor = '#ffffff';
}
if (!lineAnnotation.label.fontStyle) {
lineAnnotation.label.fontStyle = 'normal';
}
}
const annotations = {
drawTime: lineAnnotation.drawTime,
id: 'annotation' + (this._annotations.length + 1),
type: lineAnnotation.type,
value: lineAnnotation.value,
endValue: lineAnnotation.endValue,
borderColor: lineAnnotation.borderColor,
borderWidth: lineAnnotation.borderWidth,
borderDash: lineAnnotation.borderDash,
scaleID: lineAnnotation.orientation === 'vertical' ? 'x' : lineAnnotation.scaleId,
};
let label = {};
if (lineAnnotation.label) {
label = {
display: true,
backgroundColor: lineAnnotation.label.backgroundColor,
position: lineAnnotation.label.position,
content: lineAnnotation.label.text,
font: {
color: lineAnnotation.label.fontColor,
style: lineAnnotation.label.fontStyle,
},
};
}
this._annotations.push({
...annotations,
label,
});
}
getColorNumber(i) {
let colorNumber = i;
if (colorNumber > (this.defaultColors.length - 1)) {
colorNumber = 0;
}
return colorNumber;
}
addBoxAnnotation(boxAnnotation, defaultBorderColor) {
if (!boxAnnotation.borderColor) {
boxAnnotation.borderColor = defaultBorderColor;
}
if (!boxAnnotation.borderWidth) {
boxAnnotation.borderWidth = 2;
}
if (!boxAnnotation.backgroundColor) {
boxAnnotation.backgroundColor = 'transparent';
}
this._annotations.push({
drawTime: boxAnnotation.drawTime,
id: 'annotation' + (this._annotations.length + 1),
type: boxAnnotation.type,
backgroundColor: boxAnnotation.backgroundColor,
borderWidth: boxAnnotation.borderWidth,
borderColor: boxAnnotation.borderColor,
xMin: boxAnnotation.xMin,
xMax: boxAnnotation.xMax,
yMin: boxAnnotation.yMin,
yMax: boxAnnotation.yMax,
xScaleID: 'x',
yScaleID: boxAnnotation.scaleId
});
}
setAxisVisibility() {
/* Axes Labels */
this.axesVisible = !(this.type === 'pie' || this.type === 'doughnut' || this.type === 'polarArea' || this.type === 'radar');
this.xAxisLabelVisible = !!this.xLabelAxis;
this.yAxisLabelVisible = !!this.yLabelAxis;
}
legendClickCallback(event) {
event = event || window.event;
let target = event.target || event.srcElement;
while (target.nodeName !== 'LI') {
target = target.parentElement;
}
const parent = target.parentElement;
const chart = this.chart;
const index = Array.prototype.slice.call(parent.children)
.indexOf(target);
this.chart.data.datasets[index].hidden = !this.chart.data.datasets[index].hidden;
if (chart) {
if (chart.isDatasetVisible(index)) {
target.classList.remove('hidden');
}
else {
target.classList.add('hidden');
}
chart.update();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartLegacyComponent, deps: [{ token: i0.ApplicationRef }, { token: TooltipLegacyService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.2", type: ChartLegacyComponent, isStandalone: false, selector: "systelab-chart-legacy", inputs: { labels: "labels", data: "data", annotations: "annotations", showLegend: "showLegend", legendPosition: "legendPosition", intersectionSettings: "intersectionSettings", isHorizontal: "isHorizontal", yMinValue: "yMinValue", yMaxValue: "yMaxValue", xMinValue: "xMinValue", xMaxValue: "xMaxValue", xAutoSkip: "xAutoSkip", yLabelAxis: "yLabelAxis", xLabelAxis: "xLabelAxis", lineTension: "lineTension", isBackgroundGrid: "isBackgroundGrid", type: "type", responsive: "responsive", maintainAspectRatio: "maintainAspectRatio", tooltipSettings: "tooltipSettings", chartLabelSettings: "chartLabelSettings", isStacked: "isStacked", animationDuration: "animationDuration", minValueForRadar: "minValueForRadar", maxValueForRadar: "maxValueForRadar", multipleYAxisScales: "multipleYAxisScales", timeScale: "timeScale", timeUnit: "timeUnit", tooltipTimeFormat: "tooltipTimeFormat", customLegend: "customLegend", legendWithoutBox: "legendWithoutBox", hideInitialAndFinalTick: "hideInitialAndFinalTick", hideFinalTick: "hideFinalTick", chartLine: "chartLine", pointStyle: "pointStyle", canvasBackground: "canvasBackground", itemSelected: "itemSelected" }, outputs: { itemSelectedChange: "itemSelectedChange", action: "action" }, viewQueries: [{ propertyName: "canvas", first: true, predicate: ["canvas"], descendants: true, static: true }, { propertyName: "topLegend", first: true, predicate: ["topLegend"], descendants: true }, { propertyName: "bottomLegend", first: true, predicate: ["bottomLegend"], descendants: true }], ngImport: i0, template: "<div style=\"display:block;\" [class.h-100]=\"!maintainAspectRatio && !chartResized\">\n <div #topLegend class=\"chart-legend\"></div>\n <canvas #canvas id=\"canvas\" class=\"container-fluid\"></canvas>\n <div #bottomLegend class=\"chart-legend\"></div>\n</div>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartLegacyComponent, decorators: [{
type: Component,
args: [{ selector: 'systelab-chart-legacy', standalone: false, template: "<div style=\"display:block;\" [class.h-100]=\"!maintainAspectRatio && !chartResized\">\n <div #topLegend class=\"chart-legend\"></div>\n <canvas #canvas id=\"canvas\" class=\"container-fluid\"></canvas>\n <div #bottomLegend class=\"chart-legend\"></div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: TooltipLegacyService }], propDecorators: { canvas: [{
type: ViewChild,
args: ['canvas', { static: true }]
}], topLegend: [{
type: ViewChild,
args: ['topLegend', { static: false }]
}], bottomLegend: [{
type: ViewChild,
args: ['bottomLegend', { static: false }]
}], labels: [{
type: Input
}], data: [{
type: Input
}], annotations: [{
type: Input
}], showLegend: [{
type: Input
}], legendPosition: [{
type: Input
}], intersectionSettings: [{
type: Input
}], isHorizontal: [{
type: Input
}], yMinValue: [{
type: Input
}], yMaxValue: [{
type: Input
}], xMinValue: [{
type: Input
}], xMaxValue: [{
type: Input
}], xAutoSkip: [{
type: Input
}], yLabelAxis: [{
type: Input
}], xLabelAxis: [{
type: Input
}], lineTension: [{
type: Input
}], isBackgroundGrid: [{
type: Input
}], type: [{
type: Input
}], responsive: [{
type: Input
}], maintainAspectRatio: [{
type: Input
}], tooltipSettings: [{
type: Input
}], chartLabelSettings: [{
type: Input
}], isStacked: [{
type: Input
}], animationDuration: [{
type: Input
}], minValueForRadar: [{
type: Input
}], maxValueForRadar: [{
type: Input
}], multipleYAxisScales: [{
type: Input
}], timeScale: [{
type: Input
}], timeUnit: [{
type: Input
}], tooltipTimeFormat: [{
type: Input
}], customLegend: [{
type: Input
}], legendWithoutBox: [{
type: Input
}], hideInitialAndFinalTick: [{
type: Input
}], hideFinalTick: [{
type: Input
}], chartLine: [{
type: Input
}], pointStyle: [{
type: Input
}], canvasBackground: [{
type: Input
}], itemSelectedChange: [{
type: Output
}], action: [{
type: Output
}], itemSelected: [{
type: Input
}] } });
var AnnotationType;
(function (AnnotationType) {
AnnotationType["line"] = "line";
AnnotationType["box"] = "box";
AnnotationType["point"] = "point";
})(AnnotationType || (AnnotationType = {}));
var AnnotationDrawTime;
(function (AnnotationDrawTime) {
AnnotationDrawTime["afterDraw"] = "afterDraw";
AnnotationDrawTime["afterDatasetsDraw"] = "afterDatasetsDraw";
AnnotationDrawTime["beforeDraw"] = "beforeDraw";
AnnotationDrawTime["beforeDatasetsDraw"] = "beforeDatasetsDraw";
})(AnnotationDrawTime || (AnnotationDrawTime = {}));
var LineAnnotationOrientation;
(function (LineAnnotationOrientation) {
LineAnnotationOrientation["vertical"] = "vertical";
LineAnnotationOrientation["horizontal"] = "horizontal";
})(LineAnnotationOrientation || (LineAnnotationOrientation = {}));
var AnnotationLabelLabelPosition;
(function (AnnotationLabelLabelPosition) {
AnnotationLabelLabelPosition["start"] = "start";
AnnotationLabelLabelPosition["center"] = "center";
AnnotationLabelLabelPosition["end"] = "end";
})(AnnotationLabelLabelPosition || (AnnotationLabelLabelPosition = {}));
var ChartType;
(function (ChartType) {
ChartType["line"] = "line";
ChartType["bar"] = "bar";
ChartType["pie"] = "pie";
ChartType["radar"] = "radar";
ChartType["doughnut"] = "doughnut";
ChartType["polarArea"] = "polarArea";
ChartType["scatter"] = "scatter";
ChartType["bubble"] = "bubble";
})(ChartType || (ChartType = {}));
var InteractionMode;
(function (InteractionMode) {
InteractionMode["point"] = "point";
InteractionMode["nearest"] = "nearest";
InteractionMode["index"] = "index";
InteractionMode["dataset"] = "dataset";
InteractionMode["x"] = "x";
InteractionMode["y"] = "y";
})(InteractionMode || (InteractionMode = {}));
var LegendPointStyle;
(function (LegendPointStyle) {
LegendPointStyle["circle"] = "circle";
LegendPointStyle["cross"] = "cross";
LegendPointStyle["crossRot"] = "crossRot";
LegendPointStyle["dash"] = "dash";
LegendPointStyle["line"] = "line";
LegendPointStyle["rect"] = "rect";
LegendPointStyle["rectRounded"] = "rectRounded";
LegendPointStyle["rectRot"] = "rectRot";
LegendPointStyle["star"] = "star";
LegendPointStyle["triangle"] = "triangle";
})(LegendPointStyle || (LegendPointStyle = {}));
var LegendPosition;
(function (LegendPosition) {
LegendPosition["top"] = "top";
LegendPosition["right"] = "right";
LegendPosition["bottom"] = "bottom";
LegendPosition["left"] = "left";
LegendPosition["chartArea"] = "chartArea";
})(LegendPosition || (LegendPosition = {}));
var LegendAlign;
(function (LegendAlign) {
LegendAlign["start"] = "start";
LegendAlign["center"] = "center";
LegendAlign["end"] = "end";
})(LegendAlign || (LegendAlign = {}));
var LegendType;
(function (LegendType) {
LegendType["line"] = "line";
})(LegendType || (LegendType = {}));
const chartDefaultConfiguration = {
type: ChartType.line,
labels: {
data: [],
},
datasets: [],
options: {
interaction: {
intersect: false,
mode: InteractionMode.index,
},
responsive: true,
maintainAspectRatio: true,
animations: {
duration: 2000,
}
}
};
class AxesService {
mapConfiguration(configuration) {
const { labels, axes } = configuration;
const scales = axes?.dataAxes ?? {};
if (scales && !('x' in scales)) {
scales.x = {};
}
if (scales && !('ticks' in scales.x)) {
scales.x.ticks = {};
}
if (scales && labels?.skipItems) {
let count = 0;
scales.x.ticks.callback = (value, index) => {
const skipItems = labels.skipItems ?? 0;
const labelValue = configuration.labels.data[index] ? configuration.labels.data[index] : value;
count = index % skipItems;
return skipItems ? (count === 0) ? labelValue : null : labelValue;
};
}
return scales;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AxesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AxesService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AxesService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class DatasetService {
constructor() {
this.defaultColors = [
[255, 99, 132],
[54, 162, 235],
[255, 206, 86],
[75, 192, 192],
[220, 220, 220],
[247, 70, 74],
[70, 191, 189],
[253, 180, 92],
[148, 159, 177],
[151, 187, 205],
[231, 233, 237],
[77, 83, 96]
];
}
mapDatasets(chartConfiguration, cx) {
let colorNumber = 0;
const outputDatasets = [];
const { type: chartType, datasets, legend } = chartConfiguration;
const { enabled: pointStyleEnabled, pointStyle } = legend?.labels ?? { enabled: false, pointStyle: undefined };
for (const inputDataset of datasets) {
outputDatasets.push(this.mapDataset(chartType, inputDataset, colorNumber, legend, cx));
colorNumber++;
if (colorNumber > (this.defaultColors.length - 1)) {
colorNumber = 0;
}
}
return outputDatasets;
}
mapDataset(chartType, inputDataset, colorNumber, legend, cx) {
let borderColor;
let backgroundColor;
if (inputDataset.isGradient) {
const gradientStroke = cx.createLinearGradient(500, 0, 100, 0);
gradientStroke.addColorStop(0, this.toRGBA(this.defaultColors[0], 1));
gradientStroke.addColorStop(1, this.toRGBA(this.defaultColors[1], 1));
borderColor = gradientStroke;
backgroundColor = gradientStroke;
}
else if ((['pie', 'doughnut', 'polarArea'].includes(chartType)) && !inputDataset.type) {
const { borderColorList: computedBorderColorList, backgroundColorList: computedBackgroundColorList } = this.getPieDoughnutPolarAreaColors(inputDataset);
borderColor = computedBorderColorList;
backgroundColor = computedBackgroundColorList;
}
else {
borderColor = this.toRGBA(this.defaultColors[colorNumber], 1);
if (!!inputDataset.border) {
if (inputDataset.border[0]?.color) {
borderColor = inputDataset.border[0].color;
}
else if (inputDataset.border.color) {
borderColor = inputDataset.border.color;
}
}
if (!inputDataset.backgroundColor) {
if (inputDataset.fill) {
backgroundColor = this.toRGBA(this.defaultColors[colorNumber], 0.8);
}
else {
backgroundColor = 'transparent';
}
}
}
const { enabled: pointStyleEnabled, pointStyle } = legend?.labels ?? { enabled: false, pointStyle: undefined };
const outputDataset = {
type: inputDataset.type,
label: inputDataset.label,
data: inputDataset.data,
yAxisID: inputDataset.yAxisID,
fill: inputDataset.fill,
backgroundColor: inputDataset.backgroundColor ?? backgroundColor,
borderColor: ('border' in inputDataset && 'color' in inputDataset.border) ? inputDataset.border.color : borderColor,
borderWidth: ('border' in inputDataset && 'width' in inputDataset.border) ? inputDataset.border.width : 2,
pointRadius: inputDataset?.pointRadius ?? 5,
...(pointStyleEnabled && pointStyle && { pointStyle }),
datalabels: inputDataset.datalabels,
};
this.fillForScatterChart(chartType, inputDataset, outputDataset);
return outputDataset;
}
// TODO: why to pass background colors similar to the legacy component
getPieDoughnutPolarAreaColors(dataset) {
const backgroundColorList = [];
const borderColorList = [];
let colorNumber = 0;
for (let j = 0; j < dataset.data.length; j++) {
if (colorNumber > (this.defaultColors.length - 1)) {
colorNumber = 0;
}
if (dataset.border && Array.isArray(dataset.border) && dataset.border[j] && 'color' in dataset.border[j]) {
borderColorList.push(this.toRGBA(dataset.border[j].color, 1));
}
else {
borderColorList.push(this.toRGBA(this.defaultColors[colorNumber], 1));
}
if (dataset.backgroundColor && Array.isArray(dataset.backgroundColor) && dataset.backgroundColor[j]) {
backgroundColorList.push(this.toRGBA(dataset.backgroundColor[j], 1));
}
else {
backgroundColorList.push(this.toRGBA(this.defaultColors[colorNumber], 1));
}
colorNumber++;
}
return { backgroundColorList, borderColorList };
}
toRGBA(colour, alpha = 1) {
return `rgba(${colour.concat(alpha).join(',')})`;
}
fillForScatterChart(chartType, inputDataset, outputDataset) {
if (chartType !== ChartType.scatter && inputDataset.type !== 'scatter') {
return;
}
const scatterInputDataset = inputDataset;
outputDataset['showLine'] = scatterInputDataset.showLine;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DatasetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DatasetService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: DatasetService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
const defaultAnnotationColor = 'rgb(229, 60, 41)';
const defaultAnnotationFontColor = 'rgb(255,255,255)';
const defaultLineAnnotationConfiguration = {
axisID: 'y',
drawTime: AnnotationDrawTime.afterDatasetsDraw,
orientation: LineAnnotationOrientation.vertical,
border: {
width: 2,
color: defaultAnnotationColor,
dash: false,
},
label: {
border: {
width: 1,
color: defaultAnnotationColor,
radius: 3,
},
font: {
color: defaultAnnotationFontColor,
style: 'normal',
},
position: AnnotationLabelLabelPosition.center,
backgroundColor: defaultAnnotationColor,
}
};
class AnnotationService {
mapAnnotations(annotations) {
if (!annotations || annotations.length === 0) {
return undefined;
}
const computedAnnotations = [];
for (const annotation of annotations) {
if (this.isBoxAnnotation(annotation)) {
computedAnnotations.push(this.mapBoxAnnotation(annotation));
}
else if (this.isPointAnnotation(annotation)) {
computedAnnotations.push(this.mapPointAnnotation(annotation));
}
else if (this.isLineAnnotation(annotation)) {
computedAnnotations.push(this.mapLineAnnotation({
...defaultLineAnnotationConfiguration,
...annotation,
border: {
...defaultLineAnnotationConfiguration.border,
...annotation.border,
},
label: {
...defaultLineAnnotationConfiguration.label,
...annotation.label ?? undefined,
border: {
...defaultLineAnnotationConfiguration.label.border,
...annotation.label?.border ?? undefined,
},
font: {
...defaultLineAnnotationConfiguration.label.font,
...annotation.label?.font ?? undefined,
}
}
}));
}
else {
// error
}
}
return computedAnnotations;
}
mapBoxAnnotation(annotation) {
return {
type: AnnotationType.box,
backgroundColor: annotation.backgroundColor ?? 'transparent',
borderColor: annotation.border?.color ?? undefined,
borderRadius: annotation.border?.radius ?? undefined,
borderWidth: annotation.border?.width ?? 2,
xMax: annotation.limits.x.max,
xMin: annotation.limits.x.min,
yMax: annotation.limits.y.max,
yMin: annotation.limits.y.min,
drawTime: annotation.drawTime ?? AnnotationDrawTime.afterDatasetsDraw,
};
}
mapPointAnnotation(annotation) {
return {
type: AnnotationType.point,
xValue: annotation.x,
yValue: annotation.y,
xScaleID: annotation.xAxisID,
yScaleID: annotation.yAxisID,
radius: annotation.radius ?? 2,
backgroundColor: annotation.backgroundColor ?? 'transparent',
borderWidth: annotation.border?.width ?? 2,
borderColor: annotation.border?.color ?? undefined,
drawTime: annotation.drawTime ?? AnnotationDrawTime.afterDatasetsDraw,
};
}
mapLineAnnotation(annotation) {
let computedLabel;
const isVertical = annotation.orientation === LineAnnotationOrientation.vertical;
const endValue = annotation.endValue ?? annotation.value;
const { label } = annotation;
if (label) {
computedLabel = {
display: label.display,
content: label.text,
backgroundColor: label.backgroundColor,
position: label.position,
borderColor: label.border.color,
borderWidth: label.border.width,
borderRadius: label.border.radius,
fontColor: label.font.color,
fontStyle: label.font.style,
};
}
return {
type: AnnotationType.line,
scaleID: isVertical ? 'x' : annotation.axisID,
backgroundColor: annotation.backgroundColor ?? undefined,
borderColor: annotation.border?.color ?? undefined,
borderWidth: annotation.border?.width ?? 2,
borderDash: annotation.border?.dash ? [5, 15] : undefined,
value: annotation.value,
endValue: endValue,
label: computedLabel,
drawTime: annotation.drawTime ?? AnnotationDrawTime.afterDatasetsDraw,
};
}
isBoxAnnotation(annotation) {
return 'limits' in annotation;
}
isPointAnnotation(annotation) {
return 'type' in annotation && annotation.type === AnnotationType.point;
}
isLineAnnotation(annotation) {
return 'type' in annotation && annotation.type === AnnotationType.line;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AnnotationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AnnotationService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AnnotationService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class LegendService {
mapLegend(legend) {
return {
display: legend?.enabled ?? true,
...(legend?.title && {
title: {
enabled: legend.title.enabled,
text: legend.title.text,
}
}),
position: legend?.position ?? LegendPosition.top,
align: legend?.align ?? LegendAlign.center,
labels: {
usePointStyle: legend?.labels?.enabled ?? false,
pointStyle: legend?.labels?.pointStyle ?? undefined,
},
...(legend?.disabledClickEvent && { onClick: () => { } }),
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: LegendService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: LegendService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: LegendService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class TooltipService {
mapTooltip(chartConfiguration) {
const { tooltip } = chartConfiguration;
const { enabled: pointStyleEnabled } = chartConfiguration.legend?.labels ?? { enabled: false };
return {
enabled: tooltip?.enabled ?? true,
usePointStyle: pointStyleEnabled,
callbacks: {
title: (tooltipItems) => {
const enabled = tooltip?.title?.enabled ?? false;
if (!enabled) {
return null;
}
let title = tooltip?.title?.text ?? undefined;
const prefix = tooltip?.title?.prefix ?? undefined;
if (!title) {
title = tooltipItems[0].label;
}
return prefix ? `${prefix.trim()} ${title.trim()}` : title.trim();
}
}
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TooltipService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class ClickService {
map(userOnClick) {
if (userOnClick) {
return (event, elements, chart) => {
const clickPoint = this.toClickPoint(event, chart);
userOnClick(clickPoint);
};
}
}
toClickPoint(event, chart) {
return {
x: chart.scales.x.getValueForPixel(event.x),
y: chart.scales.y.getValueForPixel(event.y)
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ClickService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ClickService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ClickService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class ChartService {
constructor(axesService, datasetService, annotationService, legendService, tooltipService, clickService) {
this.axesService = axesService;
this.datasetService = datasetService;
this.annotationService = annotationService;
this.legendService = legendService;
this.tooltipService = tooltipService;
this.clickService = clickService;
}
mapConfiguration(configuration, cx) {
this._cx = cx;
const outputConfiguration = this.mapBasicInformation(configuration);
const axes = this.axesService.mapConfiguration(configuration);
const indexAxis = configuration.axes ? configuration.axes.mainAxis : 'x';
return {
...outputConfiguration,
options: {
...outputConfiguration.options,
indexAxis,
scales: axes,
},
};
}
mapBasicInformation(configuration) {
// Howto implement the fill flag
const chartConfiguration = {
...chartDefaultConfiguration,
...configuration,
options: {
...chartDefaultConfiguration.options,
...configuration.options ?? undefined,
interaction: {
...chartDefaultConfiguration.options.interaction,
...configuration.options?.interaction ?? undefined,
},
animations: {
...chartDefaultConfiguration.options.animations,
...configuration.options?.animations ?? undefined,
},
}
};
const { options: { line, datalabels } } = chartConfiguration;
const datasets = this.datasetService.mapDatasets(chartConfiguration, this._cx);
const annotations = this.annotationService.mapAnnotations(chartConfiguration.annotations);
const legend = this.legendService.mapLegend(chartConfiguration.legend);
const tooltip = this.tooltipService.mapTooltip(chartConfiguration);
const onClick = this.clickService.map(chartConfiguration.options.onClick);
return {
type: chartConfiguration.type,
data: {
labels: chartConfiguration.labels?.data,
datasets,
},
options: {
elements: {
line: {
tension: line?.tension ?? 0,
}
},
animation: {
duration: chartConfiguration.options.animations.duration,
},
responsive: chartConfiguration.options.responsive,
maintainAspectRatio: chartConfiguration.options.maintainAspectRatio,
interaction: chartConfiguration.options.interaction,
plugins: {
datalabels: {
display: datalabels?.display ?? false,
formatter: datalabels?.formatter ?? null,
},
annotation: {
annotations,
},
legend,
tooltip,
},
onClick,
},
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartService, deps: [{ token: AxesService }, { token: DatasetService }, { token: AnnotationService }, { token: LegendService }, { token: TooltipService }, { token: ClickService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: AxesService }, { type: DatasetService }, { type: AnnotationService }, { type: LegendService }, { type: TooltipService }, { type: ClickService }] });
const afterRenderingDetectorPlugin = {
id: 'afterRenderingDetector',
afterInit: (chart, args, options) => options.afterInitCallback(),
afterUpdate: (chart, args, options) => options.afterUpdateCallback(),
defaults: {
afterInitCallback: () => { },
afterUpdateCallback: () => { },
}
};
ChartJS.Chart.register(...ChartJS.registerables, ChartDataLabels, annotationPlugin, afterRenderingDetectorPlugin);
class ChartComponent {
constructor(chartService) {
this.chartService = chartService;
this.drawing = true;
}
ngAfterContentInit() {
ChartJS.Chart.defaults.interaction.intersect = this.config.options?.interaction?.intersect ?? false;
ChartJS.Chart.defaults.interaction.mode = this.config.options?.interaction?.mode ?? InteractionMode.index;
this.drawChart();
}
refresh() {
this.drawChart();
}
drawChart() {
if (!this.chartCanvas.nativeElement) {
return;
}
this.drawing = true;
if (this.chart) {
this.chart.destroy();
}
const cx = this.chartCanvas.nativeElement.getContext('2d');
let chartConfiguration = this.chartService.mapConfiguration(this.config, cx);
const animationEnabled = chartConfiguration.options.animation.duration > 0;
chartConfiguration = {
...chartConfiguration,
options: {
...chartConfiguration.options,
animation: {
...chartConfiguration.options.animation,
onComplete: () => {
this.drawing = false;
},
},
plugins: {
...chartConfiguration.options.plugins,
afterRenderingDetector: {
afterInitCallback: () => {
if (!animationEnabled) {
this.drawing = false;
}
},
afterUpdateCallback: () => {
if (!animationEnabled) {
this.drawing = false;
}
},
},
},
}
};
this.chart = new ChartJS.Chart(cx, chartConfiguration);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartComponent, deps: [{ token: ChartService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.2", type: ChartComponent, isStandalone: false, selector: "systelab-chart", inputs: { config: "config" }, viewQueries: [{ propertyName: "chartCanvas", first: true, predicate: ["chartCanvas"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"chart-container\" [class.h-100]=\"!config?.options?.maintainAspectRatio\">\n <canvas\n #chartCanvas\n id=\"chartCanvas\"\n class=\"container-fluid\"\n [class.drawing]=\"drawing\"\n ></canvas>\n</div>\n", styles: [".chart-container{display:block}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: ChartComponent, decorators: [{
type: Component,
args: [{ selector: 'systelab-chart', standalone: false, template: "<div class=\"chart-container\" [class.h-100]=\"!config?.options?.maintainAspectRatio\">\n <canvas\n #chartCanvas\n id=\"chartCanvas\"\n class=\"container-fluid\"\n [class.drawing]=\"drawing\"\n ></canvas>\n</div>\n", styles: [".chart-container{display:block}\n"] }]
}], ctorParameters: () => [{ type: ChartService }], propDecorators: { config: [{
type: Input
}], chartCanvas: [{
type: ViewChild,
args: ['chartCanvas', { static: true }]
}] } });
class SystelabChartsModule {
static forRoot(entryComponents) {
return {
ngModule: SystelabChartsModule
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: SystelabChartsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: SystelabChartsModule, declarations: [ChartLegacyComponent,
ChartComponent], imports: [CommonModule,
FormsModule], exports: [ChartLegacyComponent,
ChartComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: SystelabChartsModule, imports: [CommonModule,
FormsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: SystelabChartsModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
FormsModule,
],
declarations: [
ChartLegacyComponent,
ChartComponent,
],
exports: [
ChartLegacyComponent,
ChartComponent,
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { Annotation, AnnotationDrawTime, AnnotationLabelLabelPosition, AnnotationType, ChartBoxAnnotation, ChartComponent, ChartIntersectionSettings, ChartItem, ChartLabelAnnotation, ChartLabelColor, ChartLabelFont, ChartLabelPadding, ChartLabelPosition, ChartLabelSettings, ChartLabelText, ChartLegacyComponent, ChartLine, ChartLineAnnotation, ChartMultipleYAxisScales, ChartTooltipItem, ChartTooltipSettings, ChartType, InteractionMode, LegendAlign, LegendPointStyle, LegendPosition, LegendType, LineAnnotationOrientation, SystelabChartsModule, TooltipLegacyService };
//# sourceMappingURL=systelab-charts.mjs.map