@carbon/charts
Version:
Carbon charting components
314 lines • 15.1 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 { Roles, Events, ColorClassNameTypes } from '../../interfaces';
import { Tools } from '../../tools';
// D3 Imports
import { select } from 'd3-selection';
var Scatter = /** @class */ (function (_super) {
__extends(Scatter, _super);
function Scatter() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'scatter';
_this.handleChartHolderOnHover = function (event) {
_this.parent
.selectAll('circle.dot')
.transition(_this.services.transitions.getTransition('chart-holder-hover-scatter'))
.attr('opacity', 1);
};
_this.handleChartHolderOnMouseOut = function (event) {
_this.parent
.selectAll('circle.dot')
.transition(_this.services.transitions.getTransition('chart-holder-mouseout-scatter'))
.attr('opacity', 0);
};
_this.handleLegendOnHover = function (event) {
var hoveredElement = event.detail.hoveredElement;
var groupMapsTo = _this.getOptions().data.groupMapsTo;
_this.parent
.selectAll('circle.dot')
.transition(_this.services.transitions.getTransition('legend-hover-scatter'))
.attr('opacity', function (d) {
return d[groupMapsTo] !== hoveredElement.datum()['name'] ? 0.3 : 1;
});
};
_this.handleLegendMouseOut = function (event) {
_this.parent
.selectAll('circle.dot')
.transition(_this.services.transitions.getTransition('legend-mouseout-scatter'))
.attr('opacity', 1);
};
return _this;
}
Scatter.prototype.init = function () {
var events = this.services.events;
// Highlight correct circle on legend item hovers
events.addEventListener(Events.Legend.ITEM_HOVER, this.handleLegendOnHover);
// Un-highlight circles on legend item mouseouts
events.addEventListener(Events.Legend.ITEM_MOUSEOUT, this.handleLegendMouseOut);
var fadeInOnChartHolderMouseover = this.configs.fadeInOnChartHolderMouseover;
if (fadeInOnChartHolderMouseover) {
// Fade-in scatter circles
events.addEventListener(Events.Chart.MOUSEOVER, this.handleChartHolderOnHover);
// Fade-out scatter circles
events.addEventListener(Events.Chart.MOUSEOUT, this.handleChartHolderOnMouseOut);
}
};
Scatter.prototype.filterBasedOnZoomDomain = function (data) {
var domainIdentifier = this.services.cartesianScales.getDomainIdentifier(data);
var zoomDomain = this.model.get('zoomDomain');
if (zoomDomain !== undefined) {
return data.filter(function (d) {
return d[domainIdentifier].getTime() > zoomDomain[0].getTime() &&
d[domainIdentifier].getTime() < zoomDomain[1].getTime();
});
}
return data;
};
Scatter.prototype.getScatterData = function () {
var _this = this;
var options = this.getOptions();
var stacked = this.configs.stacked;
var scatterData;
if (stacked) {
var percentage = Object.keys(options.axes).some(function (axis) { return options.axes[axis].percentage; });
scatterData = this.model.getStackedData({
groups: this.configs.groups,
percentage: percentage,
});
}
else {
scatterData = this.model
.getDisplayData(this.configs.groups)
.filter(function (d) {
var rangeIdentifier = _this.services.cartesianScales.getRangeIdentifier(d);
return (d[rangeIdentifier] !== undefined &&
d[rangeIdentifier] !== null);
});
}
// filter out datapoints that aren't part of the zoomed domain
return this.filterBasedOnZoomDomain(scatterData);
};
Scatter.prototype.render = function (animate) {
var isScatterEnabled = Tools.getProperty(this.getOptions(), 'points', 'enabled') ||
Tools.getProperty(this.getOptions(), 'bubble', 'enabled');
if (!isScatterEnabled) {
return;
}
// Grab container SVG
var svg = this.getContainerSVG({ withinChartClip: true });
var options = this.getOptions();
var groupMapsTo = options.data.groupMapsTo;
var domainIdentifier = this.services.cartesianScales.getDomainIdentifier();
// Update data on dot groups
var circles = svg
.selectAll('circle.dot')
.data(this.getScatterData(), function (datum) { return datum[groupMapsTo] + "-" + datum[domainIdentifier]; });
// Remove circles that need to be removed
circles.exit().attr('opacity', 0).remove();
// Add the dot groups that need to be introduced
var enteringCircles = circles
.enter()
.append('circle')
.classed('dot', true)
.attr('opacity', 0);
// Apply styling & position
var circlesToStyle = enteringCircles.merge(circles);
this.styleCircles(circlesToStyle, animate);
// Add event listeners to elements drawn
this.addEventListeners();
};
// A value is an anomaly if is above all defined domain and range thresholds
Scatter.prototype.isDatapointThresholdAnomaly = function (datum, index) {
var _this = this;
var handleThresholds = this.configs.handleThresholds;
if (!handleThresholds) {
return false;
}
var cartesianScales = this.services.cartesianScales;
var orientation = cartesianScales.getOrientation();
// Get highest domain and range thresholds
var _a = Tools.flipDomainAndRangeBasedOnOrientation(this.services.cartesianScales.getHighestDomainThreshold(), this.services.cartesianScales.getHighestRangeThreshold(), orientation), xThreshold = _a[0], yThreshold = _a[1];
var _b = Tools.flipDomainAndRangeBasedOnOrientation(function (d, i) { return _this.services.cartesianScales.getDomainValue(d, i); }, function (d, i) { return _this.services.cartesianScales.getRangeValue(d, i); }, orientation), getXValue = _b[0], getYValue = _b[1];
// Get datum x and y values
var xValue = getXValue(datum, index);
var yValue = getYValue(datum, index);
// To be an anomaly, the value has to be higher or equal than the threshold value
// (if are present, both range and domain threshold values)
if (yThreshold && xThreshold) {
return (yValue <= yThreshold.scaleValue &&
xValue >= xThreshold.scaleValue);
}
if (yThreshold) {
return yValue <= yThreshold.scaleValue;
}
if (xThreshold) {
return xValue >= xThreshold.scaleValue;
}
};
Scatter.prototype.styleCircles = function (selection, animate) {
var _this = this;
// Chart options mixed with the internal configurations
var options = this.getOptions();
var _a = options.points, filled = _a.filled, fillOpacity = _a.fillOpacity;
var _b = this.services, cartesianScales = _b.cartesianScales, transitions = _b.transitions;
var groupMapsTo = options.data.groupMapsTo;
var getDomainValue = function (d, i) { return cartesianScales.getDomainValue(d, i); };
var getRangeValue = function (d, i) { return cartesianScales.getRangeValue(d, i); };
var _c = Tools.flipDomainAndRangeBasedOnOrientation(getDomainValue, getRangeValue, cartesianScales.getOrientation()), getXValue = _c[0], getYValue = _c[1];
var fadeInOnChartHolderMouseover = this.configs.fadeInOnChartHolderMouseover;
selection
.raise()
.classed('dot', true)
.attr('class', function (d) {
var domainIdentifier = cartesianScales.getDomainIdentifier(d);
var isFilled = _this.model.getIsFilled(d[groupMapsTo], d[domainIdentifier], d, filled);
var classNamesNeeded = isFilled
? [ColorClassNameTypes.FILL, ColorClassNameTypes.STROKE]
: [ColorClassNameTypes.STROKE];
return _this.model.getColorClassName({
classNameTypes: classNamesNeeded,
dataGroupName: d[groupMapsTo],
originalClassName: 'dot',
});
})
// Set class to highlight the dots that are above all the thresholds, in both directions (vertical and horizontal)
.classed('threshold-anomaly', function (d, i) {
return _this.isDatapointThresholdAnomaly(d, i);
})
.classed('filled', function (d) {
var domainIdentifier = cartesianScales.getDomainIdentifier(d);
return _this.model.getIsFilled(d[groupMapsTo], d[domainIdentifier], d, filled);
})
.classed('unfilled', function (d) {
var domainIdentifier = cartesianScales.getDomainIdentifier(d);
return !_this.model.getIsFilled(d[groupMapsTo], d[domainIdentifier], d, filled);
})
.transition(transitions.getTransition('scatter-update-enter', animate))
.attr('cx', getXValue)
.attr('cy', getYValue)
.attr('r', options.points.radius)
.style('fill', function (d) {
var domainIdentifier = cartesianScales.getDomainIdentifier(d);
if (_this.model.getIsFilled(d[groupMapsTo], d[domainIdentifier], d, filled)) {
return _this.model.getFillColor(d[groupMapsTo], d[domainIdentifier], d);
}
})
.style('stroke', function (d) {
var domainIdentifier = cartesianScales.getDomainIdentifier(d);
return _this.model.getStrokeColor(d[groupMapsTo], d[domainIdentifier], d);
})
.attr('fill-opacity', filled ? fillOpacity : 1)
.attr('opacity', fadeInOnChartHolderMouseover ? 0 : 1)
// a11y
.attr('role', Roles.GRAPHICS_SYMBOL)
.attr('aria-roledescription', 'point')
.attr('aria-label', function (d) {
var rangeIdentifier = cartesianScales.getRangeIdentifier(d);
return d[rangeIdentifier];
});
// Add event listeners to elements drawn
this.addEventListeners();
};
Scatter.prototype.addEventListeners = function () {
var self = this;
var groupMapsTo = self.getOptions().data.groupMapsTo;
this.parent
.selectAll('circle')
.on('mouseover', function (datum) {
var hoveredElement = select(this);
hoveredElement
.classed('hovered', true)
.attr('class', function (d) {
return self.model.getColorClassName({
classNameTypes: [ColorClassNameTypes.FILL],
dataGroupName: d[groupMapsTo],
originalClassName: hoveredElement.attr('class'),
});
})
.style('fill', function (d) {
var domainIdentifier = self.services.cartesianScales.getDomainIdentifier(d);
return self.model.getFillColor(d[groupMapsTo], d[domainIdentifier], d);
})
.classed('unfilled', false);
// Show tooltip for single datapoint
self.services.events.dispatchEvent(Events.Tooltip.SHOW, {
hoveredElement: hoveredElement,
data: [datum],
});
// Dispatch mouse event
self.services.events.dispatchEvent(Events.Scatter.SCATTER_MOUSEOVER, {
element: hoveredElement,
datum: datum,
});
})
.on('mousemove', function (datum) {
var hoveredElement = select(this);
// Dispatch mouse event
self.services.events.dispatchEvent(Events.Scatter.SCATTER_MOUSEMOVE, {
element: hoveredElement,
datum: datum,
});
self.services.events.dispatchEvent(Events.Tooltip.MOVE);
})
.on('click', function (datum) {
// Dispatch mouse event
self.services.events.dispatchEvent(Events.Scatter.SCATTER_CLICK, {
element: select(this),
datum: datum,
});
})
.on('mouseout', function (datum) {
var hoveredElement = select(this);
hoveredElement.classed('hovered', false);
if (!self.configs.filled) {
var filled_1 = self.getOptions().points.filled;
var domainIdentifier_1 = self.services.cartesianScales.getDomainIdentifier(datum);
hoveredElement
.classed('unfilled', !self.model.getIsFilled(datum[groupMapsTo], datum[domainIdentifier_1], datum, filled_1))
.style('fill', function (d) {
return filled_1
? self.model.getFillColor(d[groupMapsTo], d[domainIdentifier_1], d)
: null;
});
}
// Dispatch mouse event
self.services.events.dispatchEvent(Events.Scatter.SCATTER_MOUSEOUT, {
element: hoveredElement,
datum: datum,
});
// Hide tooltip
self.services.events.dispatchEvent(Events.Tooltip.HIDE, {
hoveredElement: hoveredElement,
});
});
};
Scatter.prototype.destroy = function () {
// Remove event listeners
this.parent
.selectAll('circle')
.on('mousemove', null)
.on('mouseout', null);
// Remove legend listeners
var events = this.services.events;
events.removeEventListener(Events.Legend.ITEM_HOVER, this.handleLegendOnHover);
events.removeEventListener(Events.Legend.ITEM_MOUSEOUT, this.handleLegendMouseOut);
events.removeEventListener(Events.Chart.MOUSEOVER, this.handleChartHolderOnHover);
events.removeEventListener(Events.Chart.MOUSEOUT, this.handleChartHolderOnMouseOut);
};
return Scatter;
}(Component));
export { Scatter };
//# sourceMappingURL=../../../src/components/graphs/scatter.js.map