import type { LineChartArgs, LineChartDataArgs, LineChartDataSet, LineChartTicksX } from './ts/line-chart-contracts'; import Chart from 'chart.js'; import { Prop, toNative } from 'vue-facing-decorator'; import TsxComponent, { Component } from '../../app/vuetsx'; import ChartColorHelper from './ts/color-helper'; import 'chartjs-plugin-zoom'; import { globalState } from '../../app/global-state'; @Component class LineChartComponent extends TsxComponent implements LineChartArgs { @Prop() cssClasses!: string; @Prop() chartData!: LineChartDataArgs; @Prop() height!: number; @Prop() showLegend!: boolean; @Prop() tickLimit!: number; @Prop() customTickFormatX?: (value?: any, index?: number, values?: any) => any | any[]; _chart: Chart | null = null; _uuid: string = null; destroyChart() { if (this._chart) { try { this._chart.destroy(); } catch (e) { } this._chart = null; } } getCanvasContext(): CanvasRenderingContext2D { return (this.$refs.canvas as HTMLCanvasElement).getContext('2d'); } getData(): LineChartDataSet[] { if (this.chartData == null || this.chartData.dataSets == null) { return [] as any; } return this.chartData.dataSets; } getColors(count: number): string[] { return ChartColorHelper.getColors(count); } bindChart() { if (!globalState.windowExists) { return; } this.$nextTick(() => { const canvas = document.getElementById(this._uuid) as HTMLCanvasElement; canvas.width = canvas.parentElement.offsetWidth; const ctx = canvas.getContext('2d'); this._chart = new Chart(ctx, this.getChartBindingData()); }); } getChartBindingData() { const data = this.getData(); const datasets: any[] = []; const labels: string[] = []; const colorArr = this.getColors(data.length); const showLegend = typeof this.showLegend === 'undefined' ? false : this.showLegend; let ticksX: LineChartTicksX; data.forEach((dataset, i) => { const itemData: number[] = []; dataset.items.forEach((datasetItem) => { itemData.push(datasetItem.value); if (!labels.includes(datasetItem.caption as string)) { labels.push(datasetItem.caption as string); } }); datasets.push({ label: dataset.label, data: itemData, fill: data.length < 2, borderColor: colorArr[0], backgroundColor: `${colorArr[0].slice(0, -1)},0.4)`, pointBackgroundColor: colorArr[0], pointBorderColor: colorArr[0], }); }); ticksX = { maxTicksLimit: this.tickLimit, maxRotation: 0, minRotation: 0, }; if (this.customTickFormatX != null) { ticksX.callback = (value) => { return this.customTickFormatX(value); }; } return { type: 'line', data: { labels, datasets, }, options: { elements: { line: { tension: 0.2, }, }, legend: { display: showLegend, }, responsive: true, maintainAspectRatio: false, title: { display: false, text: '', }, tooltips: { mode: 'index', intersect: false, }, hover: { mode: 'nearest', intersect: true, }, scales: { xAxes: [ { display: true, scaleLabel: { display: true, labelString: '', }, ticks: ticksX, }, ], yAxes: [ { display: true, scaleLabel: { display: true, }, }, ], }, pan: { enabled: true, mode: 'x', }, zoom: { enabled: true, mode: 'x', speed: 75.5, sensitivity: 0, }, }, }; } updateChartInstance() { if (this._chart != null) { setTimeout(() => { const bindingData = this.getChartBindingData(); this._chart.data.labels = bindingData.data.labels; this._chart.data.datasets = bindingData.data.datasets; this._chart.update(); }, 10); } else { this.bindChart(); } } mounted() { this.bindChart(); const z = 'z'; } updated() { if (this._chart != null) { this.updateChartInstance(); } else { this.bindChart(); } } beforeUnmount() { this.destroyChart(); } render(h) { if (this._uuid == null || this._uuid.length == 0) { this._uuid = `chrt-${(Math.floor(Math.random() * 999999999) + 1).toString()}`; } let height = this.height; if (height == null || height < 1) { height = 400; } if (this.chartData == null) { height = 400; } return (
); } } const LineChart = toNative(LineChartComponent); export default LineChart;