@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
1,053 lines • 65.2 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
// (C) 2007-2020 GoodData Corporation
var numberjs_1 = require("@gooddata/numberjs");
var typings_1 = require("@gooddata/typings");
var invariant = require("invariant");
var cloneDeep = require("lodash/cloneDeep");
var compact = require("lodash/compact");
var escape = require("lodash/escape");
var get = require("lodash/get");
var includes = require("lodash/includes");
var isEmpty = require("lodash/isEmpty");
var isEqual = require("lodash/isEqual");
var isUndefined = require("lodash/isUndefined");
var last = require("lodash/last");
var range = require("lodash/range");
var unescape = require("lodash/unescape");
var without = require("lodash/without");
var isNil = require("lodash/isNil");
var cx = require("classnames");
var bucketNames_1 = require("../../../constants/bucketNames");
var visualizationTypes_1 = require("../../../constants/visualizationTypes");
var executionResultHelper_1 = require("../../../helpers/executionResultHelper");
var utils_1 = require("../../../helpers/utils");
var mdObjBucketHelper_1 = require("../../../helpers/mdObjBucketHelper");
var headerPredicate_1 = require("../../../helpers/headerPredicate");
var color_1 = require("../utils/color");
var common_1 = require("../utils/common");
var drilldownEventing_1 = require("../utils/drilldownEventing");
var comboChartOptions_1 = require("./chartOptions/comboChartOptions");
var bulletChartOptions_1 = require("./chartOptions/bulletChartOptions");
var colorFactory_1 = require("./colorFactory");
var constants_1 = require("./constants");
var tooltip_1 = require("./tooltip");
var commonConfiguration_1 = require("./highcharts/commonConfiguration");
var helpers_1 = require("./highcharts/helpers");
var highChartsCreators_1 = require("./highChartsCreators");
var getOptionalStackingConfiguration_1 = require("./highcharts/getOptionalStackingConfiguration");
var extendedStackingChartOptions_1 = require("./chartOptions/extendedStackingChartOptions");
var dualAxis_1 = require("../../../helpers/dualAxis");
var domUtils_1 = require("../../../helpers/domUtils");
var omit = require("lodash/omit");
var bucketHelper_1 = require("../../../internal/utils/bucketHelper");
var TOOLTIP_PADDING = 10;
var isAreaChartStackingEnabled = function (options) {
var type = options.type, stacking = options.stacking, stackMeasures = options.stackMeasures;
if (!common_1.isAreaChart(type)) {
return false;
}
if (isUndefined(stackMeasures)) {
return stacking || isUndefined(stacking);
}
return stackMeasures;
};
// types with only many measures or one measure and one attribute
var multiMeasuresAlternatingTypes = [
visualizationTypes_1.VisualizationTypes.PIE,
visualizationTypes_1.VisualizationTypes.DONUT,
visualizationTypes_1.VisualizationTypes.FUNNEL,
visualizationTypes_1.VisualizationTypes.TREEMAP,
];
var unsupportedNegativeValuesTypes = [
visualizationTypes_1.VisualizationTypes.PIE,
visualizationTypes_1.VisualizationTypes.DONUT,
visualizationTypes_1.VisualizationTypes.FUNNEL,
visualizationTypes_1.VisualizationTypes.TREEMAP,
];
// charts sorted by default by measure value
var sortedByMeasureTypes = [visualizationTypes_1.VisualizationTypes.PIE, visualizationTypes_1.VisualizationTypes.DONUT, visualizationTypes_1.VisualizationTypes.FUNNEL];
var unsupportedStackingTypes = [
visualizationTypes_1.VisualizationTypes.LINE,
visualizationTypes_1.VisualizationTypes.AREA,
visualizationTypes_1.VisualizationTypes.SCATTER,
visualizationTypes_1.VisualizationTypes.BUBBLE,
];
var nullColor = {
pattern: {
path: {
d: "M 10 0 L 0 10 M 9 11 L 11 9 M 4 11 L 11 4 M -1 1 L 1 -1 M -1 6 L 6 -1",
stroke: color_1.GRAY,
strokeWidth: 1,
fill: color_1.WHITE,
},
width: 10,
height: 10,
},
};
exports.supportedDualAxesChartTypes = [
visualizationTypes_1.VisualizationTypes.COLUMN,
visualizationTypes_1.VisualizationTypes.BAR,
visualizationTypes_1.VisualizationTypes.BULLET,
visualizationTypes_1.VisualizationTypes.LINE,
visualizationTypes_1.VisualizationTypes.AREA,
visualizationTypes_1.VisualizationTypes.COMBO,
visualizationTypes_1.VisualizationTypes.COMBO2,
];
exports.supportedTooltipFollowPointerChartTypes = [
visualizationTypes_1.VisualizationTypes.COLUMN,
visualizationTypes_1.VisualizationTypes.BAR,
visualizationTypes_1.VisualizationTypes.BULLET,
visualizationTypes_1.VisualizationTypes.COMBO,
visualizationTypes_1.VisualizationTypes.COMBO2,
];
exports.supportedStackingAttributesChartTypes = [
visualizationTypes_1.VisualizationTypes.COLUMN,
visualizationTypes_1.VisualizationTypes.BAR,
visualizationTypes_1.VisualizationTypes.BULLET,
visualizationTypes_1.VisualizationTypes.AREA,
visualizationTypes_1.VisualizationTypes.COMBO,
visualizationTypes_1.VisualizationTypes.COMBO2,
];
function isNegativeValueIncluded(series) {
return series.some(function (seriesItem) {
return (seriesItem.data || []).some(function (_a) {
var y = _a.y, value = _a.value;
return y < 0 || value < 0;
});
});
}
exports.isNegativeValueIncluded = isNegativeValueIncluded;
function getChartLimits(type) {
switch (type) {
case visualizationTypes_1.VisualizationTypes.SCATTER:
return {
series: commonConfiguration_1.DEFAULT_SERIES_LIMIT,
categories: commonConfiguration_1.DEFAULT_SERIES_LIMIT,
};
case visualizationTypes_1.VisualizationTypes.PIE:
case visualizationTypes_1.VisualizationTypes.DONUT:
case visualizationTypes_1.VisualizationTypes.FUNNEL:
return {
series: 1,
categories: constants_1.PIE_CHART_LIMIT,
};
case visualizationTypes_1.VisualizationTypes.TREEMAP:
return {
series: commonConfiguration_1.DEFAULT_SERIES_LIMIT,
categories: commonConfiguration_1.DEFAULT_DATA_POINTS_LIMIT,
dataPoints: commonConfiguration_1.DEFAULT_DATA_POINTS_LIMIT,
};
case visualizationTypes_1.VisualizationTypes.HEATMAP:
return {
series: commonConfiguration_1.DEFAULT_SERIES_LIMIT,
categories: commonConfiguration_1.DEFAULT_CATEGORIES_LIMIT,
dataPoints: constants_1.HEATMAP_DATA_POINTS_LIMIT,
};
default:
return {
series: commonConfiguration_1.DEFAULT_SERIES_LIMIT,
categories: commonConfiguration_1.DEFAULT_CATEGORIES_LIMIT,
};
}
}
function cannotShowNegativeValues(type) {
return common_1.isOneOfTypes(type, unsupportedNegativeValuesTypes);
}
exports.cannotShowNegativeValues = cannotShowNegativeValues;
function getTreemapDataForValidation(data) {
// filter out root nodes
return __assign({}, data, { series: data.series.map(function (serie) { return (__assign({}, serie, { data: serie.data.filter(function (dataItem) { return dataItem.id === undefined; }) })); }) });
}
function validateData(limits, chartOptions) {
var type = chartOptions.type, isViewByTwoAttributes = chartOptions.isViewByTwoAttributes;
var finalLimits = limits || getChartLimits(type);
var dataToValidate = chartOptions.data;
if (common_1.isTreemap(type)) {
dataToValidate = getTreemapDataForValidation(chartOptions.data);
}
return {
dataTooLarge: !highChartsCreators_1.isDataOfReasonableSize(dataToValidate, finalLimits, isViewByTwoAttributes),
hasNegativeValue: cannotShowNegativeValues(type) && isNegativeValueIncluded(chartOptions.data.series),
};
}
exports.validateData = validateData;
function isDerivedMeasure(measureItem, afm) {
return afm.measures.some(function (measure) {
var measureDefinition = get(measure, "definition.popMeasure") || get(measure, "definition.previousPeriodMeasure");
var derivedMeasureIdentifier = measureDefinition ? measure.localIdentifier : null;
return (derivedMeasureIdentifier &&
derivedMeasureIdentifier === measureItem.measureHeaderItem.localIdentifier);
});
}
exports.isDerivedMeasure = isDerivedMeasure;
function findMeasureIndex(afm, measureIdentifier) {
return afm.measures.findIndex(function (measure) { return measure.localIdentifier === measureIdentifier; });
}
function findParentMeasureIndex(afm, measureItemIndex) {
var measureDefinition = afm.measures[measureItemIndex].definition;
if (typings_1.AFM.isPopMeasureDefinition(measureDefinition)) {
var sourceMeasureIdentifier = measureDefinition.popMeasure.measureIdentifier;
return findMeasureIndex(afm, sourceMeasureIdentifier);
}
if (typings_1.AFM.isPreviousPeriodMeasureDefinition(measureDefinition)) {
var sourceMeasureIdentifier = measureDefinition.previousPeriodMeasure.measureIdentifier;
return findMeasureIndex(afm, sourceMeasureIdentifier);
}
return -1;
}
exports.findParentMeasureIndex = findParentMeasureIndex;
function getSeriesItemData(seriesItem, seriesIndex, measureGroup, viewByAttribute, stackByAttribute, type, colorStrategy) {
return seriesItem.map(function (pointValue, pointIndex) {
// by default seriesIndex corresponds to measureGroup label index
var measureIndex = seriesIndex;
// by default pointIndex corresponds to viewBy label index
var viewByIndex = pointIndex;
// drillContext can have 1 to 3 items
// viewBy attribute label, stackby label if available
// last drillContextItem is always current serie measure
if (stackByAttribute) {
// pointIndex corresponds to viewBy attribute label (if available)
viewByIndex = pointIndex;
// stack bar chart has always just one measure
measureIndex = 0;
}
else if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes) && !viewByAttribute) {
measureIndex = pointIndex;
}
var valueProp = {
y: common_1.parseValue(pointValue),
};
if (common_1.isTreemap(type)) {
valueProp = {
value: common_1.parseValue(pointValue),
};
}
var pointData = __assign({}, valueProp, { format: utils_1.unwrap(measureGroup.items[measureIndex]).format, marker: {
enabled: pointValue !== null,
} });
if (stackByAttribute) {
// if there is a stackBy attribute, then seriesIndex corresponds to stackBy label index
pointData.name = utils_1.unwrap(stackByAttribute.items[seriesIndex]).name;
}
else if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes) && viewByAttribute) {
pointData.name = utils_1.unwrap(viewByAttribute.items[viewByIndex]).name;
}
else {
pointData.name = utils_1.unwrap(measureGroup.items[measureIndex]).name;
}
if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes)) {
pointData.color = colorStrategy.getColorByIndex(pointIndex);
// Pie and Treemap charts use pointData viewByIndex as legendIndex if available
// instead of seriesItem legendIndex
pointData.legendIndex = viewByAttribute ? viewByIndex : pointIndex;
}
return pointData;
});
}
exports.getSeriesItemData = getSeriesItemData;
function getHeatmapSeries(executionResultData, measureGroup) {
var data = [];
executionResultData.forEach(function (rowItem, rowItemIndex) {
rowItem.forEach(function (columnItem, columnItemIndex) {
var value = common_1.parseValue(String(columnItem));
var pointData = { x: columnItemIndex, y: rowItemIndex, value: value };
if (isNil(value)) {
data.push(__assign({}, pointData, { borderWidth: 1, borderColor: color_1.GRAY, color: color_1.TRANSPARENT }));
data.push(__assign({}, pointData, { borderWidth: 0, pointPadding: 2, color: nullColor,
// ignoredInDrillEventContext flag is used internally, not related to Highchart
// to check and remove this null-value point in drill message
ignoredInDrillEventContext: true }));
}
else {
data.push(pointData);
}
});
});
return [
{
name: measureGroup.items[0].measureHeaderItem.name,
data: data,
turboThreshold: 0,
yAxis: 0,
dataLabels: {
formatGD: utils_1.unwrap(measureGroup.items[0]).format,
},
legendIndex: 0,
},
];
}
exports.getHeatmapSeries = getHeatmapSeries;
function getScatterPlotSeries(executionResultData, stackByAttribute, mdObject, colorStrategy) {
var buckets = get(mdObject, "buckets", []);
var primaryMeasuresBucketEmpty = mdObjBucketHelper_1.isBucketEmpty(buckets, bucketNames_1.MEASURES);
var secondaryMeasuresBucketEmpty = mdObjBucketHelper_1.isBucketEmpty(buckets, bucketNames_1.SECONDARY_MEASURES);
var data = executionResultData.map(function (seriesItem, seriesIndex) {
var values = seriesItem.map(function (value) {
return common_1.parseValue(value);
});
return {
x: !primaryMeasuresBucketEmpty ? values[0] : 0,
y: !secondaryMeasuresBucketEmpty ? (primaryMeasuresBucketEmpty ? values[0] : values[1]) : 0,
name: stackByAttribute ? stackByAttribute.items[seriesIndex].attributeHeaderItem.name : "",
};
});
return [
{
turboThreshold: 0,
color: colorStrategy.getColorByIndex(0),
legendIndex: 0,
data: data,
},
];
}
exports.getScatterPlotSeries = getScatterPlotSeries;
function getCountOfEmptyBuckets(bucketEmptyFlags) {
if (bucketEmptyFlags === void 0) { bucketEmptyFlags = []; }
return bucketEmptyFlags.filter(function (bucketEmpyFlag) { return bucketEmpyFlag; }).length;
}
function getBubbleChartSeries(executionResultData, measureGroup, stackByAttribute, mdObject, colorStrategy) {
var primaryMeasuresBucket = get(mdObject, ["buckets"], []).find(function (bucket) { return bucket.localIdentifier === bucketNames_1.MEASURES; });
var secondaryMeasuresBucket = get(mdObject, ["buckets"], []).find(function (bucket) { return bucket.localIdentifier === bucketNames_1.SECONDARY_MEASURES; });
var primaryMeasuresBucketEmpty = isEmpty(get(primaryMeasuresBucket, "items", []));
var secondaryMeasuresBucketEmpty = isEmpty(get(secondaryMeasuresBucket, "items", []));
return executionResultData.map(function (resData, index) {
var data = [];
if (resData[0] !== null && resData[1] !== null && resData[2] !== null) {
var emptyBucketsCount = getCountOfEmptyBuckets([
primaryMeasuresBucketEmpty,
secondaryMeasuresBucketEmpty,
]);
data = [
{
x: !primaryMeasuresBucketEmpty ? common_1.parseValue(resData[0]) : 0,
y: !secondaryMeasuresBucketEmpty ? common_1.parseValue(resData[1 - emptyBucketsCount]) : 0,
// we want to allow NaN on z to be able show bubble of default size when Size bucket is empty
z: parseFloat(resData[2 - emptyBucketsCount]),
format: utils_1.unwrap(last(measureGroup.items)).format,
},
];
}
return {
name: stackByAttribute ? stackByAttribute.items[index].attributeHeaderItem.name : "",
color: colorStrategy.getColorByIndex(index),
legendIndex: index,
data: data,
};
});
}
exports.getBubbleChartSeries = getBubbleChartSeries;
function getColorStep(valuesCount) {
var MAX_COLOR_BRIGHTNESS = 0.8;
return MAX_COLOR_BRIGHTNESS / valuesCount;
}
function gradientPreviousGroup(solidColorLeafs) {
var colorChange = getColorStep(solidColorLeafs.length);
return solidColorLeafs.map(function (leaf, index) { return (__assign({}, leaf, { color: color_1.getLighterColor(leaf.color, colorChange * index) })); });
}
function getRootPoint(rootName, index, format, colorStrategy) {
return {
id: "" + index,
name: rootName,
color: colorStrategy.getColorByIndex(index),
showInLegend: true,
legendIndex: index,
format: format,
};
}
function getLeafPoint(stackByAttribute, parentIndex, seriesIndex, data, format, colorStrategy) {
return {
name: stackByAttribute.items[seriesIndex].attributeHeaderItem.name,
parent: "" + parentIndex,
value: common_1.parseValue(data),
x: seriesIndex,
y: seriesIndex,
showInLegend: false,
color: colorStrategy.getColorByIndex(parentIndex),
format: format,
};
}
function isLastSerie(seriesIndex, dataLength) {
return seriesIndex === dataLength - 1;
}
function getTreemapStackedSeriesDataWithViewBy(executionResultData, measureGroup, viewByAttribute, stackByAttribute, colorStrategy) {
var roots = [];
var leafs = [];
var rootId = -1;
var uncoloredLeafs = [];
var lastRoot = null;
var dataLength = executionResultData.length;
var format = utils_1.unwrap(measureGroup.items[0]).format; // this configuration has only one measure
executionResultData.forEach(function (seriesItems, seriesIndex) {
var currentRoot = viewByAttribute.items[seriesIndex].attributeHeaderItem;
if (!isEqual(currentRoot, lastRoot)) {
// store previous group leafs
leafs.push.apply(leafs, gradientPreviousGroup(uncoloredLeafs));
rootId++;
lastRoot = currentRoot;
uncoloredLeafs = [];
// create parent for pasted leafs
var lastRootName = get(lastRoot, "name");
roots.push(getRootPoint(lastRootName, rootId, format, colorStrategy));
}
// create leafs which will be colored at the end of group
uncoloredLeafs.push(getLeafPoint(stackByAttribute, rootId, seriesIndex, seriesItems[0], format, colorStrategy));
if (isLastSerie(seriesIndex, dataLength)) {
// store last group leafs
leafs.push.apply(leafs, gradientPreviousGroup(uncoloredLeafs));
}
});
return roots.concat(leafs); // roots need to be first items in data to keep legend working
}
exports.getTreemapStackedSeriesDataWithViewBy = getTreemapStackedSeriesDataWithViewBy;
function getTreemapStackedSeriesDataWithMeasures(executionResultData, measureGroup, stackByAttribute, colorStrategy) {
var data = [];
measureGroup.items.reduce(function (data, measureGroupItem, index) {
data.push({
id: "" + index,
name: measureGroupItem.measureHeaderItem.name,
format: measureGroupItem.measureHeaderItem.format,
color: colorStrategy.getColorByIndex(index),
showInLegend: true,
legendIndex: index,
});
return data;
}, data);
executionResultData.forEach(function (seriesItems, seriesIndex) {
var colorChange = getColorStep(seriesItems.length);
var unsortedLeafs = [];
seriesItems.forEach(function (seriesItem, seriesItemIndex) {
unsortedLeafs.push({
name: stackByAttribute.items[seriesItemIndex].attributeHeaderItem.name,
parent: "" + seriesIndex,
format: utils_1.unwrap(measureGroup.items[seriesIndex]).format,
value: common_1.parseValue(seriesItem),
x: seriesIndex,
y: seriesItemIndex,
showInLegend: false,
});
});
var sortedLeafs = unsortedLeafs.sort(function (a, b) { return b.value - a.value; });
data = data.concat(sortedLeafs.map(function (leaf, seriesItemIndex) { return (__assign({}, leaf, { color: color_1.getLighterColor(colorStrategy.getColorByIndex(seriesIndex), colorChange * seriesItemIndex) })); }));
});
return data;
}
exports.getTreemapStackedSeriesDataWithMeasures = getTreemapStackedSeriesDataWithMeasures;
function getTreemapStackedSeries(executionResultData, measureGroup, viewByAttribute, stackByAttribute, colorStrategy) {
var data = [];
if (viewByAttribute) {
data = getTreemapStackedSeriesDataWithViewBy(executionResultData, measureGroup, viewByAttribute, stackByAttribute, colorStrategy);
}
else {
data = getTreemapStackedSeriesDataWithMeasures(executionResultData, measureGroup, stackByAttribute, colorStrategy);
}
var seriesName = measureGroup.items
.map(function (wrappedMeasure) {
return utils_1.unwrap(wrappedMeasure).name;
})
.join(", ");
return [
{
name: seriesName,
legendType: "point",
showInLegend: true,
data: data,
turboThreshold: 0,
},
];
}
exports.getTreemapStackedSeries = getTreemapStackedSeries;
function getSeries(executionResultData, measureGroup, viewByAttribute, stackByAttribute, type, mdObject, colorStrategy, occupiedMeasureBucketsLocalIdentifiers) {
if (common_1.isHeatmap(type)) {
return getHeatmapSeries(executionResultData, measureGroup);
}
else if (common_1.isScatterPlot(type)) {
return getScatterPlotSeries(executionResultData, stackByAttribute, mdObject, colorStrategy);
}
else if (common_1.isBubbleChart(type)) {
return getBubbleChartSeries(executionResultData, measureGroup, stackByAttribute, mdObject, colorStrategy);
}
else if (common_1.isTreemap(type) && stackByAttribute) {
return getTreemapStackedSeries(executionResultData, measureGroup, viewByAttribute, stackByAttribute, colorStrategy);
}
else if (common_1.isBulletChart(type)) {
return bulletChartOptions_1.getBulletChartSeries(executionResultData, measureGroup, colorStrategy, occupiedMeasureBucketsLocalIdentifiers);
}
return executionResultData.map(function (seriesItem, seriesIndex) {
var seriesItemData = getSeriesItemData(seriesItem, seriesIndex, measureGroup, viewByAttribute, stackByAttribute, type, colorStrategy);
var seriesItemConfig = {
color: colorStrategy.getColorByIndex(seriesIndex),
legendIndex: seriesIndex,
data: seriesItemData,
};
if (stackByAttribute) {
// if stackBy attribute is available, seriesName is a stackBy attribute value of index seriesIndex
// this is a limitiation of highcharts and a reason why you can not have multi-measure stacked charts
seriesItemConfig.name = stackByAttribute.items[seriesIndex].attributeHeaderItem.name;
}
else if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes) && !viewByAttribute) {
// Pie charts with measures only have a single series which name would is ambiguous
seriesItemConfig.name = measureGroup.items
.map(function (wrappedMeasure) {
return utils_1.unwrap(wrappedMeasure).name;
})
.join(", ");
}
else {
// otherwise seriesName is a measure name of index seriesIndex
seriesItemConfig.name = measureGroup.items[seriesIndex].measureHeaderItem.name;
}
var turboThresholdProp = common_1.isTreemap(type) ? { turboThreshold: 0 } : {};
return __assign({}, seriesItemConfig, turboThresholdProp);
});
}
exports.getSeries = getSeries;
exports.customEscape = function (str) { return str && escape(unescape(str)); };
var renderTooltipHTML = function (textData, maxTooltipContentWidth) {
var maxItemWidth = maxTooltipContentWidth - TOOLTIP_PADDING * 2;
var titleMaxWidth = maxItemWidth;
var multiLineTruncationSupported = domUtils_1.isCssMultiLineTruncationSupported();
var threeDotsWidth = 16;
var valueMaxWidth = multiLineTruncationSupported ? maxItemWidth : maxItemWidth - threeDotsWidth;
var titleStyle = "style=\"max-width: " + titleMaxWidth + "px;\"";
var valueStyle = "style=\"max-width: " + valueMaxWidth + "px;\"";
var itemClass = cx("gd-viz-tooltip-item", {
"multiline-supported": multiLineTruncationSupported,
});
var valueClass = cx("gd-viz-tooltip-value", {
"clamp-two-line": multiLineTruncationSupported,
});
return textData
.map(function (item) {
// the third span is hidden, that help to have tooltip work with max-width
return "<div class=\"" + itemClass + "\">\n <span class=\"gd-viz-tooltip-title\" " + titleStyle + ">" + item[0] + "</span>\n <div class=\"gd-viz-tooltip-value-wraper\" " + titleStyle + ">\n <span class=\"" + valueClass + "\" " + valueStyle + ">" + item[1] + "</span>\n </div>\n <div class=\"gd-viz-tooltip-value-wraper\" " + titleStyle + ">\n <span class=\"gd-viz-tooltip-value-max-content\" " + valueStyle + ">" + item[1] + "</span>\n </div>\n </div>";
})
.join("\n");
};
function isPointOnOppositeAxis(point) {
return get(point, ["series", "yAxis", "opposite"], false);
}
function buildTooltipFactory(viewByAttribute, type, config, isDualAxis) {
if (config === void 0) { config = {}; }
if (isDualAxis === void 0) { isDualAxis = false; }
var separators = config.separators, _a = config.stackMeasuresToPercent, stackMeasuresToPercent = _a === void 0 ? false : _a;
return function (point, maxTooltipContentWidth, percentageValue) {
var isDualChartWithRightAxis = isDualAxis && isPointOnOppositeAxis(point);
var formattedValue = tooltip_1.getFormattedValueForTooltip(isDualChartWithRightAxis, stackMeasuresToPercent, point, separators, percentageValue);
var textData = [[exports.customEscape(point.series.name), formattedValue]];
if (viewByAttribute) {
// For some reason, highcharts ommit categories for pie charts with attribute. Use point.name instead.
// use attribute name instead of attribute display form name
textData.unshift([
exports.customEscape(viewByAttribute.formOf.name),
// since applying 'grouped-categories' plugin,
// 'category' type is replaced from string to object in highchart
exports.customEscape((point.category && point.category.name) || point.name),
]);
}
else if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes)) {
// Pie charts with measure only have to use point.name instead of series.name to get the measure name
textData[0][0] = exports.customEscape(point.name);
}
return renderTooltipHTML(textData, maxTooltipContentWidth);
};
}
exports.buildTooltipFactory = buildTooltipFactory;
function buildTooltipForTwoAttributesFactory(viewByAttribute, viewByParentAttribute, config, isDualAxis) {
if (config === void 0) { config = {}; }
if (isDualAxis === void 0) { isDualAxis = false; }
var separators = config.separators, _a = config.stackMeasuresToPercent, stackMeasuresToPercent = _a === void 0 ? false : _a;
return function (point, maxTooltipContentWidth, percentageValue) {
var category = point.category;
var isDualChartWithRightAxis = isDualAxis && isPointOnOppositeAxis(point);
var formattedValue = tooltip_1.getFormattedValueForTooltip(isDualChartWithRightAxis, stackMeasuresToPercent, point, separators, percentageValue);
var textData = [[exports.customEscape(point.series.name), formattedValue]];
if (category) {
if (viewByAttribute) {
textData.unshift([exports.customEscape(viewByAttribute.formOf.name), exports.customEscape(category.name)]);
}
if (viewByParentAttribute && category.parent) {
textData.unshift([
exports.customEscape(viewByParentAttribute.formOf.name),
exports.customEscape(category.parent.name),
]);
}
}
return renderTooltipHTML(textData, maxTooltipContentWidth);
};
}
exports.buildTooltipForTwoAttributesFactory = buildTooltipForTwoAttributesFactory;
function generateTooltipXYFn(measures, stackByAttribute, config) {
if (config === void 0) { config = {}; }
var separators = config.separators;
return function (point, maxTooltipContentWidth) {
var textData = [];
var name = point.name ? point.name : point.series.name;
if (stackByAttribute) {
textData.unshift([exports.customEscape(stackByAttribute.formOf.name), exports.customEscape(name)]);
}
if (measures[0]) {
textData.push([
exports.customEscape(measures[0].measureHeaderItem.name),
tooltip_1.formatValueForTooltip(point.x, measures[0].measureHeaderItem.format, separators),
]);
}
if (measures[1]) {
textData.push([
exports.customEscape(measures[1].measureHeaderItem.name),
tooltip_1.formatValueForTooltip(point.y, measures[1].measureHeaderItem.format, separators),
]);
}
if (measures[2]) {
textData.push([
exports.customEscape(measures[2].measureHeaderItem.name),
tooltip_1.formatValueForTooltip(point.z, measures[2].measureHeaderItem.format, separators),
]);
}
return renderTooltipHTML(textData, maxTooltipContentWidth);
};
}
exports.generateTooltipXYFn = generateTooltipXYFn;
function generateTooltipHeatmapFn(viewByAttribute, stackByAttribute, config) {
if (config === void 0) { config = {}; }
var separators = config.separators;
var formatValue = function (val, format) {
return numberjs_1.colors2Object(val === null ? "-" : numberjs_1.numberFormat(val, format, undefined, separators));
};
return function (point, maxTooltipContentWidth) {
var formattedValue = exports.customEscape(formatValue(point.value, point.series.userOptions.dataLabels.formatGD).label);
var textData = [];
textData.unshift([exports.customEscape(point.series.name), formattedValue]);
if (viewByAttribute) {
textData.unshift([
exports.customEscape(viewByAttribute.formOf.name),
exports.customEscape(viewByAttribute.items[point.x].attributeHeaderItem.name),
]);
}
if (stackByAttribute) {
textData.unshift([
exports.customEscape(stackByAttribute.formOf.name),
exports.customEscape(stackByAttribute.items[point.y].attributeHeaderItem.name),
]);
}
return renderTooltipHTML(textData, maxTooltipContentWidth);
};
}
exports.generateTooltipHeatmapFn = generateTooltipHeatmapFn;
function buildTooltipTreemapFactory(viewByAttribute, stackByAttribute, config) {
if (config === void 0) { config = {}; }
var separators = config.separators;
return function (point, maxTooltipContentWidth) {
// show tooltip for leaf node only
if (!point.node || point.node.isLeaf === false) {
return null;
}
var formattedValue = tooltip_1.formatValueForTooltip(point.value, point.format, separators);
var textData = [];
if (stackByAttribute) {
textData.push([
exports.customEscape(stackByAttribute.formOf.name),
exports.customEscape(stackByAttribute.items[point.y].attributeHeaderItem.name),
]);
}
if (viewByAttribute) {
textData.unshift([
exports.customEscape(viewByAttribute.formOf.name),
exports.customEscape(viewByAttribute.items[point.x].attributeHeaderItem.name),
]);
textData.push([exports.customEscape(point.series.name), formattedValue]);
}
else {
textData.push([exports.customEscape(point.category && point.category.name), formattedValue]);
}
return renderTooltipHTML(textData, maxTooltipContentWidth);
};
}
exports.buildTooltipTreemapFactory = buildTooltipTreemapFactory;
function isLegacyAttributeHeader(header) {
return header.attribute !== undefined;
}
exports.isLegacyAttributeHeader = isLegacyAttributeHeader;
function getViewBy(viewByAttribute, viewByIndex) {
var viewByItemHeader = null;
var viewByAttributeHeader = null;
if (viewByAttribute) {
viewByItemHeader = viewByAttribute.items[viewByIndex];
viewByAttributeHeader = { attributeHeader: omit(viewByAttribute, "items") };
}
return {
viewByItemHeader: viewByItemHeader,
viewByAttributeHeader: viewByAttributeHeader,
};
}
function getStackBy(stackByAttribute, stackByIndex) {
var stackByItemHeader = null;
var stackByAttributeHeader = null;
if (stackByAttribute) {
// stackBy item index is always equal to seriesIndex
stackByItemHeader = stackByAttribute.items[stackByIndex];
stackByAttributeHeader = { attributeHeader: omit(stackByAttribute, "items") };
}
return {
stackByItemHeader: stackByItemHeader,
stackByAttributeHeader: stackByAttributeHeader,
};
}
function getDrillableSeries(series, drillableItems, viewByAttributes, stackByAttribute, executionResponse, afm, type) {
var viewByChildAttribute = viewByAttributes[0], viewByParentAttribute = viewByAttributes[1];
var isMultiMeasureWithOnlyMeasures = common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes) && !viewByChildAttribute;
var measureGroup = executionResultHelper_1.findMeasureGroupInDimensions(executionResponse.dimensions);
return series.map(function (seriesItem, seriesIndex) {
var isSeriesDrillable = false;
var data = seriesItem.data &&
seriesItem.data.map(function (pointData, pointIndex) {
var measureHeaders = [];
var isStackedTreemap = common_1.isTreemap(type) && !!stackByAttribute;
if (common_1.isScatterPlot(type)) {
measureHeaders = get(measureGroup, "items", []).slice(0, 2);
}
else if (common_1.isBubbleChart(type)) {
measureHeaders = get(measureGroup, "items", []).slice(0, 3);
}
else if (isStackedTreemap) {
if (pointData.id !== undefined) {
// not leaf -> can't be drillable
return pointData;
}
var measureIndex = viewByChildAttribute ? 0 : parseInt(pointData.parent, 10);
measureHeaders = [measureGroup.items[measureIndex]];
}
else {
// measureIndex is usually seriesIndex,
// except for stack by attribute and metricOnly pie or donut chart
// it is looped-around pointIndex instead
// Looping around the end of items array only works when
// measureGroup is the last header on it's dimension
// We do not support setups with measureGroup before attributeHeaders
var measureIndex = !stackByAttribute && !isMultiMeasureWithOnlyMeasures
? seriesIndex
: pointIndex % measureGroup.items.length;
measureHeaders = [measureGroup.items[measureIndex]];
}
var viewByIndex = common_1.isHeatmap(type) || isStackedTreemap ? pointData.x : pointIndex;
var stackByIndex = common_1.isHeatmap(type) || isStackedTreemap ? pointData.y : seriesIndex;
if (common_1.isScatterPlot(type)) {
stackByIndex = viewByIndex; // scatter plot uses stack by attribute but has only one serie
}
var _a = getStackBy(stackByAttribute, stackByIndex), stackByItemHeader = _a.stackByItemHeader, stackByAttributeHeader = _a.stackByAttributeHeader;
var _b = getViewBy(viewByChildAttribute, viewByIndex), viewByChildItemHeader = _b.viewByItemHeader, viewByChildAttributeHeader = _b.viewByAttributeHeader;
var _c = getViewBy(viewByParentAttribute, viewByIndex), viewByParentItemHeader = _c.viewByItemHeader, viewByParentAttributeHeader = _c.viewByAttributeHeader;
// point is drillable if a drillableItem matches:
// point's measure,
// point's viewBy attribute,
// point's viewBy attribute item,
// point's stackBy attribute,
// point's stackBy attribute item,
var drillableHooks = without(measureHeaders.concat([
viewByChildAttributeHeader,
viewByChildItemHeader,
viewByParentAttributeHeader,
viewByParentItemHeader,
stackByAttributeHeader,
stackByItemHeader,
]), null);
var drilldown = drillableHooks.some(function (drillableHook) {
return headerPredicate_1.isSomeHeaderPredicateMatched(drillableItems, drillableHook, afm, executionResponse);
});
var drillableProps = {
drilldown: drilldown,
};
if (drilldown) {
var headers = measureHeaders.concat([
viewByChildItemHeader,
viewByChildAttributeHeader,
viewByParentItemHeader,
viewByParentAttributeHeader,
stackByItemHeader,
stackByAttributeHeader,
]);
var sanitizedHeaders = without(headers.slice(), null);
drillableProps.drillIntersection = drilldownEventing_1.getDrillIntersection(sanitizedHeaders);
isSeriesDrillable = true;
}
return __assign({}, pointData, drillableProps);
});
if (common_1.isScatterPlot(type)) {
data = data.filter(function (dataItem) {
return dataItem.x !== null && dataItem.y !== null;
});
}
return __assign({}, seriesItem, { data: data, isDrillable: isSeriesDrillable });
});
}
exports.getDrillableSeries = getDrillableSeries;
function getCategories(type, measureGroup, viewByAttribute, stackByAttribute) {
if (common_1.isHeatmap(type)) {
return [
viewByAttribute ? viewByAttribute.items.map(function (item) { return item.attributeHeaderItem.name; }) : [""],
stackByAttribute
? stackByAttribute.items.map(function (item) { return item.attributeHeaderItem.name; })
: [""],
];
}
if (common_1.isScatterPlot(type)) {
return stackByAttribute
? stackByAttribute.items.map(function (item) { return item.attributeHeaderItem.name; })
: [""];
}
// Categories make up bar/slice labels in charts. These usually match view by attribute values.
// Measure only pie or treemap charts get categories from measure names
if (viewByAttribute) {
return viewByAttribute.items.map(function (_a) {
var attributeHeaderItem = _a.attributeHeaderItem;
return attributeHeaderItem.name;
});
}
if (common_1.isOneOfTypes(type, multiMeasuresAlternatingTypes)) {
// Pie or Treemap chart with measures only (no viewByAttribute) needs to list
return measureGroup.items.map(function (wrappedMeasure) { return utils_1.unwrap(wrappedMeasure).name; });
// Pie chart categories are later sorted by seriesItem pointValue
}
return [];
}
function getStackingConfig(stackByAttribute, options) {
var type = options.type, stackMeasures = options.stackMeasures, stackMeasuresToPercent = options.stackMeasuresToPercent;
var stackingValue = stackMeasuresToPercent ? getOptionalStackingConfiguration_1.PERCENT_STACK : getOptionalStackingConfiguration_1.NORMAL_STACK;
var supportsStacking = !common_1.isOneOfTypes(type, unsupportedStackingTypes);
/**
* we should enable stacking for one of the following cases :
* 1) If stackby attribute have been set and chart supports stacking
* 2) If chart is an area chart and stacking is enabled (stackBy attribute doesn't matter)
* 3) If chart is column/bar chart and 'Stack Measures' is enabled
*/
var isStackByChart = stackByAttribute && supportsStacking;
var isAreaChartWithEnabledStacking = isAreaChartStackingEnabled(options);
if (isStackByChart || isAreaChartWithEnabledStacking || stackMeasures || stackMeasuresToPercent) {
return stackingValue;
}
return null; // no stacking
}
function preprocessMeasureGroupItems(measureGroup, defaultValues) {
return measureGroup.items.map(function (item, index) {
var unwrapped = utils_1.unwrap(item);
return index
? {
label: unwrapped.name,
format: unwrapped.format,
}
: {
label: defaultValues.label || unwrapped.name,
format: defaultValues.format || unwrapped.format,
};
});
}
function getXAxes(config, measureGroup, viewByAttribute) {
var type = config.type, mdObject = config.mdObject;
var buckets = get(mdObject, "buckets", []);
var measureGroupItems = preprocessMeasureGroupItems(measureGroup, {
label: config.xLabel,
format: config.xFormat,
});
var firstMeasureGroupItem = measureGroupItems[0];
if (common_1.isScatterPlot(type) || common_1.isBubbleChart(type)) {
var noPrimaryMeasures = mdObjBucketHelper_1.isBucketEmpty(buckets, bucketNames_1.MEASURES);
if (noPrimaryMeasures) {
return [
{
label: "",
},
];
}
else {
return [
{
label: firstMeasureGroupItem.label || "",
format: firstMeasureGroupItem.format || "",
},
];
}
}
var xLabel = config.xLabel || (viewByAttribute ? viewByAttribute.formOf.name : "");
return [
{
label: xLabel,
},
];
}
function getMeasureFormatKey(measureGroupItems) {
var percentageFormat = getMeasureFormat(measureGroupItems.find(function (measure) {
return isPercentage(getMeasureFormat(measure));
}));
return percentageFormat !== ""
? {
format: percentageFormat,
}
: {};
}
function getMeasureFormat(measure) {
return get(measure, "format", "");
}
function isPercentage(format) {
return format.includes("%");
}
function getYAxes(config, measureGroup, stackByAttribute) {
var type = config.type, mdObject = config.mdObject;
var buckets = get(mdObject, "buckets", []);
var measureGroupItems = preprocessMeasureGroupItems(measureGroup, {
label: config.yLabel,
format: config.yFormat,
});
var firstMeasureGroupItem = measureGroupItems[0];
var secondMeasureGroupItem = measureGroupItems[1];
var hasMoreThanOneMeasure = measureGroupItems.length > 1;
var noPrimaryMeasures = mdObjBucketHelper_1.isBucketEmpty(buckets, bucketNames_1.MEASURES);
var _a = ((common_1.isBarChart(type) ? config.secondary_xaxis : config.secondary_yaxis) || {}).measures, secondaryAxisMeasures = _a === void 0 ? [] : _a;
var yAxes = [];
if (common_1.isScatterPlot(type) || common_1.isBubbleChart(type)) {
var hasSecondaryMeasure = get(mdObject, "buckets", []).find(function (m) { return m.localIdentifier === bucketNames_1.SECONDARY_MEASURES && m.items.length > 0; });
if (hasSecondaryMeasure) {
if (noPrimaryMeasures) {
yAxes = [
__assign({}, firstMeasureGroupItem),
];
}
else {
yAxes = [
__assign({}, secondMeasureGroupItem),
];
}
}
else {
yAxes = [{ label: "" }];
}
}
else if (common_1.isHeatmap(type)) {
yAxes = [
{
label: stackByAttribute ? stackByAttribute.formOf.name : "",
},
];
}
else if (common_1.isOneOfTypes(type, exports.supportedDualAxesChartTypes) &&
!isEmpty(measureGroupItems) &&
!isEmpty(secondaryAxisMeasures)) {
var _b = assignMeasuresToAxes(secondaryAxisMeasures, measureGroup), measuresInFirstAxis = _b.measuresInFirstAxis, measuresInSecondAxis = _b.measuresInSecondAxis;
var firstAxis = createYAxisItem(measuresInFirstAxis, false);
var secondAxis = createYAxisItem(measuresInSecondAxis, true);
if (firstAxis) {
firstAxis = __assign({}, firstAxis, getMeasureFormatKey(measuresInFirstAxis), { seriesIndices: measuresInFirstAxis.map(function (_a) {
var index = _a.index;
return index;
}) });
}
if (secondAxis) {
secondAxis = __assign({}, secondAxis, getMeasureFormatKey(measuresInSecondAxis), { seriesIndices: measuresInSecondAxis.map(function (_a) {
var index = _a.index;
return index;
}) });
}
yAxes = compact([firstAxis, secondAxis]);
}
else {
// if more than one measure and NOT dual, then have empty item name
var nonDualMeasureAxis = hasMoreThanOneMeasure
? {
label: "",
}
: {};
yAxes = [
__assign({}, firstMeasureGroupItem, nonDualMeasureAxis, { seriesIndices: range(measureGroupItems.length) }, getMeasureFormatKey(measureGroupItems)),
];
}
return yAxes;
}
function assignMeasuresToAxes(secondMeasures, measureGroup) {
return measureGroup.items.reduce(function (result, _a, index) {
var _b = _a.measureHeaderItem, name = _b.name, format = _b.format, localIdentifier = _b.localIdentifier;
if (includes(secondMeasures, localIdentifier)) {
result.measuresInSecondAxis.push({ name: name, format: format, index: index });
}
else {
result.measuresInFirstAxis.push({ name: name, format: format, index: index });
}
return result;
}, {
measuresInFirstAxis: [],
measuresInSecondAxis: [],
});
}
function createYAxisItem(measuresInAxis, opposite) {
if (opposite === void 0) { opposite = false; }
var length = measuresInAxis.length;
if (length) {
var _a = measuresInAxis[0], name_1 = _a.name, format = _a.format;
return {
label: length === 1 ? name_1 : "",
format: format,
opposite: opposite,
};
}
return null;
}
function assignYAxes(series, yAxes) {
return series.reduce(function (result, item, index) {
var yAxisIndex = yAxes.findIndex(function (axis) {
return includes(get(axis, "seriesIndices", []), index);
});
// for case viewBy and stackBy have one attribute, and one measure is sliced to multiple series
// then 'yAxis' in other series should follow the first one
var firstYAxisIndex = result.lenght > 0 ? result[0].yAxis : 0;
var seriesItem = __assign({}, item, { yAxis: yAxisIndex !== -1 ? yAxisIndex : firstYAxisIndex });
result.push(seriesItem);
return result;
}, []);
}
exports.HEAT_MAP_CATEGORIES_COUNT = 7;
exports.HIGHCHARTS_PRECISION = 15;
exports.DEFAULT_HEATMAP_COLOR_INDEX = 1;
function getHeatmapDataClasses(series, colorStrategy) {
if (series === void 0) { series = []; }
var values = without(get(series, "0.data", []).map(function (item) { return item.value; }), null, undefined, NaN);
if (isEmpty(values)) {
return [];
}
var min = Math.min.apply(Math, values);
var max = Math.max.apply(Math, values);
var safeMin = parseFloat(Number(min).toPrecision(exports.HIGHCHARTS_PRECISION));
var safeMax = parseFloat(Number(max).toPrecision(exports.HIGHCHARTS_PRECISION));
var dataClasses = [];
if (min === max) {
dataClasses.push({
from: min,
to: max,
color: colorStrategy.getColorByIndex(exports.DEFAULT_HEATMAP_COLOR_INDEX),
});
}
else {
var step = (safeMax - safeMin) / exports.HEAT_MAP_CATEGORIES_COUNT;
var currentSum = safeMin;
for (var i = 0; i < exports.HEAT_MAP_CATEGORIES_COUNT; i += 1) {
dataClasses.push({
from: currentSum,
to: i === exports.HEAT_MAP_CATEGORIES_COUNT - 1 ? safeMax : currentSum + step,
color: colorStrategy.getColorByIndex(i),
});
currentSum += step;
}
}
return dataClasses;
}
exports.getHeatmapDataClasses = getHeatmapDataClasses;
function getDefaultTreemapAttributes(dimensions, attributeHeaderItems) {
var viewByAttribute = executionRe