]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard_v2: improve tooltip of cdSparkline
authorTiago Melo <tmelo@suse.com>
Wed, 28 Feb 2018 17:53:55 +0000 (17:53 +0000)
committerTiago Melo <tmelo@suse.com>
Mon, 12 Mar 2018 14:50:11 +0000 (14:50 +0000)
Added a new scss and class for the tooltip.

Signed-off-by: Tiago Melo <tmelo@suse.com>
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/sparkline/sparkline.component.html
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/sparkline/sparkline.component.scss
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/sparkline/sparkline.component.ts
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/models/chart-tooltip.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/styles/chart-tooltip.scss [new file with mode: 0644]

index 91f0d73e0f240dcad861166c6e9ac05dec7878b1..4b7a1b872239363cf1b66da57f0bb7693a9de886 100644 (file)
@@ -1,10 +1,13 @@
 <div class="chart-container"
      [ngStyle]="style">
-  <canvas baseChart
+  <canvas baseChart #sparkCanvas
           [labels]="labels"
           [datasets]="datasets"
           [options]="options"
           [colors]="colors"
           [chartType]="'line'">
   </canvas>
+  <div class="chartjs-tooltip" #sparkTooltip>
+    <table></table>
+  </div>
 </div>
index ba8a8ebb7d88a085079e3dabef279f2d52066371..ec7d98291e8ced4b7af5aa1b07ef7c31f37d5d6d 100644 (file)
@@ -1,4 +1,5 @@
+@import '../../../../styles/chart-tooltip.scss';
+
 .chart-container {
-  position: relative;
-  margin: auto;
+  position: static !important;
 }
index 35b3d91c8d135cd49c8f4a79dfdf72fc82485a95..fa20ce301864af137b76214ca591776118ab0040 100644 (file)
@@ -1,12 +1,17 @@
-import { Component, OnChanges, OnInit, SimpleChanges } from '@angular/core';
+import { Component, ElementRef, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
 import { Input } from '@angular/core';
 
+import { ChartTooltip } from '../../../shared/models/chart-tooltip';
+
 @Component({
   selector: 'cd-sparkline',
   templateUrl: './sparkline.component.html',
   styleUrls: ['./sparkline.component.scss']
 })
 export class SparklineComponent implements OnInit, OnChanges {
+  @ViewChild('sparkCanvas') chartCanvasRef: ElementRef;
+  @ViewChild('sparkTooltip') chartTooltipRef: ElementRef;
+
   @Input() data: any;
   @Input()
   style = {
@@ -40,7 +45,10 @@ export class SparklineComponent implements OnInit, OnChanges {
       }
     },
     tooltips: {
-      enabled: true
+      enabled: false,
+      mode: 'index',
+      intersect: false,
+      custom: undefined
     },
     scales: {
       yAxes: [
@@ -66,7 +74,31 @@ export class SparklineComponent implements OnInit, OnChanges {
 
   constructor() {}
 
-  ngOnInit() {}
+  ngOnInit() {
+    const getStyleTop = (tooltip, positionY) => {
+      return (tooltip.caretY - tooltip.height - tooltip.yPadding - 5) + 'px';
+    };
+
+    const getStyleLeft = (tooltip, positionX) => {
+      return positionX + tooltip.caretX + 'px';
+    };
+
+    const chartTooltip = new ChartTooltip(
+      this.chartCanvasRef,
+      this.chartTooltipRef,
+      getStyleLeft,
+      getStyleTop
+    );
+
+    chartTooltip.customColors = {
+      backgroundColor: this.colors[0].pointBackgroundColor,
+      borderColor: this.colors[0].pointBorderColor
+    };
+
+    this.options.tooltips.custom = tooltip => {
+      chartTooltip.customTooltips(tooltip);
+    };
+  }
 
   ngOnChanges(changes: SimpleChanges) {
     this.datasets[0].data = changes['data'].currentValue;
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/models/chart-tooltip.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/models/chart-tooltip.ts
new file mode 100644 (file)
index 0000000..56962f3
--- /dev/null
@@ -0,0 +1,117 @@
+import { ElementRef } from '@angular/core';
+
+import * as _ from 'lodash';
+
+export class ChartTooltip {
+  tooltipEl: any;
+  chartEl: any;
+  getStyleLeft: Function;
+  getStyleTop: Function;
+  customColors = {
+    backgroundColor: undefined,
+    borderColor: undefined
+  };
+  checkOffset = false;
+
+  /**
+   * Creates an instance of ChartTooltip.
+   * @param {ElementRef} chartCanvas Canvas Element
+   * @param {ElementRef} chartTooltip Tooltip Element
+   * @param {Function} getStyleLeft Function that calculates the value of Left
+   * @param {Function} getStyleTop Function that calculates the value of Top
+   * @memberof ChartTooltip
+   */
+  constructor(
+    chartCanvas: ElementRef,
+    chartTooltip: ElementRef,
+    getStyleLeft: Function,
+    getStyleTop: Function
+  ) {
+    this.chartEl = chartCanvas.nativeElement;
+    this.getStyleLeft = getStyleLeft;
+    this.getStyleTop = getStyleTop;
+    this.tooltipEl = chartTooltip.nativeElement;
+  }
+
+  /**
+   * Implementation of a ChartJS custom tooltip function.
+   *
+   * @param {any} tooltip
+   * @memberof ChartTooltip
+   */
+  customTooltips(tooltip) {
+    // Hide if no tooltip
+    if (tooltip.opacity === 0) {
+      this.tooltipEl.style.opacity = 0;
+      return;
+    }
+
+    // Set caret Position
+    this.tooltipEl.classList.remove('above', 'below', 'no-transform');
+    if (tooltip.yAlign) {
+      this.tooltipEl.classList.add(tooltip.yAlign);
+    } else {
+      this.tooltipEl.classList.add('no-transform');
+    }
+
+    // Set Text
+    if (tooltip.body) {
+      const titleLines = tooltip.title || [];
+      const bodyLines = tooltip.body.map(bodyItem => {
+        return bodyItem.lines;
+      });
+
+      let innerHtml = '<thead>';
+
+      titleLines.forEach(title => {
+        innerHtml += '<tr><th>' + this.getTitle(title) + '</th></tr>';
+      });
+      innerHtml += '</thead><tbody>';
+
+      bodyLines.forEach((body, i) => {
+        const colors = tooltip.labelColors[i];
+        let style = 'background:' + (this.customColors.backgroundColor || colors.backgroundColor);
+        style += '; border-color:' + (this.customColors.borderColor || colors.borderColor);
+        style += '; border-width: 2px';
+        const span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>';
+        innerHtml += '<tr><td nowrap>' + span + this.getBody(body) + '</td></tr>';
+      });
+      innerHtml += '</tbody>';
+
+      const tableRoot = this.tooltipEl.querySelector('table');
+      tableRoot.innerHTML = innerHtml;
+    }
+
+    const positionY = this.chartEl.offsetTop;
+    const positionX = this.chartEl.offsetLeft;
+
+    // Display, position, and set styles for font
+    if (this.checkOffset) {
+      const halfWidth = tooltip.width / 2;
+      this.tooltipEl.classList.remove('transform-left');
+      this.tooltipEl.classList.remove('transform-right');
+      if (tooltip.caretX - halfWidth < 0) {
+        this.tooltipEl.classList.add('transform-left');
+      } else if (tooltip.caretX + halfWidth > this.chartEl.width) {
+        this.tooltipEl.classList.add('transform-right');
+      }
+    }
+
+    this.tooltipEl.style.left = this.getStyleLeft(tooltip, positionX);
+    this.tooltipEl.style.top = this.getStyleTop(tooltip, positionY);
+
+    this.tooltipEl.style.opacity = 1;
+    this.tooltipEl.style.fontFamily = tooltip._fontFamily;
+    this.tooltipEl.style.fontSize = tooltip.fontSize;
+    this.tooltipEl.style.fontStyle = tooltip._fontStyle;
+    this.tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px';
+  }
+
+  getBody(body) {
+    return body;
+  }
+
+  getTitle(title) {
+    return title;
+  }
+}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/styles/chart-tooltip.scss b/src/pybind/mgr/dashboard_v2/frontend/src/styles/chart-tooltip.scss
new file mode 100644 (file)
index 0000000..835bb36
--- /dev/null
@@ -0,0 +1,62 @@
+.chart-container {
+  position: absolute;
+  margin: auto;
+  cursor: pointer;
+  overflow: visible;
+}
+
+canvas {
+  -moz-user-select: none;
+  -webkit-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.chartjs-tooltip {
+  opacity: 0;
+  position: absolute;
+  background: rgba(0, 0, 0, 0.7);
+  color: white;
+  border-radius: 3px;
+  -webkit-transition: all 0.1s ease;
+  transition: all 0.1s ease;
+  pointer-events: none;
+  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif !important;
+
+  -webkit-transform: translate(-50%, 0);
+  transform: translate(-50%, 0);
+
+  &.transform-left {
+    transform: translate(-10%, 0);
+
+    &::after {
+      left: 10%;
+    }
+  }
+
+  &.transform-right {
+    transform: translate(-90%, 0);
+
+    &::after {
+      left: 90%;
+    }
+  }
+}
+
+.chartjs-tooltip::after {
+  content: ' ';
+  position: absolute;
+  top: 100%; /* At the bottom of the tooltip */
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px;
+  border-style: solid;
+  border-color: black transparent transparent transparent;
+}
+
+::ng-deep .chartjs-tooltip-key {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  margin-right: 10px;
+}