@syncfusion/ej2-charts
Version:
Feature-rich chart control with built-in support for over 25 chart types, technical indictors, trendline, zooming, tooltip, selection, crosshair and trackball.
930 lines • 77 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 __());
};
})();
/**
* AccumulationChart DataLabel module file
*/
import { extend, createElement, getValue, isNullOrUndefined, animationMode } from '@syncfusion/ej2-base';
import { Rect, Size, PathOption, measureText, TextOption } from '@syncfusion/ej2-svg-base';
import { ChartLocation, degreeToLocation, isOverlap, stringToNumber, getAngle, appendChildElement } from '../../common/utils/helper';
import { textTrim, subtractThickness, Thickness, getElement } from '../../common/utils/helper';
import { removeElement, RectOption, textElement, showTooltip } from '../../common/utils/helper';
import { colorNameToHex, convertHexToColor, containsRect, textWrap, CircleOption } from '../../common/utils/helper';
import { getSeriesFromIndex } from '../model/acc-base';
import { textRender } from '../../common/model/constants';
import { getFontStyle, createTemplate, measureElementRect, templateAnimate } from '../../common/utils/helper';
import { AccumulationBase } from './accumulation-base';
/**
* The `AccumulationDataLabel` module is used to render data labels for the Accumulation chart.
*/
var AccumulationDataLabel = /** @class */ (function (_super) {
__extends(AccumulationDataLabel, _super);
function AccumulationDataLabel(accumulation) {
var _this = _super.call(this, accumulation) || this;
_this.rightSideRenderingPoints = [];
_this.leftSideRenderingPoints = [];
_this.id = accumulation.element.id + '_datalabel_Series_';
return _this;
}
/**
* Method to get datalabel text location.
*
* @private
* @param {AccPoints} point - The data point for which to calculate the label text location.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings for the series.
* @param {Size} textSize - The size of the text to be displayed.
* @param {AccPoints[]} points - The array of data points in the series.
* @returns {void}
*/
AccumulationDataLabel.prototype.getDataLabelPosition = function (point, dataLabel, textSize, points) {
var radius = this.isCircular() ? (!this.isVariousRadius() ? this.accumulation.pieSeriesModule.labelRadius :
this.accumulation.pieSeriesModule.getLabelRadius(this.accumulation.visibleSeries[0], point)) :
this.getLabelDistance(point, dataLabel);
//let radius: number = this.isCircular() ? this.labelRadius : this.getLabelDistance(point, dataLabel);
if (this.accumulation.title) {
var titleSize = measureText(this.accumulation.title, this.accumulation.titleStyle, this.accumulation.themeStyle.datalabelFont);
this.titleRect = new Rect(this.accumulation.availableSize.width / 2 - titleSize.width / 2, this.accumulation.margin.top, titleSize.width, titleSize.height);
}
this.getLabelRegion(point, dataLabel.position, textSize, radius, this.marginValue);
point.labelAngle = point.midAngle;
point.labelPosition = dataLabel.position;
if (this.accumulation.enableSmartLabels) {
this.getSmartLabel(point, dataLabel, textSize, points);
}
};
/**
* Method to get datalabel bound.
*/
AccumulationDataLabel.prototype.getLabelRegion = function (point, position, textSize, labelRadius, margin, endAngle) {
if (endAngle === void 0) { endAngle = 0; }
var labelAngle = endAngle || point.midAngle;
var space = 20;
var location = degreeToLocation(labelAngle, labelRadius, this.isCircular() ? this.center :
this.getLabelLocation(point, position));
location.y = (position === 'Inside') ? (location.y - textSize.height / 2) : location.y;
location.x = (position === 'Inside') ? (location.x - textSize.width / 2) : location.x;
point.labelRegion = new Rect(location.x, location.y, textSize.width + (margin * 2), textSize.height + (margin * 2));
if (position === 'Outside') {
point.labelRegion.y -= point.labelRegion.height / 2;
if (labelAngle >= 90 && labelAngle <= 270) {
point.labelRegion.x -= (point.labelRegion.width + space);
}
else {
point.labelRegion.x += space;
}
}
};
/**
* Method to get data label collection.
*
* @param {AccPoints} point - The data point for which to calculate the label collection.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings for the series.
* @returns {void}
* @private
*/
AccumulationDataLabel.prototype.calculateLabelCollection = function (point, dataLabel) {
if (point.argsData.template !== null) {
return null;
}
var position = point.labelPosition || dataLabel.position;
var labelRadius = this.isCircular() ? (!this.isVariousRadius() ? this.accumulation.pieSeriesModule.labelRadius :
this.accumulation.pieSeriesModule.getLabelRadius(this.accumulation.visibleSeries[0], point)) :
this.getLabelDistance(point, dataLabel);
var radius = (!this.isVariousRadius() ?
(this.accumulation.pieSeriesModule.radius - this.accumulation.pieSeriesModule.innerRadius) :
this.accumulation.pieSeriesModule.getLabelRadius(this.accumulation.visibleSeries[0], point));
var location = degreeToLocation(point.midAngle, labelRadius, this.isCircular() ? this.center :
this.getLabelLocation(point, position));
var padding = 20;
var maxWidth = dataLabel.maxWidth;
if (!maxWidth) {
if (position === 'Outside') {
maxWidth = this.isCircular() ? (location.x >= this.center.x) ? (this.areaRect.x + this.areaRect.width - location.x) :
(location.x - this.areaRect.x) : (location.x >= point.region.x) ?
(this.areaRect.x + this.areaRect.width - location.x) : (location.x - this.areaRect.x);
}
else {
maxWidth = this.isCircular() ? (radius - padding) : point.region.width;
}
}
if ((point.label.indexOf('<br>') !== -1)) {
point.labelCollection = point.label.split('<br>');
}
else if (dataLabel.textWrap === 'Normal' && dataLabel.textOverflow === 'Ellipsis') {
point.labelCollection[0] = textTrim(maxWidth, point.label, point.argsData.font, this.accumulation.enableRtl, this.accumulation.themeStyle.datalabelFont);
}
else if (dataLabel.textWrap === 'Wrap' || dataLabel.textWrap === 'AnyWhere') {
point.labelCollection = textWrap(point.label, maxWidth, point.argsData.font, this.accumulation.enableRtl, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont);
}
else {
point.labelCollection[0] = point.label;
}
};
/**
* To calculate label collection text size.
*
* @param {string[]} labelCollection - The collection of label texts.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings for the series.
* @returns {Size} - The size of the label text collection.
* @private
*/
AccumulationDataLabel.prototype.getTextSize = function (labelCollection, dataLabel) {
var height = 0;
var font = dataLabel.font;
var width = dataLabel.maxWidth ? dataLabel.maxWidth : 0;
var textSize;
for (var i = 0; i < labelCollection.length; i++) {
textSize = measureText(labelCollection[i], font, this.accumulation.themeStyle.datalabelFont);
width = Math.max(textSize.width, width);
height += textSize.height;
}
if (dataLabel.textOverflow === 'Clip' && dataLabel.textWrap !== 'Normal' && dataLabel.maxWidth) {
width = dataLabel.maxWidth;
}
return (new Size(width, height));
};
/**
* Method to get datalabel smart position.
*
* @param {AccPoints} point - The data point for which to calculate the label smart position.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings for the series.
* @param {Size} textSize - The size of the text.
* @param {AccPoints[]} points - The collection of data points.
* @returns {void}
*/
AccumulationDataLabel.prototype.getSmartLabel = function (point, dataLabel, textSize, points) {
var circular = this.isCircular();
var labelRadius = circular ? this.radius : this.getLabelDistance(point, dataLabel);
var connectorLength = circular ? (dataLabel.connectorStyle.length || '4%') :
'0px';
labelRadius += stringToNumber(connectorLength, labelRadius);
var previousPoint = this.findPreviousPoint(points, point.index, point.labelPosition);
if (dataLabel.position === 'Inside') {
// `4` is padding adding to height and width of label region.
point.labelRegion.height -= 4;
point.labelRegion.width -= 4;
if (previousPoint && previousPoint.labelRegion && !dataLabel.enableRotation &&
(isOverlap(point.labelRegion, previousPoint.labelRegion)
|| this.isOverlapping(point, points)) || !circular && !containsRect(point.region, point.labelRegion)) {
point.labelPosition = 'Outside';
if (!circular) {
labelRadius = this.getLabelDistance(point, dataLabel);
}
this.calculateLabelCollection(point, dataLabel);
textSize = this.getTextSize(point.labelCollection, dataLabel);
textSize.height += 4; // 4 for calculation with padding for smart label shape
textSize.width += 4;
point.textSize = textSize;
this.getLabelRegion(point, point.labelPosition, textSize, labelRadius, this.marginValue);
previousPoint = this.findPreviousPoint(points, point.index, point.labelPosition);
if (previousPoint && (isOverlap(point.labelRegion, previousPoint.labelRegion) ||
this.isConnectorLineOverlapping(point, previousPoint))) {
this.setOuterSmartLabel(previousPoint, point, dataLabel.border.width, labelRadius, textSize, this.marginValue);
}
}
}
else {
if (previousPoint && previousPoint.labelRegion && (isOverlap(point.labelRegion, previousPoint.labelRegion)
|| this.isOverlapping(point, points) || this.isConnectorLineOverlapping(point, previousPoint))) {
this.setOuterSmartLabel(previousPoint, point, dataLabel.border.width, labelRadius, textSize, this.marginValue);
}
}
if (this.isOverlapping(point, points) && (this.accumulation.type === 'Pyramid' || this.accumulation.type === 'Funnel')) {
var position = 'OutsideLeft';
var space = 20;
var labelAngle = point.midAngle || 0;
var labelRadius_1 = circular ? this.radius : this.getLabelDistance(point, dataLabel);
var location_1 = degreeToLocation(labelAngle, -labelRadius_1, this.isCircular() ? this.center :
this.getLabelLocation(point, position));
point.labelRegion = new Rect(location_1.x, location_1.y, textSize.width + (this.marginValue * 2), textSize.height + (this.marginValue * 2));
point.labelRegion.y -= point.labelRegion.height / 2;
point.labelRegion.x = point.labelRegion.x - space - point.labelRegion.width;
if (previousPoint && previousPoint.labelRegion && (isOverlap(point.labelRegion, previousPoint.labelRegion)
|| this.isOverlapping(point, points) || this.isConnectorLineOverlapping(point, previousPoint))) {
this.setOuterSmartLabel(previousPoint, point, dataLabel.border.width, labelRadius_1, textSize, this.marginValue);
}
}
};
/**
* To find trimmed datalabel tooltip needed.
*
* @param {Event} e - The move event.
* @param {number} x - The x-coordinate.
* @param {number} y - The y-coordinate.
* @param {boolean} isTouch - Indicates if the interaction is touch-based.
* @returns {void}
* @private
*/
AccumulationDataLabel.prototype.move = function (e, x, y, isTouch) {
var _this = this;
if (e.target.textContent.indexOf('...') > -1) {
var targetId = e.target.id.split(this.id);
if (targetId.length === 2) {
var seriesIndex = parseInt(targetId[1].split('_text_')[0], 10);
var pointIndex = parseInt(targetId[1].split('_text_')[1], 10);
if (!isNaN(seriesIndex) && !isNaN(pointIndex)) {
if (isTouch) {
removeElement(this.accumulation.element.id + '_EJ2_Datalabel_Tooltip');
}
var point = getSeriesFromIndex(seriesIndex, (this.accumulation).visibleSeries).points[pointIndex];
showTooltip(point.text || point.y.toString(), x, y, this.areaRect.width, this.accumulation.element.id + '_EJ2_Datalabel_Tooltip', getElement(this.accumulation.element.id + '_Secondary_Element'), null, null, this.accumulation.initialClipRect);
}
}
}
else {
removeElement(this.accumulation.element.id + '_EJ2_Datalabel_Tooltip');
}
if (isTouch) {
clearTimeout(this.clearTooltip);
this.clearTooltip = +setTimeout(function () { removeElement(_this.accumulation.element.id + '_EJ2_Datalabel_Tooltip'); }, 1000);
}
};
/**
* To find previous valid label point.
*
* @param {AccPoints[]} points - The array of accumulation points.
* @param {number} index - The index of the current point.
* @param {AccumulationLabelPosition} position - The position of the label.
* @returns {AccPoints} - Find the previous value of accumulation point.
*/
AccumulationDataLabel.prototype.findPreviousPoint = function (points, index, position) {
var point = points[0];
for (var i = index - 1; i >= 0; i--) {
point = points[i];
if (point.visible && point.labelVisible && point.labelRegion && point.labelPosition === position) {
return point;
}
}
return null;
};
/**
* To find current point datalabel is overlapping with other points.
*
* @param {AccPoints} currentPoint - The current point.
* @param {AccPoints[]} points - The array of accumulation points.
* @returns {boolean} - It returns boolean value of overlapping.
*/
AccumulationDataLabel.prototype.isOverlapping = function (currentPoint, points) {
for (var i = currentPoint.index - 1; i >= 0; i--) {
if (points[i].visible && points[i].labelVisible &&
points[i].labelRegion && currentPoint.labelRegion &&
currentPoint.labelVisible && isOverlap(currentPoint.labelRegion, points[i].labelRegion)) {
return true;
}
}
return false;
};
/**
* To get text trimmed while exceeds the accumulation chart area.
*
* @param {AccPoints} point - The accumulation point.
* @param {Rect} rect - The area of the accumulation chart.
* @param {FontModel} font - The font settings.
* @param {string} position - The position of the data label.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings.
* @returns {void}
*/
AccumulationDataLabel.prototype.textTrimming = function (point, rect, font, position, dataLabel) {
if (isOverlap(point.labelRegion, rect)) {
var size = point.labelRegion.width;
if (position === 'Right') {
size = rect.x - point.labelRegion.x;
}
else if (position === 'Left') {
size = point.labelRegion.x - (rect.x + rect.width);
if (size < 0) {
size += point.labelRegion.width;
point.labelRegion.x = rect.x + rect.width;
}
}
else if (position === 'InsideRight') {
size = (rect.x + rect.width) - point.labelRegion.x;
}
else if (position === 'InsideLeft') {
size = (point.labelRegion.x + point.labelRegion.width) - rect.x;
if (size < point.labelRegion.width) {
point.labelRegion.x = rect.x;
}
}
else if (this.accumulation.enableSmartLabels) {
this.setPointVisibileFalse(point);
}
if (point.labelVisible && point.labelRegion) {
if ((point.label.indexOf('<br>') !== -1)) {
point.labelCollection = point.label.split('<br>');
}
else if (size < point.labelRegion.width) {
if (dataLabel.textWrap === 'Normal' && dataLabel.textOverflow === 'Ellipsis') {
point.labelCollection[0] = textTrim(size - (this.marginValue * 2), point.label, font, this.accumulation.enableRtl, this.accumulation.themeStyle.datalabelFont);
}
else if (dataLabel.textWrap === 'Wrap' || dataLabel.textWrap === 'AnyWhere') {
point.labelCollection = textWrap(point.label, size - (this.marginValue * 2), font, this.accumulation.enableRtl, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont);
}
point.labelRegion.width = size;
}
for (var i = 0; i < point.labelCollection.length; i++) {
if (point.labelCollection[i].length === 3 && point.labelCollection[i].indexOf('...') > -1) {
this.setPointVisibileFalse(point);
break;
}
}
}
}
};
/**
* To set point label visible and region to disable.
*
* @param {AccPoints} point - The accumulation point.
* @returns {void}
*/
AccumulationDataLabel.prototype.setPointVisibileFalse = function (point) {
point.labelVisible = false;
point.labelRegion = null;
};
/**
* To set point label visible to enable.
*
* @param {AccPoints} point - The accumulation point.
* @returns {void}
*/
AccumulationDataLabel.prototype.setPointVisibleTrue = function (point) {
point.labelVisible = true;
};
/**
* To set datalabel angle position for outside labels.
*
* @param {AccPoints} previousPoint - The previous accumulation point.
* @param {AccPoints} point - The accumulation point.
* @param {number} border - The border size.
* @param {number} labelRadius - The radius for the labels.
* @param {Size} textsize - The size of the labels.
* @param {number} margin - The margin value.
* @returns {void}
*/
AccumulationDataLabel.prototype.setOuterSmartLabel = function (previousPoint, point, border, labelRadius, textsize, margin) {
if (!this.isCircular()) {
this.setSmartLabelForSegments(point, previousPoint);
}
else {
var labelAngle = this.getOverlappedAngle(previousPoint.labelRegion, point.labelRegion, point.midAngle, border * 2);
this.getLabelRegion(point, 'Outside', textsize, labelRadius, margin, labelAngle);
if (labelAngle > point.endAngle) {
labelAngle = point.midAngle;
//this.setPointVisibileFalse(point);
}
point.labelAngle = labelAngle;
while (point.labelVisible && (isOverlap(previousPoint.labelRegion, point.labelRegion) || labelAngle <= previousPoint.labelAngle
|| labelAngle <= point.midAngle * 0.9 || this.isConnectorLineOverlapping(point, previousPoint))) {
if (labelAngle > point.endAngle) {
//this.setPointVisibileFalse(point);
break;
}
point.labelAngle = labelAngle;
this.getLabelRegion(point, 'Outside', textsize, labelRadius, margin, labelAngle);
labelAngle += 0.1;
}
}
};
/**
* Sets smart label positions for funnel and pyramid series.
*
* @param {AccPoints} point - The accumulation point.
* @param {AccPoints} prevPoint - The previous point.
* @returns {void} setSmartLabelForSegments.
*/
AccumulationDataLabel.prototype.setSmartLabelForSegments = function (point, prevPoint) {
var textRegion = point.labelRegion;
//let overlapWidth: number = prevPoint.labelRegion.x + prevPoint.labelRegion.width - textRegion.x;
var overlapHeight = this.accumulation.type === 'Funnel' ?
prevPoint.labelRegion.y - (textRegion.y + textRegion.height) :
point.labelRegion.y - (prevPoint.labelRegion.y + prevPoint.labelRegion.height);
if (overlapHeight < 0) {
point.labelRegion.y += this.accumulation.type === 'Funnel' ? overlapHeight : -overlapHeight;
}
};
/**
* To find connector line overlapping.
*
* @param {AccPoints} point - The accumulation point.
* @param {AccPoints} previous - The previous point.
* @returns {boolean} - To find connector line overlapping or not.
*/
AccumulationDataLabel.prototype.isConnectorLineOverlapping = function (point, previous) {
var position;
if (!this.isCircular() && point.labelRegion.x < point.region.x) {
position = 'outsideLeft';
}
var start = this.getLabelLocation(point, position);
var end = new ChartLocation(0, 0);
this.getEdgeOfLabel(point.labelRegion, point.labelAngle, end, 0, point);
var previousstart = this.getLabelLocation(previous);
var previousend = new ChartLocation(0, 0);
this.getEdgeOfLabel(previous.labelRegion, previous.labelAngle, previousend, 0, point);
return this.isLineRectangleIntersect(start, end, point.labelRegion) ||
this.isLineRectangleIntersect(start, end, previous.labelRegion) ||
this.isLineRectangleIntersect(previousstart, previousend, point.labelRegion);
};
/**
* To find two rectangle intersect.
*
* @param {ChartLocation} line1 - The first line.
* @param {ChartLocation} line2 - The second line.
* @param {Rect} rect - The rectangle to check against.
* @returns {boolean} - To find line rectangle intersect value.
*/
AccumulationDataLabel.prototype.isLineRectangleIntersect = function (line1, line2, rect) {
var rectPoints = [
new ChartLocation(Math.round(rect.x), Math.round(rect.y)),
new ChartLocation(Math.round((rect.x + rect.width)), Math.round(rect.y)),
new ChartLocation(Math.round((rect.x + rect.width)), Math.round((rect.y + rect.height))),
new ChartLocation(Math.round(rect.x), Math.round((rect.y + rect.height)))
];
line1.x = Math.round(line1.x);
line1.y = Math.round(line1.y);
line2.x = Math.round(line2.x);
line2.y = Math.round(line2.y);
for (var i = 0; i < rectPoints.length; i++) {
if (this.isLinesIntersect(line1, line2, rectPoints[i], rectPoints[(i + 1) % rectPoints.length])) {
return true;
}
}
return false;
};
/**
* To find two line intersect.
*
* @param {ChartLocation} point1 - The first point of the first line.
* @param {ChartLocation} point2 - The second point of the first line.
* @param {ChartLocation} point11 - The first point of the second line.
* @param {ChartLocation} point12 - The second point of the second line.
* @returns {boolean} - To find line intersect or not.
*/
AccumulationDataLabel.prototype.isLinesIntersect = function (point1, point2, point11, point12) {
var a1 = point2.y - point1.y;
var b1 = point1.x - point2.x;
var c1 = a1 * point1.x + b1 * point1.y;
var a2 = point12.y - point11.y;
var b2 = point11.x - point12.x;
var c2 = a2 * point11.x + b2 * point11.y;
var delta = a1 * b2 - a2 * b1;
if (delta !== 0) {
var x = (b2 * c1 - b1 * c2) / delta;
var y = (a1 * c2 - a2 * c1) / delta;
var lies = Math.min(point1.x, point2.x) <= x && x <= Math.max(point1.x, point2.x);
lies = lies && Math.min(point1.y, point2.y) <= y && y <= Math.max(point1.y, point2.y);
lies = lies && Math.min(point11.x, point12.x) <= x && x <= Math.max(point11.x, point12.x);
lies = lies && Math.min(point11.y, point12.y) <= y && y <= Math.max(point11.y, point12.y);
return lies;
}
return false;
};
/**
* To get two rectangle overlapping angles.
*
* @param {Rect} first - The first rectangle.
* @param {Rect} second - The second rectangle.
* @param {number} angle - The angle.
* @param {number} padding - The padding.
* @returns {number} - Get overlapped angle.
*/
AccumulationDataLabel.prototype.getOverlappedAngle = function (first, second, angle, padding) {
var x = first.x;
if (angle >= 90 && angle <= 270) {
second.y = first.y - (padding + second.height / 2);
x = first.x + first.width;
}
else {
second.y = first.y + first.height + padding;
}
return getAngle(this.center, new ChartLocation(x, second.y));
};
/**
* To get connector line path.
*
* @param {Rect} label - The label.
* @param {AccPoints} point - The accumulation point.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings.
* @param {number} end - The end.
* @returns {string} - Get connector line path.
*/
AccumulationDataLabel.prototype.getConnectorPath = function (label, point, dataLabel, end) {
if (end === void 0) { end = 0; }
var connector = dataLabel.connectorStyle;
var labelRadius = this.isCircular() ? (!this.isVariousRadius() ? this.labelRadius :
this.accumulation.pieSeriesModule.getLabelRadius(this.accumulation.visibleSeries[0], point)) :
this.getLabelDistance(point, dataLabel);
//let labelRadius: number = this.isCircular() ? this.labelRadius : this.getLabelDistance(point, dataLabel);
var start = this.getConnectorStartPoint(point, connector);
var labelAngle = this.accumulation.enableSmartLabels ? point.midAngle : end || point.midAngle;
var middle = new ChartLocation(0, 0);
var endPoint = this.getEdgeOfLabel(label, labelAngle, middle, connector.width, point);
if (connector.type === 'Curve') {
if (this.isCircular()) {
var r = labelRadius - (this.isVariousRadius() ? stringToNumber(point.sliceRadius, this.accumulation.pieSeriesModule.size / 2) :
this.radius);
//let r: number = labelRadius - this.radius;
if (point.isLabelUpdated) {
middle = this.getPerpendicularDistance(start, point);
}
else {
middle = degreeToLocation(labelAngle, labelRadius - (r / 2), this.center);
if (point.labelPosition === 'Outside' && dataLabel.position === 'Inside') {
middle = degreeToLocation(labelAngle, labelRadius - r * 1.25, this.center);
}
}
return 'M ' + start.x + ' ' + start.y + ' Q ' + middle.x + ' ' + middle.y + ' ' + endPoint.x + ' ' + endPoint.y;
}
else {
return this.getPolyLinePath(start, endPoint);
}
}
else {
return 'M ' + start.x + ' ' + start.y + ' L ' + middle.x + ' ' + middle.y + ' L ' + endPoint.x + ' ' + endPoint.y;
}
};
/**
* Finds the curved path for funnel/pyramid data label connectors.
*
* @param {ChartLocation} start - The start location.
* @param {ChartLocation} end - The end location.
* @returns {string} - Get poly line path.
*/
AccumulationDataLabel.prototype.getPolyLinePath = function (start, end) {
var controlPoints = [start, end];
if (start.y === end.y) {
return 'M ' + start.x + ' ' + start.y + ' L ' + end.x + ' ' + end.y;
}
var path = 'M';
for (var i = 0; i <= 16; i++) {
var t = i / 16;
var points = this.getBezierPoint(t, controlPoints, 0, 2);
path += points.x + ',' + points.y;
if (i !== 16) {
path += ' L';
}
}
return path;
};
/**
* Finds the bezier point for funnel/pyramid data label connectors.
*
* @param {number} t - The parameter value.
* @param {ChartLocation[]} controlPoints - The control points for the bezier point.
* @param {number} index - The index of the point.
* @param {number} count - The total count of points.
* @returns {ChartLocation} - Get bazier point.
*/
AccumulationDataLabel.prototype.getBezierPoint = function (t, controlPoints, index, count) {
if (count === 1) {
return controlPoints[index];
}
var p0 = this.getBezierPoint(t, controlPoints, index, count - 1);
var p1 = this.getBezierPoint(t, controlPoints, index + 1, count - 1);
var x = (p0.x) ? p0.x : p0.x;
var y = (p0.y) ? p0.y : p0.y;
var x1 = (p1.x) ? p1.x : p1.x;
var y1 = (p1.y) ? p1.y : p1.y;
var x2 = (1 - t) * x + t * x1;
var y2 = (1 - t) * y + t * y1;
if (p0.x) {
return { x: x2, y: y2 };
}
else {
return { x: x2, y: y2 };
}
};
/**
* To get label edges based on the center and label rect position.
*
* @param {Rect} labelshape - The label shape.
* @param {number} angle - The angle of the label.
* @param {ChartLocation} middle - The middle point of the label.
* @param {number} border - The border value.
* @param {AccPoints} point - The accumulation point.
* @returns {ChartLocation} - Get label edge value.
*/
AccumulationDataLabel.prototype.getEdgeOfLabel = function (labelshape, angle, middle, border, point) {
if (border === void 0) { border = 1; }
var edge = new ChartLocation(labelshape.x, labelshape.y);
var space = 10;
if (angle >= 90 && angle <= 270) {
edge.x += labelshape.width + border / 2 + space;
edge.y += labelshape.height / 2;
middle.x = edge.x + 10;
middle.y = edge.y;
}
else if (point && point.region && point.region.x > point.labelRegion.x) {
edge.x += border * 2 + labelshape.width + space;
edge.y += labelshape.height / 2;
middle.x = edge.x + 10;
middle.y = edge.y;
}
else {
edge.x -= space - border / 2;
edge.y += labelshape.height / 2;
middle.x = edge.x - 10;
middle.y = edge.y;
}
return edge;
};
/**
* Finds the distance between the label position and the edge/center of the funnel/pyramid.
*
* @param {AccPoints} point - The accumulation point.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The data label settings.
* @returns {number} - Get label distance.
*/
AccumulationDataLabel.prototype.getLabelDistance = function (point, dataLabel) {
if (point.labelPosition && dataLabel.position !== point.labelPosition || (dataLabel.connectorStyle.length && dataLabel.position === 'Outside')) {
var length_1 = stringToNumber(dataLabel.connectorStyle.length || '70px', this.accumulation.initialClipRect.width);
if (length_1 < this.accumulation.initialClipRect.width) {
return length_1;
}
}
var position = point.labelPosition || dataLabel.position;
var series = this.accumulation.visibleSeries[0];
var extraSpace = (this.accumulation.initialClipRect.width - series.triangleSize.width) / 2;
var labelLocation;
switch (position) {
case 'Inside':
return 0;
case 'Outside':
labelLocation = point.symbolLocation.x + point.labelOffset.x;
return this.accumulation.initialClipRect.width - labelLocation - extraSpace;
}
};
/**
* Finds the label position / beginning of the connector(ouside funnel labels).
*
* @param {AccPoints} point - The accumulation point.
* @param {AccumulationLabelPosition | string} position - The data label position.
* @returns {ChartLocation} - Get label location.
*/
AccumulationDataLabel.prototype.getLabelLocation = function (point, position) {
if (position === void 0) { position = 'Outside'; }
if (this.accumulation.type !== 'Pie' && this.accumulation.series[0].funnelMode !== 'Trapezoidal') {
position = position === 'OutsideLeft' ? 'OutsideLeft' : point.labelPosition || position;
var location_2 = {
x: point.symbolLocation.x,
y: point.symbolLocation.y - point.labelOffset.y
};
switch (position) {
case 'Inside':
location_2.y = point.region.y + point.region.height / 2;
break;
case 'Outside':
location_2.x += point.labelOffset.x;
break;
case 'OutsideLeft':
location_2.x -= point.labelOffset.x;
}
return location_2;
}
else if (this.accumulation.series[0].funnelMode === 'Trapezoidal' && this.accumulation.type === 'Funnel') {
var location_3 = {
x: point.symbolLocation.x,
y: point.symbolLocation.y
};
if (position === 'Outside') {
location_3.x = point.labelOffset.x;
}
return location_3;
}
else {
//return degreeToLocation(point.midAngle, this.radius, this.center);
return degreeToLocation(point.midAngle, (this.isVariousRadius() ? stringToNumber(point.sliceRadius, this.accumulation.pieSeriesModule.seriesRadius) :
this.radius), this.center);
}
};
/**
* Finds the beginning of connector line.
*
* @param {AccPoints} point - The accumulation point.
* @param {ConnectorModel} connector - The connector line.
* @returns {ChartLocation} - Staring point of connector line.
*/
AccumulationDataLabel.prototype.getConnectorStartPoint = function (point, connector) {
// return this.isCircular() ? degreeToLocation(point.midAngle, this.radius - connector.width, this.center) :
// this.getLabelLocation(point);
var position;
if (!this.isCircular() && point.region.x > point.labelRegion.x) {
position = 'OutsideLeft';
}
return this.isCircular() ? degreeToLocation(point.midAngle, (this.isVariousRadius() ? stringToNumber(point.sliceRadius, this.accumulation.pieSeriesModule.seriesRadius) :
this.radius) - connector.width, this.center) : this.getLabelLocation(point, position);
};
/**
* To find area rect based on margin, available size.
*
* @private
* @returns {void}
*/
AccumulationDataLabel.prototype.findAreaRect = function () {
this.areaRect = new Rect(0, 0, this.accumulation.availableSize.width, this.accumulation.availableSize.height);
var margin = this.accumulation.margin;
subtractThickness(this.areaRect, new Thickness(margin.left, margin.right, margin.top, margin.bottom));
};
/**
* To render the data labels from series points.
*
* @param {AccPoints} point - The point for which to render the data label.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The settings for the data labels.
* @param {Element} parent - The parent element to which the data labels are appended.
* @param {AccPoints[]} points - The collection of points in the series.
* @param {number} series - The index of the series.
* @param {HTMLElement} templateElement - The template element for the data label.
* @param {boolean} redraw - Indicates whether the data labels are being redrawn.
* @returns {void}
* @private
*/
AccumulationDataLabel.prototype.renderDataLabel = function (point, dataLabel, parent, points, series, templateElement, redraw) {
var id = this.accumulation.element.id + '_datalabel_Series_' + series + '_';
var datalabelGroup = this.accumulation.renderer.createGroup({ id: id + 'g_' + point.index });
var border = { width: dataLabel.border.width, color: dataLabel.border.color };
var argsFont = (extend({}, getValue('properties', dataLabel.font), null, true));
point.label = this.getDatalabelText(dataLabel.format, this.accumulation, point.originalText || point.y.toString());
var argsData = {
cancel: false, name: textRender, series: this.accumulation.visibleSeries[0], point: point,
text: point.label, border: border, color: dataLabel.fill, template: this.accumulation.enableHtmlSanitizer ?
this.accumulation.sanitize(dataLabel.template) : dataLabel.template, font: argsFont
};
this.accumulation.trigger(textRender, argsData);
point.argsData = argsData;
var isTemplate = argsData.template !== null;
point.labelVisible = !argsData.cancel;
point.text = point.label = argsData.text;
point.labelCollection = [];
this.marginValue = argsData.border.width ? (5 + argsData.border.width) : 1;
var childElement = createElement('div', {
id: this.accumulation.element.id + '_Series_' + 0 + '_DataLabel_' + point.index,
styles: 'position: absolute;background-color:' + argsData.color + ';' +
getFontStyle(dataLabel.font, this.accumulation.themeStyle.datalabelFont) + ';border:' + argsData.border.width + 'px solid ' + argsData.border.color + ';'
});
this.calculateLabelSize(isTemplate, childElement, point, points, argsData, datalabelGroup, id, dataLabel, redraw);
};
AccumulationDataLabel.prototype.getDatalabelText = function (labelFormat, chart, labelText) {
if (Number(labelText)) {
var customLabelFormat = labelFormat.match('{value}') !== null;
var format = chart.intl.getNumberFormat({
format: customLabelFormat ? '' : labelFormat,
useGrouping: chart.useGroupingSeparator
});
labelText = customLabelFormat ? labelFormat.replace('{value}', format(parseFloat(labelText))) : format(parseFloat(labelText));
}
return labelText;
};
/**
* To calculate label size.
*
* @param {boolean} isTemplate - Indicates whether the label is a template.
* @param {HTMLElement} childElement - The child element of the label.
* @param {AccPoints} point - The point associated with the label.
* @param {AccPoints[]} points - The collection of points.
* @param {IAccTextRenderEventArgs} argsData - The arguments data for text rendering.
* @param {Element} datalabelGroup - The group element for data labels.
* @param {string} id - The id of the label.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The settings for the data labels.
* @param {boolean} redraw - Indicates whether the labels are being redrawn.
* @param {ClientRect} clientRect - The client rectangle.
* @param {boolean} isReactCallback - Indicates whether a React callback is being used.
* @returns {void}
* @private
*/
AccumulationDataLabel.prototype.calculateLabelSize = function (isTemplate, childElement, point, points, argsData, datalabelGroup, id, dataLabel, redraw, clientRect, isReactCallback) {
this.calculateLabelCollection(point, dataLabel);
var textSize = isTemplate ? (isReactCallback ? { width: clientRect.width, height: clientRect.height } :
this.getTemplateSize(childElement, point, argsData, redraw, isTemplate, points, datalabelGroup, id, dataLabel)) : this.getTextSize(point.labelCollection, dataLabel);
textSize.height += 4; // 4 for calculation with padding for smart label shape
textSize.width += 4;
point.textSize = textSize;
point.templateElement = childElement;
this.getDataLabelPosition(point, dataLabel, textSize, points);
if (point.labelRegion) {
this.correctLabelRegion(point.labelRegion, point.textSize);
}
};
/**
* To draw a data label.
*
* @param {AccumulationSeries} series - The series associated with the data label.
* @param {AccumulationDataLabelSettingsModel} dataLabel - The settings for the data labels.
* @param {HTMLElement} parent - The parent element of the data labels.
* @param {HTMLElement} templateElement - The template element for the data label.
* @param {boolean} redraw - Indicates whether the data labels are being redrawn.
* @returns {void}
* @private
*/
AccumulationDataLabel.prototype.drawDataLabels = function (series, dataLabel, parent, templateElement, redraw) {
var angle;
var degree;
var modifiedPoints = series.leftSidePoints.concat(series.rightSidePoints);
modifiedPoints.sort(function (a, b) { return a.index - b.index; });
if (series.type === 'Pie' && this.accumulation.enableSmartLabels) {
this.extendedLabelsCalculation();
}
for (var _i = 0, modifiedPoints_1 = modifiedPoints; _i < modifiedPoints_1.length; _i++) {
var point = modifiedPoints_1[_i];
if (!isNullOrUndefined(point.argsData) && !isNullOrUndefined(point.y)) {
this.finalizeDatalabels(point, modifiedPoints, dataLabel);
var pointElement = document.getElementById(this.accumulation.element.id + '_Series_0_Point_' + point.index);
var id = this.accumulation.element.id + '_datalabel_Series_' + 0 + '_';
var datalabelGroup = this.accumulation.renderer.createGroup({ id: id + 'g_' + point.index });
datalabelGroup.setAttribute('aria-hidden', 'true');
var dataLabelElement = void 0;
var location_4 = void 0;
var element = void 0;
if (point.visible && point.labelVisible) {
angle = degree = dataLabel.angle;
if (point.argsData.template) {
this.setTemplateStyle(point.templateElement, point, templateElement, dataLabel.font.color, point.color, redraw);
}
else {
location_4 = new ChartLocation(point.labelRegion.x + this.marginValue, point.labelRegion.y
+ (point.textSize.height * 3 / (point.labelCollection.length * 4)) + this.marginValue);
element = getElement(id + 'shape_' + point.index);
var startLocation = element ? new ChartLocation(+element.getAttribute('x'), +element.getAttribute('y')) : null;
var textWidth = point.textSize.width;
if (dataLabel.enableRotation) {
if (angle === 0) {
if (point.labelPosition === 'Outside') {
degree = 0;
}
else if (point.midAngle >= 90 && point.midAngle <= 260) {
degree = point.midAngle + 180;
}
else {
degree = point.midAngle;
}
}
else {
degree = (angle > 360) ? angle - 360 : (angle < -360) ? angle + 360 : angle;
}
}
else {
degree = 0;
}
var rotate = 'rotate(' + degree + ',' + (location_4.x + (textWidth / 2)) + ',' + (location_4.y) + ')';
point.transform = rotate;
dataLabelElement = this.accumulation.renderer.drawRectangle(new RectOption(id + 'shape_' + point.index, point.argsData.color, point.argsData.border, 1, point.labelRegion, dataLabel.rx, dataLabel.ry, rotate, series.dataLabel.border.dashArray));
appendChildElement(false, datalabelGroup, dataLabelElement, redraw, true, 'x', 'y', startLocation, null, false, false, null, this.accumulation.duration);
textElement(this.accumulation.renderer, new TextOption(id + 'text_' + point.index, location_4.x, location_4.y, this.accumulation.enableRtl ? 'end' : 'start', point.labelCollection, rotate, 'auto', degree), point.argsData.font, point.argsData.font.color || this.getSaturatedColor(point, point.argsData.color), datalabelGroup, false, redraw, true, false, this.accumulation.duration, null, null, null, null, true, this.accumulation.themeStyle.datalabelFont);
element = null;
}
if (pointElement && this.accumulation.highlightMode !== 'None') {
datalabelGroup.setAttribute('class', pointElement.getAttribute('class') ? pointElement.getAttribute('class') : '');
for (var i = 0; i < datalabelGroup.children.length; i++) {
var existing = document.getElementById(datalabelGroup.children[i].id);
if (existing) {
datalabelGroup.children[i].style.opacity = existing.style.opacity;
}
}
}
if (this.accumulation.accumulationLegendModule && this.accumulation.legendSettings.visible && !this.accumulation.redraw && (dataLabel.position === 'Outside'
|| this.accumulation.enableSmartLabels)) {
this.accumulation.visibleSeries[0].findMaxBounds(this.accumulation.visibleSeries[0].labelBound, point.labelRegion);
}
if (point.labelPosition === 'Outside') {
var element_1 = getElement(id + 'connector_' + point.index);
var previousDirection = element_1 ? element_1.getAttribute('d') : '';
var pathElement = this.accumulation.renderer.drawPath(new PathOption(id + 'connector_' + point.index, 'transparent', dataLabel.connectorStyle.width, dataLabel.connectorStyle.color || point.color, 1, dataLabel.connectorStyle.dashArray, this.getConnectorPath(extend({}, point.labelRegion, null, true), point, dataLabel, point.labelAngle)));
appendChildElement(false, datalabelGroup, pathElement, redraw, true, null, null, null, previousDirection, false, false, null, this.accumulation.duration);
}
appendChildElement(false, parent, datalabelGroup, redraw);
}
else if (getElement(datalabelGroup.id)) {
(getElement(datalabelGroup.id)).parentNode.removeChild(getElement(datalabelGroup.id));
}
}
}
if (this.accumulation.type === 'Pie' && dataLabel.textWrap === 'Normal' && dataLabel.textOverflow === 'Clip') {
this.dataLabelClipPath(dataLabel, parent);
}
};
/**
* To calculate data label clip path.
*
* @param {AccumulationDataLabelSettingsModel} dataLabel - The settings for the data labels.
* @param {HTMLElement} parent - The parent element of the data labels.
* @returns {void}
*/
AccumulationDataLabel.prototype.dataLabelClipPath = function (dataLabel, parent) {
var id = this.accumulation.element.id + '_datalabel_Series_' + 0 + '_';
var clippath = this.accumulation.renderer.createClipPath({ id: id + 'clipPath' });
var clipRect;
var radius = this.accumulation.pieSeriesModule.pieBaseRadius;
if (dataLabel.position === 'Inside') {
clipRect = this.accumulation.renderer.drawCircle(new CircleOption(id + 'clipPath_rect', 'transparent', { width: 0 }, 0, this.center.x, this.center.y, radius));
}
else if (dataLabel.maxWidth) {
var x = this.center.x - radius - stringToNumber((dataLabel.connectorStyle.length || '4%'), radius) - dataLabel.maxWidth;
var y = this.center.y - radius - stringToNumber((dataLabel.connectorStyle.le