UNPKG

veritatisquam

Version:
118 lines (101 loc) 3.36 kB
// Internal Imports import { Component } from '../component'; import { DOMUtils } from '../../services'; import { Tools } from '../../tools'; import { Roles, ColorClassNameTypes } from '../../interfaces'; // D3 Imports import { scaleLinear } from 'd3-scale'; export class Meter extends Component { type = 'meter'; render(animate = true) { const self = this; const svg = this.getContainerSVG(); const options = this.getOptions(); const data = this.model.getDisplayData(); const status = this.model.getStatus(); const { width } = DOMUtils.getSVGElementSize(this.parent, { useAttrs: true, }); const { groupMapsTo } = options.data; // each meter has a scale for the value but no visual axis const xScale = scaleLinear().domain([0, 100]).range([0, width]); // draw the container to hold the value DOMUtils.appendOrSelect(svg, 'rect.container') .attr('x', 0) .attr('y', 0) .attr('width', width) .attr('height', Tools.getProperty(options, 'meter', 'height')); // value larger than 100 will display as 100% on meter chart const maximumBarWidth = data.value >= 100; // rect with the value binded const value = svg.selectAll('rect.value').data([data]); // if user provided a color for the bar, we dont want to attach a status class const className = status != null && !self.model.isUserProvidedColorScaleValid() ? `value status--${status}` : ''; // draw the value bar value .enter() .append('rect') .classed('value', true) .merge(value) .attr('x', 0) .attr('y', 0) .attr('height', Tools.getProperty(options, 'meter', 'height')) .attr('class', (d) => this.model.getColorClassName({ classNameTypes: [ColorClassNameTypes.FILL], dataGroupName: d[groupMapsTo], originalClassName: className, }) ) .transition( this.services.transitions.getTransition( 'meter-bar-update', animate ) ) .attr('width', (d) => maximumBarWidth ? xScale(100) : xScale(d.value) ) .style('fill', (d) => self.model.getFillColor(d[groupMapsTo])) // a11y .attr('role', Roles.GRAPHICS_SYMBOL) .attr('aria-roledescription', 'value') .attr('aria-label', (d) => d.value); // draw the peak const peakValue = Tools.getProperty(options, 'meter', 'peak'); // update the peak if it is less than the value, it should be equal to the value const updatedPeak = peakValue !== null && peakValue < data.value ? data.value : peakValue; // dont display peak if there isnt one const peakData = updatedPeak === null || maximumBarWidth ? [] : [updatedPeak]; // if a peak is supplied within the domain, we want to render it const peak = svg.selectAll('line.peak').data(peakData); peak.enter() .append('line') .classed('peak', true) .merge(peak) .attr('y1', 0) .attr('y2', Tools.getProperty(options, 'meter', 'height')) .transition( this.services.transitions.getTransition( 'peak-line-update', animate ) ) .attr('x1', (d) => xScale(d)) .attr('x2', (d) => xScale(d)) // a11y .attr('role', Roles.GRAPHICS_SYMBOL) .attr('aria-roledescription', 'peak') .attr('aria-label', (d) => d); peak.exit().remove(); // this forces the meter chart to only take up as much height as needed (if no height is provided) this.services.domUtils.setSVGMaxHeight(); } }