From ae3eae31dcefea7ad52a54936ab494e18e27408e Mon Sep 17 00:00:00 2001 From: VitaliiShpital Date: Fri, 27 Dec 2019 14:16:44 +0200 Subject: [PATCH] web/storagenode: chart point's hit radius extended Change-Id: Ib231d7970b4fe9e196638d3a09d2ae96e6c4efc5 --- .../src/app/components/BandwidthChart.vue | 110 +++++--------- .../src/app/components/DiskSpaceChart.vue | 70 +++------ .../src/app/components/EgressChart.vue | 98 ++++-------- .../src/app/components/IngressChart.vue | 89 ++++------- web/storagenode/src/app/components/VChart.vue | 5 +- web/storagenode/src/app/types/tooltip.ts | 143 ++++++++++++++++++ 6 files changed, 255 insertions(+), 260 deletions(-) create mode 100644 web/storagenode/src/app/types/tooltip.ts diff --git a/web/storagenode/src/app/components/BandwidthChart.vue b/web/storagenode/src/app/components/BandwidthChart.vue index 67a782a26..b6c46525c 100644 --- a/web/storagenode/src/app/components/BandwidthChart.vue +++ b/web/storagenode/src/app/components/BandwidthChart.vue @@ -20,6 +20,7 @@ import { Component, Vue } from 'vue-property-decorator'; import VChart from '@/app/components/VChart.vue'; import { ChartData } from '@/app/types/chartData'; +import { Tooltip, TooltipParams } from '@/app/types/tooltip'; import { ChartUtils } from '@/app/utils/chart'; import { formatBytes } from '@/app/utils/converter'; import { BandwidthUsed } from '@/storagenode/satellite'; @@ -51,9 +52,6 @@ class BandwidthTooltip { }, }) export default class BandwidthChart extends Vue { - private readonly TOOLTIP_OPACITY: string = '1'; - private readonly TOOLTIP_POSITION: string = 'absolute'; - private get allBandwidth(): BandwidthUsed[] { return ChartUtils.populateEmptyBandwidth(this.$store.state.node.bandwidthChartData); } @@ -82,79 +80,45 @@ export default class BandwidthChart extends Vue { return new ChartData(daysCount, chartBackgroundColor, chartBorderColor, chartBorderWidth, data); } - public bandwidthTooltip(tooltipModel): void { - // Tooltip Element - let tooltipEl = document.getElementById('bandwidth-tooltip'); - // Create element on first render - if (!tooltipEl) { - tooltipEl = document.createElement('div'); - tooltipEl.id = 'bandwidth-tooltip'; - document.body.appendChild(tooltipEl); + public bandwidthTooltip(tooltipModel: any): void { + const tooltipParams = new TooltipParams(tooltipModel, 'bandwidth-chart', 'bandwidth-tooltip', + 'bandwidth-tooltip-arrow', 'bandwidth-tooltip-point', this.tooltipMarkUp(tooltipModel), + 303, 125, 35, 24, 6, 4, `#1f49a3`); + + Tooltip.custom(tooltipParams); + } + + private tooltipMarkUp(tooltipModel: any): string { + if (!tooltipModel.dataPoints) { + return ''; } - // Tooltip Arrow - let tooltipArrow = document.getElementById('bandwidth-tooltip-arrow'); - // Create element on first render - if (!tooltipArrow) { - tooltipArrow = document.createElement('div'); - tooltipArrow.id = 'bandwidth-tooltip-arrow'; - document.body.appendChild(tooltipArrow); - } + const dataIndex = tooltipModel.dataPoints[0].index; + const dataPoint = new BandwidthTooltip(this.allBandwidth[dataIndex]); - // Hide if no tooltip - if (!tooltipModel.opacity) { - document.body.removeChild(tooltipEl); - document.body.removeChild(tooltipArrow); - - return; - } - - // Set Text - if (tooltipModel.body) { - const dataIndex = tooltipModel.dataPoints[0].index; - const dataPoint = new BandwidthTooltip(this.allBandwidth[dataIndex]); - - tooltipEl.innerHTML = `
-

EGRESS

-

INGRESS

-
-
-
-

USAGE

-

${dataPoint.normalEgress}

-

${dataPoint.normalIngress}

-
-
-

REPAIR

-

${dataPoint.repairEgress}

-

${dataPoint.repairIngress}

-
-
-

AUDIT

-

${dataPoint.auditEgress}

-
-
- `; - } - - const bandwidthChart = document.getElementById('bandwidth-chart'); - if (!bandwidthChart) { - return; - } - - // `this` will be the overall tooltip. - const position = bandwidthChart.getBoundingClientRect(); - tooltipEl.style.opacity = this.TOOLTIP_OPACITY; - tooltipEl.style.position = this.TOOLTIP_POSITION; - tooltipEl.style.left = `${position.left + tooltipModel.caretX - 125}px`; - tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 303}px`; - - tooltipArrow.style.opacity = this.TOOLTIP_OPACITY; - tooltipArrow.style.position = this.TOOLTIP_POSITION; - tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`; - tooltipArrow.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 35}px`; + return `
+

EGRESS

+

INGRESS

+
+
+
+

USAGE

+

${dataPoint.normalEgress}

+

${dataPoint.normalIngress}

+
+
+

REPAIR

+

${dataPoint.repairEgress}

+

${dataPoint.repairIngress}

+
+
+

AUDIT

+

${dataPoint.auditEgress}

+
+
+ `; } } diff --git a/web/storagenode/src/app/components/DiskSpaceChart.vue b/web/storagenode/src/app/components/DiskSpaceChart.vue index 2e5e1138b..64f30a901 100644 --- a/web/storagenode/src/app/components/DiskSpaceChart.vue +++ b/web/storagenode/src/app/components/DiskSpaceChart.vue @@ -20,6 +20,7 @@ import { Component, Vue } from 'vue-property-decorator'; import VChart from '@/app/components/VChart.vue'; import { ChartData } from '@/app/types/chartData'; +import { Tooltip, TooltipParams } from '@/app/types/tooltip'; import { ChartUtils } from '@/app/utils/chart'; import { formatBytes } from '@/app/utils/converter'; import { Stamp } from '@/storagenode/satellite'; @@ -43,9 +44,6 @@ class StampTooltip { }, }) export default class DiskSpaceChart extends Vue { - private readonly TOOLTIP_OPACITY: string = '1'; - private readonly TOOLTIP_POSITION: string = 'absolute'; - private get allStamps(): Stamp[] { return ChartUtils.populateEmptyStamps(this.$store.state.node.storageChartData); } @@ -75,59 +73,25 @@ export default class DiskSpaceChart extends Vue { } public diskSpaceTooltip(tooltipModel): void { - // Tooltip Element - let tooltipEl = document.getElementById('disk-space-tooltip'); - // Create element on first render - if (!tooltipEl) { - tooltipEl = document.createElement('div'); - tooltipEl.id = 'disk-space-tooltip'; - document.body.appendChild(tooltipEl); + const tooltipParams = new TooltipParams(tooltipModel, 'disk-space-chart', 'disk-space-tooltip', + 'disk-space-tooltip-arrow', 'disk-space-tooltip-point', this.tooltipMarkUp(tooltipModel), + 125, 89, 38, 24, 6, 4, `#1f49a3`); + + Tooltip.custom(tooltipParams); + } + + private tooltipMarkUp(tooltipModel: any): string { + if (!tooltipModel.dataPoints) { + return ''; } - // Tooltip Arrow - let tooltipArrow = document.getElementById('disk-space-tooltip-arrow'); - // Create element on first render - if (!tooltipArrow) { - tooltipArrow = document.createElement('div'); - tooltipArrow.id = 'disk-space-tooltip-arrow'; - document.body.appendChild(tooltipArrow); - } + const dataIndex = tooltipModel.dataPoints[0].index; + const dataPoint = new StampTooltip(this.allStamps[dataIndex]); - // Hide if no tooltip - if (!tooltipModel.opacity) { - document.body.removeChild(tooltipEl); - document.body.removeChild(tooltipArrow); - - return; - } - - // Set Text - if (tooltipModel.body) { - const dataIndex = tooltipModel.dataPoints[0].index; - const dataPoint = new StampTooltip(this.allStamps[dataIndex]); - - tooltipEl.innerHTML = `
-

${dataPoint.atRestTotal}*h

- -
`; - } - - const diskSpaceChart = document.getElementById('disk-space-chart'); - if (!diskSpaceChart) { - return; - } - - // `this` will be the overall tooltip. - const position = diskSpaceChart.getBoundingClientRect(); - tooltipEl.style.opacity = this.TOOLTIP_OPACITY; - tooltipEl.style.position = this.TOOLTIP_POSITION; - tooltipEl.style.left = `${position.left + tooltipModel.caretX - 89}px`; - tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 125}px`; - - tooltipArrow.style.opacity = this.TOOLTIP_OPACITY; - tooltipArrow.style.position = this.TOOLTIP_POSITION; - tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`; - tooltipArrow.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 38}px`; + return `
+

${dataPoint.atRestTotal}*h

+ +
`; } } diff --git a/web/storagenode/src/app/components/EgressChart.vue b/web/storagenode/src/app/components/EgressChart.vue index 5678ffca2..433789ff3 100644 --- a/web/storagenode/src/app/components/EgressChart.vue +++ b/web/storagenode/src/app/components/EgressChart.vue @@ -20,6 +20,7 @@ import { Component, Vue } from 'vue-property-decorator'; import VChart from '@/app/components/VChart.vue'; import { ChartData } from '@/app/types/chartData'; +import { Tooltip, TooltipParams } from '@/app/types/tooltip'; import { ChartUtils } from '@/app/utils/chart'; import { formatBytes } from '@/app/utils/converter'; import { EgressUsed } from '@/storagenode/satellite'; @@ -47,9 +48,6 @@ class EgressTooltip { }, }) export default class EgressChart extends Vue { - private readonly TOOLTIP_OPACITY: string = '1'; - private readonly TOOLTIP_POSITION: string = 'absolute'; - private get allBandwidth(): EgressUsed[] { return ChartUtils.populateEmptyBandwidth(this.$store.state.node.egressChartData); } @@ -79,73 +77,38 @@ export default class EgressChart extends Vue { } public egressTooltip(tooltipModel): void { - // Tooltip Element - let tooltipEl = document.getElementById('egress-tooltip'); - // Create element on first render - if (!tooltipEl) { - tooltipEl = document.createElement('div'); - tooltipEl.id = 'egress-tooltip'; - document.body.appendChild(tooltipEl); + const tooltipParams = new TooltipParams(tooltipModel, 'egress-chart', 'egress-tooltip', + 'egress-tooltip-arrow', 'egress-tooltip-point', this.tooltipMarkUp(tooltipModel), + 255, 94, 35, 24, 6, 4, `#48a77f`); + + Tooltip.custom(tooltipParams); + } + + private tooltipMarkUp(tooltipModel: any): string { + if (!tooltipModel.dataPoints) { + return ''; } - // Tooltip Arrow - let tooltipArrow = document.getElementById('egress-tooltip-arrow'); - // Create element on first render - if (!tooltipArrow) { - tooltipArrow = document.createElement('div'); - tooltipArrow.id = 'egress-tooltip-arrow'; - document.body.appendChild(tooltipArrow); - } + const dataIndex = tooltipModel.dataPoints[0].index; + const dataPoint = new EgressTooltip(this.allBandwidth[dataIndex]); - // Hide if no tooltip - if (!tooltipModel.opacity) { - document.body.removeChild(tooltipEl); - document.body.removeChild(tooltipArrow); - - return; - } - - // Set Text - if (tooltipModel.body) { - const dataIndex = tooltipModel.dataPoints[0].index; - const dataPoint = new EgressTooltip(this.allBandwidth[dataIndex]); - - tooltipEl.innerHTML = `
-
-

USAGE

- ${dataPoint.normalEgress} -
-
-

REPAIR

- ${dataPoint.repairEgress} -
-
-

AUDIT

- ${dataPoint.auditEgress} -
-
- `; - } - - const egressChart = document.getElementById('egress-chart'); - if (!egressChart) { - return; - } - - // `this` will be the overall tooltip. - const position = egressChart.getBoundingClientRect(); - tooltipEl.style.opacity = this.TOOLTIP_OPACITY; - - tooltipEl.style.position = this.TOOLTIP_POSITION; - tooltipEl.style.left = `${position.left + tooltipModel.caretX - 94}px`; - tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 255}px`; - - tooltipArrow.style.opacity = this.TOOLTIP_OPACITY; - tooltipArrow.style.position = this.TOOLTIP_POSITION; - tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`; - tooltipArrow.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 35}px`; + return `
+
+

USAGE

+ ${dataPoint.normalEgress} +
+
+

REPAIR

+ ${dataPoint.repairEgress} +
+
+

AUDIT

+ ${dataPoint.auditEgress} +
+
+ `; } } @@ -204,7 +167,6 @@ export default class EgressChart extends Vue { } .egress-tooltip-bold-text { - font-family: 'font_bold', sans-serif; font-size: 14px; } diff --git a/web/storagenode/src/app/components/IngressChart.vue b/web/storagenode/src/app/components/IngressChart.vue index 6c18c31dc..cd2a19b67 100644 --- a/web/storagenode/src/app/components/IngressChart.vue +++ b/web/storagenode/src/app/components/IngressChart.vue @@ -20,6 +20,7 @@ import { Component, Vue } from 'vue-property-decorator'; import VChart from '@/app/components/VChart.vue'; import { ChartData } from '@/app/types/chartData'; +import { Tooltip, TooltipParams } from '@/app/types/tooltip'; import { ChartUtils } from '@/app/utils/chart'; import { formatBytes } from '@/app/utils/converter'; import { IngressUsed } from '@/storagenode/satellite'; @@ -45,9 +46,6 @@ class IngressTooltip { }, }) export default class IngressChart extends Vue { - private readonly TOOLTIP_OPACITY: string = '1'; - private readonly TOOLTIP_POSITION: string = 'absolute'; - private get allBandwidth(): IngressUsed[] { return ChartUtils.populateEmptyBandwidth(this.$store.state.node.ingressChartData); } @@ -77,68 +75,34 @@ export default class IngressChart extends Vue { } public ingressTooltip(tooltipModel): void { - // Tooltip Element - let tooltipEl = document.getElementById('ingress-tooltip'); - // Create element on first render - if (!tooltipEl) { - tooltipEl = document.createElement('div'); - tooltipEl.id = 'ingress-tooltip'; - document.body.appendChild(tooltipEl); + const tooltipParams = new TooltipParams(tooltipModel, 'ingress-chart', 'ingress-tooltip', + 'ingress-tooltip-arrow', 'ingress-tooltip-point', this.tooltipMarkUp(tooltipModel), + 205, 94, 35, 24, 6, 4, `#e1a128`); + + Tooltip.custom(tooltipParams); + } + + private tooltipMarkUp(tooltipModel: any): string { + if (!tooltipModel.dataPoints) { + return ''; } - // Tooltip Arrow - let tooltipArrow = document.getElementById('ingress-tooltip-arrow'); - // Create element on first render - if (!tooltipArrow) { - tooltipArrow = document.createElement('div'); - tooltipArrow.id = 'ingress-tooltip-arrow'; - document.body.appendChild(tooltipArrow); - } + const dataIndex = tooltipModel.dataPoints[0].index; + const dataPoint = new IngressTooltip(this.allBandwidth[dataIndex]); - // Hide if no tooltip - if (!tooltipModel.opacity) { - document.body.removeChild(tooltipEl); - document.body.removeChild(tooltipArrow); - - return; - } - - // Set Text - if (tooltipModel.body) { - const dataIndex = tooltipModel.dataPoints[0].index; - const dataPoint = new IngressTooltip(this.allBandwidth[dataIndex]); - - tooltipEl.innerHTML = `
-
-

USAGE

- ${dataPoint.normalIngress} -
-
-

REPAIR

- ${dataPoint.repairIngress} -
-
- `; - } - - const ingressChart = document.getElementById('ingress-chart'); - if (!ingressChart) { - return; - } - - // `this` will be the overall tooltip. - const position = ingressChart.getBoundingClientRect(); - tooltipEl.style.opacity = this.TOOLTIP_OPACITY; - tooltipEl.style.position = this.TOOLTIP_POSITION; - tooltipEl.style.left = `${position.left + tooltipModel.caretX - 94}px`; - tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 205}px`; - - tooltipArrow.style.opacity = this.TOOLTIP_OPACITY; - tooltipArrow.style.position = this.TOOLTIP_POSITION; - tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`; - tooltipArrow.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY - 35}px`; + return `
+
+

USAGE

+ ${dataPoint.normalIngress} +
+
+

REPAIR

+ ${dataPoint.repairIngress} +
+
+ `; } } @@ -197,7 +161,6 @@ export default class IngressChart extends Vue { } .ingress-tooltip-bold-text { - font-family: 'font_bold', sans-serif; font-size: 14px; } diff --git a/web/storagenode/src/app/components/VChart.vue b/web/storagenode/src/app/components/VChart.vue index cfcb8838c..b9d1f93bd 100644 --- a/web/storagenode/src/app/components/VChart.vue +++ b/web/storagenode/src/app/components/VChart.vue @@ -69,9 +69,8 @@ export default class VChart extends Vue { elements: { point: { radius: 0, - hitRadius: 5, - hoverRadius: 3, - hoverBorderWidth: 7, + hoverRadius: 0, + hitRadius: 500, } }, diff --git a/web/storagenode/src/app/types/tooltip.ts b/web/storagenode/src/app/types/tooltip.ts new file mode 100644 index 000000000..378c80e91 --- /dev/null +++ b/web/storagenode/src/app/types/tooltip.ts @@ -0,0 +1,143 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +/** + * StylingConstants holds tooltip styling constants + */ +class StylingConstants { + public static tooltipOpacity = '1'; + public static tooltipPosition = 'absolute'; + public static pointWidth = '10px'; + public static pointHeight = '10px'; + public static borderRadius = '20px'; +} + +/** + * Styling holds tooltip's styling configuration + */ +class Styling { + public constructor( + public tooltipModel: any, + public element: HTMLElement, + public topPosition: number, + public leftPosition: number, + public chartPosition: ClientRect, + ) {} +} + +/** + * TooltipParams holds tooltip's configuration + */ +export class TooltipParams { + public constructor( + public tooltipModel: any, + public chartId: string, + public tooltipId: string, + public arrowId: string, + public pointId: string, + public markUp: string, + public tooltipTop: number, + public tooltipLeft: number, + public arrowTop: number, + public arrowLeft: number, + public pointTop: number, + public pointLeft: number, + public color: string, + ) {} +} + +/** + * Tooltip provides custom tooltip rendering + */ +export class Tooltip { + public static custom(params: TooltipParams): void { + const chart = document.getElementById(params.chartId); + if (!chart) { + return; + } + + const tooltip: HTMLElement = Tooltip.createTooltip(params.tooltipId); + const arrow: HTMLElement = Tooltip.createArrow(params.arrowId); + const point: HTMLElement = Tooltip.createPoint(params.pointId); + + if (!params.tooltipModel.opacity) { + Tooltip.remove(tooltip, arrow, point); + + return; + } + + if (params.tooltipModel.body) { + Tooltip.render(tooltip, params.markUp); + } + + const position = chart.getBoundingClientRect(); + + const tooltipStyling = new Styling(params.tooltipModel, tooltip, params.tooltipTop, params.tooltipLeft, position); + Tooltip.elemStyling(tooltipStyling); + + const arrowStyling = new Styling(params.tooltipModel, arrow, params.arrowTop, params.arrowLeft, position); + Tooltip.elemStyling(arrowStyling); + + const pointStyling = new Styling(params.tooltipModel, point, params.pointTop, params.pointLeft, position); + Tooltip.elemStyling(pointStyling); + + Tooltip.pointStyling(point, params.color); + } + + private static createTooltip(id: string): HTMLElement { + let tooltipEl = document.getElementById(id); + if (!tooltipEl) { + tooltipEl = document.createElement('div'); + tooltipEl.id = id; + document.body.appendChild(tooltipEl); + } + + return tooltipEl; + } + + private static createArrow(id: string): HTMLElement { + let tooltipArrow = document.getElementById(id); + if (!tooltipArrow) { + tooltipArrow = document.createElement('div'); + tooltipArrow.id = id; + document.body.appendChild(tooltipArrow); + } + + return tooltipArrow; + } + + private static createPoint(id: string): HTMLElement { + let tooltipPoint = document.getElementById(id); + if (!tooltipPoint) { + tooltipPoint = document.createElement('div'); + tooltipPoint.id = id; + document.body.appendChild(tooltipPoint); + } + + return tooltipPoint; + } + + private static remove(tooltipEl: HTMLElement, tooltipArrow: HTMLElement, tooltipPoint: HTMLElement) { + document.body.removeChild(tooltipEl); + document.body.removeChild(tooltipArrow); + document.body.removeChild(tooltipPoint); + } + + private static render(tooltip: HTMLElement, markUp: string) { + tooltip.innerHTML = markUp; + } + + private static elemStyling(elemStyling: Styling) { + elemStyling.element.style.opacity = StylingConstants.tooltipOpacity; + elemStyling.element.style.position = StylingConstants.tooltipPosition; + elemStyling.element.style.left = `${elemStyling.chartPosition.left + elemStyling.tooltipModel.caretX - elemStyling.leftPosition}px`; + elemStyling.element.style.top = `${elemStyling.chartPosition.top + window.pageYOffset + elemStyling.tooltipModel.caretY - elemStyling.topPosition}px`; + } + + private static pointStyling(point: HTMLElement, color: string) { + point.style.width = StylingConstants.pointWidth; + point.style.height = StylingConstants.pointHeight; + point.style.backgroundColor = color; + point.style.borderRadius = StylingConstants.borderRadius; + } +}