UNPKG

@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.

748 lines 69.7 kB
import { ChartLocation, RectOption, isCollide, rotateTextSize } from '../../common/utils/helper'; import { markerAnimate, appendChildElement, getVisiblePoints } from '../../common/utils/helper'; import { getLabelText, convertHexToColor, calculateRect, textElement, colorNameToHex, animateTextElement } from '../../common/utils/helper'; import { measureText, TextOption, Rect } from '@syncfusion/ej2-svg-base'; import { textRender } from '../../common/model/constants'; import { createTemplate, getFontStyle, getElement, measureElementRect, templateAnimate, withIn, withInBounds } from '../../common/utils/helper'; import { createElement, getValue, extend } from '@syncfusion/ej2-base'; import { getPoint, isRotatedRectIntersect } from '../../common/utils/helper'; /** * The `DataLabel` module is used to render data labels for data points. */ var DataLabel = /** @class */ (function () { /** * Constructor for the data label module. * * @private */ function DataLabel(chart) { this.errorHeight = 0; /** @private */ this.dataLabelRectCollection = {}; this.chart = chart; } DataLabel.prototype.initPrivateVariables = function (series, marker) { var transform = ''; var clipPath = ''; var render = series.chart.renderer; var index = (series.index === undefined) ? series.category : series.index; if (series.chart.chartAreaType === 'Cartesian') { transform = 'translate(' + series.clipRect.x + ',' + (series.clipRect.y) + ')'; clipPath = 'url(#' + this.chart.element.id + '_ChartSeriesClipRect_' + index + ')'; } if (marker.dataLabel.visible && !this.chart.enableCanvas) { series.shapeElement = render.createGroup({ 'id': this.chart.element.id + 'ShapeGroup' + index, 'transform': transform, 'clip-path': 'url(#' + this.chart.element.id + '_ChartSeriesClipRect_' + index + ')' }); series.textElement = render.createGroup({ 'id': this.chart.element.id + 'TextGroup' + index, 'transform': transform, 'clip-path': clipPath }); series.textElement.setAttribute('aria-hidden', 'true'); } this.markerHeight = ((series.type === 'Scatter' || marker.visible)) ? (marker.height / 2) : 0; this.commonId = this.chart.element.id + '_Series_' + index + '_Point_'; this.calculateErrorHeight(series, series.marker.dataLabel.position); this.chartBackground = this.chart.chartArea.background === 'transparent' ? this.chart.background || this.chart.themeStyle.background : this.chart.chartArea.background; }; DataLabel.prototype.calculateErrorHeight = function (series, position) { if (!series.errorBar.visible) { return null; } else if (series.errorBar.visible && this.chart.chartAreaType !== 'PolarRadar') { var direction = series.errorBar.direction; var positiveHeight = this.chart.errorBarModule.positiveHeight; var negativeHeight = this.chart.errorBarModule.negativeHeight; if (this.isRectSeries(series)) { if (position === 'Top' || position === 'Auto') { if (direction === 'Both' || direction === 'Minus') { this.errorHeight = negativeHeight; } else { this.errorHeight = 0; } } if (position === 'Outer' || position === 'Auto') { if (direction === 'Both' || direction === 'Plus') { this.errorHeight = positiveHeight; } else { this.errorHeight = 0; } } } else { if (position === 'Top' || position === 'Outer' || position === 'Auto') { if ((direction === 'Both' || direction === 'Plus') && (!series.chart.isTransposed)) { this.errorHeight = positiveHeight; } else { this.errorHeight = 0; } } if (position === 'Bottom' || position === 'Auto') { if (direction === 'Both' || direction === 'Minus') { this.errorHeight = negativeHeight; } else { this.errorHeight = 0; } } } } else { this.errorHeight = 0; } }; DataLabel.prototype.isRectSeries = function (series) { return series.isRectSeries || series.type === 'RangeArea' || series.type === 'SplineRangeArea' || series.type === 'RangeStepArea'; }; /** * Render the data label for series. * * @param {Series} series - The series to render. * @param {Chart} chart - The parent chart. * @param {DataLabelSettingsModel} dataLabel - The settings for data labels. * @returns {void} * @private */ DataLabel.prototype.render = function (series, chart, dataLabel) { // initialize the private variable this.initPrivateVariables(series, series.marker); this.inverted = chart.requireInvertedAxis; this.yAxisInversed = series.yAxis.isAxisInverse; var templateId = chart.element.id + '_Series_' + (series.index === undefined ? series.category : series.index) + '_DataLabelCollections'; var element = createElement('div', { id: templateId }); var visiblePoints = getVisiblePoints(series); // Data label point iteration started if (series.visible) { for (var i = 0; i < visiblePoints.length; i++) { this.renderDataLabel(series, visiblePoints[i], element, dataLabel); } } if (element.childElementCount) { if (!chart.enableCanvas) { appendChildElement(chart.enableCanvas, getElement(chart.element.id + '_Secondary_Element'), element, chart.redraw, false, 'x', 'y', null, '', false, false, null, chart.duration); } else { getElement(chart.element.id + '_Secondary_Element').appendChild(element); } } }; DataLabel.prototype.renderDataLabel = function (series, point, element, dataLabel) { if (!dataLabel.showZero && ((point.y === 0) || (point.y === 0 && series.emptyPointSettings.mode === 'Zero'))) { return null; } this.margin = dataLabel.margin; var labelText = []; var labelLength; var xPos; var yPos; var xValue; var yValue; var degree; var rectCenterX; var rectCenterY; var labelLocation = { x: 0, y: 0 }; var textSize; var clip = series.clipRect; var shapeRect; var isDataLabelOverlap = false; var dataLabelElement = []; var startLocation; dataLabel.angle = dataLabel.labelIntersectAction === 'Rotate90' ? 90 : dataLabel.angle; dataLabel.enableRotation = dataLabel.labelIntersectAction === 'Rotate90' ? true : dataLabel.enableRotation; var angle = degree = dataLabel.angle; var border = { width: dataLabel.border.width, color: dataLabel.border.color }; var argsFont = (extend({}, getValue('properties', dataLabel.font), null, true)); if ((point.symbolLocations.length && point.symbolLocations[0]) || (series.type === 'BoxAndWhisker' && point.regions.length)) { labelText = point.text !== null ? getLabelText(point, series, this.chart) : []; labelLength = labelText.length; for (var i = 0; i < labelLength; i++) { var argsData = { cancel: false, name: textRender, series: series, point: point, text: labelText[i], border: border, color: dataLabel.fill, template: dataLabel.template, font: argsFont, location: labelLocation, textSize: measureText(labelText[i], dataLabel.font, this.chart.themeStyle.datalabelFont) }; this.chart.trigger(textRender, argsData); if (!argsData.cancel) { this.fontBackground = argsData.color; this.isDataLabelShape(argsData); this.markerHeight = series.type === 'Bubble' ? (point.regions[0].height / 2) : this.markerHeight; if (argsData.template !== null) { this.createDataLabelTemplate(element, series, dataLabel, point, argsData, i, this.chart.redraw); } else { if (dataLabel.enableRotation) { textSize = rotateTextSize(dataLabel.font, argsData.text, dataLabel.angle, this.chart, this.chart.themeStyle.datalabelFont); } else { textSize = measureText(argsData.text, dataLabel.font, this.chart.themeStyle.datalabelFont); } var rect = this.calculateTextPosition(point, series, textSize, dataLabel, i); var actualRect = new Rect(rect.x + clip.x, rect.y + clip.y, rect.width, rect.height); //let notOverlapping: boolean; if (dataLabel.enableRotation) { var rectCoordinates = this.getRectanglePoints(actualRect); rectCenterX = rect.x + (rect.width / 2); rectCenterY = (rect.y + (rect.height / 2)); isDataLabelOverlap = (dataLabel.labelIntersectAction === 'Rotate90' || angle === -90) ? false : this.isDataLabelOverlapWithChartBound(rectCoordinates, this.chart, { x: 0, y: 0, width: 0, height: 0 }); if (!isDataLabelOverlap) { this.chart.rotatedDataLabelCollections.push(rectCoordinates); var currentPointIndex = this.chart.rotatedDataLabelCollections.length - 1; for (var index = currentPointIndex; index >= 0; index--) { if (this.chart.rotatedDataLabelCollections[currentPointIndex] && this.chart.rotatedDataLabelCollections[index - 1] && isRotatedRectIntersect(this.chart.rotatedDataLabelCollections[currentPointIndex], this.chart.rotatedDataLabelCollections[index - 1])) { isDataLabelOverlap = true; this.chart.rotatedDataLabelCollections[currentPointIndex] = null; break; } } } } else { isDataLabelOverlap = isCollide(rect, this.chart.dataLabelCollections, clip); } if ((!isDataLabelOverlap || dataLabel.labelIntersectAction === 'None')) { var dataLabelShapeElement = getElement(this.commonId + point.index + '_TextShape_' + i); if (dataLabelShapeElement) { startLocation = { x: +dataLabelShapeElement.getAttribute('x'), y: +dataLabelShapeElement.getAttribute('y') }; } this.chart.dataLabelCollections.push(actualRect); if (this.isShape) { shapeRect = this.chart.renderer.drawRectangle(new RectOption(this.commonId + point.index + '_TextShape_' + i, argsData.color, argsData.border, dataLabel.opacity, rect, dataLabel.rx, dataLabel.ry, '', dataLabel.border.dashArray), new Int32Array([clip.x, clip.y])); if (series.shapeElement) { appendChildElement(this.chart.enableCanvas, series.shapeElement, shapeRect, this.chart.redraw, true, 'x', 'y', startLocation); } } // Checking the font color var backgroundColor = this.fontBackground === 'transparent' ? ((this.chart.theme.indexOf('Dark') > -1 || this.chart.theme.indexOf('HighContrast') > -1) ? 'black' : 'white') : this.fontBackground; var rgbValue = convertHexToColor(colorNameToHex(backgroundColor)); var contrast = Math.round((rgbValue.r * 299 + rgbValue.g * 587 + rgbValue.b * 114) / 1000); xPos = (rect.x + this.margin.left + textSize.width / 2) + labelLocation.x; yPos = dataLabel.enableRotation && this.chart.chartAreaType !== 'PolarRadar' ? (rect.y + this.margin.top + textSize.height / 2 + textSize.width / 4 + (dataLabel.position === 'Auto' ? point.regions[0].width / 10 : 0)) + labelLocation.y : (rect.y + this.margin.top + textSize.height * 3 / 4) + labelLocation.y; labelLocation = { x: 0, y: 0 }; if (angle !== 0 && dataLabel.enableRotation) { // xValue = xPos - (dataLabel.margin.left) / 2 + (dataLabel.margin.right / 2); xValue = rectCenterX; //yValue = yPos - (dataLabel.margin.top) / 2 - (textSize.height / dataLabel.margin.top) + // (dataLabel.margin.bottom) / 2; yValue = rectCenterY; degree = (angle > 360) ? angle - 360 : (angle < -360) ? angle + 360 : angle; } else { degree = 0; xValue = rect.x; yValue = rect.y; xPos -= this.chart.chartAreaType === 'Cartesian' && xPos + (textSize.width / 2) > clip.width ? (!this.chart.requireInvertedAxis && xPos > clip.width) ? 0 : (xPos + textSize.width / 2) - clip.width : 0; yPos -= (yPos + textSize.height > clip.y + clip.height && !(series.type.indexOf('Bar') > -1)) ? (yPos + textSize.height) - (clip.y + clip.height) : 0; } var textAnchor = dataLabel.labelIntersectAction === 'Rotate90' ? (dataLabel.position === 'Top' ? 'start' : (dataLabel.position === 'Middle' ? 'middle' : 'end')) : ((angle === -90 && dataLabel.enableRotation) ? (dataLabel.position === 'Top' ? 'end' : (dataLabel.position === 'Middle' ? 'middle' : 'start')) : 'middle'); var oldText = void 0; if (this.chart.redraw && document.getElementById(this.commonId + point.index + '_Text_' + i)) { oldText = document.getElementById(this.commonId + point.index + '_Text_' + i).textContent; } if (argsData.text) { dataLabelElement.push(textElement(this.chart.renderer, new TextOption(this.commonId + ((series.removedPointIndex !== null && series.removedPointIndex <= point.index) ? (point.index + 1) : point.index) + '_Text_' + i, xPos, yPos, textAnchor, argsData.text, 'rotate(' + degree + ',' + (xValue) + ',' + (yValue) + ')', 'auto', degree), argsData.font, argsData.font.color || (this.chart.theme === 'Bootstrap5' ? '#212529' : this.chart.theme === 'Bootstrap5Dark' ? '#DEE2E6' : ((contrast >= 128 || series.type === 'Hilo' || series.type === 'HiloOpenClose') ? this.chart.theme.indexOf('Tailwind3') > -1 ? '#111827' : 'black' : this.chart.theme.indexOf('Tailwind3') > -1 ? '#FFFFFF' : 'white')), series.textElement, false, this.chart.redraw, true, false, series.chart.duration, series.clipRect, null, null, this.chart.enableCanvas, null, this.chart.themeStyle.datalabelFont, new ChartLocation(xValue, yValue))); } if (this.isShape && dataLabel.enableRotation) { shapeRect.setAttribute('transform', 'rotate(' + dataLabel.angle + ', ' + xValue + ', ' + yValue + ')'); } if (this.chart.stackLabels.visible && series.type.indexOf('Stacking') > -1) { this.dataLabelRectCollection = !this.dataLabelRectCollection ? {} : this.dataLabelRectCollection; this.dataLabelRectCollection[this.commonId + ((series.removedPointIndex !== null && series.removedPointIndex <= point.index) ? (point.index + 1) : point.index) + '_Text_' + i] = actualRect; this.dataLabelRectCollection[this.commonId + point.index + '_TextShape_' + i] = actualRect; } if (series.removedPointIndex !== null && series.removedPointIndex <= point.index) { series.textElement.lastChild.id = this.commonId + point.index + '_Text_' + i; } if (this.chart.redraw && oldText !== argsData.text) { animateTextElement(series.textElement.querySelector('#' + this.commonId + point.index + '_Text_' + i), this.chart.duration, parseFloat(oldText), parseFloat(argsData.text), series.marker.dataLabel.format || series.yAxis.labelFormat); } } else if (getElement(this.commonId + point.index + '_Text_0') && series.chart.redraw && series.currentData) { getElement(this.commonId + point.index + '_Text_0').remove(); } } } } } return dataLabelElement; }; /** * Renders the stack labels for the chart. * * This method is responsible for displaying cumulative total values on stacked chart segments. * * @returns {void} */ DataLabel.prototype.renderStackLabels = function () { var stackLabelGroup = this.chart.renderer.createGroup({ id: this.chart.element.id + "_StackLabelGroup" }); if (!this.chart.enableCanvas && stackLabelGroup) { this.chart.seriesElements.appendChild(stackLabelGroup); } var positivePoints = {}; var negativePoints = {}; var groupingValues = []; var keys = []; var stackLabelIndex = 0; if (this.chart.visibleSeries && this.chart.visibleSeries.length > 0) { for (var i = 0; i < this.chart.visibleSeries.length; i++) { keys = Object.keys(groupingValues); var series = this.chart.visibleSeries[i]; if (!groupingValues[series.stackingGroup]) { groupingValues[series.stackingGroup] = []; groupingValues[series.stackingGroup].push(series); } else if (groupingValues[series.stackingGroup] !== undefined) { groupingValues[series.stackingGroup].push(series); } } if (keys[0] !== '') { for (var groupIndex = 0; groupIndex < keys.length; groupIndex++) { positivePoints = {}; negativePoints = {}; var count = groupingValues[keys[groupIndex]][groupingValues[keys[groupIndex]].length - 1] .index; for (var seriesIndex = count; seriesIndex >= 0; seriesIndex--) { var series = this.chart.visibleSeries[seriesIndex]; if (!this.chart.enableCanvas && series.animation.enable && this.chart.animateSeries) { stackLabelGroup.setAttribute('visibility', 'hidden'); } if (series.visible && series.points && series.points.length > 0) { for (var pointIndex = 0; pointIndex < series.points.length; pointIndex++) { var point = series.points[pointIndex]; var pointXValueAsKey = String(point.x); if (!positivePoints[pointXValueAsKey] && series.stackedValues.endValues[pointIndex] > 0 && point.visible) { positivePoints[pointXValueAsKey] = point; } if (!negativePoints[pointXValueAsKey] && series.stackedValues.endValues[pointIndex] < 0 && point.visible) { negativePoints[pointXValueAsKey] = point; } } } } stackLabelIndex = this.calculateStackLabel(positivePoints, negativePoints, stackLabelGroup, stackLabelIndex); } } else { for (var seriesIndex = this.chart.visibleSeries.length - 1; seriesIndex >= 0; seriesIndex--) { var series = this.chart.visibleSeries[seriesIndex]; if (!this.chart.enableCanvas && series.animation.enable && this.chart.animateSeries) { stackLabelGroup.setAttribute('visibility', 'hidden'); } if (series.visible && series.points && series.points.length > 0) { for (var pointIndex = 0; pointIndex < series.points.length; pointIndex++) { var point = series.points[pointIndex]; var pointXValueAsKey = String(point.x); if (!positivePoints[pointXValueAsKey] && series.stackedValues.endValues[pointIndex] > 0 && point.visible) { positivePoints[pointXValueAsKey] = point; } if (!negativePoints[pointXValueAsKey] && series.stackedValues.endValues[pointIndex] < 0 && point.visible) { negativePoints[pointXValueAsKey] = point; } } } } this.calculateStackLabel(positivePoints, negativePoints, stackLabelGroup, stackLabelIndex); } } }; /** * This method is responsible for positioning the cumulative sum of stacking column series. * * @param {Object} positivePoints - The positive points of the stacking series. * @param {Object} negativePoints - The negative points of the stacking series. * @param {Element} stackLabelGroup - The stack label group element. * @param {number} stackLabelIndex - The index of the stack label. * @returns {number} - The number of stack labels. */ DataLabel.prototype.calculateStackLabel = function (positivePoints, negativePoints, stackLabelGroup, stackLabelIndex) { var _this = this; [positivePoints, negativePoints].forEach(function (points, index) { if (points) { var totalValue_1 = 0; var currentPoint_1; Object.keys(points).forEach(function (pointXValueAsKey) { var positiveValue = points[pointXValueAsKey].series .stackedValues.endValues[points[pointXValueAsKey].index]; var negativeValue = negativePoints[pointXValueAsKey] ? negativePoints[pointXValueAsKey].series.stackedValues. endValues[negativePoints[pointXValueAsKey].index] : 0; if (index === 0) { // Handle positive points totalValue_1 = positiveValue + negativeValue; currentPoint_1 = points[pointXValueAsKey]; } else if (!positivePoints[pointXValueAsKey]) { // Handle negative points only if no corresponding positive point totalValue_1 = positiveValue; currentPoint_1 = points[pointXValueAsKey]; } if (currentPoint_1 && currentPoint_1.symbolLocations[0] && withInBounds(currentPoint_1.symbolLocations[0].x + currentPoint_1.series.clipRect.x, currentPoint_1.symbolLocations[0].y + currentPoint_1.series.clipRect.y, currentPoint_1.series.clipRect)) { var series = currentPoint_1.series; var symbolLocation = currentPoint_1.symbolLocations[0]; var labelFormat = _this.chart.stackLabels.format; var stackLabeltext = (totalValue_1 % 1 === 0) ? totalValue_1.toFixed(0) : (totalValue_1.toFixed(2).slice(-1) === '0' ? totalValue_1.toFixed(1) : totalValue_1.toFixed(2)); if (labelFormat) { var customLabelFormat = labelFormat.match('{value}') !== null; stackLabeltext = customLabelFormat ? labelFormat.replace('{value}', stackLabeltext.toString()) : _this.chart.intl.getNumberFormat({ format: labelFormat, useGrouping: _this.chart.useGroupingSeparator })(totalValue_1); } var argsStackLabelFont = (extend({}, getValue('properties', _this.chart.stackLabels.font), null, true)); var argsData = { cancel: false, name: textRender, series: series, point: null, text: stackLabeltext, border: _this.chart.stackLabels.border, color: _this.chart.stackLabels.fill, template: null, font: argsStackLabelFont, location: null, textSize: measureText(stackLabeltext, _this.chart.stackLabels.font, _this.chart.themeStyle.datalabelFont) }; _this.chart.trigger(textRender, argsData); if (!argsData.cancel) { var textSize = measureText(argsData.text, argsData.font, _this.chart.themeStyle.datalabelFont); // Define padding to maintain a consistent gap from the symbol location values var padding = 10; if (_this.chartBackground === undefined) { _this.chartBackground = _this.chart.chartArea.background === 'transparent' ? _this.chart.background || _this.chart.themeStyle.background : _this.chart.chartArea.background; } var backgroundColor = argsData.color === 'transparent' && _this.chartBackground === 'transparent' ? ((_this.chart.theme.indexOf('Dark') > -1 || _this.chart.theme.indexOf('HighContrast') > -1) ? 'black' : 'white') : _this.chart.stackLabels.fill !== 'transparent' ? _this.chart.stackLabels.fill : _this.chartBackground; var rgbValue = convertHexToColor(colorNameToHex(backgroundColor)); var contrast = Math.round((rgbValue.r * 299 + rgbValue.g * 587 + rgbValue.b * 114) / 1000); var alignmentValue = textSize.width + argsData.border.width + _this.chart.stackLabels.margin.left + _this.chart.stackLabels.margin.right - padding / 2; var yOffset = _this.chart.requireInvertedAxis ? padding / 2 : (_this.chart.primaryYAxis.isInversed ? (index === 0 ? (textSize.height + padding / 2) : -padding) : (index === 0 ? -padding : (textSize.height + padding / 2))); var xOffset = _this.chart.requireInvertedAxis ? ((_this.chart.primaryYAxis.isInversed ? (index === 0 ? -(padding + textSize.width / 2) : (padding + textSize.width / 2)) : (index === 0 ? (padding + textSize.width / 2) : -(padding + textSize.width / 2)))) : 0; xOffset += argsData.font.textAlignment === 'Far' ? alignmentValue : (argsData.font.textAlignment === 'Near' ? -alignmentValue : 0); var xPosition = Math.max(series.clipRect.x + textSize.width, Math.min(xOffset + series.clipRect.x + symbolLocation.x, series.clipRect.x + series.clipRect.width - textSize.width)); var yPosition = Math.max(series.clipRect.y + textSize.height, Math.min(yOffset + series.clipRect.y + symbolLocation.y - ((_this.chart.stackLabels.angle > 0 && !_this.chart.requireInvertedAxis) ? textSize.width / 2 : 0), series.clipRect.y + series.clipRect.height - textSize.height)); if (_this.chart.enableCanvas) { xPosition -= series.clipRect.x; yPosition -= series.clipRect.y; } var rect = new Rect(xPosition - textSize.width / 2 - _this.chart.stackLabels.margin.left, yPosition - textSize.height - _this.chart.stackLabels.margin.top, textSize.width + (_this.chart.stackLabels.margin.left + _this.chart.stackLabels.margin.right), textSize.height + padding / 2 + (_this.chart.stackLabels.margin.top + _this.chart.stackLabels.margin.bottom)); var shapeRect = _this.chart.renderer.drawRectangle(new RectOption(_this.chart.element.id + "StackLabel_TextShape_" + stackLabelIndex, argsData.color, argsData.border, null, rect, _this.chart.stackLabels.rx, _this.chart.stackLabels.ry, '', null), new Int32Array([symbolLocation.x, symbolLocation.y])); shapeRect.setAttribute('transform', "rotate(" + _this.chart.stackLabels.angle + ", " + xPosition + ", " + yPosition + ")"); appendChildElement(_this.chart.enableCanvas, stackLabelGroup, shapeRect, _this.chart.redraw); textElement(_this.chart.renderer, new TextOption(_this.chart.element.id + "_StackLabel_" + stackLabelIndex, xPosition, yPosition, 'middle', argsData.text, "rotate(" + _this.chart.stackLabels.angle + ", " + xPosition + ", " + yPosition + ")", 'auto', _this.chart.stackLabels.angle), argsData.font, (argsData.font.color || (_this.chart.theme === 'Bootstrap5' ? '#212529' : _this.chart.theme === 'Bootstrap5Dark' ? '#DEE2E6' : ((contrast >= 128) ? _this.chart.theme.indexOf('Tailwind3') > -1 ? '#111827' : 'black' : _this.chart.theme.indexOf('Tailwind3') > -1 ? '#FFFFFF' : 'white'))), stackLabelGroup, null, _this.chart.redraw, true, null, _this.chart.duration, series.clipRect, null, null, _this.chart.enableCanvas, null, _this.chart.themeStyle.datalabelFont, null); if (series.type === 'StackingLine' || series.type === 'StackingArea') { document.querySelectorAll("[id^=\"" + _this.chart.element.id + "_Series_" + series.index + "_Point_" + currentPoint_1.index + "_Text_\"], \n [id^=\"" + _this.chart.element.id + "_Series_" + series.index + "_Point_" + currentPoint_1.index + "_TextShape_\"]").forEach(function (element) { if (element.id) { element.style.visibility = 'hidden'; element.setAttribute('data-collide', 'true'); } }); } for (var dataLabelID in _this.dataLabelRectCollection) { if (Object.prototype.hasOwnProperty.call(_this.dataLabelRectCollection, dataLabelID)) { var dataLabelRect = _this.dataLabelRectCollection[dataLabelID]; if (dataLabelRect) { var isCollided = isCollide(rect, [dataLabelRect], { x: 0, y: 0, height: 0, width: 0 }); if (isCollided) { var dataLabelElement = document.getElementById(dataLabelID); if (dataLabelElement) { dataLabelElement.style.visibility = 'hidden'; dataLabelElement.setAttribute('data-collide', 'true'); } } } } } } } stackLabelIndex++; }); } }); return stackLabelIndex; }; /** * Retrieves the points of a rectangle. * * @param {Rect} rect - The rectangle whose points are to be retrieved. * @returns {ChartLocation[]} - The points of the rectangle. */ DataLabel.prototype.getRectanglePoints = function (rect) { var loc1 = new ChartLocation(rect.x, rect.y); var loc2 = new ChartLocation(rect.x + rect.width, rect.y); var loc3 = new ChartLocation(rect.x + rect.width, rect.y + rect.height); var loc4 = new ChartLocation(rect.x, rect.y + rect.height); return [loc1, loc2, loc3, loc4]; }; DataLabel.prototype.isDataLabelOverlapWithChartBound = function (rectCoordinates, chart, clip) { for (var index = 0; index < rectCoordinates.length; index++) { if (!withInBounds(rectCoordinates[index].x + clip.x, rectCoordinates[index].y + clip.y, chart.initialClipRect)) { return true; } } return false; }; /** * Creates a template for data labels. * * @param {HTMLElement} parentElement - The parent element to which the template will be appended. * @param {Series} series - The series associated with the data label. * @param {DataLabelSettingsModel} dataLabel - The settings for the data label. * @param {Points} point - The data point to which the data label is associated. * @param {ITextRenderEventArgs} data - The event data associated with rendering the data label. * @param {number} labelIndex - The index of the data label. * @param {boolean} redraw - Specifies whether to redraw the template. * @returns {void} */ DataLabel.prototype.createDataLabelTemplate = function (parentElement, series, dataLabel, point, data, labelIndex, redraw) { this.margin = { left: 0, right: 0, bottom: 0, top: 0 }; var clip = series.clipRect; var childElement = createTemplate(createElement('div', { id: this.chart.element.id + '_Series_' + (series.index === undefined ? series.category : series.index) + '_DataLabel_' + point.index + (labelIndex ? ('_' + labelIndex) : ''), styles: 'position: absolute;background-color:' + data.color + ';' + getFontStyle(dataLabel.font, this.chart.themeStyle.datalabelFont) + ';border:' + data.border.width + 'px solid ' + data.border.color + ';' }), point.index, (this.chart.enableHtmlSanitizer ? this.chart.sanitize(data.template) : data.template), this.chart, point, series, this.chart.element.id + '_DataLabel', labelIndex); this.calculateTemplateLabelSize(parentElement, childElement, point, series, dataLabel, labelIndex, clip, redraw); }; DataLabel.prototype.calculateTemplateLabelSize = function (parentElement, childElement, point, series, dataLabel, labelIndex, clip, redraw, isReactCallback) { var elementRect = measureElementRect(childElement, redraw, isReactCallback); var rect = this.calculateTextPosition(point, series, { width: elementRect.width, height: elementRect.height }, dataLabel, labelIndex); var clipWidth = 0; var clipHeight = 0; var isOverlap = false; if (isReactCallback) { isOverlap = (elementRect.width === 0 || elementRect.height === 0); // To check the data label already overlap before react callback call // clipWidth = ((series.clipRect.x + rect.x) + elementRect.width) > parentElement.clientWidth ? // (parentElement.clientWidth - (series.clipRect.x + rect.x)) : 0; // clipHeight = (series.points.length - 1 === point.index) ? elementRect.height / 2 : 0; } childElement.style.left = ((this.chart.chartAreaType === 'PolarRadar' ? 0 : series.clipRect.x) + rect.x - clipWidth) + 'px'; childElement.style.top = ((this.chart.chartAreaType === 'PolarRadar' ? 0 : series.clipRect.y) + rect.y + clipHeight) + 'px'; var vAxis = series.chart.requireInvertedAxis ? series.xAxis : series.yAxis; var hAxis = series.chart.requireInvertedAxis ? series.yAxis : series.xAxis; if (childElement.childElementCount && !isOverlap && (!isCollide(rect, this.chart.dataLabelCollections, clip) || dataLabel.labelIntersectAction === 'None') && (series.seriesType !== 'XY' || point.yValue === undefined || withIn(point.yValue, series.yAxis.visibleRange) || (series.type.indexOf('Stacking') > -1) || (series.type.indexOf('100') > -1 && withIn(series.stackedValues.endValues[point.index], series.yAxis.visibleRange))) && withIn(point.xValue, series.xAxis.visibleRange) && parseFloat(childElement.style.top) >= vAxis.rect.y && parseFloat(childElement.style.left) >= hAxis.rect.x && parseFloat(childElement.style.top) <= vAxis.rect.y + vAxis.rect.height && parseFloat(childElement.style.left) <= hAxis.rect.x + hAxis.rect.width) { this.chart.dataLabelCollections.push(new Rect(rect.x + clip.x, rect.y + clip.y, rect.width, rect.height)); appendChildElement(this.chart.enableCanvas, parentElement, childElement, redraw, true, 'left', 'top'); if (series.animation.enable && this.chart.animateSeries && !this.chart.enableCanvas) { this.doDataLabelAnimation(series, childElement); } else if (this.chart.enableCanvas) { parentElement.appendChild(childElement); } } }; DataLabel.prototype.calculateTextPosition = function (point, series, textSize, dataLabel, labelIndex) { var labelRegion = labelIndex > 1 ? (series.type === 'Candle') ? point.regions[1] : point.regions[0] : point.regions[0]; if (labelIndex > 1 && series.type === 'HiloOpenClose') { labelRegion = (labelIndex === 2) ? point.regions[1] : point.regions[2]; } var location; location = this.getLabelLocation(point, series, textSize, labelIndex); var padding = 5; var clipRect = series.clipRect; // calculating alignment if (!this.chart.requireInvertedAxis || !this.isRectSeries(series) || series.type === 'BoxAndWhisker') { this.locationX = location.x; var alignmentValue = textSize.height + (this.borderWidth * 2) + this.markerHeight + this.margin.bottom + this.margin.top + padding; location.x = (dataLabel.position === 'Auto') ? location.x : this.calculateAlignment(alignmentValue, location.x, dataLabel.alignment, this.isRectSeries(series) ? point.yValue < 0 : false); // calculating position location.y = (!this.isRectSeries(series) || series.type === 'BoxAndWhisker') ? this.calculatePathPosition(location.y, dataLabel.position, series, point, textSize, labelIndex) : this.calculateRectPosition(location.y, labelRegion, point.yValue < 0 !== this.yAxisInversed, dataLabel.position, series, textSize, labelIndex, point); if (this.isRectSeries(series) && this.chart.chartAreaType === 'PolarRadar') { location = this.calculatePolarRectPosition(location, dataLabel.position, series, point, textSize, labelIndex, dataLabel.alignment, alignmentValue); } } else { this.locationY = location.y; var alignmentValue = textSize.width + this.borderWidth + this.margin.left + this.margin.right - padding; location.x = dataLabel.position === 'Auto' ? location.x : this.calculateAlignment(alignmentValue, location.x, dataLabel.alignment, point.yValue < 0); location.x = this.calculateRectPosition(location.x, labelRegion, point.yValue < 0 !== this.yAxisInversed, dataLabel.position, series, textSize, labelIndex, point); } var rect = calculateRect(location, textSize, this.margin); // Checking the condition whether data Label has been exist the clip rect if (!(dataLabel.enableRotation === true && dataLabel.angle !== 0) && !((rect.y > (clipRect.y + clipRect.height)) || (rect.x > (clipRect.x + clipRect.width)) || (rect.x + rect.width < 0) || (rect.y + rect.height < 0))) { rect.x = rect.x < 0 ? (series.type === 'StackingColumn' && !this.inverted ? 0 : padding) : rect.x; rect.y = (rect.y < 0 && !this.chart.requireInvertedAxis) && !(dataLabel.labelIntersectAction === 'None') ? padding : rect.y; rect.x -= (rect.x + rect.width) > (clipRect.x + clipRect.width) ? (rect.x + rect.width) - (clipRect.x + clipRect.width) + padding : 0; rect.y -= (rect.y + rect.height) > (clipRect.y + clipRect.height) ? (rect.y + rect.height) - (clipRect.y + clipRect.height) + padding : 0; this.fontBackground = this.fontBackground === 'transparent' ? this.chartBackground : this.fontBackground; } var dataLabelOutRegion; if (this.inverted && series.isRectSeries && (rect.x + rect.width > labelRegion.x + labelRegion.width)) { dataLabelOutRegion = true; } this.fontBackground = dataLabelOutRegion ? this.chartBackground : this.fontBackground; return rect; }; // Calculation label location for polar column draw types DataLabel.prototype.calculatePolarRectPosition = function (location, position, series, point, size, labelIndex, alignment, alignmentValue) { var padding = 5; var columnRadius; var chartWidth = this.chart.availableSize.width; var alignmentSign = (alignment === 'Center') ? 0 : (alignment === 'Far' ? 1 : -1); var angle = (point.regionData.startAngle - 0.5 * Math.PI) + (point.regionData.endAngle - point.regionData.startAngle) / 2; if (labelIndex === 0) { columnRadius = point.regionData.radius < point.regionData.innerRadius ? point.regionData.innerRadius : point.regionData.radius; } else { columnRadius = point.regionData.radius > point.regionData.innerRadius ? point.regionData.innerRadius : point.regionData.radius; } this.fontBackground = this.fontBackground === 'transparent' ? this.chartBackground : this.fontBackground; if (series.drawType.indexOf('Stacking') > -1) { position = position === 'Outer' ? 'Top' : position; } else if (series.drawType.indexOf('Range') > -1) { position = (position === 'Outer' || position === 'Top') ? position : 'Auto'; } if (position === 'Outer') { columnRadius = labelIndex === 0 ? columnRadius + 2 * padding + this.markerHeight : columnRadius - 2 * padding - this.markerHeight; } else if (position === 'Middle') { columnRadius = columnRadius / 2 + padding; if (series.drawType === 'StackingColumn') { columnRadius = point.regionData.innerRadius + ((point.regionData.radius - point.regionData.innerRadius) / 2) + padding - (size.height / 2); } } else if (position === 'Top') { columnRadius = labelIndex === 0 ? columnRadius - 2 * padding - this.markerHeight : columnRadius + 2 * padding + this.markerHeight; } else if (position === 'Bottom') { columnRadius = 2 * padding; columnRadius += (series.drawType === 'StackingColumn') ? (point.regionData.innerRadius + this.markerHeight) : 0; } else { if (labelIndex === 0) { columnRadius = columnRadius >= series.chart.radius ? columnRadius - padding : series.drawType === 'StackingColumn' ? columnRadius - 2 * padding : columnRadius + 2 * padding; } else { columnRadius = columnRadius >= series.chart.radius ? columnRadius + padding : columnRadius - 2 * padding; } } columnRadius += (alignmentValue * alignmentSign); location.x = series.clipRect.width / 2 + series.clipRect.x + columnRadius * Math.cos(angle); // To change x location based on text anchor for column and stackingcolumn chart if (series.drawType === 'StackingColumn') { location.x = location.x < chartWidth / 2 ? location.x + size.width / 2 : (location.x > chartWidth / 2 ? location.x - size.width / 2 : location.x); } else if (series.drawType === 'Column') { location.x = location.x < chartWidth / 2 ? location.x - size.width / 2 : (location.x > chartWidth / 2 ? location.x + size.width / 2 : location.x); } location.y = series.clipRect.height / 2 + series.clipRect.y + columnRadius * Math.sin(angle); return location; }; /** * Gets the location for the data label. * * @param {Points} point - The data point associated with the label. * @param {Series} series - The series associated with the data label. * @param {Size} textSize - The size of the text to be displayed in the data label. * @param {number} labelIndex - The index of the data label. * @returns {ChartLocation} - The location for the data label. */ DataLabel.prototype.getLabelLocation = function (point, series, textSize, labelIndex) { var location = new ChartLocation(0, 0); var labelRegion = (series.type === 'Candle' && labelIndex > 1) ? point.regions[1] : point.regions[0]; if (series.type === 'HiloOpenClose') { labelRegion = (labelIndex === 2) ? point.regions[1] : point.regions[2]; } var xAxis = series.xAxis; var yAxis = series.yAxis; var isInverted = series.chart.requireInvertedAxis; if (series.type === 'BoxAndWhisker') { this.markerHeight = 0; switch (labelIndex) { case 0: location = getPoint(point.xValue, point.median, xAxis, yAxis, isInverted); break; case 1: location = getPoint(point.xValue, point.maximum, xAxis, yAxis, isInverted); break; case 2: location = getPoint(point.xValue, point.minimum, xAxis, yAxis, isInverted); break; case 3: location = getPoint(point.xValue, point.upperQuartile, xAxis, yAxis, isInverted); break; case 4: location = getPoint(point.xValue, point.lowerQuartile, xAxis, yAxis, isInverted); break; default: { location = getPoint(point.xValue, point.outliers[labelIndex - 5], xAxis, yAxis, isInverted); this.markerHeight = series.marker.height / 2; break; } } if (isInverted) { location.y = point.regions[0].y + (point.regions[0].height / 2); } else { location.x = point.regions[0].x + (point.regions[0].width / 2); } } else if (isInverted && series.type.indexOf('Stacking') > -1 && point.yValue === 0) { location = { x: labelRegion.x + labelRegion.width, y: labelRegion.y + (labelRegion.height) / 2 }; } else if (labelIndex === 0 || labelIndex === 1) { location = new ChartLocation(point.symbolLocations[0].x, point.symbolLocations[0].y); } else if ((labelIndex === 2 || labelIndex === 3) && series.type === 'Candle') { location = new ChartLocation(point.symbolLocations[1].x, point.symbolLocations[1].y); } else if (isInverted) { location = { x: labelRegion.x + (labelRegion.width) / 2, y: labelRegion.y }; } else { location = { x: labelRegion.x + labelRegion.width, y: labelRegion.y + (labelRegion.height) / 2 }; } //Aligning the label at the beginning of