web/satellite: migrate VChart component to use SFC composition API
Upgraded chart.js dependency to V4. Removed vue-chart and types/chart.js dependencies. Refactored VChart component to rely only on chart.js dep without any additional wrappers. Refactored VChart component to use SFC composition API. Change-Id: Ic5e0131bff413f3205d4449db930644d113fe36d
This commit is contained in:
parent
6c49cc883e
commit
37d7c0efbf
@ -12,7 +12,7 @@
|
||||
"@hcaptcha/vue-hcaptcha": "0.3.2",
|
||||
"aws-sdk": "2.1128.0",
|
||||
"bip39": "3.0.4",
|
||||
"chart.js": "2.9.4",
|
||||
"chart.js": "4.2.1",
|
||||
"core-js": "3.22.4",
|
||||
"graphql": "15.3.0",
|
||||
"load-script": "1.0.0",
|
||||
@ -24,7 +24,6 @@
|
||||
"stripe": "8.215.0",
|
||||
"util": "0.12.4",
|
||||
"vue": "2.7.10",
|
||||
"vue-chartjs": "3.5.1",
|
||||
"vue-class-component": "7.2.6",
|
||||
"vue-clipboard2": "0.3.3",
|
||||
"vue-fragment": "1.6.0",
|
||||
@ -35,7 +34,6 @@
|
||||
"vuex": "3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chart.js": "2.9.36",
|
||||
"@types/filesystem": "0.0.32",
|
||||
"@types/jest": "27.5.0",
|
||||
"@types/node": "16.18.14",
|
||||
@ -2705,6 +2703,11 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"node_modules/@leichtgewicht/ip-codec": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
@ -2972,14 +2975,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chart.js": {
|
||||
"version": "2.9.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.36.tgz",
|
||||
"integrity": "sha512-lkG9C6O5TqKyW3JDWMfCedVUATH4AeQCo8Q/vflT/0VDj6KMcba+jr1wLe+IXQ+F9mVWn80DVsDnt6cPuGutIg==",
|
||||
"dependencies": {
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
@ -5605,29 +5600,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
|
||||
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz",
|
||||
"integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
|
||||
"dependencies": {
|
||||
"chartjs-color": "^2.1.0",
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-color": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
|
||||
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
|
||||
"dependencies": {
|
||||
"chartjs-color-string": "^0.6.0",
|
||||
"color-convert": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-color-string": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
|
||||
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0"
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
@ -5945,6 +5925,7 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@ -5952,7 +5933,8 @@
|
||||
"node_modules/color-convert/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
@ -13417,14 +13399,6 @@
|
||||
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
|
||||
@ -17784,21 +17758,6 @@
|
||||
"csstype": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-chartjs": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-3.5.1.tgz",
|
||||
"integrity": "sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==",
|
||||
"dependencies": {
|
||||
"@types/chart.js": "^2.7.55"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"chart.js": ">= 2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-class-component": {
|
||||
"version": "7.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
|
||||
@ -21016,6 +20975,11 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"@leichtgewicht/ip-codec": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
@ -21243,14 +21207,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/chart.js": {
|
||||
"version": "2.9.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.36.tgz",
|
||||
"integrity": "sha512-lkG9C6O5TqKyW3JDWMfCedVUATH4AeQCo8Q/vflT/0VDj6KMcba+jr1wLe+IXQ+F9mVWn80DVsDnt6cPuGutIg==",
|
||||
"requires": {
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
@ -23258,29 +23214,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"chart.js": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
|
||||
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz",
|
||||
"integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
|
||||
"requires": {
|
||||
"chartjs-color": "^2.1.0",
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"chartjs-color": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
|
||||
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
|
||||
"requires": {
|
||||
"chartjs-color-string": "^0.6.0",
|
||||
"color-convert": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"chartjs-color-string": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
|
||||
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0"
|
||||
"@kurkle/color": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
@ -23522,6 +23460,7 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
},
|
||||
@ -23529,7 +23468,8 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -29128,11 +29068,6 @@
|
||||
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||
},
|
||||
"mrmime": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
|
||||
@ -32329,14 +32264,6 @@
|
||||
"csstype": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"vue-chartjs": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-3.5.1.tgz",
|
||||
"integrity": "sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==",
|
||||
"requires": {
|
||||
"@types/chart.js": "^2.7.55"
|
||||
}
|
||||
},
|
||||
"vue-class-component": {
|
||||
"version": "7.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
|
||||
|
@ -17,7 +17,7 @@
|
||||
"@hcaptcha/vue-hcaptcha": "0.3.2",
|
||||
"aws-sdk": "2.1128.0",
|
||||
"bip39": "3.0.4",
|
||||
"chart.js": "2.9.4",
|
||||
"chart.js": "4.2.1",
|
||||
"core-js": "3.22.4",
|
||||
"graphql": "15.3.0",
|
||||
"load-script": "1.0.0",
|
||||
@ -29,7 +29,6 @@
|
||||
"stripe": "8.215.0",
|
||||
"util": "0.12.4",
|
||||
"vue": "2.7.10",
|
||||
"vue-chartjs": "3.5.1",
|
||||
"vue-class-component": "7.2.6",
|
||||
"vue-clipboard2": "0.3.3",
|
||||
"vue-fragment": "1.6.0",
|
||||
@ -40,7 +39,6 @@
|
||||
"vuex": "3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chart.js": "2.9.36",
|
||||
"@types/filesystem": "0.0.32",
|
||||
"@types/jest": "27.5.0",
|
||||
"@types/node": "16.18.14",
|
||||
|
@ -222,7 +222,7 @@ onMounted(() => {
|
||||
@media screen and (max-width: 500px) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
@ -243,12 +243,12 @@ onMounted(() => {
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
justify-content: flex-end;
|
||||
gap: 5px;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
width: 100%;
|
||||
justify-content: start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,192 +1,174 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<script lang="ts">
|
||||
import { Line } from 'vue-chartjs';
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
<template>
|
||||
<canvas :id="chartId" :width="width" :height="height" />
|
||||
</template>
|
||||
|
||||
import { ChartData, RenderChart } from '@/types/chart';
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import {
|
||||
CategoryScale,
|
||||
Chart as ChartJS,
|
||||
LinearScale,
|
||||
Tooltip as VTooltip,
|
||||
LineController,
|
||||
LineElement,
|
||||
Filler,
|
||||
PointElement,
|
||||
TooltipModel,
|
||||
ChartType,
|
||||
ChartOptions,
|
||||
ChartData,
|
||||
Plugin,
|
||||
} from 'chart.js';
|
||||
|
||||
ChartJS.register(LineElement, PointElement, VTooltip, Filler, LineController, CategoryScale, LinearScale);
|
||||
|
||||
const props = defineProps<{
|
||||
chartId: string,
|
||||
chartData: ChartData,
|
||||
tooltipConstructor: (tooltipModel: TooltipModel<ChartType>) => void,
|
||||
width: number,
|
||||
height: number,
|
||||
}>();
|
||||
|
||||
const chart = ref<ChartJS>();
|
||||
|
||||
/**
|
||||
* Used to filter days displayed on x-axis.
|
||||
* Returns a plugin which draws a dashed line under active datapoint.
|
||||
*/
|
||||
class DayShowingConditions {
|
||||
public constructor(
|
||||
public day: string,
|
||||
public daysArray: string[],
|
||||
) {}
|
||||
|
||||
public countMiddleDateValue(): number {
|
||||
return this.daysArray.length / 2;
|
||||
}
|
||||
|
||||
public isDayFirstOrLast(): boolean {
|
||||
return this.day === this.daysArray[0] || this.day === this.daysArray[this.daysArray.length - 1];
|
||||
}
|
||||
|
||||
public isDayAfterEighthDayOfTheMonth(): boolean {
|
||||
return this.daysArray.length > 8 && this.daysArray.length <= 31;
|
||||
}
|
||||
}
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
extends: Line,
|
||||
})
|
||||
export default class VChart extends Vue {
|
||||
@Prop({ default: () => () => { console.error('Tooltip constructor is undefined'); } })
|
||||
private tooltipConstructor: (tooltipModel) => void;
|
||||
@Prop({ default: {} })
|
||||
private readonly chartData: ChartData;
|
||||
|
||||
/**
|
||||
* Mounted hook after initial render.
|
||||
* Adds chart plugin to draw dashed line under data point.
|
||||
* Renders chart.
|
||||
*/
|
||||
public mounted(): void {
|
||||
(this as unknown as RenderChart).addPlugin({
|
||||
afterDatasetsDraw: (chart): void => {
|
||||
if (chart.tooltip._active && chart.tooltip._active.length) {
|
||||
const activePoint = chart.tooltip._active[0];
|
||||
const afterDatasetsDrawPlugin = computed((): Plugin => {
|
||||
return {
|
||||
id: 'afterDatasetsDraw',
|
||||
afterDatasetsDraw: (chart) => {
|
||||
if (chart.tooltip) {
|
||||
const activePoint = chart.tooltip.getActiveElements();
|
||||
if (activePoint[0]) {
|
||||
const ctx = chart.ctx;
|
||||
const y_axis = chart.scales['y-axis-0'];
|
||||
const tooltipPosition = activePoint.tooltipPosition();
|
||||
const yAxis = chart.scales['y'];
|
||||
const tooltipPosition = activePoint[0].element.tooltipPosition(true);
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.setLineDash([8, 5]);
|
||||
ctx.moveTo(tooltipPosition.x, tooltipPosition.y + 12);
|
||||
ctx.lineTo(tooltipPosition.x, y_axis.bottom);
|
||||
ctx.lineTo(tooltipPosition.x, yAxis.bottom);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = '#C8D3DE';
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns chart options.
|
||||
*/
|
||||
const chartOptions = computed((): ChartOptions => {
|
||||
return {
|
||||
responsive: false,
|
||||
maintainAspectRatio: false,
|
||||
animation: false,
|
||||
clip: false,
|
||||
layout: {
|
||||
padding: {
|
||||
top: 20,
|
||||
left: 10,
|
||||
right: 10,
|
||||
},
|
||||
});
|
||||
(this as unknown as RenderChart).renderChart(this.chartData, this.chartOptions);
|
||||
}
|
||||
|
||||
@Watch('chartData')
|
||||
private onDataChange(_news: Record<string, unknown>, _old: Record<string, unknown>) {
|
||||
/**
|
||||
* renderChart method is inherited from BaseChart which is extended by VChart.Line
|
||||
*/
|
||||
(this as unknown as RenderChart).renderChart(this.chartData, this.chartOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns chart options.
|
||||
*/
|
||||
public get chartOptions(): Record<string, unknown> {
|
||||
const filterCallback = this.filterDaysDisplayed;
|
||||
|
||||
return {
|
||||
responsive: false,
|
||||
maintainAspectRatios: false,
|
||||
animation: false,
|
||||
hover: {
|
||||
animationDuration: 0,
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0,
|
||||
},
|
||||
responsiveAnimationDuration: 0,
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
border: {
|
||||
display: false,
|
||||
},
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
suggestedMin: 0,
|
||||
suggestedMax: 150,
|
||||
ticks: {
|
||||
font: {
|
||||
family: 'sans-serif',
|
||||
},
|
||||
maxTicksLimit: 5,
|
||||
callback: function(value, _, ticks) {
|
||||
const numDigits = ticks[ticks.length - 2].value.toString().length;
|
||||
|
||||
const power = Math.floor((numDigits - 1) / 3);
|
||||
return (value as number) / Math.pow(1000, power);
|
||||
},
|
||||
},
|
||||
},
|
||||
x: {
|
||||
type: 'category',
|
||||
display: true,
|
||||
border: {
|
||||
display: false,
|
||||
},
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
font: {
|
||||
family: 'sans-serif',
|
||||
},
|
||||
autoSkip: true,
|
||||
maxRotation: 0,
|
||||
minRotation: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
layout: {
|
||||
padding: {
|
||||
top: 40,
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: this.chartData.labels.length === 1 ? 10 : 0,
|
||||
hoverRadius: 10,
|
||||
hitRadius: 8,
|
||||
},
|
||||
line: {
|
||||
tension: 0,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: true,
|
||||
ticks: {
|
||||
callback: function(value, _, ticks) {
|
||||
const numDigits = ticks[ticks.length - 2].toString().length;
|
||||
|
||||
const power = Math.floor((numDigits - 1) / 3);
|
||||
const result = value / Math.pow(1000, power);
|
||||
return result;
|
||||
},
|
||||
suggestedMin: 0,
|
||||
suggestedMax: 150,
|
||||
maxTicksLimit: 7,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
}],
|
||||
xAxes: [{
|
||||
display: true,
|
||||
ticks: {
|
||||
fontFamily: 'font_regular',
|
||||
autoSkip: false,
|
||||
maxRotation: 0,
|
||||
minRotation: 0,
|
||||
callback: filterCallback,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
}],
|
||||
},
|
||||
tooltips: {
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
axis: 'x',
|
||||
custom: (tooltipModel) => {
|
||||
this.tooltipConstructor(tooltipModel);
|
||||
external: (context) => {
|
||||
props.tooltipConstructor(context.tooltip);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Used as callback to filter days displayed on chart.
|
||||
*/
|
||||
private filterDaysDisplayed(day: string, _dayIndex: string, labelArray: string[]): string | undefined {
|
||||
const eighthDayOfTheMonth = 8;
|
||||
const isBeforeEighthDayOfTheMonth = labelArray.length <= eighthDayOfTheMonth;
|
||||
const dayShowingConditions = new DayShowingConditions(day, labelArray);
|
||||
onMounted(() => {
|
||||
chart.value = new ChartJS(
|
||||
document.getElementById(props.chartId) as HTMLCanvasElement,
|
||||
{
|
||||
type: 'line',
|
||||
data: props.chartData,
|
||||
options: chartOptions.value,
|
||||
plugins: [afterDatasetsDrawPlugin.value],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (isBeforeEighthDayOfTheMonth || this.areDaysShownOnEvenDaysAmount(dayShowingConditions)
|
||||
|| this.areDaysShownOnNotEvenDaysAmount(dayShowingConditions)) {
|
||||
return day;
|
||||
}
|
||||
}
|
||||
onUnmounted(() => {
|
||||
chart.value?.destroy();
|
||||
});
|
||||
|
||||
/**
|
||||
* Indicates if days are shown on even days amount.
|
||||
*/
|
||||
private areDaysShownOnEvenDaysAmount(dayShowingConditions: DayShowingConditions): boolean {
|
||||
const isDaysAmountEven = dayShowingConditions.daysArray.length % 2 === 0;
|
||||
const isDateValueInMiddleInEvenAmount = dayShowingConditions.day ===
|
||||
dayShowingConditions.daysArray[dayShowingConditions.countMiddleDateValue() - 1];
|
||||
|
||||
return dayShowingConditions.isDayFirstOrLast() || (isDaysAmountEven
|
||||
&& dayShowingConditions.isDayAfterEighthDayOfTheMonth() && isDateValueInMiddleInEvenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if days are shown on not even days amount.
|
||||
*/
|
||||
private areDaysShownOnNotEvenDaysAmount(dayShowingConditions: DayShowingConditions): boolean {
|
||||
const isDaysAmountNotEven = dayShowingConditions.daysArray.length % 2 !== 0;
|
||||
const isDateValueInMiddleInNotEvenAmount = dayShowingConditions.day
|
||||
=== dayShowingConditions.daysArray[Math.floor(dayShowingConditions.countMiddleDateValue())];
|
||||
|
||||
return dayShowingConditions.isDayFirstOrLast() || (isDaysAmountNotEven
|
||||
&& dayShowingConditions.isDayAfterEighthDayOfTheMonth() && isDateValueInMiddleInNotEvenAmount);
|
||||
}
|
||||
}
|
||||
watch(() => props.chartData, () => {
|
||||
chart.value?.destroy();
|
||||
chart.value = new ChartJS(
|
||||
document.getElementById(props.chartId) as HTMLCanvasElement,
|
||||
{
|
||||
type: 'line',
|
||||
data: props.chartData,
|
||||
options: chartOptions.value,
|
||||
plugins: [afterDatasetsDrawPlugin.value],
|
||||
},
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
<template>
|
||||
<VChart
|
||||
id="bandwidth-chart"
|
||||
:key="chartKey"
|
||||
chart-id="bandwidth-chart"
|
||||
:chart-data="chartData"
|
||||
:width="width"
|
||||
:height="height"
|
||||
@ -14,8 +14,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { ChartType, TooltipModel, ChartData } from 'chart.js';
|
||||
|
||||
import { ChartData, Tooltip, TooltipParams, TooltipModel, ChartTooltipData } from '@/types/chart';
|
||||
import { Tooltip, TooltipParams, ChartTooltipData } from '@/types/chart';
|
||||
import { DataStamp } from '@/types/projects';
|
||||
import { ChartUtils } from '@/utils/chart';
|
||||
|
||||
@ -47,23 +48,40 @@ const chartData = computed((): ChartData => {
|
||||
const secondaryData: number[] = props.allocatedData.map(el => el.value);
|
||||
const xAxisDateLabels: string[] = ChartUtils.daysDisplayedOnChart(props.since, props.before);
|
||||
|
||||
return new ChartData(
|
||||
xAxisDateLabels,
|
||||
'rgba(226, 220, 255, .3)',
|
||||
'#c5baff',
|
||||
'#c5baff',
|
||||
mainData,
|
||||
'rgba(226, 220, 255, .7)',
|
||||
'#a18eff',
|
||||
'#a18eff',
|
||||
secondaryData,
|
||||
);
|
||||
return {
|
||||
labels: xAxisDateLabels,
|
||||
datasets: [{
|
||||
data: mainData,
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(226, 220, 255, .3)',
|
||||
borderColor: '#c5baff',
|
||||
pointHoverBackgroundColor: '#FFFFFF',
|
||||
pointBorderColor: '#c5baff',
|
||||
pointHoverBorderWidth: 4,
|
||||
radius: xAxisDateLabels.length === 1 ? 10 : 0,
|
||||
hoverRadius: 10,
|
||||
hitRadius: 8,
|
||||
order: 0,
|
||||
}, {
|
||||
data: secondaryData,
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(226, 220, 255, .7)',
|
||||
borderColor: '#a18eff',
|
||||
pointHoverBackgroundColor: '#FFFFFF',
|
||||
pointBorderColor: '#a18eff',
|
||||
pointHoverBorderWidth: 4,
|
||||
radius: xAxisDateLabels.length === 1 ? 10 : 0,
|
||||
hoverRadius: 10,
|
||||
hitRadius: 8,
|
||||
order: 0,
|
||||
}],
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Used as constructor of custom tooltip.
|
||||
*/
|
||||
function tooltip(tooltipModel: TooltipModel): void {
|
||||
function tooltip(tooltipModel: TooltipModel<ChartType>): void {
|
||||
if (!tooltipModel.dataPoints) {
|
||||
const settledTooltip = Tooltip.createTooltip('settled-bandwidth-tooltip');
|
||||
const allocatedTooltip = Tooltip.createTooltip('allocated-bandwidth-tooltip');
|
||||
@ -90,12 +108,12 @@ function tooltip(tooltipModel: TooltipModel): void {
|
||||
/**
|
||||
* Returns allocated bandwidth tooltip's html mark up.
|
||||
*/
|
||||
function allocatedTooltipMarkUp(tooltipModel: TooltipModel): string {
|
||||
function allocatedTooltipMarkUp(tooltipModel: TooltipModel<ChartType>): string {
|
||||
if (!tooltipModel.dataPoints) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const dataIndex = tooltipModel.dataPoints[0].index;
|
||||
const dataIndex = tooltipModel.dataPoints[0].dataIndex;
|
||||
const dataPoint = new ChartTooltipData(props.allocatedData[dataIndex]);
|
||||
|
||||
return `<div class='allocated-tooltip'>
|
||||
@ -108,12 +126,12 @@ function allocatedTooltipMarkUp(tooltipModel: TooltipModel): string {
|
||||
/**
|
||||
* Returns settled bandwidth tooltip's html mark up.
|
||||
*/
|
||||
function settledTooltipMarkUp(tooltipModel: TooltipModel): string {
|
||||
function settledTooltipMarkUp(tooltipModel: TooltipModel<ChartType>): string {
|
||||
if (!tooltipModel.dataPoints) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const dataIndex = tooltipModel.dataPoints[0].index;
|
||||
const dataIndex = tooltipModel.dataPoints[0].dataIndex;
|
||||
const dataPoint = new ChartTooltipData(props.settledData[dataIndex]);
|
||||
|
||||
return `<div class='settled-tooltip'>
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
<template>
|
||||
<VChart
|
||||
id="storage-chart"
|
||||
:key="chartKey"
|
||||
chart-id="storage-chart"
|
||||
:chart-data="chartData"
|
||||
:width="width"
|
||||
:height="height"
|
||||
@ -14,8 +14,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { ChartType, TooltipModel, ChartData } from 'chart.js';
|
||||
|
||||
import { ChartData, Tooltip, TooltipParams, TooltipModel, ChartTooltipData } from '@/types/chart';
|
||||
import { Tooltip, TooltipParams, ChartTooltipData } from '@/types/chart';
|
||||
import { DataStamp } from '@/types/projects';
|
||||
import { ChartUtils } from '@/utils/chart';
|
||||
|
||||
@ -44,19 +45,27 @@ const chartData = computed((): ChartData => {
|
||||
const data: number[] = props.data.map(el => el.value);
|
||||
const xAxisDateLabels: string[] = ChartUtils.daysDisplayedOnChart(props.since, props.before);
|
||||
|
||||
return new ChartData(
|
||||
xAxisDateLabels,
|
||||
'#E6EDF7',
|
||||
'#D7E8FF',
|
||||
'#003DC1',
|
||||
data,
|
||||
);
|
||||
return {
|
||||
labels: xAxisDateLabels,
|
||||
datasets: [{
|
||||
data,
|
||||
fill: true,
|
||||
backgroundColor: '#E6EDF7',
|
||||
borderColor: '#D7E8FF',
|
||||
pointHoverBackgroundColor: '#FFFFFF',
|
||||
pointBorderColor: '#003DC1',
|
||||
pointHoverBorderWidth: 4,
|
||||
radius: xAxisDateLabels.length === 1 ? 10 : 0,
|
||||
hoverRadius: 10,
|
||||
hitRadius: 8,
|
||||
}],
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Used as constructor of custom tooltip.
|
||||
*/
|
||||
function tooltip(tooltipModel: TooltipModel): void {
|
||||
function tooltip(tooltipModel: TooltipModel<ChartType>): void {
|
||||
const tooltipParams = new TooltipParams(tooltipModel, 'storage-chart', 'storage-tooltip',
|
||||
tooltipMarkUp(tooltipModel), 76, 81);
|
||||
|
||||
@ -66,12 +75,12 @@ function tooltip(tooltipModel: TooltipModel): void {
|
||||
/**
|
||||
* Returns tooltip's html mark up.
|
||||
*/
|
||||
function tooltipMarkUp(tooltipModel: TooltipModel): string {
|
||||
function tooltipMarkUp(tooltipModel: TooltipModel<ChartType>): string {
|
||||
if (!tooltipModel.dataPoints) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const dataIndex = tooltipModel.dataPoints[0].index;
|
||||
const dataIndex = tooltipModel.dataPoints[0].dataIndex;
|
||||
const dataPoint = new ChartTooltipData(props.data[dataIndex]);
|
||||
|
||||
return `<div class='tooltip'>
|
||||
|
@ -1,57 +1,17 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { TooltipModel, ChartType } from 'chart.js';
|
||||
|
||||
import { DataStamp } from '@/types/projects';
|
||||
import { Size } from '@/utils/bytesSize';
|
||||
|
||||
/**
|
||||
* ChartData class holds info for ChartData entity.
|
||||
*/
|
||||
export class ChartData {
|
||||
public labels: string[];
|
||||
public datasets: DataSets[] = [];
|
||||
|
||||
public constructor(
|
||||
labels: string[],
|
||||
backgroundColor: string,
|
||||
borderColor: string,
|
||||
pointBorderColor: string,
|
||||
data: number[],
|
||||
secondaryBackgroundColor?: string,
|
||||
secondaryBorderColor?: string,
|
||||
secondaryPointBorderColor?: string,
|
||||
secondaryData?: number[],
|
||||
) {
|
||||
this.labels = labels;
|
||||
this.datasets[0] = new DataSets(backgroundColor, borderColor, pointBorderColor, data);
|
||||
|
||||
if (secondaryData && secondaryBackgroundColor && secondaryBorderColor && secondaryPointBorderColor) {
|
||||
this.datasets[1] = new DataSets(secondaryBackgroundColor, secondaryBorderColor, secondaryPointBorderColor, secondaryData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DataSets class holds info for chart's DataSets entity.
|
||||
*/
|
||||
class DataSets {
|
||||
public constructor(
|
||||
public backgroundColor: string,
|
||||
public borderColor: string,
|
||||
public pointBorderColor: string,
|
||||
public data: number[],
|
||||
public borderWidth: number = 4,
|
||||
public pointHoverBackgroundColor: string = 'white',
|
||||
public pointHoverBorderWidth: number = 5,
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* TooltipParams holds tooltip's configuration
|
||||
*/
|
||||
export class TooltipParams {
|
||||
public constructor(
|
||||
public tooltipModel: TooltipModel,
|
||||
public tooltipModel: TooltipModel<ChartType>,
|
||||
public chartId: string,
|
||||
public tooltipId: string,
|
||||
public markUp: string,
|
||||
@ -73,7 +33,7 @@ class StylingConstants {
|
||||
*/
|
||||
class Styling {
|
||||
public constructor(
|
||||
public tooltipModel: TooltipModel,
|
||||
public tooltipModel: TooltipModel<ChartType>,
|
||||
public element: HTMLElement,
|
||||
public topPosition: number,
|
||||
public leftPosition: number,
|
||||
@ -81,122 +41,6 @@ class Styling {
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Color is a color definition.
|
||||
*/
|
||||
export type Color = string
|
||||
|
||||
/**
|
||||
* TooltipItem contains datapoint information.
|
||||
*/
|
||||
export interface TooltipItem {
|
||||
// Label for the tooltip
|
||||
label: string,
|
||||
|
||||
// Value for the tooltip
|
||||
value: string,
|
||||
|
||||
// X Value of the tooltip
|
||||
// (deprecated) use `value` or `label` instead
|
||||
xLabel: number | string,
|
||||
|
||||
// Y value of the tooltip
|
||||
// (deprecated) use `value` or `label` instead
|
||||
yLabel: number | string,
|
||||
|
||||
// Index of the dataset the item comes from
|
||||
datasetIndex: number,
|
||||
|
||||
// Index of this data item in the dataset
|
||||
index: number,
|
||||
|
||||
// X position of matching point
|
||||
x: number,
|
||||
|
||||
// Y position of matching point
|
||||
y: number
|
||||
}
|
||||
|
||||
/**
|
||||
* TooltipModel contains parameters that can be used to render the tooltip.
|
||||
*/
|
||||
export interface TooltipModel {
|
||||
// The items that we are rendering in the tooltip. See Tooltip Item Interface section
|
||||
dataPoints: TooltipItem[],
|
||||
|
||||
// Positioning
|
||||
xPadding: number,
|
||||
yPadding: number,
|
||||
xAlign: string,
|
||||
yAlign: string,
|
||||
|
||||
// X and Y properties are the top left of the tooltip
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
// Where the tooltip points to
|
||||
caretX: number,
|
||||
caretY: number,
|
||||
|
||||
// Body
|
||||
// The body lines that need to be rendered
|
||||
// Each object contains 3 parameters
|
||||
// before: string[] // lines of text before the line with the color square
|
||||
// lines: string[], // lines of text to render as the main item with color square
|
||||
// after: string[], // lines of text to render after the main lines
|
||||
body: {before: string[]; lines: string[], after: string[]}[],
|
||||
// lines of text that appear after the title but before the body
|
||||
beforeBody: string[],
|
||||
// line of text that appear after the body and before the footer
|
||||
afterBody: string[],
|
||||
bodyFontColor: Color,
|
||||
_bodyFontFamily: string,
|
||||
_bodyFontStyle: string,
|
||||
_bodyAlign: string,
|
||||
bodyFontSize: number,
|
||||
bodySpacing: number,
|
||||
|
||||
// Title
|
||||
// lines of text that form the title
|
||||
title: string[],
|
||||
titleFontColor: Color,
|
||||
_titleFontFamily: string,
|
||||
_titleFontStyle: string,
|
||||
titleFontSize: number,
|
||||
_titleAlign: string,
|
||||
titleSpacing: number,
|
||||
titleMarginBottom: number,
|
||||
|
||||
// Footer
|
||||
// lines of text that form the footer
|
||||
footer: string[],
|
||||
footerFontColor: Color,
|
||||
_footerFontFamily: string,
|
||||
_footerFontStyle: string,
|
||||
footerFontSize: number,
|
||||
_footerAlign: string,
|
||||
footerSpacing: number,
|
||||
footerMarginTop: number,
|
||||
|
||||
// Appearance
|
||||
caretSize: number,
|
||||
caretPadding: number,
|
||||
cornerRadius: number,
|
||||
backgroundColor: Color,
|
||||
|
||||
// colors to render for each item in body[]. This is the color of the squares in the tooltip
|
||||
labelColors: Color[],
|
||||
labelTextColors: Color[],
|
||||
|
||||
// 0 opacity is a hidden tooltip
|
||||
opacity: number,
|
||||
legendColorBackground: Color,
|
||||
displayColors: boolean,
|
||||
borderColor: Color,
|
||||
borderWidth: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltip provides custom tooltip rendering
|
||||
*/
|
||||
@ -268,12 +112,3 @@ export class ChartTooltipData {
|
||||
this.value = `${size.formattedBytes} ${size.label}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RenderChart contains definition for renderChart and addPlugin, that can be used to cast
|
||||
* a derived chart type, with `(this as unknown as RenderChart).renderChart`
|
||||
*/
|
||||
export interface RenderChart {
|
||||
renderChart<A, B>(A, B): void
|
||||
addPlugin (plugin?: Record<string, (chart) => void>): void
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user