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.

1,108 lines 108 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { Component, Property, NotifyPropertyChanges, Internationalization, remove, Complex, Collection, Browser, EventHandler, extend, Animation, animationMode } from '@syncfusion/ej2-base'; import { L10n, isNullOrUndefined, Touch } from '@syncfusion/ej2-base'; import { Event } from '@syncfusion/ej2-base'; import { Rect, Size, SvgRenderer, TextOption, measureText, removeElement } from '@syncfusion/ej2-svg-base'; import { ImageOption, RectOption, appendChildElement, createSvg, getElement, getTextAnchor, getTitle, redrawElement, textElement, titlePositionX, showTooltip, appendClipElement, getAnimationFunction, withInBounds } from '../common/utils/helper'; import { beforeResize, load, pointClick, pointMove, resized } from '../common/model/constants'; import { TitleSettings } from './model/chart3d-Interface'; import { CartesianAxisLayoutPanel } from './axis/cartesian-panel'; import { get3DSeriesColor, get3DThemeColor } from './model/theme'; import { Border, Indexes, Margin } from '../common/model/base'; import { Vector3D, Matrix3D, Graphics3D, BinaryTreeBuilder, Polygon3D, ChartTransform3D, Svg3DRenderer, Chart3DRender } from './utils/chart3dRender'; import { AxisRenderer, WallRenderer } from './utils/renderer'; import { Chart3DAxis, Chart3DColumn, Chart3DRow } from './axis/axis'; import { Data } from '../common/model/data'; import { Chart3DSeries } from './series/chart-series'; import { Chart3DTooltipSettings } from './user-interaction/tooltip'; import { Chart3DLegendSettings } from './legend/legend'; import { PrintUtils } from '../common/utils/print'; /** * The Chart3D class represents a 3D chart component that extends the Component class * and implements the INotifyPropertyChanged interface. * * @public * @class * @extends Component<HTMLElement> * @implements {INotifyPropertyChanged} INotifyPropertyChanged */ var Chart3D = /** @class */ (function (_super) { __extends(Chart3D, _super); /** * Constructor for creating the 3D chart * * @param {Chart3DModel} options - Specifies the 3D chart model. * @param {string | HTMLElement} element - Specifies the element for the 3D chart. * @hidden */ function Chart3D(options, element) { var _this = _super.call(this, options, element) || this; _this.previousTargetId = ''; _this.currentPointIndex = 0; _this.currentSeriesIndex = 0; _this.currentLegendIndex = 0; /** @public */ _this.animated = false; /** @private */ _this.isPointMouseDown = false; /** @private */ _this.visible = 0; /** @private */ _this.clickCount = 0; /** @private */ _this.maxPointCount = 0; /** @private */ _this.singleClickTimer = 0; /** @private */ _this.isRtlEnabled = false; /** @private */ _this.scaleX = 1; /** @private */ _this.scaleY = 1; _this.chartId = 57723; /** @private */ _this.isLegendClicked = false; /** @private */ _this.rotateActivate = false; /** @private */ _this.isRemove = false; /** @private */ _this.polygons = []; return _this; } /** * Checks if the given elementId has special characters and modifies it if necessary. * * @param {string} elementId - The input elementId to be checked. * @returns {string} - The modified elementId. */ Chart3D.prototype.isIdHasSpecialCharacter = function (elementId) { var regex = /^[A-Za-z ]+$/; var numberRegex = /^[0-9 ]+$/; var childElementId = ''; if (!regex.test(elementId)) { var start = 0; if (numberRegex.test(elementId[0])) { childElementId += ('\\3' + elementId[0]); start = 1; } for (var i = start; i < elementId.length; i++) { if (!regex.test(elementId[i]) && elementId.indexOf('-') === -1 && elementId.indexOf('_') === -1 && elementId.indexOf('\\') === -1 && !numberRegex.test(elementId[i])) { childElementId += ('\\' + elementId[i]); } else { childElementId += elementId[i]; } } return childElementId; } else { return elementId; } }; /** * For internal use only - Initialize the event handler; * * @returns {void} */ Chart3D.prototype.preRender = function () { this.element.id = this.isIdHasSpecialCharacter(this.element.id); this.allowServerDataBinding = false; this.unWireEvents(); this.initPrivateVariable(); this.setCulture(); this.wireEvents(); this.element.setAttribute('dir', this.enableRtl ? 'rtl' : ''); }; /** * Initializes private variables and prepares the chart component for rendering. * * @returns {void} */ Chart3D.prototype.initPrivateVariable = function () { this.delayRedraw = false; this.animateSeries = true; this.horizontalAxes = []; this.verticalAxes = []; this.polygons = []; this.vector = new Vector3D(0, 0, 0); this.wallRender = new WallRenderer(); this.matrixObj = new Matrix3D(); this.bspTreeObj = new BinaryTreeBuilder(); this.polygon = new Polygon3D(); this.graphics = new Graphics3D(); this.transform3D = new ChartTransform3D(); this.svg3DRenderer = new Svg3DRenderer(); this.axisRender = new AxisRenderer(); this.chart3DRender = new Chart3DRender(); this.chartAxisLayoutPanel = new CartesianAxisLayoutPanel(this); this.refreshAxis(); this.refreshDefinition(this.rows); this.refreshDefinition(this.columns); if (this.tooltip3DModule) { this.tooltip3DModule.previousPoints = []; } this.element.setAttribute('role', 'region'); this.element.setAttribute('tabindex', '0'); this.element.style.outline = 'none'; this.element.setAttribute('aria-label', this.description || this.title + '. Syncfusion interactive chart.'); if (!(this.element.classList.contains('e-chart-focused'))) { this.element.setAttribute('class', this.element.getAttribute('class') + ' e-chart-focused'); } if (this.element.id === '') { var collection = document.getElementsByClassName('e-chart').length; this.element.id = 'chart_' + this.chartId + '_' + collection; } this.svgId = this.element.id + '_svg'; }; /** * Method to set culture for chart. * * @returns {void} */ Chart3D.prototype.setCulture = function () { this.intl = new Internationalization(); this.localeObject = new L10n(this.getModuleName(), this.defaultLocalConstants, this.locale); }; /** * To Initialize the 3D chart rendering. * * @returns {void} */ Chart3D.prototype.render = function () { var _this = this; this.svgRenderer = new SvgRenderer(this.element.id); var loadEventData = { chart: this, theme: this.theme, cancel: false }; /** * Load event for the 3D chart componet. */ this.trigger(load, loadEventData, function () { if (!loadEventData.cancel) { _this.cartesianChartRendering(); } }); }; /** * Renders the chart using a Cartesian coordinate system. * * This function is responsible for rendering the chart's graphical elements and data points using a Cartesian coordinate system. * It may include actions such as drawing axes, plotting data, and applying visual styles. * * @returns {void} */ Chart3D.prototype.cartesianChartRendering = function () { this.setTheme(); this.createChartSvg(); this.calculateVisibleSeries(); this.calculateVisibleAxis(); this.processData(); this.renderComplete(); this.allowServerDataBinding = true; }; /** * Method to create SVG element. * * @returns {void} */ Chart3D.prototype.createChartSvg = function () { this.removeSvg(); createSvg(this); }; /** * Method to remove the SVG. * * @returns {void} * @private */ Chart3D.prototype.removeSvg = function () { if (this.redraw) { return null; } removeElement(this.element.id + '_Secondary_Element'); // eslint-disable-next-line @typescript-eslint/no-explicit-any if (this.isReact) { this.clearTemplate(); } var removeLength = 0; if (this.svgObject) { while (this.svgObject.childNodes.length > removeLength) { this.svgObject.removeChild(this.svgObject.firstChild); } if (!this.svgObject.hasChildNodes() && this.svgObject.parentNode) { remove(this.svgObject); } } }; /** * Processes and prepares data for rendering. * * @param {boolean} render - (Optional) Indicates whether to trigger rendering after data processing. * @returns {void} */ Chart3D.prototype.processData = function (render) { if (render === void 0) { render = true; } this.visibleSeriesCount = 0; var check = true; for (var _i = 0, _a = this.visibleSeries; _i < _a.length; _i++) { var series = _a[_i]; if (!series.visible && !this.legendSettings.visible) { this.visibleSeriesCount++; continue; } this.initializeDataModule(series); } if (render && (!this.visibleSeries.length || this.visibleSeriesCount === this.visibleSeries.length && check)) { this.refreshBound(); this.trigger('loaded', { chart: this }); } }; /** * Initializes the data module for a three-dimensional series. * * @param {Chart3DSeries} series - The series for which data module is initialized. * @returns {void} */ Chart3D.prototype.initializeDataModule = function (series) { series.xData = []; series.yData = []; var dataSource; var isAngular = 'isAngular'; if (this[isAngular]) { dataSource = Object.keys(series.dataSource).length ? series.dataSource : this.dataSource; } else { dataSource = series.dataSource || this.dataSource; } series.dataModule = new Data(dataSource, series.query); series.points = []; series.refreshDataManager(this); }; /** * Animate the series bounds. * * @param {number} duration - Specifies the duration of the animation. * @private * @returns {void} */ Chart3D.prototype.animate = function (duration) { this.redraw = true; this.animated = true; //used to set duration as 1000 for animation at default 300 this.duration = duration ? duration : 1000; }; /** * Refresh the chart bounds. * * @private * @returns {void} */ Chart3D.prototype.refreshBound = function () { if (this.legend3DModule && this.legendSettings.visible) { this.legend3DModule.getLegendOptions(this.visibleSeries, this); } if (this.tooltip.enable && this.tooltip3DModule) { this.tooltip3DModule.previousPoints = []; } this.calculateStackValues(); this.calculateBounds(); this.renderElements(); removeElement('chartmeasuretext'); this.removeSelection(); }; /** * Clears the selection state in the chart. * * @returns {void} */ Chart3D.prototype.removeSelection = function () { for (var _i = 0, _a = this.visibleSeries; _i < _a.length; _i++) { var series = _a[_i]; if (series.visible) { for (var _b = 0, _c = series.points; _b < _c.length; _b++) { var point = _c[_b]; point.isSelect = false; } } } }; /** * Calculates stacked values for three-dimensional series in the chart. * * @returns {void} */ Chart3D.prototype.calculateStackValues = function () { var series; var isCalculateStacking = false; for (var i = 0, len = this.visibleSeries.length; i < len; i++) { series = this.visibleSeries[i]; series.position = series.rectCount = undefined; if (((series.type.indexOf('Stacking') !== -1)) && !isCalculateStacking) { series.calculateStackedValue(series.type.indexOf('100') > -1, this); isCalculateStacking = true; } } }; /** * Calculates the bounds and dimensions for the chart area. * * @returns {void} */ Chart3D.prototype.calculateBounds = function () { var margin = this.margin; // Title Height; var titleHeight = 0; var subTitleHeight = 0; var titleWidth = 0; var padding = this.titleStyle.position === 'Top' || (this.titleStyle.position === 'Bottom') ? 15 : 5; var left = margin.left + this.border.width; var width = this.availableSize.width - left - margin.right - this.border.width; var elementSpacing = 0; var top = margin.top + this.border.width; var height = this.availableSize.height - top - this.border.width - margin.bottom; this.titleCollection = []; this.subTitleCollection = []; if (this.title) { this.titleCollection = getTitle(this.title, this.titleStyle, (this.titleStyle.position === 'Left' || this.titleStyle.position === 'Right' ? height : width), this.enableRtl, this.themeStyle.chartTitleFont); titleHeight = (measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont).height * this.titleCollection.length) + padding; if (this.subTitle) { var maxWidth = 0; for (var _i = 0, _a = this.titleCollection; _i < _a.length; _i++) { var titleText = _a[_i]; titleWidth = measureText(titleText, this.titleStyle, this.themeStyle.chartSubTitleFont).width; maxWidth = titleWidth > maxWidth ? titleWidth : maxWidth; } this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, maxWidth, this.enableRtl, this.themeStyle.chartSubTitleFont); subTitleHeight = (measureText(this.subTitle, this.subTitleStyle, this.themeStyle.chartSubTitleFont).height * this.subTitleCollection.length) + padding; } } else if (this.legendSettings.position !== 'Top' && this.border.width) { elementSpacing = 10; } top = margin.top + elementSpacing + this.border.width; height = this.availableSize.height - top - this.border.width - margin.bottom; var marginTotal = subTitleHeight + titleHeight + this.titleStyle.border.width + this.subTitleStyle.border.width; switch (this.titleStyle.position) { case 'Top': top += marginTotal; height -= marginTotal; break; case 'Bottom': height -= marginTotal; break; case 'Left': left += marginTotal; width -= marginTotal; break; case 'Right': left -= (this.titleStyle.border.width + this.subTitleStyle.border.width); width -= marginTotal; break; } this.initialClipRect = new Rect(left, top, width, height); if (this.legend3DModule && this.legendSettings.visible) { this.legend3DModule.calculateLegendBounds(this.initialClipRect, this.availableSize, null); } this.chartAxisLayoutPanel.measureAxis(this.initialClipRect); }; /** * Renders various chart elements, including the border, title, series, legend, and datalabel etc. * * @returns {void} */ Chart3D.prototype.renderElements = function () { this.renderBorder(); this.renderTitle(); this.createSeriesElements(); this.render3DChart(); this.renderLegend(); this.performSelection(); this.setSecondaryElementPosition(); this.doAnimation(); }; /** * Animates the height of an SVG element. * * @param {HTMLElement} element - The SVG element to animate. * @param {Chart3DSeries} series - The series related to the animation. * @param {Chart3DPoint} point - The point related to the animation. * @param {HTMLElement} dataLabelElement - The data label element related to the animation. * @param {HTMLElement} shapeElement - The shape element related to the animation. * @param {HTMLElement} templateElement - The template element related to the animation. * @returns {void} */ Chart3D.prototype.animateRect = function (element, series, point, dataLabelElement, shapeElement, templateElement) { var option = series.animation; var duration = series.chart.animated ? series.chart.duration : option.duration; var effect = getAnimationFunction('Linear'); var elementHeight = element.getAttribute('height') ? +element.getAttribute('height') : 0; var elementWidth = element.getAttribute('width') ? +element.getAttribute('width') : 0; var isPlot = point.yValue < 0; var centerX; var centerY; var x = +element.getAttribute('x'); var y = +element.getAttribute('y'); if (!series.chart.requireInvertedAxis) { centerY = (isPlot !== series.yAxis.isAxisInverse) ? y : y + elementHeight; centerX = isPlot ? x : x + elementWidth; } else { if (series.type.indexOf('Stacking') > -1) { centerX = x; centerY = y; } else { centerY = isPlot ? y : y + elementHeight; centerX = (isPlot !== series.yAxis.isAxisInverse) ? x + elementWidth : x; } } var value; if (!isNullOrUndefined(element)) { element.style.visibility = 'hidden'; if (dataLabelElement) { dataLabelElement.style.visibility = 'hidden'; } if (shapeElement) { shapeElement.style.visibility = 'hidden'; } if (templateElement) { templateElement.style.visibility = 'hidden'; } new Animation({}).animate(element, { duration: (duration === 0 && animationMode === 'Enable') ? 1000 : duration, delay: option.delay, progress: function (args) { if (args.timeStamp >= args.delay) { element.style.visibility = 'visible'; if (!series.chart.requireInvertedAxis) { elementHeight = elementHeight ? elementHeight : 1; value = effect(args.timeStamp - args.delay, 0, elementHeight, args.duration); element.setAttribute('transform', 'translate(' + centerX + ' ' + centerY + ') scale(1,' + (value / elementHeight) + ') translate(' + (-centerX) + ' ' + (-centerY) + ')'); } else { elementWidth = elementWidth ? elementWidth : 1; value = effect(args.timeStamp - args.delay, 0, elementWidth, args.duration); element.setAttribute('transform', 'translate(' + centerX + ' ' + centerY + ') scale(' + (value / elementWidth) + ', 1) translate(' + (-centerX) + ' ' + (-centerY) + ')'); } } }, end: function () { element.setAttribute('transform', 'translate(0,0)'); if (dataLabelElement) { dataLabelElement.style.visibility = 'visible'; } if (shapeElement) { shapeElement.style.visibility = 'visible'; } if (templateElement) { templateElement.style.visibility = 'visible'; } series.chart.trigger('animationComplete', { series: series }); } }); } }; /** * Animates the series. * * @returns {void} */ Chart3D.prototype.doAnimation = function () { var _this = this; var _loop_1 = function (i) { var series = this_1.visibleSeries[i]; if (series.visible && series.animation.enable && this_1.animateSeries && !this_1.rotateActivate) { var dataLabelElement = void 0; var shapeElement = void 0; var templateElement = void 0; var options = new RectOption(this_1.element.id + '_ChartSeriesClipRect_' + i, 'transparent', { width: 1, color: 'Gray' }, 1, { x: 0, y: 0, width: this_1.availableSize.width, height: this_1.availableSize.height }); var clipRectElement = appendClipElement(this_1.redraw, options, this_1.svgRenderer); appendChildElement(false, this_1.chart3D, clipRectElement.children[0], this_1.redraw); for (var k = 0; series.visiblePoints && k < series.visiblePoints.length; k++) { var point = series.visiblePoints[k]; var elements = document.querySelectorAll("[id*=\"region-series-" + i + "\"]"); elements.forEach(function (element) { element.setAttribute('clip-path', 'url(#' + _this.element.id + '_ChartSeriesClipRect_' + i + ')'); }); if (series.dataLabel.visible) { dataLabelElement = getElement(this_1.element.id + '-svg-series-' + series.index + '-point-' + k + '-data-label'); shapeElement = getElement(this_1.element.id + '-svg-data-label-series-' + series.index + '-point-' + k); templateElement = getElement(this_1.element.id + '-series-' + series.index + '-data-label-' + k); } this_1.animateRect(document.getElementById(this_1.element.id + '_ChartSeriesClipRect_' + i).children[0], series, point, dataLabelElement, shapeElement, templateElement); } } }; var this_1 = this; for (var i = 0; i < this.visibleSeries.length; i++) { _loop_1(i); } }; /** * Performs data selection based on selected data indexes. * * @returns {void} */ Chart3D.prototype.performSelection = function () { var selectedDataIndexes = []; if (this.selection3DModule) { selectedDataIndexes = extend([], this.selection3DModule.selectedDataIndexes, null, true); this.selection3DModule.invokeSelection(this); } if (this.highlight3DModule) { this.highlight3DModule.invokeHighlight(this); } if ((!this.highlight3DModule || (this.legendSettings.enableHighlight && this.highlightMode === 'None')) && this.tooltip3DModule) { this.tooltip3DModule.seriesStyles(); } if (selectedDataIndexes.length > 0) { this.selection3DModule.selectedDataIndexes = selectedDataIndexes; this.selection3DModule.redrawSelection(this, this.selectionMode); } }; /** * To render the legend. * * @returns {void} */ Chart3D.prototype.renderLegend = function () { if (this.legend3DModule && this.legend3DModule.legendCollections.length && this.legendSettings.visible) { this.legend3DModule.calTotalPage = true; var bounds = this.legend3DModule.legendBounds; this.legend3DModule.renderLegend(this, this.legendSettings, bounds); } if (!this.redraw) { this.element.appendChild(this.svgObject); } }; /** * To set the left and top position for secondary element in chart. * * @returns {void} */ Chart3D.prototype.setSecondaryElementPosition = function () { var element = getElement(this.element.id + '_Secondary_Element'); if (!element) { return; } var rect = this.element.getBoundingClientRect(); var svgRect = getElement(this.svgId).getBoundingClientRect(); element.style.left = Math.max(svgRect.left - rect.left, 0) + 'px'; element.style.top = Math.max(svgRect.top - rect.top, 0) + 'px'; }; /** * Initializes module-specific elements and settings for the chart. * * @returns {void} */ Chart3D.prototype.initializeModuleElements = function () { this.dataLabelCollections = []; var elementId = this.element.id; if (this.series.length) { this.seriesElements = this.svgRenderer.createGroup({ id: elementId + 'SeriesCollection' }); } this.dataLabelElements = this.renderer.createGroup({ id: elementId + 'DataLabelCollection' }); }; /** * Renders elements specific to chart series. * * @returns {void} */ Chart3D.prototype.createSeriesElements = function () { // Initialize the series elements values this.initializeModuleElements(); var elementId = this.element.id; var tooltipDiv = redrawElement(this.redraw, elementId + '_Secondary_Element') || this.createElement('div'); tooltipDiv.id = elementId + '_Secondary_Element'; tooltipDiv.style.cssText = 'position: relative'; appendChildElement(false, this.element, tooltipDiv, this.redraw); // For userInteraction if (this.tooltip.enable) { appendChildElement(false, this.svgObject, this.renderer.createGroup({ id: elementId + '_UserInteraction', style: 'pointer-events:none;' }), this.redraw); } }; /** * Renders the chart title. * * @returns {void} */ Chart3D.prototype.renderTitle = function () { var rect; var margin = this.margin; var elementSpacing = 5; if (this.title) { var getAnchor = getTextAnchor(this.titleStyle.textAlignment, this.enableRtl); var elementSize = measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont); rect = new Rect(margin.left, 0, this.availableSize.width - margin.left - margin.right, 0); var borderWidth = this.titleStyle.border.width; var positionY = this.margin.top + ((elementSize.height) * 3 / 4); var positionX = titlePositionX(rect, this.titleStyle || this.themeStyle.chartTitleFont) + borderWidth; var rotation = void 0; var alignment = this.titleStyle.textAlignment; var subtitleSize = measureText(this.subTitle, this.subTitleStyle, this.themeStyle.chartSubTitleFont); switch (this.titleStyle.position) { case 'Top': positionY += borderWidth * 0.5; positionX += getAnchor === 'start' ? borderWidth * 0.5 + this.border.width : getAnchor === 'end' ? ((-borderWidth * 2) - this.border.width) : 0; break; case 'Bottom': positionX += getAnchor === 'start' ? (borderWidth * 0.5) + this.border.width : getAnchor === 'end' ? (-borderWidth * 2) - this.border.width : 0; positionY = this.availableSize.height - this.margin.bottom - subtitleSize.height - (elementSize.height / 2) - (borderWidth * 0.5) - (this.subTitleStyle.border.width * 0.5); break; case 'Left': positionX = this.margin.left + ((elementSize.height) * 3 / 4) + (borderWidth * 0.5); positionY = alignment === 'Near' ? margin.bottom + (borderWidth * 0.5) + this.border.width : alignment === 'Far' ? this.availableSize.height - margin.bottom - (borderWidth * 0.5) - this.border.width : this.availableSize.height / 2; getAnchor = alignment === 'Near' ? 'end' : alignment === 'Far' ? 'start' : 'middle'; getAnchor = this.enableRtl ? (getAnchor === 'end' ? 'start' : getAnchor === 'start' ? 'end' : getAnchor) : getAnchor; rotation = 'rotate(' + -90 + ',' + positionX + ',' + positionY + ')'; break; case 'Right': positionX = this.availableSize.width - this.margin.right - ((elementSize.height) * 3 / 4) - (borderWidth * 0.5); positionY = alignment === 'Near' ? margin.bottom + (borderWidth * 0.5) + this.border.width : alignment === 'Far' ? this.availableSize.height - margin.bottom - (borderWidth * 0.5) - this.border.width : this.availableSize.height / 2; getAnchor = alignment === 'Near' ? 'start' : alignment === 'Far' ? 'end' : 'middle'; getAnchor = this.enableRtl ? (getAnchor === 'end' ? 'start' : getAnchor === 'start' ? 'end' : getAnchor) : getAnchor; rotation = 'rotate(' + 90 + ',' + positionX + ',' + positionY + ')'; break; case 'Custom': positionX = this.titleStyle.x; positionY = this.titleStyle.y; getAnchor = 'middle'; break; } var borderOptions = { 'id': this.element.id + '-chart-title-border', 'x': positionX - (getAnchor === 'middle' ? (elementSize.width / 2) + elementSpacing : getAnchor === 'end' ? elementSize.width + elementSpacing : elementSpacing), 'y': positionY - elementSize.height + (elementSize.height / 4), 'rx': this.titleStyle.border.cornerRadius, 'ry': this.titleStyle.border.cornerRadius, 'width': elementSize.width + (elementSpacing * 2), 'height': elementSize.height * this.titleCollection.length, 'fill': this.titleStyle.background, 'stroke-width': borderWidth, 'stroke': this.titleStyle.border.color, 'transform': rotation ? rotation : '' }; var htmlObject = redrawElement(this.redraw, this.element.id + '-chart-title-border', borderOptions, this.renderer) || this.renderer.drawRectangle(borderOptions); appendChildElement(false, this.svgObject, htmlObject, this.redraw); var options = new TextOption(this.element.id + '-chart-title', positionX, positionY, getAnchor, this.titleCollection, rotation, 'auto'); var element = redrawElement(this.redraw, this.element.id + '-chart-title', options, this.renderer) || textElement(this.renderer, options, this.titleStyle, this.titleStyle.color || this.themeStyle.chartTitleFont.color, this.svgObject, null, null, null, null, null, null, null, null, false, null, this.themeStyle.chartTitleFont); if (element) { element.setAttribute('tabindex', '0'); element.style.outline = 'none'; element.setAttribute('class', 'e-chart-focused'); } if (this.subTitle) { this.renderSubTitle(options); } } }; /** * Renders the chart sub title. * * @param {TextOption} options - Specifies the text option. * @returns {void} */ Chart3D.prototype.renderSubTitle = function (options) { var maxWidth = 0; var titleWidth = 0; var padding = 10; var alignment = this.titleStyle.textAlignment; for (var _i = 0, _a = this.titleCollection; _i < _a.length; _i++) { var titleText = _a[_i]; titleWidth = measureText(titleText, this.titleStyle, this.themeStyle.chartTitleFont).width; maxWidth = titleWidth > maxWidth ? titleWidth : maxWidth; } var subTitleElementSize = measureText(this.subTitleCollection. reduce(function (a, b) { return (a.length > b.length ? a : b); }), this.subTitleStyle, this.themeStyle.chartSubTitleFont); var getAnchor = getTextAnchor(this.subTitleStyle.textAlignment, this.enableRtl); var rect = new Rect(alignment === 'Center' ? (options.x - maxWidth * 0.5) : alignment === 'Far' ? options.x - maxWidth : options.x, 0, maxWidth, 0); if (this.titleStyle.position === 'Left') { rect.x = alignment === 'Center' ? (options.x - maxWidth * 0.5) : alignment === 'Far' ? this.margin.left + ((subTitleElementSize.height) * 3 / 4) : (options.x - maxWidth); } var elementSize = measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont); var positionY = options.y * options.text.length + subTitleElementSize.height + (padding / 2) + this.titleStyle.border.width + (this.subTitleStyle.border.width * 0.5); if (this.titleStyle.position === 'Bottom') { positionY = options.y * options.text.length + (padding / 2) + (elementSize.height / 2) + (subTitleElementSize.height / 2); } var borderOptions = { 'id': this.element.id + '-chart-sub-title-border', 'x': titlePositionX(rect, this.subTitleStyle) - (getAnchor === 'middle' ? (subTitleElementSize.width / 2) + padding / 2 : getAnchor === 'end' ? subTitleElementSize.width + padding / 2 : padding / 2), 'y': positionY - subTitleElementSize.height + (subTitleElementSize.height / 4), 'rx': this.subTitleStyle.border.cornerRadius, 'ry': this.subTitleStyle.border.cornerRadius, 'width': subTitleElementSize.width + padding, 'height': subTitleElementSize.height * this.subTitleCollection.length, 'fill': this.subTitleStyle.background, 'stroke-width': this.subTitleStyle.border.width, 'stroke': this.subTitleStyle.border.color, 'transform': options.transform }; var htmlObject = redrawElement(this.redraw, this.element.id + '-chart-sub-title-border', borderOptions, this.renderer) || this.renderer.drawRectangle(borderOptions); appendChildElement(false, this.svgObject, htmlObject, this.redraw); var subTitleOptions = new TextOption(this.element.id + '-chart-sub-title', titlePositionX(rect, this.subTitleStyle), positionY, getTextAnchor(this.subTitleStyle.textAlignment, this.enableRtl), this.subTitleCollection, options.transform, 'auto'); var element = redrawElement(this.redraw, this.element.id + '-chart-sub-title', subTitleOptions, this.renderer) || textElement(this.renderer, subTitleOptions, this.subTitleStyle, this.subTitleStyle.color || this.themeStyle.chartSubTitleFont.color, this.svgObject, null, null, null, null, null, null, null, null, false, null, this.themeStyle.chartSubTitleFont); }; /** * Renders the chart border. * * @returns {void} */ Chart3D.prototype.renderBorder = function () { var x = 0; var y = 0; var width = this.border.width; var backGroundImage = this.backgroundImage; var fillColor = backGroundImage ? 'transparent' : (this.background || this.themeStyle.background); var rect = new RectOption(this.element.id + '-chart-border', fillColor, this.border, 1, new Rect(width * 0.5 + x, width * 0.5 + y, this.availableSize.width - width, this.availableSize.height - width), 0, 0, '', this.border.dashArray); this.htmlObject = redrawElement(this.redraw, this.element.id + '-chart-border', rect, this.renderer) || this.renderer.drawRectangle(rect); this.htmlObject.setAttribute('aria-hidden', 'true'); appendChildElement(false, this.svgObject, this.htmlObject, this.redraw); // to draw back ground image for chart if (backGroundImage) { var image = new ImageOption(this.availableSize.height - width, this.availableSize.width - width, backGroundImage, 0, 0, this.element.id + '-chart-background', 'visible', 'none'); this.htmlObject = redrawElement(this.redraw, this.element.id + '-chart-background', image, this.renderer) || this.renderer.drawImage(image); appendChildElement(false, this.svgObject, this.htmlObject, this.redraw); } }; /** * To provide the array of modules needed for control rendering * * @returns {ModuleDeclaration[]} - Array of modules needed for control rendering * @private */ Chart3D.prototype.requiredModules = function () { var _this = this; var modules = []; var series = this.series; var moduleName; var dataLabelEnable = false; if (this.tooltip.enable) { modules.push({ member: 'Tooltip3D', args: [this] }); } series.map(function (value) { _this.isLegend = (_this.legendSettings.visible && ((value.name !== '') || !!_this.isLegend)); moduleName = value.type.indexOf('100') !== -1 ? value.type.replace('100', '') + 'Series3D' : value.type + 'Series3D'; dataLabelEnable = value.dataLabel.visible || dataLabelEnable; if (!modules.some(function (currentModule) { return currentModule.member === moduleName; })) { modules.push({ member: moduleName, args: [_this, series] }); } }); if (dataLabelEnable) { modules.push({ member: 'DataLabel3D', args: [this, series] }); } modules = this.findAxisModule(modules); if (this.isLegend) { modules.push({ member: 'Legend3D', args: [this] }); } if (this.enableExport) { modules.push({ member: 'Export3D', args: [this] }); } if (this.selectionMode !== 'None') { modules.push({ member: 'Selection3D', args: [this] }); } if (this.highlightMode !== 'None' || this.legendSettings.enableHighlight) { modules.push({ member: 'Highlight3D', args: [this] }); } return modules; }; /** * Finds axis modules within a collection of module declarations. * * @param {ModuleDeclaration[]} modules - The collection of module declarations to search for axis modules. * @returns {ModuleDeclaration[]} - An array of module declarations representing axis modules. */ Chart3D.prototype.findAxisModule = function (modules) { var axisCollections = []; axisCollections.push(this.primaryXAxis); axisCollections.push(this.primaryYAxis); axisCollections = axisCollections.concat(this.axes); var datetimeEnabled = false; var categoryEnabled = false; var logarithmicEnabled = false; var dateTimeCategoryEnabled = false; for (var _i = 0, axisCollections_1 = axisCollections; _i < axisCollections_1.length; _i++) { var axis = axisCollections_1[_i]; datetimeEnabled = axis.valueType === 'DateTime' || datetimeEnabled; categoryEnabled = axis.valueType === 'Category' || categoryEnabled; logarithmicEnabled = axis.valueType === 'Logarithmic' || logarithmicEnabled; dateTimeCategoryEnabled = axis.valueType === 'DateTimeCategory' || dateTimeCategoryEnabled; } if (datetimeEnabled) { modules.push({ member: 'DateTime3D', args: [this] }); } if (categoryEnabled) { modules.push({ member: 'Category3D', args: [this] }); } if (logarithmicEnabled) { modules.push({ member: 'Logarithmic3D', args: [this] }); } if (dateTimeCategoryEnabled) { modules.push({ member: 'DateTimeCategory3D', args: [this] }); } return modules; }; /** * Sets the theme for the chart. * * @returns {void} */ Chart3D.prototype.setTheme = function () { /** Set theme */ this.themeStyle = get3DThemeColor(this.theme); }; /** * Handles to set style for key event on the document. * * @param {target} target - element which currently focused. * @returns {void} * @private */ Chart3D.prototype.setNavigationStyle = function (target) { var currentElement = document.getElementById(target); if (currentElement) { currentElement.style.setProperty('outline', "1.5px solid " + this.themeStyle.tabColor); } }; /** * Handles to remove style for key event on the document. * * @returns {void} * @private */ Chart3D.prototype.removeNavigationStyle = function () { var currentElement = document.querySelectorAll("[id*=_Point_], [id*=" + this.element.id + "], [id*=_ChartBorder], text[id*=_ChartTitle],g[id*=_chart_legend], text[id*=_ChartSubTitle], div[id*=_Annotation]"); if (currentElement) { currentElement.forEach(function (element) { if (element instanceof HTMLElement || element instanceof SVGElement) { element.style.setProperty('outline', 'none'); } }); } }; /** * Renders the three-dimensional chart, creating a 3D visualization. * * The function sets up a 3D perspective, depth, rotation, and tilt to create a 3D visualization of the chart. * * @returns {void} */ Chart3D.prototype.render3DChart = function () { this.chart3D = this.svgRenderer.createGroup({ 'id': this.element.id + '-svg-chart-3d' }); this.chart3D.setAttribute('role', 'region'); this.chart3D.setAttribute('aria-hidden', 'false'); this.draw3DAxis(); this.wallRender.update3DWall(this); this.renderSeries(); appendChildElement(false, this.svgObject, this.chart3D, this.redraw); var size = new Size(this.availableSize.width, this.availableSize.height); this.graphics.prepareView(this.perspectiveAngle, this.depth, this.rotation, this.tilt, size, this); this.graphics.view(this.svgObject, this); }; /** * Draws three-dimensional axes for the chart. * * @returns {void} */ Chart3D.prototype.draw3DAxis = function () { for (var i = 0; i < this.axisCollections.length; i++) { this.axisRender.drawAxes(i, this.axisCollections[i], this); } }; /** * Renders chart series elements. * * @private * @returns {void} */ Chart3D.prototype.renderSeries = function () { var visibility; for (var _i = 0, _a = this.visibleSeries; _i < _a.length; _i++) { var item = _a[_i]; visibility = item.visible; if (visibility) { this.visible++; item.renderSeries(this); } } this.visible = 0; }; /** * Initializes the configuration for an axis within a three-dimensional chart series. * * @param {Chart3DSeries} series - The series to which the axis belongs. * @param {Chart3DAxis} axis - The axis to be configured and initialized. * @param {boolean} isSeries - Indicates whether the axis configuration is for the series. * @returns {void} */ Chart3D.prototype.initAxis = function (series, axis, isSeries) { if (series.xAxisName === axis.name || (series.xAxisName == null && axis.name === 'primaryXAxis')) { axis.orientation = this.requireInvertedAxis ? 'Vertical' : 'Horizontal'; series.xAxis = axis; if (isSeries) { axis.series.push(series); } } else if (series.yAxisName === axis.name || (series.yAxisName == null && axis.name === 'primaryYAxis')) { axis.orientation = this.requireInvertedAxis ? 'Horizontal' : 'Vertical'; series.yAxis = axis; if (isSeries) { axis.series.push(series); } } }; /** * Calculate the visible axis. * * @private * @returns {void} */ Chart3D.prototype.calculateVisibleAxis = function () { var axis; var axes = [this.primaryXAxis, this.primaryYAxis]; axes = axes.concat(this.axes); this.axisCollections = []; for (var i = 0, len = axes.length; i < len; i++) { axis = axes[i]; axis.series = []; axis.labels = []; axis.indexLabels = {}; axis.orientation = (i === 0) ? (this.requireInvertedAxis ? 'Vertical' : 'Horizontal') : (i === 1) ? (this.requireInvertedAxis ? 'Horizontal' : 'Vertical') : axis.orientation; for (var _i = 0, _a = this.visibleSeries; _i < _a.length; _i++) { var series = _a[_i]; this.initAxis(series, axis, true); } if (axis.orientation != null) { this.axisCollections.push(axis); } } if (this.rows.length > 0 && this.columns.length > 0) { this.chartAxisLayoutPanel.measure(); } }; /** * Unbinding events from the element while component destroy. * * @hidden * @returns {void} */ Chart3D.prototype.unWireEvents = function () { var startEvent = Browser.touchStartEvent; var moveEvent = Browser.touchMoveEvent; var stopEvent = Browser.touchEndEvent; var cancelEvent = Browser.isPointer ? 'pointerleave' : 'mouseleave'; /** UnBind the Event handler */ EventHandler.remove(this.element, startEvent, this.chartOnMouseDown); EventHandler.remove(this.element, moveEvent, this.mouseMove); EventHandler.remove(this.element, stopEvent, this.mouseEnd); EventHandler.remove(this.element, 'click', this.chartOnMouseClick); EventHandler.remove(this.element, cancelEvent, this.mouseLeave); EventHandler.remove(this.element, 'keydown', this.chartKeyDown); EventHandler.remove(document.body, 'keydown', this.documentKeyHandler); EventHandler.remove(this.element, 'keyup', this.chartKeyUp); window.removeEventListener((Browser.isTouch && ('orientation' in window && 'onorientationchange' in window)) ? 'orientationchange' : 'resize', this.resizeBound); /** * To fix memory issue */ if (this.touchObject) { this.touchObject.destroy(); this.touchObject = null; } }; /** * Binding events to the element while component creation. * * @hidden * @returns {void} */ Chart3D.prototype.wireEvents = function () { /** * To fix react timeout destroy issue. */ if (!this.element) { return; } /** Find the Events type */ var cancelEvent = Browser.isPointer ? 'pointerleave' : 'mouseleave'; /** Bind the Event handler */ EventHandler.add(this.element, Browser.touchStartEvent, this.chartOnMouseDown, this); EventHandler.add(this.element, Browser.touchMoveEvent, this.mouseMove, this); EventHandler.add(this.element, Browser.touchEndEvent, this.mouseEnd, this); EventHandler.add(this.element, 'click', this.chartOnMouseClick, this); EventHandler.add(this.element, cancelEvent, this.mouseLeave, this); EventHandler.add(this.element, 'keydown', this.chartKeyDown, this); EventHandler.add(do