@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.
483 lines (482 loc) • 25.6 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 __());
};
})();
import { valueToCoefficient, textElement, firstToLowerCase, withIn } from '../../common/utils/helper';
import { PathOption, Rect, measureText, TextOption } from '@syncfusion/ej2-svg-base';
import { DateTime } from '../../chart/axis/date-time-axis';
import { VisibleLabels } from '../../chart/axis/axis';
/**
* class for axis
*/
var RangeNavigatorAxis = /** @class */ (function (_super) {
__extends(RangeNavigatorAxis, _super);
function RangeNavigatorAxis(range) {
var _this = _super.call(this) || this;
_this.firstLevelLabels = [];
_this.secondLevelLabels = [];
_this.rangeNavigator = range;
return _this;
}
/**
* To render grid lines of axis.
*
* @returns {void}
*/
RangeNavigatorAxis.prototype.renderGridLines = function () {
var pointX = 0;
var control = this.rangeNavigator;
var majorGridLines = control.majorGridLines;
var majorTickLines = control.majorTickLines;
var majorGrid = '';
var majorTick = '';
var rect = control.bounds;
var chartAxis = control.chartSeries.xAxis;
var disabledColor = (control.disableRangeSelector) ? 'transparent' : null;
this.gridLines = control.renderer.createGroup({ id: control.element.id + '_GridLines' });
var tick = (control.tickPosition === 'Outside' || control.series.length === 0) ?
rect.y + rect.height + majorTickLines.height : rect.y + rect.height - majorTickLines.height;
//Gridlines
this.firstLevelLabels = [];
chartAxis.labelStyle = control.labelStyle;
chartAxis.skeleton = control.skeleton;
chartAxis.skeletonType = control.skeletonType;
chartAxis.isChart = false;
if (control.valueType.indexOf('DateTime') > -1) {
var interval = this.calculateDateTimeNiceInterval(chartAxis, rect, chartAxis.doubleRange.start, chartAxis.doubleRange.end, chartAxis.isChart);
if (control.valueType === 'DateTime') {
this.findAxisLabels(chartAxis, interval);
}
this.actualIntervalType = chartAxis.actualIntervalType;
if (control.valueType === 'DateTimeCategory' && (this.actualIntervalType === 'Quarter' || this.actualIntervalType === 'Weeks')) {
this.findSecondaryAxisLabels(chartAxis);
}
}
this.firstLevelLabels = chartAxis.visibleLabels;
this.lowerValues = [];
var labelLength = chartAxis.visibleLabels.length;
for (var i = 0; i < labelLength; i++) {
this.lowerValues.push(this.firstLevelLabels[i].value);
pointX = (valueToCoefficient(this.firstLevelLabels[i].value, chartAxis) * rect.width) + rect.x;
if (pointX >= rect.x && (rect.x + rect.width) >= pointX) {
majorGrid = majorGrid.concat('M ' + pointX + ' ' + (control.bounds.y + control.bounds.height) +
' L ' + pointX + ' ' + control.bounds.y + ' ');
majorTick = majorTick.concat('M ' + (pointX) + ' ' + (rect.y + rect.height) +
' L ' + (pointX) + ' ' + tick + ' ');
}
}
var options = new PathOption(control.element.id + '_MajorGridLine', 'transparent', majorGridLines.width, control.series.length ? disabledColor || majorGridLines.color || control.themeStyle.gridLineColor : 'transparent', 1, majorGridLines.dashArray, majorGrid);
this.gridLines.appendChild(control.renderer.drawPath(options));
options = new PathOption(control.element.id + '_MajorTickLine', 'transparent', majorTickLines.width, disabledColor || majorTickLines.color || control.themeStyle.gridLineColor, 1, majorGridLines.dashArray, majorTick);
this.gridLines.appendChild(control.renderer.drawPath(options));
};
/**
* To render of axis labels.
*
* @returns {void}
*/
RangeNavigatorAxis.prototype.renderAxisLabels = function () {
var axis = this.rangeNavigator.chartSeries.xAxis;
var control = this.rangeNavigator;
var pointY;
var labelElement = control.renderer.createGroup({ id: control.element.id + '_AxisLabels' });
var firstLevelElement = control.renderer.createGroup({ id: control.element.id + '_FirstLevelAxisLabels' });
var secondLevelElement = control.renderer.createGroup({ id: control.element.id + '_SecondLevelAxisLabels' });
var secondaryAxis = axis;
pointY = this.findLabelY(control, false);
this.placeAxisLabels(axis, pointY, '_AxisLabel_', control, firstLevelElement);
secondaryAxis.intervalType = secondaryAxis.actualIntervalType = (control.groupBy ||
this.getSecondaryLabelType(axis.actualIntervalType));
secondaryAxis.labelFormat = '';
if (control.enableGrouping && control.valueType.indexOf('DateTime') > -1 && this.actualIntervalType !== 'Years') {
secondaryAxis.visibleRange.interval = 1;
secondaryAxis.visibleLabels = [];
var interval = this.calculateDateTimeNiceInterval(secondaryAxis, control.bounds, secondaryAxis.doubleRange.start, secondaryAxis.doubleRange.end, secondaryAxis.isChart);
if (control.valueType === 'DateTime') {
this.findAxisLabels(secondaryAxis, interval);
}
else {
this.findSecondaryAxisLabels(secondaryAxis);
}
this.secondLevelLabels = secondaryAxis.visibleLabels;
pointY = this.findLabelY(control, true);
var border = this.placeAxisLabels(secondaryAxis, pointY, '_SecondaryLabel_', control, secondLevelElement);
var path = new PathOption(control.element.id + '_SecondaryMajorLines', 'transparent', control.majorTickLines.width, control.majorTickLines.color || control.themeStyle.gridLineColor, 1, control.majorGridLines.dashArray, border);
this.gridLines.appendChild(control.renderer.drawPath(path));
}
control.chartSeries.xAxis.visibleLabels = control.chartSeries.xAxis.visibleLabels.concat(secondaryAxis.visibleLabels);
labelElement.style.cursor = axis.valueType.indexOf('DateTime') > -1 ? 'cursor: pointer' : 'cursor: default';
labelElement.appendChild(firstLevelElement);
labelElement.appendChild(secondLevelElement);
//gridlines and axis label append to element
control.svgObject.appendChild(this.gridLines);
control.svgObject.appendChild(labelElement);
};
/**
* To find the secondary level label type.
*
* @param {RangeIntervalType} type - The type of range interval.
* @returns {RangeIntervalType} - The secondary level label type.
*/
RangeNavigatorAxis.prototype.getSecondaryLabelType = function (type) {
var types = ['Years', 'Quarter', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'];
return (type === 'Years' ? 'Years' : types[types.indexOf(type) - 1]);
};
/**
* To find labels for date time category axis.
*
* @param {Axis} axis - Range axis.
* @returns {void}
*/
RangeNavigatorAxis.prototype.findSecondaryAxisLabels = function (axis) {
axis.visibleLabels = [];
axis.visibleRange.interval = Math.max(axis.visibleRange.interval, 1);
var previousIndex;
this.rangeNavigator.format = this.rangeNavigator.intl.getDateFormat({
format: axis.labelFormat || '',
type: firstToLowerCase(axis.skeleton), skeleton: this.getSkeleton(axis, null, null)
});
for (var i = Math.ceil(axis.visibleRange.min); i <= axis.visibleRange.max; i += axis.visibleRange.interval) {
if ((!this.rangeNavigator.dateTimeCategoryModule.sameInterval(axis.labels.map(Number)[i], axis.labels.map(Number)[i - axis.visibleRange.interval], axis.actualIntervalType, i) || axis.isIndexed)
&& withIn(i, axis.visibleRange)
&& this.rangeNavigator.dateTimeCategoryModule.isMaximum(i, previousIndex, axis)) {
var currentLabel = new Date(axis.labels.map(Number)[i]);
if (axis.actualIntervalType === 'Quarter') {
var quarterMonths = [0, 3, 6, 9];
var quarterIndex = Math.floor(currentLabel.getMonth() / 3);
currentLabel.setMonth(quarterMonths[quarterIndex]);
}
axis.visibleLabels.push(new VisibleLabels(this.dateFormats(this.rangeNavigator.format(currentLabel), axis, axis.visibleLabels.length), i, this.rangeNavigator.labelStyle, this.rangeNavigator.format(currentLabel)));
previousIndex = i;
}
}
};
/**
* To find labels for date time axis.
*
* @param {Axis} axis - Range axis.
* @param {number} interval - Interval for the date time axis.
* @returns {void}
*/
RangeNavigatorAxis.prototype.findAxisLabels = function (axis, interval) {
axis.visibleLabels = [];
var start = new Date(axis.visibleRange.min);
var nextInterval;
var text;
interval = this.rangeNavigator.interval ? this.rangeNavigator.interval : interval;
switch (axis.actualIntervalType) {
case 'Years':
start = new Date(start.getFullYear(), 0, 1);
break;
case 'Quarter':
if (start.getMonth() <= 2) {
start = new Date(start.getFullYear(), 0, 1);
}
else if (start.getMonth() <= 5) {
start = new Date(start.getFullYear(), 3, 1);
}
else if (start.getMonth() <= 8) {
start = new Date(start.getFullYear(), 6, 1);
}
else {
start = new Date(start.getFullYear(), 9, 1);
}
break;
case 'Months':
start = new Date(start.getFullYear(), start.getMonth());
break;
case 'Weeks':
start = new Date(start.getFullYear(), start.getMonth(), start.getDate() - start.getDay());
break;
case 'Days':
start = new Date(start.getFullYear(), start.getMonth(), start.getDate());
break;
case 'Hours':
start = new Date(start.getFullYear(), start.getMonth(), start.getDate(), start.getHours());
break;
case 'Minutes':
start = new Date(start.getFullYear(), start.getMonth(), start.getDate(), start.getHours(), start.getMinutes());
break;
case 'Seconds':
start = new Date(start.getFullYear(), start.getMonth(), start.getDate(), start.getHours(), start.getMinutes(), start.getSeconds());
break;
}
nextInterval = start.getTime();
this.rangeNavigator.format = this.rangeNavigator.intl.getDateFormat({
format: axis.labelFormat || '',
type: firstToLowerCase(axis.skeletonType), skeleton: this.getSkeleton(axis, null, null)
});
while (nextInterval <= axis.visibleRange.max) {
text = this.dateFormats(this.rangeNavigator.format(new Date(nextInterval)), axis, axis.visibleLabels.length);
axis.visibleLabels.push(new VisibleLabels(text, nextInterval, this.rangeNavigator.labelStyle, text));
nextInterval = this.increaseDateTimeInterval(axis, nextInterval, interval).getTime();
}
};
/**
* To find date time formats for Quarter and week interval type.
*
* @param {string} text - The text.
* @param {Axis} axis - The axis.
* @param {number} index - The index.
* @returns {string} - The modified text.
*/
RangeNavigatorAxis.prototype.dateFormats = function (text, axis, index) {
var changedText = text;
var isFirstLevel = this.rangeNavigator.enableGrouping && this.firstLevelLabels.length === 0;
switch (axis.actualIntervalType) {
case 'Quarter':
if (text.indexOf('Jan') > -1) {
changedText = !isFirstLevel ? text.replace('Jan', 'Quarter1') : 'Quarter1';
}
else if (text.indexOf('Apr') > -1) {
changedText = !isFirstLevel ? text.replace('Apr', 'Quarter2') : 'Quarter2';
}
else if (text.indexOf('Jul') > -1) {
changedText = !isFirstLevel ? text.replace('Jul', 'Quarter3') : 'Quarter3';
}
else if (text.indexOf('Oct') > -1) {
changedText = !isFirstLevel ? text.replace('Oct', 'Quarter4') : 'Quarter4';
}
break;
case 'Weeks':
changedText = 'Week' + ++index;
break;
default:
changedText = text;
break;
}
return changedText;
};
/**
* To find the y co-ordinate for axis labels.
*
* @param {RangeNavigator} control - The RangeNavigator control.
* @param {boolean} isSecondary - If sets to true, indicates that the axis is a secondary axis.
* @returns {number} - The y-coordinate for the labels.
*/
RangeNavigatorAxis.prototype.findLabelY = function (control, isSecondary) {
var pointY;
var reference = control.bounds.y + control.bounds.height;
var tickHeight = control.majorTickLines.height;
var textHeight = measureText('Quarter1 2011', control.labelStyle, control.themeStyle.axisLabelFont).height;
var padding = control.labelPosition === 'Inside' ? 3 : 8;
if ((control.labelPosition === 'Outside' && control.tickPosition === 'Outside') || control.series.length === 0) {
pointY = reference + tickHeight + padding + textHeight * 0.75;
}
else if (control.labelPosition === 'Inside' && control.tickPosition === 'Inside') {
pointY = reference - tickHeight - padding;
}
else if (control.labelPosition === 'Inside' && control.tickPosition === 'Outside') {
pointY = reference - padding;
}
else {
pointY = reference + padding + (textHeight * 0.75);
}
if (isSecondary) {
padding = 15;
if (control.labelPosition === 'Outside' || control.series.length === 0) {
pointY += padding + textHeight * 0.75;
}
else {
pointY = (control.tickPosition === 'Outside' || control.series.length === 0) ?
reference + tickHeight + padding + textHeight * 0.75 : reference + padding + textHeight * 0.75;
}
}
return pointY;
};
/**
* It places the axis labels and returns border for secondary axis labels.
*
* @param {Axis} axis - Axis for the lables placed.
* @param {number} pointY - The y co-ordinate for axis labels.
* @param {string} id - The id for the axis elements.
* @param {RangeNavigator} control - The range navigator control.
* @param {Element} labelElement - The parent element in which the axis labels are appended.
* @returns {string} - The border for the secondary axis labels.
*/
RangeNavigatorAxis.prototype.placeAxisLabels = function (axis, pointY, id, control, labelElement) {
var maxLabels = axis.visibleLabels.length;
var label;
var prevLabel;
var pointX;
var padding = 2;
var rect = control.bounds;
var border = '';
var pointXGrid;
var disabledColor = (control.disableRangeSelector) ? 'transparent' : null;
var prevX = control.enableRtl ? (rect.x + rect.width) : rect.x;
var intervalType = axis.actualIntervalType;
var intervalInTime = ((control.labelPlacement === 'Auto' && control.valueType === 'DateTime') || control.labelPlacement === 'BetweenTicks') ?
maxLabels > 1 ? (axis.visibleLabels[1].value - axis.visibleLabels[0].value) :
(axis.visibleRange.max - axis.visibleLabels[0].value) / 2 : 0;
if (control.valueType.indexOf('DateTime') > -1 && (intervalType === 'Quarter' || intervalType === 'Weeks')) {
this.findSuitableFormat(axis, control);
}
for (var i = 0, len = maxLabels; i < len; i++) {
label = axis.visibleLabels[i];
label.size = measureText(label.text, axis.labelStyle, control.themeStyle.axisLabelFont);
if ((control.secondaryLabelAlignment === 'Middle' || id.indexOf('_AxisLabel_') > -1) && (control.labelPlacement === 'Auto' || control.labelPlacement === 'BetweenTicks')) {
pointX = (valueToCoefficient((label.value + intervalInTime / 2), axis) * rect.width) + rect.x;
}
else if ((id.indexOf('Secondary') > -1)) {
pointX = this.findAlignment(axis, i);
}
if (control.labelPlacement === 'OnTicks' && control.labelPosition === 'Inside') {
pointX = (valueToCoefficient(label.value, axis) * rect.width) + rect.x + label.size.width / 2 + padding;
}
else if (control.labelPlacement === 'OnTicks' && control.labelPosition === 'Outside') {
pointX = (valueToCoefficient(label.value, axis) * rect.width) + rect.x;
}
pointXGrid = (valueToCoefficient((label.value), axis) * rect.width) + rect.x;
//edgelabelPlacements
if ((i === 0 || (i === axis.visibleLabels.length - 1 && control.enableRtl)) && pointX < rect.x) {
pointX = rect.x + label.size.width / 2;
}
if ((i === axis.visibleLabels.length - 1 || (i === 0 && control.enableRtl)) &&
((pointX + label.size.width) > (rect.x + rect.width))) {
pointX = rect.x + rect.width - label.size.width / 2;
}
//secondary axis grid lines
if (id.indexOf('_SecondaryLabel_') > -1) {
if (pointX >= rect.x && (rect.x + rect.width) >= pointX) {
border = border.concat('M ' + pointXGrid + ' ' + pointY +
' L ' + pointXGrid + ' ' + (pointY - label.size.height));
}
}
//smart axis label position,
if (control.labelIntersectAction === 'Hide' &&
i !== 0 && this.isIntersect(axis, pointX, label.size.width, prevX, prevLabel.size.width)) {
continue;
}
//label alignment for single visible label
if (control.secondaryLabelAlignment === 'Middle' && axis.visibleLabels.length === 1) {
pointX = valueToCoefficient(label.value, axis) + (rect.x + (rect.width / 2));
}
//labelrender event
var labelStyle = control.labelStyle;
var style = {
size: labelStyle.size, color: disabledColor || labelStyle.color || control.themeStyle.axisLabelFont.color,
fontFamily: labelStyle.fontFamily,
fontStyle: labelStyle.fontStyle || control.labelStyle.fontStyle,
fontWeight: labelStyle.fontWeight || control.labelStyle.fontWeight,
opacity: labelStyle.opacity || control.labelStyle.opacity,
textAlignment: labelStyle.textAlignment || control.labelStyle.textAlignment,
textOverflow: labelStyle.textOverflow || control.labelStyle.textOverflow
};
var argsData = {
cancel: false, name: 'labelRender',
text: label.text, value: label.value, labelStyle: style,
region: new Rect(pointX, pointY, label.size.width, label.size.height)
};
control.trigger('labelRender', argsData);
if (!argsData.cancel) {
control.labels.push(argsData);
}
else {
continue;
}
textElement(this.rangeNavigator.renderer, new TextOption(this.rangeNavigator.element.id + id + i, pointX, pointY, 'middle', argsData.text), argsData.labelStyle, argsData.labelStyle.color || control.themeStyle.labelFontColor, labelElement, null, null, null, null, null, null, null, null, null, null, control.themeStyle.axisLabelFont).style.cursor = axis.valueType.indexOf('DateTime') > -1 ? 'cursor: pointer' : 'cursor: default';
prevX = pointX;
prevLabel = label;
}
return border;
};
/**
* To check label is intersected with successive label or not.
*
* @param {Axis} axis - The axis for which the labels are placed.
* @param {number} currentX - The x-coordinate for the current label.
* @param {number} currentWidth - The width of the current label.
* @param {number} prevX - The x-coordinate for the previous label.
* @param {number} prevWidth - The width of the previous label.
* @returns {boolean} - True if the labels intersect; otherwise, false.
*/
RangeNavigatorAxis.prototype.isIntersect = function (axis, currentX, currentWidth, prevX, prevWidth) {
return (axis.isInversed) ? (currentX + currentWidth / 2 > prevX - prevWidth / 2) :
(currentX - currentWidth / 2 < prevX + prevWidth / 2);
};
/**
* To find suitable label format for Quarter and week Interval types.
*
* @param {Axis} axis - RangeNavigator axis.
* @param {RangeNavigator} control - RangeNavigator instance.
* @returns {void}
*/
RangeNavigatorAxis.prototype.findSuitableFormat = function (axis, control) {
var labels = axis.visibleLabels;
var labelLength = labels.length;
var bounds = control.bounds;
var prevX;
var currentX;
var interval = control.valueType === 'DateTime' ?
labelLength > 1 ? (labels[1].value - labels[0].value) : axis.visibleRange.interval
: 0;
for (var i = 0; i < labelLength; i++) {
currentX = (valueToCoefficient((labels[i].value + interval / 2), axis) * bounds.width) + bounds.x;
labels[i].size = measureText(labels[i].text, axis.labelStyle, control.themeStyle.axisLabelFont);
//edgelabelPlacements
if (i === 0 && currentX < bounds.x) {
currentX = bounds.x + labels[i].size.width / 2;
}
if (axis.actualIntervalType === 'Quarter') {
if (i !== 0) {
if ((labels[i].text.indexOf('Quarter') > -1) &&
(this.isIntersect(axis, currentX, labels[i].size.width, prevX, labels[i - 1].size.width))) {
labels.every(function (label) {
label.text = label.text.toString().replace('Quarter', 'QTR');
return true;
});
axis.visibleLabels = labels;
this.findSuitableFormat(axis, control);
}
else {
if (this.isIntersect(axis, currentX, labels[i].size.width, prevX, labels[i - 1].size.width)) {
labels.every(function (label) {
label.text = label.text.toString().replace('QTR', 'Q');
return true;
});
axis.visibleLabels = labels;
}
}
}
}
else if (axis.actualIntervalType === 'Weeks') {
if ((i !== 0) && ((labels[i].text.indexOf('Week') > -1) &&
(this.isIntersect(axis, currentX, labels[i].size.width, prevX, labels[i - 1].size.width)))) {
labels.every(function (label) {
label.text = label.text.toString().replace('Week', 'W');
return true;
});
axis.visibleLabels = labels;
}
}
prevX = currentX;
}
};
/**
* Alignment position for secondary level labels in date time axis.
*
* @param {Axis} axis - The axis.
* @param {number} index - The index of the label.
* @returns {number} - The alignment position for the secondary axis labels.
*/
RangeNavigatorAxis.prototype.findAlignment = function (axis, index) {
var label = axis.visibleLabels[index];
var nextLabel = axis.visibleLabels[index + 1];
var bounds = this.rangeNavigator.bounds;
return (this.rangeNavigator.secondaryLabelAlignment === 'Near' ?
(valueToCoefficient((label.value), axis) * bounds.width) + bounds.x + label.size.width / 2 :
(valueToCoefficient((nextLabel ? nextLabel.value : axis.visibleRange.max), axis) * bounds.width) + bounds.x - label.size.width);
};
return RangeNavigatorAxis;
}(DateTime));
export { RangeNavigatorAxis };