-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 = {
}
},
tooltips: {
- enabled: true
+ enabled: false,
+ mode: 'index',
+ intersect: false,
+ custom: undefined
},
scales: {
yAxes: [
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;
--- /dev/null
+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;
+ }
+}
--- /dev/null
+.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;
+}