@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
907 lines (722 loc) • 29.8 kB
JavaScript
import PlotAreaBase from './plotarea-base';
import AxisGroupRangeTracker from '../axis-group-range-tracker';
import PlotAreaEventsMixin from '../mixins/plotarea-events-mixin';
import SeriesAggregator from '../aggregates/series-aggregator';
import DefaultAggregates from '../aggregates/default-aggregates';
import SeriesBinder from '../series-binder';
import BarChart from '../bar-chart/bar-chart';
import RangeBarChart from '../range-bar-chart/range-bar-chart';
import BulletChart from '../bullet-chart/bullet-chart';
import LineChart from '../line-chart/line-chart';
import AreaChart from '../area-chart/area-chart';
import RangeAreaChart from '../range-area-chart/range-area-chart';
import OHLCChart from '../ohlc-chart/ohlc-chart';
import CandlestickChart from '../candlestick-chart/candlestick-chart';
import BoxPlotChart from '../box-plot-chart/box-plot-chart';
import WaterfallChart from '../waterfall-chart/waterfall-chart';
import trendlineFactory from '../trendlines/trendline-factory';
import trendlineRegistry from '../trendlines/trendline-registry';
import { CategoryAxis, DateCategoryAxis, NumericAxis, LogarithmicAxis, Point } from '../../core';
import { appendIfNotNull, categoriesCount, createOutOfRangePoints, equalsIgnoreCase, filterSeriesByType,
isDateAxis, parseDateCategory, singleItemOrArray } from '../utils';
import { BAR, COLUMN, BULLET, VERTICAL_BULLET, LINE, VERTICAL_LINE, AREA, VERTICAL_AREA,
RANGE_AREA, VERTICAL_RANGE_AREA, RANGE_COLUMN, RANGE_BAR, WATERFALL, HORIZONTAL_WATERFALL,
BOX_PLOT, VERTICAL_BOX_PLOT, OHLC, CANDLESTICK, LOGARITHMIC, STEP, EQUALLY_SPACED_SERIES, RADAR_LINE, RADAR_AREA } from '../constants';
import { DATE, MAX_VALUE } from '../../common/constants';
import { setDefaultOptions, inArray, deepExtend, defined, eventElement, grep, cycleIndex, hasOwnProperty } from '../../common';
const AREA_SERIES = [ AREA, VERTICAL_AREA, RANGE_AREA, VERTICAL_RANGE_AREA ];
const OUT_OF_RANGE_SERIES = [ LINE, VERTICAL_LINE ].concat(AREA_SERIES);
class CategoricalPlotArea extends PlotAreaBase {
initFields(series) {
this.namedCategoryAxes = {};
this.namedValueAxes = {};
this.valueAxisRangeTracker = new AxisGroupRangeTracker();
this._seriesPointsCache = {};
this._currentPointsCache = {};
if (series.length > 0) {
this.invertAxes = inArray(
series[0].type, [ BAR, BULLET, VERTICAL_LINE, VERTICAL_AREA, VERTICAL_RANGE_AREA,
RANGE_BAR, HORIZONTAL_WATERFALL, VERTICAL_BOX_PLOT ]
);
for (let i = 0; i < series.length; i++) {
const stack = series[i].stack;
if (stack && stack.type === "100%") {
this.stack100 = true;
break;
}
}
}
}
render(panes = this.panes) {
this.series = [...this.originalSeries];
this.createCategoryAxes(panes);
this.aggregateCategories(panes);
this.createTrendlineSeries(panes);
this.createCategoryAxesLabels(panes);
this.createCharts(panes);
this.createValueAxes(panes);
}
removeAxis(axis) {
const axisName = axis.options.name;
super.removeAxis(axis);
if (axis instanceof CategoryAxis) {
delete this.namedCategoryAxes[axisName];
} else {
this.valueAxisRangeTracker.reset(axisName);
delete this.namedValueAxes[axisName];
}
if (axis === this.categoryAxis) {
delete this.categoryAxis;
}
if (axis === this.valueAxis) {
delete this.valueAxis;
}
}
trendlineFactory(options, series) {
const categoryAxis = this.seriesCategoryAxis(options);
const seriesValues = this.seriesValues.bind(this, series.index);
const trendline = trendlineFactory(trendlineRegistry, options.type, {
options,
categoryAxis,
seriesValues
});
if (trendline) {
// Inherit settings
trendline.categoryAxis = series.categoryAxis;
trendline.valueAxis = series.valueAxis;
return this.filterSeries(trendline, categoryAxis);
}
return trendline;
}
trendlineAggregateForecast() {
return this.series
.map(series => (series.trendline || {}).forecast)
.filter(forecast => forecast !== undefined)
.reduce((result, forecast) => ({
before: Math.max(result.before, forecast.before || 0),
after: Math.max(result.after, forecast.after || 0)
}), { before: 0, after: 0 });
}
seriesValues(seriesIx, range) {
const result = [];
let series = this.srcSeries[seriesIx];
const categoryAxis = this.seriesCategoryAxis(series);
const dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
if (dateAxis) {
this._seriesPointsCache = {};
this._currentPointsCache = {};
categoryAxis.options.dataItems = [];
series = this.aggregateSeries(series, categoryAxis, categoryAxis.totalRangeIndices());
}
const min = range ? range.min : 0;
const max = range ? range.max : series.data.length;
for (let categoryIx = min; categoryIx < max; categoryIx++) {
const data = this.bindPoint(series, categoryIx);
result.push({ categoryIx, category: data.fields.category, valueFields: data.valueFields });
}
return result;
}
createCharts(panes) {
const seriesByPane = this.groupSeriesByPane();
for (let i = 0; i < panes.length; i++) {
const pane = panes[i];
const paneSeries = seriesByPane[pane.options.name || "default"] || [];
this.addToLegend(paneSeries);
const visibleSeries = this.filterVisibleSeries(paneSeries);
if (!visibleSeries) {
continue;
}
const groups = this.groupSeriesByCategoryAxis(visibleSeries);
for (let groupIx = 0; groupIx < groups.length; groupIx++) {
this.createChartGroup(groups[groupIx], pane);
}
}
}
createChartGroup(series, pane) {
this.createAreaChart(
filterSeriesByType(series, [ AREA, VERTICAL_AREA ]), pane
);
this.createRangeAreaChart(
filterSeriesByType(series, [ RANGE_AREA, VERTICAL_RANGE_AREA ]), pane
);
this.createBarChart(
filterSeriesByType(series, [ COLUMN, BAR ]), pane
);
this.createRangeBarChart(
filterSeriesByType(series, [ RANGE_COLUMN, RANGE_BAR ]), pane
);
this.createBulletChart(
filterSeriesByType(series, [ BULLET, VERTICAL_BULLET ]), pane
);
this.createCandlestickChart(
filterSeriesByType(series, CANDLESTICK), pane
);
this.createBoxPlotChart(
filterSeriesByType(series, [ BOX_PLOT, VERTICAL_BOX_PLOT ]), pane
);
this.createOHLCChart(
filterSeriesByType(series, OHLC), pane
);
this.createWaterfallChart(
filterSeriesByType(series, [ WATERFALL, HORIZONTAL_WATERFALL ]), pane
);
this.createLineChart(
filterSeriesByType(series, [ LINE, VERTICAL_LINE ]), pane
);
}
aggregateCategories(panes) {
const series = [...this.series];
const processedSeries = [];
this._currentPointsCache = {};
this._seriesPointsCache = this._seriesPointsCache || {};
for (let i = 0; i < series.length; i++) {
let currentSeries = series[i];
if (!this.isTrendline(currentSeries)) {
const categoryAxis = this.seriesCategoryAxis(currentSeries);
const axisPane = this.findPane(categoryAxis.options.pane);
const dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
if ((dateAxis || currentSeries.categoryField) && inArray(axisPane, panes)) {
currentSeries = this.aggregateSeries(currentSeries, categoryAxis, categoryAxis.currentRangeIndices());
} else {
currentSeries = this.filterSeries(currentSeries, categoryAxis);
}
}
processedSeries.push(currentSeries);
}
this._seriesPointsCache = this._currentPointsCache;
this._currentPointsCache = null;
this.srcSeries = series;
this.series = processedSeries;
}
filterSeries(series, categoryAxis) {
const dataLength = (series.data || {}).length;
categoryAxis._seriesMax = Math.max(categoryAxis._seriesMax || 0, dataLength);
if (!(defined(categoryAxis.options.min) || defined(categoryAxis.options.max))) {
return series;
}
const range = categoryAxis.currentRangeIndices();
const outOfRangePoints = inArray(series.type, OUT_OF_RANGE_SERIES);
const currentSeries = deepExtend({}, series);
currentSeries.data = (currentSeries.data || []).slice(range.min, range.max + 1);
if (outOfRangePoints) {
createOutOfRangePoints(currentSeries, range, dataLength, (idx) => ({
item: series.data[idx],
category: categoryAxis.categoryAt(idx, true),
categoryIx: idx - range.min
}), (idx) => defined(series.data[idx]));
}
return currentSeries;
}
clearSeriesPointsCache() {
this._seriesPointsCache = {};
}
seriesSourcePoints(series, categoryAxis) {
const key = `${ series.index };${ categoryAxis.categoriesHash() }`;
if (this._seriesPointsCache && this._seriesPointsCache[key]) {
this._currentPointsCache[key] = this._seriesPointsCache[key];
return this._seriesPointsCache[key];
}
const axisOptions = categoryAxis.options;
const srcCategories = axisOptions.srcCategories;
const dateAxis = equalsIgnoreCase(axisOptions.type, DATE);
const srcData = series.data;
const result = [];
if (!dateAxis) {
categoryAxis.indexCategories();
}
for (let idx = 0; idx < srcData.length; idx++) {
let category = SeriesBinder.current.bindPoint(series, idx).fields.category;
if (dateAxis) {
category = parseDateCategory(category, srcData[idx], this.chartService.intl);
}
if (category === undefined) {
category = srcCategories[idx];
}
if (category !== undefined && category !== null) {
const categoryIx = categoryAxis.totalIndex(category);
result[categoryIx] = result[categoryIx] || { items: [], category: category };
result[categoryIx].items.push(idx);
}
}
this._currentPointsCache[key] = result;
return result;
}
aggregateSeries(series, categoryAxis, range) {
const srcData = series.data;
if (!srcData.length) {
return series;
}
const srcPoints = this.seriesSourcePoints(series, categoryAxis);
const result = deepExtend({}, series);
const aggregator = new SeriesAggregator(deepExtend({}, series), SeriesBinder.current, DefaultAggregates.current);
const data = result.data = [];
const dataItems = categoryAxis.options.dataItems || [];
const categoryItem = (idx) => {
const categoryIdx = idx - range.min;
let point = srcPoints[idx];
if (!point) {
point = srcPoints[idx] = {};
}
point.categoryIx = categoryIdx;
if (!point.item) {
const category = categoryAxis.categoryAt(idx, true);
point.category = category;
point.item = aggregator.aggregatePoints(point.items, category);
}
return point;
};
for (let idx = range.min; idx <= range.max; idx++) {
const point = categoryItem(idx);
data[point.categoryIx] = point.item;
if (point.items && point.items.length) {
dataItems[point.categoryIx] = point.item;
}
}
if (inArray(result.type, OUT_OF_RANGE_SERIES)) {
createOutOfRangePoints(result, range, categoryAxis.totalCount(), categoryItem, (idx) => srcPoints[idx]);
}
categoryAxis.options.dataItems = dataItems;
return result;
}
appendChart(chart, pane) {
const series = chart.options.series;
const categoryAxis = this.seriesCategoryAxis(series[0]);
let categories = categoryAxis.options.categories;
let categoriesToAdd = Math.max(0, categoriesCount(series) - categories.length);
if (categoriesToAdd > 0) {//consider setting an option to axis instead of adding fake categories
categories = categoryAxis.options.categories = categoryAxis.options.categories.slice(0);
while (categoriesToAdd--) {
categories.push("");
}
}
this.valueAxisRangeTracker.update(chart.valueAxisRanges);
super.appendChart(chart, pane);
}
// TODO: Refactor, optionally use series.pane option
seriesPaneName(series) {
const options = this.options;
const axisName = series.axis;
const axisOptions = [].concat(options.valueAxis);
const axis = grep(axisOptions, function(a) { return a.name === axisName; })[0];
const panes = options.panes || [ {} ];
const defaultPaneName = (panes[0] || {}).name || "default";
const paneName = (axis || {}).pane || defaultPaneName;
return paneName;
}
seriesCategoryAxis(series) {
const axisName = series.categoryAxis;
const axis = axisName ? this.namedCategoryAxes[axisName] : this.categoryAxis;
if (!axis) {
throw new Error("Unable to locate category axis with name " + axisName);
}
return axis;
}
stackableChartOptions(series, pane) {
const anyStackedSeries = series.some(s => s.stack);
const isStacked100 = series.some(s => s.stack && s.stack.type === "100%");
const clip = pane.options.clip;
return {
defaultStack: series[0].stack,
isStacked: anyStackedSeries,
isStacked100: isStacked100,
clip: clip
};
}
groupSeriesByCategoryAxis(series) {
const categoryAxes = [];
const unique = {};
for (let idx = 0; idx < series.length; idx++) {
const name = series[idx].categoryAxis || "$$default$$";
if (!hasOwnProperty(unique, name)) {
unique[name] = true;
categoryAxes.push(name);
}
}
const groups = [];
for (let axisIx = 0; axisIx < categoryAxes.length; axisIx++) {
const axis = categoryAxes[axisIx];
const axisSeries = groupSeries(series, axis, axisIx);
if (axisSeries.length === 0) {
continue;
}
groups.push(axisSeries);
}
return groups;
}
createBarChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const barChart = new BarChart(this, Object.assign({
series: series,
invertAxes: this.invertAxes,
gap: firstSeries.gap,
spacing: firstSeries.spacing
}, this.stackableChartOptions(series, pane)));
this.appendChart(barChart, pane);
}
createRangeBarChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const rangeColumnChart = new RangeBarChart(this, {
series: series,
invertAxes: this.invertAxes,
gap: firstSeries.gap,
spacing: firstSeries.spacing
});
this.appendChart(rangeColumnChart, pane);
}
createBulletChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const bulletChart = new BulletChart(this, {
series: series,
invertAxes: this.invertAxes,
gap: firstSeries.gap,
spacing: firstSeries.spacing,
clip: pane.options.clip
});
this.appendChart(bulletChart, pane);
}
createLineChart(series, pane) {
if (series.length === 0) {
return;
}
const lineChart = new LineChart(this, Object.assign({
invertAxes: this.invertAxes,
series: series
}, this.stackableChartOptions(series, pane)));
this.appendChart(lineChart, pane);
}
createAreaChart(series, pane) {
if (series.length === 0) {
return;
}
const areaChart = new AreaChart(this, Object.assign({
invertAxes: this.invertAxes,
series: series
}, this.stackableChartOptions(series, pane)));
this.appendChart(areaChart, pane);
}
createRangeAreaChart(series, pane) {
if (series.length === 0) {
return;
}
const rangeAreaChart = new RangeAreaChart(this, {
invertAxes: this.invertAxes,
series: series,
clip: pane.options.clip
});
this.appendChart(rangeAreaChart, pane);
}
createOHLCChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const chart = new OHLCChart(this, {
invertAxes: this.invertAxes,
gap: firstSeries.gap,
series: series,
spacing: firstSeries.spacing,
clip: pane.options.clip
});
this.appendChart(chart, pane);
}
createCandlestickChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const chart = new CandlestickChart(this, {
invertAxes: this.invertAxes,
gap: firstSeries.gap,
series: series,
spacing: firstSeries.spacing,
clip: pane.options.clip
});
this.appendChart(chart, pane);
}
createBoxPlotChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const chart = new BoxPlotChart(this, {
invertAxes: this.invertAxes,
gap: firstSeries.gap,
series: series,
spacing: firstSeries.spacing,
clip: pane.options.clip
});
this.appendChart(chart, pane);
}
createWaterfallChart(series, pane) {
if (series.length === 0) {
return;
}
const firstSeries = series[0];
const waterfallChart = new WaterfallChart(this, {
series: series,
invertAxes: this.invertAxes,
gap: firstSeries.gap,
spacing: firstSeries.spacing
});
this.appendChart(waterfallChart, pane);
}
axisRequiresRounding(categoryAxisName, categoryAxisIndex) {
const centeredSeries = filterSeriesByType(this.series, EQUALLY_SPACED_SERIES);
for (let seriesIx = 0; seriesIx < this.series.length; seriesIx++) {
const currentSeries = this.series[seriesIx];
if (inArray(currentSeries.type, AREA_SERIES)) {
const line = currentSeries.line;
if (line && line.style === STEP) {
centeredSeries.push(currentSeries);
}
}
}
for (let seriesIx = 0; seriesIx < centeredSeries.length; seriesIx++) {
const seriesAxis = centeredSeries[seriesIx].categoryAxis || "";
if (seriesAxis === categoryAxisName || (!seriesAxis && categoryAxisIndex === 0)) {
return true;
}
}
}
aggregatedAxis(categoryAxisName, categoryAxisIndex) {
const series = this.series;
for (let seriesIx = 0; seriesIx < series.length; seriesIx++) {
const seriesAxis = series[seriesIx].categoryAxis || "";
if ((seriesAxis === categoryAxisName || (!seriesAxis && categoryAxisIndex === 0)) && series[seriesIx].categoryField) {
return true;
}
}
}
createCategoryAxesLabels() {
const axes = this.axes;
for (let i = 0; i < axes.length; i++) {
if (axes[i] instanceof CategoryAxis) {
axes[i].createLabels();
}
}
}
createCategoryAxes(panes) {
const invertAxes = this.invertAxes;
const definitions = [].concat(this.options.categoryAxis);
const axes = [];
for (let i = 0; i < definitions.length; i++) {
let axisOptions = definitions[i];
const axisPane = this.findPane(axisOptions.pane);
if (inArray(axisPane, panes)) {
const { name, categories = [] } = axisOptions;
axisOptions = deepExtend({
vertical: invertAxes,
reverse: !invertAxes && this.chartService.rtl,
axisCrossingValue: invertAxes ? MAX_VALUE : 0
}, axisOptions);
if (!defined(axisOptions.justified)) {
axisOptions.justified = this.isJustified();
}
if (this.axisRequiresRounding(name, i)) {
axisOptions.justified = false;
}
let categoryAxis;
if (isDateAxis(axisOptions, categories[0])) {
axisOptions._forecast = this.trendlineAggregateForecast();
categoryAxis = new DateCategoryAxis(axisOptions, this.chartService);
} else {
categoryAxis = new CategoryAxis(axisOptions, this.chartService);
}
definitions[i].categories = categoryAxis.options.srcCategories;
if (name) {
if (this.namedCategoryAxes[name]) {
throw new Error(`Category axis with name ${ name } is already defined`);
}
this.namedCategoryAxes[name] = categoryAxis;
}
categoryAxis.axisIndex = i;
axes.push(categoryAxis);
this.appendAxis(categoryAxis);
}
}
const primaryAxis = this.categoryAxis || axes[0];
this.categoryAxis = primaryAxis;
if (invertAxes) {
this.axisY = primaryAxis;
} else {
this.axisX = primaryAxis;
}
}
isJustified() {
const series = this.series;
for (let i = 0; i < series.length; i++) {
const currentSeries = series[i];
if (!inArray(currentSeries.type, AREA_SERIES)) {
return false;
}
}
return true;
}
createValueAxes(panes) {
const tracker = this.valueAxisRangeTracker;
const defaultRange = tracker.query();
const definitions = [].concat(this.options.valueAxis);
const invertAxes = this.invertAxes;
const baseOptions = { vertical: !invertAxes, reverse: invertAxes && this.chartService.rtl };
const axes = [];
if (this.stack100) {
baseOptions.roundToMajorUnit = false;
baseOptions.labels = { format: "P0" };
}
for (let i = 0; i < definitions.length; i++) {
const axisOptions = definitions[i];
const axisPane = this.findPane(axisOptions.pane);
if (inArray(axisPane, panes)) {
const name = axisOptions.name;
const defaultAxisRange = equalsIgnoreCase(axisOptions.type, LOGARITHMIC) ? { min: 0.1, max: 1 } : { min: 0, max: 1 };
const range = tracker.query(name) || defaultRange || defaultAxisRange;
if (i === 0 && range && defaultRange) {
range.min = Math.min(range.min, defaultRange.min);
range.max = Math.max(range.max, defaultRange.max);
}
let axisType;
if (equalsIgnoreCase(axisOptions.type, LOGARITHMIC)) {
axisType = LogarithmicAxis;
} else {
axisType = NumericAxis;
}
const valueAxis = new axisType(range.min, range.max,
deepExtend({}, baseOptions, axisOptions),
this.chartService
);
if (name) {
if (this.namedValueAxes[name]) {
throw new Error(`Value axis with name ${ name } is already defined`);
}
this.namedValueAxes[name] = valueAxis;
}
valueAxis.axisIndex = i;
axes.push(valueAxis);
this.appendAxis(valueAxis);
}
}
const primaryAxis = this.valueAxis || axes[0];
this.valueAxis = primaryAxis;
if (invertAxes) {
this.axisX = primaryAxis;
} else {
this.axisY = primaryAxis;
}
}
_dispatchEvent(chart, e, eventType) {
const coords = chart._eventCoordinates(e);
const point = new Point(coords.x, coords.y);
const pane = this.pointPane(point);
const categories = [];
const values = [];
if (!pane) {
return;
}
const allAxes = pane.axes;
for (let i = 0; i < allAxes.length; i++) {
const axis = allAxes[i];
if (axis.getValue) {
appendIfNotNull(values, axis.getValue(point));
} else {
appendIfNotNull(categories, axis.getCategory(point));
}
}
if (categories.length === 0) {
appendIfNotNull(categories, this.categoryAxis.getCategory(point));
}
if (categories.length > 0 && values.length > 0) {
chart.trigger(eventType, {
element: eventElement(e),
originalEvent: e,
category: singleItemOrArray(categories),
value: singleItemOrArray(values)
});
}
}
pointPane(point) {
const panes = this.panes;
for (let i = 0; i < panes.length; i++) {
const currentPane = panes[i];
if (currentPane.contentBox.containsPoint(point)) {
return currentPane;
}
}
}
updateAxisOptions(axis, options) {
updateAxisOptions(this.options, axis, options);
updateAxisOptions(this.originalOptions, axis, options);
}
_pointsByVertical(basePoint, offset = 0) {
if (this.invertAxes) {
return this._siblingsBySeriesIndex(basePoint.series.index, offset);
}
return this._siblingsByPointIndex(basePoint.getIndex());
}
_pointsByHorizontal(basePoint, offset = 0) {
if (this.invertAxes) {
return this._siblingsByPointIndex(basePoint.getIndex());
}
const siblings = this._siblingsBySeriesIndex(basePoint.series.index, offset);
if (this.chartService.rtl) {
return siblings.reverse();
}
return siblings;
}
_siblingsByPointIndex(pointIndex) {
const charts = this.charts;
const result = [];
for (let i = 0; i < charts.length; i++) {
let chart = charts[i];
if (chart.pane && chart.pane.options.name === "_navigator") {
continue;
}
let chartPoints = chart.points
.filter(point =>
point && point.visible !== false && point.getIndex() === pointIndex
);
result.push(...chartPoints.sort(this._getSeriesCompareFn(chartPoints[0])));
}
return result;
}
_siblingsBySeriesIndex(seriesIndex, offset) {
const index = cycleIndex(seriesIndex + offset, this.series.length);
return this.pointsBySeriesIndex(index);
}
_getSeriesCompareFn(point) {
const isStacked = this._isInStackedSeries(point);
if (isStacked && this.invertAxes || !isStacked && !this.invertAxes) {
return (a, b) => a.box.center().x - b.box.center().x;
}
return (a, b) => a.box.center().y - b.box.center().y;
}
_isInStackedSeries(point) {
const sortableSeries = inArray(point.series.type,
[ AREA, VERTICAL_AREA, RANGE_AREA, VERTICAL_RANGE_AREA, LINE, VERTICAL_LINE, RADAR_LINE, RADAR_AREA]);
const stackableSeries = inArray(point.series.type, [ COLUMN, BAR]);
return sortableSeries || stackableSeries && point.options.isStacked;
}
}
function updateAxisOptions(targetOptions, axis, options) {
const axesOptions = axis instanceof CategoryAxis ? [].concat(targetOptions.categoryAxis) : [].concat(targetOptions.valueAxis);
deepExtend(axesOptions[axis.axisIndex], options);
}
function groupSeries(series, axis, axisIx) {
return grep(series, function(s) {
return (axisIx === 0 && !s.categoryAxis) || (s.categoryAxis === axis);
});
}
setDefaultOptions(CategoricalPlotArea, {
categoryAxis: {},
valueAxis: {}
});
deepExtend(CategoricalPlotArea.prototype, PlotAreaEventsMixin);
export default CategoricalPlotArea;