UNPKG

@carbon/charts

Version:
211 lines 9.98 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); // Internal Imports import { Component } from '../component'; import { DOMUtils } from '../../services'; import { CartesianOrientations, Events } from '../../interfaces'; import { Tools } from '../../tools'; // D3 Imports import { mouse } from 'd3-selection'; var THRESHOLD = 5; /** check if x is inside threshold area extents */ function pointIsWithinThreshold(dx, x) { return dx > x - THRESHOLD && dx < x + THRESHOLD; } var Ruler = /** @class */ (function (_super) { __extends(Ruler, _super); function Ruler() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.type = 'ruler'; _this.isXGridEnabled = Tools.getProperty(_this.getOptions(), 'grid', 'x', 'enabled'); _this.isYGridEnabled = Tools.getProperty(_this.getOptions(), 'grid', 'y', 'enabled'); // flag for checking whether ruler event listener is added or not _this.isEventListenerAdded = false; return _this; } Ruler.prototype.render = function () { var isRulerEnabled = Tools.getProperty(this.getOptions(), 'ruler', 'enabled'); this.drawBackdrop(); if (isRulerEnabled && !this.isEventListenerAdded) { this.addBackdropEventListeners(); } else if (!isRulerEnabled && this.isEventListenerAdded) { this.removeBackdropEventListeners(); } }; Ruler.prototype.removeBackdropEventListeners = function () { this.isEventListenerAdded = false; this.backdrop.on('mousemove mouseover mouseout', null); }; Ruler.prototype.formatTooltipData = function (tooltipData) { return tooltipData; }; Ruler.prototype.showRuler = function (_a) { var _this = this; var x = _a[0], y = _a[1]; var svg = this.parent; var orientation = this.services.cartesianScales.getOrientation(); var mouseCoordinate = orientation === CartesianOrientations.HORIZONTAL ? y : x; var ruler = DOMUtils.appendOrSelect(svg, 'g.ruler').attr('aria-label', 'ruler'); var rulerLine = DOMUtils.appendOrSelect(ruler, 'line.ruler-line'); var dataPointElements = svg.selectAll('[role=graphics-symbol]'); var displayData = this.model.getDisplayData(); var rangeScale = this.services.cartesianScales.getRangeScale(); var _b = rangeScale.range(), yScaleEnd = _b[0], yScaleStart = _b[1]; var pointsWithinLine = displayData .map(function (d) { return ({ domainValue: _this.services.cartesianScales.getDomainValue(d), originalData: d, }); }) .filter(function (d) { return pointIsWithinThreshold(d.domainValue, mouseCoordinate); }); if (this.pointsWithinLine && pointsWithinLine.length === this.pointsWithinLine.length && pointsWithinLine.map(function (point) { return point.domainValue; }).join() === this.pointsWithinLine.map(function (point) { return point.domainValue; }).join()) { this.pointsWithinLine = pointsWithinLine; return this.services.events.dispatchEvent(Events.Tooltip.MOVE, { mousePosition: [x, y], }); } this.pointsWithinLine = pointsWithinLine; /** * Find matches, reduce is used instead of filter * to only get elements which belong to the same axis coordinate */ var dataPointsMatchingRulerLine = this.pointsWithinLine.reduce(function (accum, currentValue) { if (accum.length === 0) { accum.push(currentValue); return accum; } // store the first element of the accumulator array to compare it with current element being processed var sampleAccumValue = accum[0].domainValue; var distanceToCurrentValue = Math.abs(mouseCoordinate - currentValue.domainValue); var distanceToAccumValue = Math.abs(mouseCoordinate - sampleAccumValue); if (distanceToCurrentValue > distanceToAccumValue) { // if distance with current value is bigger than already existing value in the accumulator, skip current iteration return accum; } else if (distanceToCurrentValue < distanceToAccumValue) { // currentValue data point is closer to mouse inside the threshold area, so reinstantiate array accum = [currentValue]; } else { // currentValue is equal to already stored values, which means there's another match on the same coordinate accum.push(currentValue); } return accum; }, []); // some data point match if (dataPointsMatchingRulerLine.length > 0) { var tooltipData = dataPointsMatchingRulerLine .map(function (d) { return d.originalData; }) .filter(function (d) { var rangeIdentifier = _this.services.cartesianScales.getRangeIdentifier(d); var value = d[rangeIdentifier]; return value !== null && value !== undefined; }); // get elements on which we should trigger mouse events var domainValuesMatchingRulerLine_1 = dataPointsMatchingRulerLine.map(function (d) { return d.domainValue; }); var elementsToHighlight = dataPointElements.filter(function (d) { var domainValue = _this.services.cartesianScales.getDomainValue(d); return domainValuesMatchingRulerLine_1.includes(domainValue); }); /** if we pass from a trigger area to another one * mouseout on previous elements won't get dispatched * so we need to do it manually */ if (this.elementsToHighlight && this.elementsToHighlight.size() > 0 && !Tools.isEqual(this.elementsToHighlight, elementsToHighlight)) { this.hideRuler(); } elementsToHighlight.dispatch('mouseover'); // set current hovered elements this.elementsToHighlight = elementsToHighlight; this.services.events.dispatchEvent(Events.Tooltip.SHOW, { mousePosition: [x, y], hoveredElement: rulerLine, data: this.formatTooltipData(tooltipData), }); ruler.attr('opacity', 1); // line snaps to matching point var sampleMatch = dataPointsMatchingRulerLine[0]; if (orientation === 'horizontal') { rulerLine .attr('x1', yScaleStart) .attr('x2', yScaleEnd) .attr('y1', sampleMatch.domainValue) .attr('y2', sampleMatch.domainValue); } else { rulerLine .attr('y1', yScaleStart) .attr('y2', yScaleEnd) .attr('x1', sampleMatch.domainValue) .attr('x2', sampleMatch.domainValue); } } else { this.hideRuler(); } }; Ruler.prototype.hideRuler = function () { var svg = this.parent; var ruler = DOMUtils.appendOrSelect(svg, 'g.ruler'); var dataPointElements = svg.selectAll('[role=graphics-symbol]'); dataPointElements.dispatch('mouseout'); this.services.events.dispatchEvent(Events.Tooltip.HIDE); ruler.attr('opacity', 0); }; /** * Adds the listener on the X grid to trigger multiple point tooltips along the x axis. */ Ruler.prototype.addBackdropEventListeners = function () { this.isEventListenerAdded = true; var self = this; var displayData = this.model.getDisplayData(); var mouseMoveCallback = function () { var pos = mouse(self.parent.node()); self.showRuler(pos); }; // Debounce mouseMoveCallback if there are more than 100 datapoints if (displayData.length > 100) { var debounceThreshold = (displayData.length % 50) * 12.5; mouseMoveCallback = Tools.debounceWithD3MousePosition(function () { var mousePosition = this.mousePosition; self.showRuler(mousePosition); }, debounceThreshold, this.parent.node()); } this.backdrop .on('mousemove mouseover', mouseMoveCallback) .on('mouseout', this.hideRuler.bind(this)); }; Ruler.prototype.drawBackdrop = function () { var svg = this.parent; var mainXScale = this.services.cartesianScales.getMainXScale(); var mainYScale = this.services.cartesianScales.getMainYScale(); var _a = mainXScale.range(), xScaleStart = _a[0], xScaleEnd = _a[1]; var _b = mainYScale.range(), yScaleEnd = _b[0], yScaleStart = _b[1]; // Get height from the grid this.backdrop = DOMUtils.appendOrSelect(svg, 'svg.chart-grid-backdrop'); var backdropRect = DOMUtils.appendOrSelect(this.backdrop, this.isXGridEnabled || this.isYGridEnabled ? 'rect.chart-grid-backdrop.stroked' : 'rect.chart-grid-backdrop'); }; return Ruler; }(Component)); export { Ruler }; //# sourceMappingURL=../../../src/components/axes/ruler.js.map