veritatisquam
Version:
Carbon charting components, lerna monorepo
162 lines (144 loc) • 4.25 kB
text/typescript
// Internal Imports
import { Scatter } from './scatter';
import { Roles } from '../../interfaces';
import { Tools } from '../../tools';
export class StackedScatter extends Scatter {
type = 'scatter-stacked';
render(animate: boolean) {
const isScatterEnabled = Tools.getProperty(
this.getOptions(),
'points',
'enabled'
);
if (!isScatterEnabled) {
return;
}
// Grab container SVG
const svg = this.getContainerSVG({ withinChartClip: true });
const options = this.getOptions();
const { groupMapsTo } = options.data;
const percentage = Object.keys(options.axes).some(
(axis) => options.axes[axis].percentage
);
const stackedData = this.model.getStackedData({
groups: this.configs.groups,
percentage,
});
// Update data on dot groups
const circleGroups = svg
.selectAll('g.dots')
.data(stackedData, (d) => d[0][groupMapsTo]);
// Remove dot groups that need to be removed
circleGroups.exit().attr('opacity', 0).remove();
// Add the dot groups that need to be introduced
const circleGroupsEnter = circleGroups
.enter()
.append('g')
.classed('dots', true)
.attr('role', Roles.GROUP);
// Update data on all circles
const circles = circleGroupsEnter
.merge(circleGroups)
.selectAll('circle.dot')
.data((d) => d);
// Remove circles that need to be removed
circles.exit().attr('opacity', 0).remove();
// Add the dot groups that need to be introduced
const enteringCircles = circles
.enter()
.append('circle')
.classed('dot', true)
.attr('opacity', 0);
// Apply styling & position
const circlesToStyle = enteringCircles.merge(circles).datum((d) => {
const group = d[groupMapsTo];
const domainIdentifier = this.services.cartesianScales.getDomainIdentifier(
d
);
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier(
d
);
return {
[groupMapsTo]: group,
[domainIdentifier]: d['data']['sharedStackKey'],
[rangeIdentifier]: d[1],
};
});
this.styleCircles(circlesToStyle, animate);
// Add event listeners to elements drawn
this.addEventListeners();
}
getTooltipData(hoveredX, hoveredY) {
const options = this.getOptions();
const { groupMapsTo } = options.data;
const percentage = Object.keys(options.axes).some(
(axis) => options.axes[axis].percentage
);
const stackedData = this.model.getStackedData({
groups: this.configs.groups,
percentage,
});
const tooltipData = [];
stackedData.forEach((groupData, groupDataIndex) => {
groupData.forEach((datum, dataIndex) => {
const group = datum[groupMapsTo];
const domainValue = datum['data']['sharedStackKey'];
let rangeValue = datum['data'][group];
const stackedRangeValue = datum[1];
const domainIdentifier = this.services.cartesianScales.getDomainIdentifier(
datum
);
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier(
datum
);
if (
rangeValue !== null &&
rangeValue !== undefined &&
hoveredX ===
this.services.cartesianScales.getDomainValue(
domainValue
) &&
hoveredY ===
this.services.cartesianScales.getRangeValue(
stackedRangeValue
)
) {
if (percentage) {
rangeValue = this.model.getStackedData({
groups: this.configs.groups,
})[groupDataIndex][dataIndex]['data'][group];
}
if (rangeValue !== null) {
tooltipData.push({
[groupMapsTo]: group,
[domainIdentifier]: domainValue,
[rangeIdentifier]: rangeValue,
});
}
}
});
});
return this.model
.getDisplayData(this.configs.groups)
.filter((datapoint) => {
const domainIdentifier = this.services.cartesianScales.getDomainIdentifier(
datapoint
);
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier(
datapoint
);
return (
tooltipData.find((tooltipDatapoint) => {
return (
tooltipDatapoint[groupMapsTo] ==
datapoint[groupMapsTo] &&
tooltipDatapoint[domainIdentifier] ==
datapoint[domainIdentifier] &&
tooltipDatapoint[rangeIdentifier] ==
datapoint[rangeIdentifier]
);
}) !== undefined
);
});
}
}