@carbon/charts
Version:
Carbon charting components
211 lines • 9.98 kB
JavaScript
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