import Chart from 'chart.js'; import { Prop, toNative } from 'vue-facing-decorator'; import TsxComponent, { Component } from '../../app/vuetsx'; import { hexToRGB } from './utils'; const defaultOptions = { maintainAspectRatio: false, tooltips: { bodySpacing: 4, mode: 'nearest', intersect: 0, position: 'nearest', xPadding: 10, yPadding: 10, caretPadding: 10, }, responsive: 1, scales: { yAxes: [ { gridLines: { zeroLineColor: 'transparent', drawBorder: false, }, ticks: { beginAtZero: true, userCallback( label, index, labels, ) { // when the floored value is the same as the value we have a whole number if (Math.floor(label) === label) { return label; } }, }, }, ], xAxes: [ { gridLines: { zeroLineColor: 'transparent', drawTicks: false, display: false, drawBorder: false, }, }, ], }, layout: { padding: { left: 0, right: 0, top: 15, bottom: 15 }, }, }; export enum PlotBackground { gradientSolid = 0, gradientTransparent = 1, } // TODO: Turn into approperiate types interface PlotArgs { id?: string; cssClasses?: string; chartType?: string; labels?: string[]; datasets?: any[]; color?: string; chartData?: any[]; plugins?: any[]; title?: string; height?: number; extraOptions?: any[]; backgroundColor?: PlotBackground; } @Component class PlotComponent extends TsxComponent implements PlotArgs { @Prop() id!: string; @Prop() cssClasses!: string; @Prop() chartType!: string; @Prop() labels!: string[]; @Prop() datasets!: any[]; @Prop() color!: string; @Prop() chartData!: any[]; @Prop() plugins!: any[]; @Prop() title!: string; @Prop() height!: number; @Prop() extraOptions: any[]; @Prop() backgroundColor: PlotBackground; _chart: Chart | null = null; _plugins = this.plugins; addPlugin(plugin: any) { this._plugins.push(plugin); } renderChart(data: any, options: any) { this.destroyChart(); this._chart = new Chart(this.getCanvasContext(), { type: this.chartType, data, options, plugins: this._plugins, }); } destroyChart() { if (this._chart) { try { this._chart.destroy(); } catch (e) { } this._chart = null; } } getGrandientFill() { const color = this.getBaseColor(); const canvasContext = this.getCanvasContext(); if (this.backgroundColor == PlotBackground.gradientSolid || this.backgroundColor == null) { const purple_orange_gradient = canvasContext.createLinearGradient( 0, 0, 0, 600, ); purple_orange_gradient.addColorStop(0, 'orange'); purple_orange_gradient.addColorStop(1, 'purple'); return purple_orange_gradient; } else if (this.backgroundColor == PlotBackground.gradientTransparent) { const gradientFill = canvasContext.createLinearGradient( 0, this.height, 0, 50, ); gradientFill.addColorStop(0, 'rgba(128, 182, 244, 0)'); gradientFill.addColorStop(0.6, hexToRGB(color, 0.8)); return gradientFill; } } getBaseColor(): string { const fallBackColor = '#f96332'; const color = this.color || fallBackColor; return color; } getCanvasContext(): CanvasRenderingContext2D { return (this.$refs.canvas as HTMLCanvasElement).getContext('2d'); } getChartOptions() { return Object.assign(defaultOptions, this.extraOptions || {}); } bindChart() { this.$nextTick(() => { const color = this.getBaseColor(); const background = this.getGrandientFill(); this.renderChart({ labels: this.labels || [], datasets: this.datasets ? this.datasets : [ { label: this.title || '', backgroundColor: background, borderColor: color, pointBorderColor: '#FFF', pointBackgroundColor: color, pointBorderWidth: 2, pointHoverRadius: 4, pointHoverBorderWidth: 1, pointRadius: 4, fill: true, borderWidth: 1, data: this.chartData || [], }, ], }, this.getChartOptions()); }); } mounted() { this.bindChart(); } updated() { this.bindChart(); } beforeUnmount() { this.destroyChart(); } render(h) { if (this.id == null || this.id.length == 0) { this.id = `chrt-${(Math.floor(Math.random() * 999999999) + 1).toString()}`; } if (this.height == null || this.height == null) { this.height = 400; } return (
); } } const Plot = toNative(PlotComponent); export default Plot;