import Chart from 'chart.js'; import { Prop, toNative } from 'vue-facing-decorator'; import { globalState } from '../../app/global-state'; import PowerduckState from '../../app/powerduck-state'; import TsxComponent, { Component } from '../../app/vuetsx'; import ChartColorHelper from './ts/color-helper'; import './css/pie-chart.css'; export interface PieDataArgs { items: PieData[]; } export interface PieData { caption: string; value: number; } interface PieDataOperational extends PieData { color: string; } export enum PieChartType { Doughnut = 'doughnut', Pie = 'pie', } interface PieArgs { chartData: PieDataArgs; cssClasses?: string; type?: PieChartType; getSize?: (innerWidth?: number) => number; } @Component class PieChartComponent extends TsxComponent implements PieArgs { @Prop() cssClasses!: string; @Prop() chartData!: PieDataArgs; @Prop() type!: PieChartType; @Prop() getSize?: (innerWidth?: number) => number; size = 220; _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(): PieDataOperational[] { if (this.chartData == null || this.chartData.items == null) { return [] as any; } const colors = this.getColors(this.chartData.items.length); const retVal: PieDataOperational[] = []; this.chartData.items.forEach((dataItem, i) => { retVal.push({ caption: dataItem.caption, value: dataItem.value, color: colors[i], }); }); return retVal; } getColors(count: number): string[] { return ChartColorHelper.getColors(count); } bindChart() { this.$nextTick(() => { const data = this.getData(); this.destroyChart(); this.$nextTick(() => { // console.log(this.getCanvasContext() == null); this._chart = new Chart(this.getCanvasContext(), { type: this.type || PieChartType.Doughnut, data: { labels: data.map(p => p.caption), datasets: [ { label: '', data: data.map(p => p.value), backgroundColor: this.getColors(data.length), }, ], }, options: { responsive: true, maintainAspectRatio: false, animation: { animateRotate: false, animateScale: false, }, legend: { display: false, }, }, }); }); }); } updateChartInstance() { const newData = this.getData(); const chart = this._chart; const dataset = chart.data.datasets[0]; chart.data.labels = []; dataset.data = []; dataset.backgroundColor = []; newData.forEach((dataItem) => { chart.data.labels.push(dataItem.caption); dataset.data.push(dataItem.value); }); this.getColors(newData.length).forEach((color) => { dataset.backgroundColor.push(color); }); chart.update(); } mounted() { this.bindChart(); } updated() { if (this._chart != null) { this.updateChartInstance(); } else { this.bindChart(); } } beforeUnmount() { this.destroyChart(); } created() { globalState.addEventListener('resize', this.setSize); } unmounted() { globalState.removeEventListener('resize', this.setSize); } setSize() { if (this.getSize != null) { this.size = this.getSize(globalState.innerWidth); } } render(h) { const chartPieInnerStyle = `width: ${this.size}px; height: ${this.size}`; if (this._uuid == null || this._uuid.length == 0) { this._uuid = `chrt-${(Math.floor(Math.random() * 999999999) + 1).toString()}`; } const data = this.getData(); if (this.chartData != null) { data.splice( 0, 0, { caption: PowerduckState.getResourceValue('allTogether'), value: data.length > 0 ? data.reduce((ty, u) => ty + u.value, 0) : 0, color: '#ffffff', }, ); } return (
{data.map(dataItem => (
{dataItem.caption}  [ {dataItem.value} ]
))}
); } } const PieChart = toNative(PieChartComponent); export default PieChart;