import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; // was imported in bar-chart.ts import LineChart from 'britecharts/dist/umd/line.min'; import BarChart from 'britecharts/dist/umd/bar.min.js'; import miniTooltip from 'britecharts/dist/umd/miniTooltip.min'; import * as d3Selection from 'd3-selection'; import * as d3 from 'd3'; import moment from 'moment'; @Component({ selector: 'onguard-bar-chart', templateUrl: './bar-chart.component.html', styleUrls: ['./bar-chart.component.scss'], }) export class BarChartComponent implements OnInit, AfterViewInit { // litle hack to get proper element for tooltip private static i = 0; @ViewChild('barChartElement') barChartElementRef: ElementRef; @Input() colorSchema: string[] = ['#00A6E8']; // Static tooltip control variables @Input() generateTooltipFunction: (dataPoint: any) => any = null; @Input() tooltipType = 2; @Input() xAxisLabel = 'x-Axis'; @Input() yAxisLabel = 'y-Axis'; @Input() yAxisLabelOffset = -40; @Input() customAxis = false; @Input() isHorizontal = false; @Input() valueLabel = 'value'; @Input() xTicks = 5; @Input() labelsNumberFormat = '.0%'; @Input() hasPercentage = false; @Input() betweenBarsPadding = 0.1; @Input() height = 200; @Input() axisToMaxRatio = 1.3; @Input() margin = { left: 70, right: 1, top: 20, bottom: 50, }; @Input() targetLine: number; public tooltipDataPoint: any; public tooltipShow = false; private chartUpdateRef; private width: number; private overTooltip = false; // Inputs for BriteChart Bar chart settings private _data: any; private _count: number = BarChartComponent.i++; @Input() set data(data: any) { this._data = data; if (this.chartUpdateRef) { this.chartUpdateRef(this._data); // update axis labels this.customAxisLabel(); } } constructor() {} ngOnInit(): void {} ngAfterViewInit(): void { this.chartUpdateRef = this.barChart( this.barChartElementRef.nativeElement, this._count, this._data, ); this.customAxisLabel(); if (this.targetLine) { this.drawTargetLine(); } } private customAxisLabel(): void { if (!this.customAxis) { return; } // Customize x axis const ticks = d3Selection.selectAll( '.bar-chart-uniq-id-' + this._count + ' .x-axis-group .tick', ); const current = { year: 0, month: 0, day: 0, }; ticks.each((d, i, nodes) => { // get references to d3 node element and the date text it contains const node = d3Selection.select(nodes[i]); const date = moment(node.select('text').text()); if (!date.isValid()) { console.error(node.select('text').text() + ' is invalid date format'); } // clear existing label node.selectAll('*').remove(); // Handle updated year, month and day displays let format = '[]'; let rotation = 0; let anchor = 'inherit'; if (date.year() !== current.year) { format = 'MMM. DD'; rotation = 45; anchor = 'start'; } else if (date.month() !== current.month) { format = 'MMM. DD'; rotation = 45; anchor = 'start'; } else if (date.day() !== current.day) { format = 'DD'; } else { return; } current.year = date.year(); current.month = date.month(); current.day = date.day(); // add the label node.append('text') .attr('fill', '#000') .attr('y', 15) .attr('dy', '0.71em') .text(date.format(format)) .attr('text-anchor', anchor) .attr('transform', 'rotate(' + rotation + ')'); // add a tick mark node.append('rect') .attr('x', 0) .attr('y', 0) .attr('width', 1) .attr('height', 5) .attr('fill', '#D1D5DE'); }); const axisTitle = d3Selection.select( '.bar-chart-uniq-id-' + this._count + ' .x-axis-group .x-axis-label text', ); axisTitle.attr('y', 45); } private drawTargetLine(): void { // Draw a horizontal line to mark some target value // get the data max let maxValue = 0; this._data.forEach((dataPoint) => { if (dataPoint.value > maxValue) { maxValue = dataPoint.value; } }); // Calculate the scale used to do this. LN 556 Daw Vertical bars const yScale = d3 .scaleLinear() .domain([0, maxValue * this.axisToMaxRatio]) .rangeRound([this.height - (this.margin.top + this.margin.bottom), 0]); // calculate the line height const lineHeight = yScale(this.targetLine); // Get the horizontal line group. const lineGroup = d3Selection.select( '.bar-chart-uniq-id-' + this._count + ' .grid-lines-group', ); // Add (and hide) label for capacity line const lineLabel = lineGroup .append('text') .attr('fill', '#39B54A') .attr('x', 0) .attr('y', lineHeight - 3) .style('display', 'none') // .attr('alignment-baseline', 'middle') .text(this.targetLine); // use surrounding rect for mouse events lineGroup .append('rect') .attr('x', 0) .attr('y', lineHeight - 2) .attr('width', this.width) .attr('height', 5) .attr('fill', 'transparent') .on('mouseover', () => { // show capacity lineLabel.style('display', 'block'); }) .on('mouseout', () => { // hide capacity lineLabel.style('display', 'none'); }); lineGroup .append('line') .style('stroke', '#39B54A') .attr('class', 'horizontal-grid-line') .attr('x1', 0) .attr('y1', lineHeight) .attr('x2', this.width) .attr('y2', lineHeight); } public mouseOverTooltip(over: boolean): void { if (over) { this.tooltipShow = true; this.overTooltip = true; } else { this.tooltipShow = false; this.overTooltip = false; } } public barChart(el, index, data): (data: any) => void { const chartTooltip = miniTooltip(); const container = d3Selection.select(el); this.width = container.node() ? container.node().getBoundingClientRect().width : 300; const barChart = new BarChart(); barChart .height(this.height) .width(this.width) .margin(this.margin) .isHorizontal(this.isHorizontal) .xAxisLabel(this.xAxisLabel) .yAxisLabel(this.yAxisLabel) .yAxisLabelOffset(this.yAxisLabelOffset) .isAnimated(true) .xTicks(this.xTicks) .valueLabel(this.valueLabel) .labelsNumberFormat(this.labelsNumberFormat) .hasPercentage(this.hasPercentage) .percentageAxisToMaxRatio(this.axisToMaxRatio) .betweenBarsPadding(this.betweenBarsPadding) .colorSchema(this.colorSchema) .on('customMouseOver', () => { this.tooltipShow = true; }) .on('customMouseMove', (dataPoint, topicColorMap, x, y) => { this.tooltipShow = true; if (this.generateTooltipFunction) { this.tooltipDataPoint = this.generateTooltipFunction(dataPoint); } }) .on('customMouseOut', () => { if (!this.overTooltip) { this.tooltipShow = false; } }); container.datum(data).call(barChart); // chartTooltip.nameLabel('fullDate'); let tooltipContainer; // wait til chart was rendered to be presetn ".metadata-group" element setTimeout(() => { tooltipContainer = d3Selection.select( '.bar-chart-uniq-id-' + index + ' .metadata-group', ); // tooltipContainer.datum([]).call(chartTooltip); }, 0); return function (data: any) { // chartTooltip.hide(); container.datum(data).call(barChart); this.customAxisLabel(); }; } get count(): number { return this._count; } }