2021-10-28 07:48:51 +00:00
|
|
|
import { Chart, ChartConfiguration, ChartData, ChartOptions, Plugin, Tick } from "chart.js";
|
2021-05-06 11:02:07 +00:00
|
|
|
import { Legend, Tooltip } from "chart.js";
|
2021-10-28 07:48:51 +00:00
|
|
|
import { BarController, DoughnutController, LineController } from "chart.js";
|
2021-05-06 11:02:07 +00:00
|
|
|
import { ArcElement, BarElement } from "chart.js";
|
2021-10-28 07:48:51 +00:00
|
|
|
import { LinearScale, TimeScale } from "chart.js";
|
2021-04-05 09:28:22 +00:00
|
|
|
import "chartjs-adapter-moment";
|
2021-09-21 09:31:37 +00:00
|
|
|
|
2021-12-14 21:04:16 +00:00
|
|
|
import { t } from "@lingui/macro";
|
|
|
|
|
2021-10-28 07:48:51 +00:00
|
|
|
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
2021-11-04 21:34:48 +00:00
|
|
|
import { property } from "lit/decorators.js";
|
2021-09-21 09:31:37 +00:00
|
|
|
|
2021-08-03 15:52:21 +00:00
|
|
|
import { EVENT_REFRESH } from "../../constants";
|
2021-09-21 09:31:37 +00:00
|
|
|
import { FONT_COLOUR_DARK_MODE, FONT_COLOUR_LIGHT_MODE } from "../../pages/flows/FlowDiagram";
|
2021-03-08 10:14:00 +00:00
|
|
|
|
2021-05-06 11:02:07 +00:00
|
|
|
Chart.register(Legend, Tooltip);
|
|
|
|
Chart.register(LineController, BarController, DoughnutController);
|
|
|
|
Chart.register(ArcElement, BarElement);
|
|
|
|
Chart.register(TimeScale, LinearScale);
|
2021-03-08 10:14:00 +00:00
|
|
|
|
|
|
|
export abstract class AKChart<T> extends LitElement {
|
|
|
|
abstract apiRequest(): Promise<T>;
|
2021-05-05 20:15:11 +00:00
|
|
|
abstract getChartData(data: T): ChartData;
|
2021-03-08 10:14:00 +00:00
|
|
|
|
|
|
|
chart?: Chart;
|
|
|
|
|
2021-05-06 11:02:07 +00:00
|
|
|
@property()
|
|
|
|
centerText?: string;
|
|
|
|
|
2021-05-06 14:25:29 +00:00
|
|
|
fontColour = FONT_COLOUR_LIGHT_MODE;
|
|
|
|
|
2021-03-08 10:14:00 +00:00
|
|
|
static get styles(): CSSResult[] {
|
2021-08-03 15:52:21 +00:00
|
|
|
return [
|
|
|
|
css`
|
|
|
|
.container {
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
canvas {
|
|
|
|
width: 100px;
|
|
|
|
height: 100px;
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
];
|
2021-03-08 10:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
window.addEventListener("resize", () => {
|
|
|
|
if (this.chart) {
|
|
|
|
this.chart.resize();
|
|
|
|
}
|
|
|
|
});
|
2021-05-10 13:57:52 +00:00
|
|
|
window.addEventListener(EVENT_REFRESH, () => {
|
|
|
|
this.apiRequest().then((r: T) => {
|
|
|
|
if (!this.chart) return;
|
|
|
|
this.chart.data = this.getChartData(r);
|
|
|
|
this.chart.update();
|
|
|
|
});
|
|
|
|
});
|
2021-05-06 14:25:29 +00:00
|
|
|
const matcher = window.matchMedia("(prefers-color-scheme: light)");
|
|
|
|
const handler = (ev?: MediaQueryListEvent) => {
|
|
|
|
if (ev?.matches || matcher.matches) {
|
|
|
|
this.fontColour = FONT_COLOUR_LIGHT_MODE;
|
|
|
|
} else {
|
|
|
|
this.fontColour = FONT_COLOUR_DARK_MODE;
|
|
|
|
}
|
|
|
|
this.chart?.update();
|
|
|
|
};
|
|
|
|
matcher.addEventListener("change", handler);
|
|
|
|
handler();
|
2021-03-08 10:14:00 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 13:57:52 +00:00
|
|
|
firstUpdated(): void {
|
|
|
|
this.apiRequest().then((r) => {
|
2021-09-21 09:31:37 +00:00
|
|
|
const canvas = this.shadowRoot?.querySelector<HTMLCanvasElement>("canvas");
|
2021-05-10 13:57:52 +00:00
|
|
|
if (!canvas) {
|
|
|
|
console.warn("Failed to get canvas element");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
if (!ctx) {
|
|
|
|
console.warn("failed to get 2d context");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
this.chart = this.configureChart(r, ctx);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-05-06 11:02:07 +00:00
|
|
|
getChartType(): string {
|
|
|
|
return "bar";
|
|
|
|
}
|
|
|
|
|
|
|
|
getPlugins(): Plugin[] {
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
id: "center-text",
|
|
|
|
beforeDraw: (chart) => {
|
|
|
|
if (!chart.ctx) return;
|
|
|
|
if (!this.centerText) return;
|
|
|
|
const width = chart.width || 0;
|
|
|
|
const height = chart.height || 0;
|
|
|
|
|
|
|
|
const fontSize = (height / 114).toFixed(2);
|
2021-10-14 10:50:52 +00:00
|
|
|
chart.ctx.font = `${fontSize}em Overpass, Arial, sans-serif`;
|
2021-05-06 11:02:07 +00:00
|
|
|
chart.ctx.textBaseline = "middle";
|
2021-05-06 14:25:29 +00:00
|
|
|
chart.ctx.fillStyle = this.fontColour;
|
2021-05-06 11:02:07 +00:00
|
|
|
|
2021-08-03 15:52:21 +00:00
|
|
|
const textX = Math.round(
|
|
|
|
(width - chart.ctx.measureText(this.centerText).width) / 2,
|
|
|
|
);
|
2021-05-06 11:02:07 +00:00
|
|
|
const textY = height / 2;
|
|
|
|
|
|
|
|
chart.ctx.fillText(this.centerText, textX, textY);
|
2021-08-03 15:52:21 +00:00
|
|
|
},
|
|
|
|
},
|
2021-05-06 11:02:07 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2021-12-14 21:04:16 +00:00
|
|
|
timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string {
|
|
|
|
const valueStamp = ticks[index];
|
|
|
|
const delta = Date.now() - valueStamp.value;
|
|
|
|
const ago = Math.round(delta / 1000 / 3600);
|
|
|
|
return t`${ago} hours ago`;
|
|
|
|
}
|
|
|
|
|
2021-05-06 11:02:07 +00:00
|
|
|
getOptions(): ChartOptions {
|
|
|
|
return {
|
|
|
|
maintainAspectRatio: false,
|
|
|
|
scales: {
|
|
|
|
x: {
|
|
|
|
type: "time",
|
|
|
|
display: true,
|
|
|
|
ticks: {
|
2021-12-14 21:04:16 +00:00
|
|
|
callback: (tickValue: string | number, index: number, ticks: Tick[]) => {
|
|
|
|
return this.timeTickCallback(tickValue, index, ticks);
|
2021-03-08 10:14:00 +00:00
|
|
|
},
|
2021-05-06 11:02:07 +00:00
|
|
|
autoSkip: true,
|
|
|
|
maxTicksLimit: 8,
|
2021-04-05 09:28:22 +00:00
|
|
|
},
|
2021-05-06 11:02:07 +00:00
|
|
|
stacked: true,
|
|
|
|
grid: {
|
|
|
|
color: "rgba(0, 0, 0, 0)",
|
|
|
|
},
|
2021-08-03 15:52:21 +00:00
|
|
|
offset: true,
|
2021-03-08 10:14:00 +00:00
|
|
|
},
|
2021-05-06 11:02:07 +00:00
|
|
|
y: {
|
|
|
|
type: "linear",
|
|
|
|
display: true,
|
|
|
|
stacked: true,
|
|
|
|
grid: {
|
|
|
|
color: "rgba(0, 0, 0, 0)",
|
|
|
|
},
|
2021-08-03 15:52:21 +00:00
|
|
|
},
|
2021-03-08 10:14:00 +00:00
|
|
|
},
|
2021-05-06 11:02:07 +00:00
|
|
|
} as ChartOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
configureChart(data: T, ctx: CanvasRenderingContext2D): Chart {
|
|
|
|
const config = {
|
|
|
|
type: this.getChartType(),
|
|
|
|
data: this.getChartData(data),
|
|
|
|
options: this.getOptions(),
|
|
|
|
plugins: this.getPlugins(),
|
2021-04-05 09:28:22 +00:00
|
|
|
};
|
|
|
|
return new Chart(ctx, config as ChartConfiguration);
|
2021-03-08 10:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render(): TemplateResult {
|
2021-05-06 11:02:07 +00:00
|
|
|
return html`
|
|
|
|
<div class="container">
|
|
|
|
<canvas></canvas>
|
|
|
|
</div>
|
|
|
|
`;
|
2021-03-08 10:14:00 +00:00
|
|
|
}
|
|
|
|
}
|