@carbon/charts
Version:
Carbon charting components
556 lines • 26 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 __());
};
})();
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
// Internal Imports
import * as Configuration from '../configuration';
import { Service } from './service';
import { AxisPositions, CartesianOrientations, ScaleTypes, } from '../interfaces';
import { Tools } from '../tools';
// D3 Imports
import { scaleBand, scaleLinear, scaleTime, scaleLog } from 'd3-scale';
import { extent, sum } from 'd3-array';
import { map, values } from 'd3-collection';
// Misc
import { differenceInYears, addYears, subYears, differenceInMonths, addMonths, subMonths, differenceInDays, addDays, subDays, differenceInHours, addHours, subHours, differenceInMinutes, addMinutes, subMinutes, differenceInSeconds, subSeconds, addSeconds, } from 'date-fns';
var CartesianScales = /** @class */ (function (_super) {
__extends(CartesianScales, _super);
function CartesianScales() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.scaleTypes = {
top: null,
right: null,
bottom: null,
left: null,
};
_this.scales = {
top: null,
right: null,
bottom: null,
left: null,
};
return _this;
}
CartesianScales.prototype.getDomainAxisPosition = function (_a) {
var _b = (_a === void 0 ? {} : _a).datum, datum = _b === void 0 ? null : _b;
var _c;
if (this.dualAxes && datum) {
var options = this.model.getOptions();
var groupMapsTo = options.data.groupMapsTo;
var axesOptions = Tools.getProperty(options, 'axes', this.secondaryDomainAxisPosition);
var dataset = datum[groupMapsTo];
if (((_c = axesOptions) === null || _c === void 0 ? void 0 : _c.correspondingDatasets) &&
axesOptions.correspondingDatasets.includes(dataset)) {
return this.secondaryDomainAxisPosition;
}
}
return this.domainAxisPosition;
};
CartesianScales.prototype.getRangeAxisPosition = function (_a) {
var _b = _a === void 0 ? {} : _a, _c = _b.datum, datum = _c === void 0 ? null : _c, _d = _b.groups, groups = _d === void 0 ? null : _d;
var _e;
if (this.dualAxes) {
var options = this.model.getOptions();
var groupMapsTo = options.data.groupMapsTo;
var axisOptions = Tools.getProperty(options, 'axes', this.secondaryRangeAxisPosition);
var dataset = void 0;
if (datum !== null) {
dataset = datum[groupMapsTo];
}
else if (groups && groups.length > 0) {
dataset = groups[0];
}
if (((_e = axisOptions) === null || _e === void 0 ? void 0 : _e.correspondingDatasets) &&
axisOptions.correspondingDatasets.includes(dataset)) {
return this.secondaryRangeAxisPosition;
}
}
return this.rangeAxisPosition;
};
CartesianScales.prototype.getAxisOptions = function (position) {
return Tools.getProperty(this.model.getOptions(), 'axes', position);
};
CartesianScales.prototype.getDomainAxisOptions = function () {
var domainAxisPosition = this.getDomainAxisPosition();
return this.getAxisOptions(domainAxisPosition);
};
CartesianScales.prototype.getRangeAxisOptions = function () {
var rangeAxisPosition = this.getRangeAxisPosition();
return this.getAxisOptions(rangeAxisPosition);
};
CartesianScales.prototype.update = function (animate) {
var _this = this;
if (animate === void 0) { animate = true; }
this.determineAxisDuality();
this.findDomainAndRangeAxes();
this.determineOrientation();
var axisPositions = Object.keys(AxisPositions).map(function (axisPositionKey) { return AxisPositions[axisPositionKey]; });
axisPositions.forEach(function (axisPosition) {
_this.scales[axisPosition] = _this.createScale(axisPosition);
});
};
CartesianScales.prototype.findDomainAndRangeAxes = function () {
// find main axes between (left & right) && (bottom & top)
var verticalAxesPositions = this.findVerticalAxesPositions();
var horizontalAxesPositions = this.findHorizontalAxesPositions();
// Now we have horizontal & vertical main axes to choose domain & range axes from
var domainAndRangeAxesPositions = this.findDomainAndRangeAxesPositions(verticalAxesPositions, horizontalAxesPositions);
this.domainAxisPosition =
domainAndRangeAxesPositions.primaryDomainAxisPosition;
this.rangeAxisPosition =
domainAndRangeAxesPositions.primaryRangeAxisPosition;
if (this.isDualAxes()) {
this.secondaryDomainAxisPosition =
domainAndRangeAxesPositions.secondaryDomainAxisPosition;
this.secondaryRangeAxisPosition =
domainAndRangeAxesPositions.secondaryRangeAxisPosition;
}
};
CartesianScales.prototype.determineOrientation = function () {
if ((this.rangeAxisPosition === AxisPositions.LEFT ||
this.rangeAxisPosition === AxisPositions.RIGHT) &&
(this.domainAxisPosition === AxisPositions.BOTTOM ||
this.domainAxisPosition === AxisPositions.TOP)) {
this.orientation = CartesianOrientations.VERTICAL;
}
else {
this.orientation = CartesianOrientations.HORIZONTAL;
}
};
CartesianScales.prototype.isDualAxes = function () {
return this.dualAxes;
};
// if any of the axes objects have correspondingDatasets [] asserted we flag the chart as dual axes
// it does not count as dual axes if it just has another axis turned on but is not actually using it to map a dataset
CartesianScales.prototype.determineAxisDuality = function () {
var _a, _b, _c, _d;
var options = this.model.getOptions();
var axesOptions = Tools.getProperty(options, 'axes');
if ((((_a = axesOptions[AxisPositions.LEFT]) === null || _a === void 0 ? void 0 : _a.correspondingDatasets) &&
axesOptions[AxisPositions.RIGHT]) ||
(((_b = axesOptions[AxisPositions.RIGHT]) === null || _b === void 0 ? void 0 : _b.correspondingDatasets) &&
axesOptions[AxisPositions.LEFT]) ||
(((_c = axesOptions[AxisPositions.TOP]) === null || _c === void 0 ? void 0 : _c.correspondingDatasets) &&
axesOptions[AxisPositions.BOTTOM]) ||
(((_d = axesOptions[AxisPositions.BOTTOM]) === null || _d === void 0 ? void 0 : _d.correspondingDatasets) &&
axesOptions[AxisPositions.TOP])) {
this.dualAxes = true;
}
};
CartesianScales.prototype.getOrientation = function () {
return this.orientation;
};
CartesianScales.prototype.getScaleByPosition = function (axisPosition) {
return this.scales[axisPosition];
};
CartesianScales.prototype.getScaleTypeByPosition = function (axisPosition) {
return this.scaleTypes[axisPosition];
};
CartesianScales.prototype.getDomainAxisScaleType = function () {
var domainAxisPosition = this.getDomainAxisPosition();
return this.getScaleTypeByPosition(domainAxisPosition);
};
CartesianScales.prototype.getRangeAxisScaleType = function () {
var rangeAxisPosition = this.getRangeAxisPosition();
return this.getScaleTypeByPosition(rangeAxisPosition);
};
CartesianScales.prototype.getDomainScale = function () {
return this.scales[this.domainAxisPosition];
};
CartesianScales.prototype.getRangeScale = function () {
return this.scales[this.rangeAxisPosition];
};
// Find the main x-axis out of the 2 x-axis on the chart (when 2D axis is used)
CartesianScales.prototype.getMainXAxisPosition = function () {
var possibleXAxisPositions = [
AxisPositions.BOTTOM,
AxisPositions.TOP,
];
return [this.domainAxisPosition, this.rangeAxisPosition].find(function (position) { return possibleXAxisPositions.indexOf(position) > -1; });
};
// Find the main y-axis out of the 2 y-axis on the chart (when 2D axis is used)
CartesianScales.prototype.getMainYAxisPosition = function () {
var possibleYAxisPositions = [
AxisPositions.LEFT,
AxisPositions.RIGHT,
];
return [this.domainAxisPosition, this.rangeAxisPosition].find(function (position) { return possibleYAxisPositions.indexOf(position) > -1; });
};
CartesianScales.prototype.getMainXScale = function () {
return this.scales[this.getMainXAxisPosition()];
};
CartesianScales.prototype.getMainYScale = function () {
return this.scales[this.getMainYAxisPosition()];
};
CartesianScales.prototype.getValueFromScale = function (scale, scaleType, axisPosition, datum, index) {
var options = this.model.getOptions();
var axesOptions = Tools.getProperty(options, 'axes');
var axisOptions = axesOptions[axisPosition];
var mapsTo = axisOptions.mapsTo;
var value = datum[mapsTo] !== undefined ? datum[mapsTo] : datum;
var scaledValue;
switch (scaleType) {
case ScaleTypes.LABELS:
scaledValue = scale(value) + scale.step() / 2;
break;
case ScaleTypes.TIME:
scaledValue = scale(new Date(value));
break;
default:
scaledValue = scale(value);
}
return scaledValue;
};
CartesianScales.prototype.getBoundedScaledValues = function (datum, index) {
var bounds = this.model.getOptions().bounds;
var scale = this.scales[this.rangeAxisPosition];
var options = this.model.getOptions();
var axesOptions = Tools.getProperty(options, 'axes');
var axisOptions = axesOptions[this.rangeAxisPosition];
var mapsTo = axisOptions.mapsTo;
var value = datum[mapsTo] !== undefined ? datum[mapsTo] : datum;
var boundedValues = [
scale(datum[bounds.upperBoundMapsTo]
? datum[bounds.upperBoundMapsTo]
: value),
scale(datum[bounds.lowerBoundMapsTo]
? datum[bounds.lowerBoundMapsTo]
: value),
];
return boundedValues;
};
CartesianScales.prototype.getValueThroughAxisPosition = function (axisPosition, datum, index) {
var scaleType = this.scaleTypes[axisPosition];
var scale = this.scales[axisPosition];
return this.getValueFromScale(scale, scaleType, axisPosition, datum, index);
};
CartesianScales.prototype.getDomainValue = function (d, i) {
var axisPosition = this.getDomainAxisPosition({ datum: d });
return this.getValueThroughAxisPosition(axisPosition, d, i);
};
CartesianScales.prototype.getRangeValue = function (d, i) {
var axisPosition = this.getRangeAxisPosition({ datum: d });
return this.getValueThroughAxisPosition(axisPosition, d, i);
};
CartesianScales.prototype.getMainXScaleType = function () {
return this.getScaleTypeByPosition(this.getMainXAxisPosition());
};
CartesianScales.prototype.getMainYScaleType = function () {
return this.getScaleTypeByPosition(this.getMainYAxisPosition());
};
CartesianScales.prototype.getDomainIdentifier = function (datum) {
var options = this.model.getOptions();
return Tools.getProperty(options, 'axes', this.getDomainAxisPosition({ datum: datum }), 'mapsTo');
};
CartesianScales.prototype.getRangeIdentifier = function (datum) {
var options = this.model.getOptions();
return Tools.getProperty(options, 'axes', this.getRangeAxisPosition({ datum: datum }), 'mapsTo');
};
CartesianScales.prototype.extendsDomain = function (axisPosition, domain) {
var options = this.model.getOptions();
var axisOptions = Tools.getProperty(options, 'axes', axisPosition);
if (axisOptions.scaleType === ScaleTypes.TIME) {
var spaceToAddToEdges = Tools.getProperty(options, 'timeScale', 'addSpaceOnEdges');
return addSpacingToTimeDomain(domain, spaceToAddToEdges);
}
else {
return addSpacingToContinuousDomain(domain, Configuration.axis.paddingRatio);
}
};
CartesianScales.prototype.findVerticalAxesPositions = function () {
var options = this.model.getOptions();
var axesOptions = Tools.getProperty(options, 'axes');
var dualAxes = this.isDualAxes();
// If right axis has been specified as `main`
if ((Tools.getProperty(axesOptions, AxisPositions.LEFT) === null &&
Tools.getProperty(axesOptions, AxisPositions.RIGHT) !== null) ||
Tools.getProperty(axesOptions, AxisPositions.RIGHT, 'main') ===
true ||
(dualAxes &&
Tools.getProperty(axesOptions, AxisPositions.LEFT, 'correspondingDatasets'))) {
return {
primary: AxisPositions.RIGHT,
secondary: AxisPositions.LEFT,
};
}
return { primary: AxisPositions.LEFT, secondary: AxisPositions.RIGHT };
};
CartesianScales.prototype.findHorizontalAxesPositions = function () {
var options = this.model.getOptions();
var axesOptions = Tools.getProperty(options, 'axes');
var dualAxes = this.isDualAxes();
// If top axis has been specified as `main`
if ((Tools.getProperty(axesOptions, AxisPositions.BOTTOM) === null &&
Tools.getProperty(axesOptions, AxisPositions.TOP) !== null) ||
Tools.getProperty(axesOptions, AxisPositions.TOP, 'main') ===
true ||
(dualAxes &&
Tools.getProperty(axesOptions, AxisPositions.BOTTOM, 'correspondingDatasets'))) {
return {
primary: AxisPositions.TOP,
secondary: AxisPositions.BOTTOM,
};
}
return { primary: AxisPositions.BOTTOM, secondary: AxisPositions.TOP };
};
CartesianScales.prototype.findDomainAndRangeAxesPositions = function (verticalAxesPositions, horizontalAxesPositions) {
var options = this.model.getOptions();
var mainVerticalAxisOptions = Tools.getProperty(options, 'axes', verticalAxesPositions.primary);
var mainHorizontalAxisOptions = Tools.getProperty(options, 'axes', horizontalAxesPositions.primary);
var mainVerticalScaleType = mainVerticalAxisOptions.scaleType || ScaleTypes.LINEAR;
var mainHorizontalScaleType = mainHorizontalAxisOptions.scaleType || ScaleTypes.LINEAR;
var result = {
primaryDomainAxisPosition: null,
secondaryDomainAxisPosition: null,
primaryRangeAxisPosition: null,
secondaryRangeAxisPosition: null,
};
// assign to to be a vertical chart by default
result.primaryDomainAxisPosition = horizontalAxesPositions.primary;
result.primaryRangeAxisPosition = verticalAxesPositions.primary;
// secondary axes
result.secondaryDomainAxisPosition = horizontalAxesPositions.secondary;
result.secondaryRangeAxisPosition = verticalAxesPositions.secondary;
// if neither the horizontal axes are label or time
// and atleast one of the main vertical ones are labels or time then it should be horizontal
if ((!(mainHorizontalScaleType === ScaleTypes.LABELS ||
mainHorizontalScaleType === ScaleTypes.TIME) &&
mainVerticalScaleType === ScaleTypes.LABELS) ||
mainVerticalScaleType === ScaleTypes.TIME) {
result.primaryDomainAxisPosition = verticalAxesPositions.primary;
result.primaryRangeAxisPosition = horizontalAxesPositions.primary;
// secondary axes
result.secondaryDomainAxisPosition =
verticalAxesPositions.secondary;
result.secondaryRangeAxisPosition =
horizontalAxesPositions.secondary;
}
return result;
};
CartesianScales.prototype.getScaleDomain = function (axisPosition) {
var options = this.model.getOptions();
var axisOptions = Tools.getProperty(options, 'axes', axisPosition);
var bounds = Tools.getProperty(options, 'bounds');
var includeZero = axisOptions.includeZero;
var scaleType = Tools.getProperty(axisOptions, 'scaleType') || ScaleTypes.LINEAR;
if (this.model.isDataEmpty()) {
return [];
}
var displayData = this.model.getDisplayData();
var mapsTo = axisOptions.mapsTo, percentage = axisOptions.percentage;
// If domain is specified return that domain
if (axisOptions.domain) {
if (scaleType === ScaleTypes.LABELS) {
return axisOptions.domain;
}
else if (scaleType === ScaleTypes.TIME) {
axisOptions.domain = axisOptions.domain.map(function (d) {
return d.getTime === undefined ? new Date(d) : d;
});
}
return this.extendsDomain(axisPosition, axisOptions.domain);
}
// Return [0, 100] for percentage axis scale
if (percentage) {
return [0, 100];
}
// If scale is a LABELS scale, return some labels as the domain
if (axisOptions && scaleType === ScaleTypes.LABELS) {
// Get unique values
return map(displayData, function (d) { return d[mapsTo]; }).keys();
}
// Get the extent of the domain
var domain;
var allDataValues;
var dataGroupNames = this.model.getDataGroupNames();
if (scaleType === ScaleTypes.TIME) {
allDataValues = displayData.map(function (datum) { return +new Date(datum[mapsTo]); });
}
else if (bounds && options.axes) {
allDataValues = [];
displayData.forEach(function (datum) {
allDataValues.push(datum[mapsTo]);
if (datum[bounds.upperBoundMapsTo]) {
allDataValues.push(datum[bounds.upperBoundMapsTo]);
}
if (datum[bounds.lowerBoundMapsTo]) {
allDataValues.push(datum[bounds.lowerBoundMapsTo]);
}
});
}
else if (axisOptions.stacked === true &&
dataGroupNames &&
axisPosition === this.getRangeAxisPosition()) {
var groupMapsTo_1 = options.data.groupMapsTo;
var dataValuesGroupedByKeys = this.model.getDataValuesGroupedByKeys(dataGroupNames);
var nonStackedGroupsData = displayData.filter(function (datum) { return !dataGroupNames.includes(datum[groupMapsTo_1]); });
var stackedValues = dataValuesGroupedByKeys.map(function (dataValues) {
var sharedStackKey = dataValues.sharedStackKey, numericalValues = __rest(dataValues, ["sharedStackKey"]);
return sum(values(numericalValues));
});
allDataValues = __spreadArrays(stackedValues, nonStackedGroupsData.map(function (datum) { return datum[mapsTo]; }));
}
else {
allDataValues = displayData.map(function (datum) { return datum[mapsTo]; });
}
if (scaleType !== ScaleTypes.TIME && includeZero) {
allDataValues.push(0);
}
domain = extent(allDataValues);
domain = this.extendsDomain(axisPosition, domain);
return domain;
};
CartesianScales.prototype.createScale = function (axisPosition) {
var options = this.model.getOptions();
var axisOptions = Tools.getProperty(options, 'axes', axisPosition);
if (!axisOptions) {
return null;
}
var scaleType = Tools.getProperty(axisOptions, 'scaleType') || ScaleTypes.LINEAR;
this.scaleTypes[axisPosition] = scaleType;
var scale;
if (scaleType === ScaleTypes.TIME) {
scale = scaleTime();
}
else if (scaleType === ScaleTypes.LOG) {
scale = scaleLog().base(axisOptions.base || 10);
}
else if (scaleType === ScaleTypes.LABELS) {
scale = scaleBand();
}
else {
scale = scaleLinear();
}
scale.domain(this.getScaleDomain(axisPosition));
return scale;
};
CartesianScales.prototype.getHighestDomainThreshold = function () {
var axesOptions = Tools.getProperty(this.model.getOptions(), 'axes');
var domainAxisPosition = this.getDomainAxisPosition();
var thresholds = axesOptions[domainAxisPosition].thresholds;
if (!thresholds) {
return null;
}
var domainScale = this.getDomainScale();
// Find the highest threshold for the domain
var highestThreshold = thresholds.sort(function (a, b) { return b.value - a.value; })[0];
var scaleType = this.getScaleTypeByPosition(domainAxisPosition);
if (scaleType === ScaleTypes.TIME &&
(typeof highestThreshold.value === 'string' ||
highestThreshold.value.getTime === undefined)) {
highestThreshold.value = new Date(highestThreshold.value);
}
return {
threshold: highestThreshold,
scaleValue: domainScale(highestThreshold.value),
};
};
CartesianScales.prototype.getHighestRangeThreshold = function () {
var axesOptions = Tools.getProperty(this.model.getOptions(), 'axes');
var rangeAxisPosition = this.getRangeAxisPosition();
var thresholds = axesOptions[rangeAxisPosition].thresholds;
if (!thresholds) {
return null;
}
var rangeScale = this.getRangeScale();
// Find the highest threshold for the range
var highestThreshold = thresholds.sort(function (a, b) { return b.value - a.value; })[0];
return {
threshold: highestThreshold,
scaleValue: rangeScale(highestThreshold.value),
};
};
return CartesianScales;
}(Service));
export { CartesianScales };
function addSpacingToTimeDomain(domain, spaceToAddToEdges) {
var startDate = new Date(domain[0]);
var endDate = new Date(domain[1]);
if (differenceInYears(endDate, startDate) > 1) {
return [
subYears(startDate, spaceToAddToEdges),
addYears(endDate, spaceToAddToEdges),
];
}
if (differenceInMonths(endDate, startDate) > 1) {
return [
subMonths(startDate, spaceToAddToEdges),
addMonths(endDate, spaceToAddToEdges),
];
}
if (differenceInDays(endDate, startDate) > 1) {
return [
subDays(startDate, spaceToAddToEdges),
addDays(endDate, spaceToAddToEdges),
];
}
if (differenceInHours(endDate, startDate) > 1) {
return [
subHours(startDate, spaceToAddToEdges),
addHours(endDate, spaceToAddToEdges),
];
}
if (differenceInMinutes(endDate, startDate) > 30) {
return [
subMinutes(startDate, spaceToAddToEdges * 30),
addMinutes(endDate, spaceToAddToEdges * 30),
];
}
if (differenceInMinutes(endDate, startDate) > 1) {
return [
subMinutes(startDate, spaceToAddToEdges),
addMinutes(endDate, spaceToAddToEdges),
];
}
if (differenceInSeconds(endDate, startDate) > 15) {
return [
subSeconds(startDate, spaceToAddToEdges * 15),
addSeconds(endDate, spaceToAddToEdges * 15),
];
}
if (differenceInSeconds(endDate, startDate) > 1) {
return [
subSeconds(startDate, spaceToAddToEdges),
addSeconds(endDate, spaceToAddToEdges),
];
}
return [startDate, endDate];
}
function addSpacingToContinuousDomain(_a, paddingRatio) {
var lower = _a[0], upper = _a[1];
var domainLength = upper - lower;
var padding = domainLength * paddingRatio;
// If padding crosses 0, keep 0 as new upper bound
var newUpper = upper <= 0 && upper + padding > 0 ? 0 : upper + padding;
// If padding crosses 0, keep 0 as new lower bound
var newLower = lower >= 0 && lower - padding < 0 ? 0 : lower - padding;
return [newLower, newUpper];
}
//# sourceMappingURL=../../src/services/scales-cartesian.js.map