UNPKG

veritatisquam

Version:
243 lines (203 loc) 6.16 kB
// Internal Imports import { Component } from '../component'; import { Tools } from '../../tools'; import { DOMUtils } from '../../services'; // D3 Imports import { axisBottom, axisLeft } from 'd3-axis'; export class Grid extends Component { type = 'grid'; backdrop: any; render(animate = true) { const isXGridEnabled = Tools.getProperty( this.getOptions(), 'grid', 'x', 'enabled' ); const isYGridEnabled = Tools.getProperty( this.getOptions(), 'grid', 'y', 'enabled' ); if (!isXGridEnabled && !isYGridEnabled) { return; } // Draw the backdrop this.drawBackdrop(isXGridEnabled, isYGridEnabled); if (isXGridEnabled) { DOMUtils.appendOrSelect(this.backdrop, 'g.x.grid'); this.drawXGrid(animate); } if (isYGridEnabled) { DOMUtils.appendOrSelect(this.backdrop, 'g.y.grid'); this.drawYGrid(animate); } } drawXGrid(animate: boolean) { const svg = this.parent; const height = this.backdrop.attr('height'); const mainXScale = this.services.cartesianScales.getMainXScale(); const xGrid = axisBottom(mainXScale) .tickSizeInner(-height) .tickSizeOuter(0); // Determine number of ticks const numberOfTicks = Tools.getProperty( this.getOptions(), 'grid', 'x', 'numberOfTicks' ); xGrid.ticks(numberOfTicks); const g = svg .select('.x.grid') .attr( 'transform', `translate(${-this.backdrop.attr('x')}, ${height})` ); if (animate) { const transition = this.services.transitions.getTransition( 'grid-update' ); g.transition(transition).call(xGrid); } else { g.call(xGrid); } this.cleanGrid(g); } drawYGrid(animate: boolean) { const svg = this.parent; const width = this.backdrop.attr('width'); const mainYScale = this.services.cartesianScales.getMainYScale(); const yGrid = axisLeft(mainYScale) .tickSizeInner(-width) .tickSizeOuter(0); // Determine number of ticks const numberOfTicks = Tools.getProperty( this.getOptions(), 'grid', 'y', 'numberOfTicks' ); yGrid.ticks(numberOfTicks); const g = svg .select('.y.grid') .attr('transform', `translate(0, ${-this.backdrop.attr('y')})`); if (animate) { const transition = this.services.transitions.getTransition( 'grid-update' ); g.transition(transition).call(yGrid); } else { g.call(yGrid); } this.cleanGrid(g); } /** * Returns the threshold for the gridline tooltips based on the mouse location. * Calculated based on the mouse position between the two closest gridlines or edges of chart. */ getGridlineThreshold(mousePos) { // use the space between axis grid ticks to adjust the threshold for the tooltips const svg = this.parent; // sort in ascending x translation value order const gridlinesX = svg .selectAll('.x.grid .tick') .nodes() .sort((a, b) => { return ( Number(Tools.getTranslationValues(a).tx) - Number(Tools.getTranslationValues(b).tx) ); }); // find the 2 gridlines on either side of the mouse let floor = -1; let ceiling; if (!gridlinesX.length) { return; } gridlinesX.forEach((line: HTMLElement, i: any) => { if (mousePos[0] >= +Tools.getTranslationValues(line).tx) { floor++; } }); ceiling = floor + 1 < gridlinesX.length ? floor + 1 : gridlinesX.length; // get the 'step' between chart gridlines const line1 = gridlinesX[floor]; const line2 = gridlinesX[ceiling]; let lineSpacing; // if the mouse is on edge of charts (mouseX < first gridline || mouseX > last gridline) // we can use the chart edge to determind the threshold for the gridlines if (!line1) { // we are between the first gridline and the chart edge lineSpacing = +Tools.getTranslationValues(line2).tx; } else if (!line2) { // we need to use the chart right bounds in case there isnt a domain axis const gridElement = svg.select('rect.chart-grid-backdrop').node(); const width = DOMUtils.getSVGElementSize(gridElement).width; lineSpacing = width - +Tools.getTranslationValues(line1).tx; } else { // there are two gridlines to use lineSpacing = +Tools.getTranslationValues(line2).tx - +Tools.getTranslationValues(line1).tx; } const { threshold } = this.getOptions().tooltip.gridline; // return the threshold return lineSpacing * threshold; } /** * Returns the active gridlines based on the gridline threshold and mouse position. * @param position mouse positon */ getActiveGridline(position) { const userSpecifiedThreshold = Tools.getProperty( this.getOptions, 'tooltip', 'gridline', 'threshold' ); const threshold = userSpecifiedThreshold ? userSpecifiedThreshold : this.getGridlineThreshold(position); const svg = this.parent; const xGridlines = svg.selectAll('.x.grid .tick').filter(function () { const translations = Tools.getTranslationValues(this); // threshold for when to display a gridline tooltip const bounds = { min: Number(translations.tx) - threshold, max: Number(translations.tx) + threshold, }; return bounds.min <= position[0] && position[0] <= bounds.max; }); return xGridlines; } drawBackdrop(isXGridEnabled, isYGridEnabled) { const svg = this.parent; const mainXScale = this.services.cartesianScales.getMainXScale(); const mainYScale = this.services.cartesianScales.getMainYScale(); const [xScaleStart, xScaleEnd] = mainXScale.range(); const [yScaleEnd, yScaleStart] = mainYScale.range(); // Get height from the grid this.backdrop = DOMUtils.appendOrSelect(svg, 'svg.chart-grid-backdrop'); const backdropRect = DOMUtils.appendOrSelect( this.backdrop, isXGridEnabled || isYGridEnabled ? 'rect.chart-grid-backdrop.stroked' : 'rect.chart-grid-backdrop' ); this.backdrop .merge(backdropRect) .attr('x', xScaleStart) .attr('y', yScaleStart) .attr('width', Math.abs(xScaleEnd - xScaleStart)) .attr('height', Math.abs(yScaleEnd - yScaleStart)) .lower(); backdropRect.attr('width', '100%').attr('height', '100%'); } cleanGrid(g) { // Remove extra elements g.selectAll('text').remove(); g.select('.domain').remove(); } }