@progress/kendo-charts 2.10.0-develop.1 → 2.10.0-develop.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,11 +4,19 @@ import { INITIAL_ANIMATION_DURATION, BAR, START_SCALE } from '../constants';
4
4
 
5
5
  import { X, Y } from '../../common/constants';
6
6
  import { interpolateValue, setDefaultOptions } from '../../common';
7
+ import { easingMap } from './easingMap';
7
8
 
8
9
  class BarChartAnimation extends draw.Animation {
9
10
 
10
11
  setup() {
11
12
  const { element, options } = this;
13
+ if (options.motion && options.motion.enabled) {
14
+ options.motion.duration = options.motionConfig.initialDuration;
15
+ if (options.easing && easingMap[options.easing]) {
16
+ options.motion.easing = easingMap[options.easing];
17
+ }
18
+ }
19
+
12
20
  const bbox = element.bbox();
13
21
 
14
22
  if (bbox) {
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class BubbleAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
11
+ this.options.motion.easing = this.options.motionConfig.elasticEasing;
12
+ }
9
13
  const center = this.center = this.element.bbox().center();
10
14
  this.element.transform(geom.transform()
11
15
  .scale(START_SCALE, START_SCALE, center)
@@ -3,9 +3,16 @@ import { drawing as draw } from '@progress/kendo-drawing';
3
3
  import { INITIAL_ANIMATION_DURATION } from '../constants';
4
4
 
5
5
  import { interpolateValue, setDefaultOptions } from '../../common';
6
+ import { easingMap } from './easingMap';
6
7
 
7
8
  class ClipAnimation extends draw.Animation {
8
9
  setup() {
10
+ if (this.options.motion && this.options.motion.enabled) {
11
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
12
+ if (this.options.easing && easingMap[this.options.easing]) {
13
+ this.options.motion.easing = easingMap[this.options.easing];
14
+ }
15
+ }
9
16
  this._setEnd(this.options.box.x1);
10
17
  }
11
18
 
@@ -0,0 +1,8 @@
1
+ export const easingMap = {
2
+ swing: [0.3583, 0, 0.6422, 1],
3
+ linear: [0, 0, 1, 1],
4
+ ease: [0.25, 0.1, 0.25, 1],
5
+ "ease-in": [0.42, 0, 1, 1],
6
+ "ease-out": [0, 0, 0.58, 1],
7
+ "ease-in-out": [0.42, 0, 0.58, 1]
8
+ };
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class FadeInAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.fadeInDuration;
11
+ this.options.motion.easing = this.options.motionConfig.linearEasing;
12
+ }
9
13
  this.fadeTo = this.element.opacity();
10
14
  this.element.opacity(0);
11
15
  }
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class PieAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
11
+ this.options.motion.easing = this.options.motionConfig.elasticEasing;
12
+ }
9
13
  this.element.transform(geom.transform()
10
14
  .scale(START_SCALE, START_SCALE, this.options.center)
11
15
  );
@@ -366,11 +366,15 @@ class Chart {
366
366
  model.renderVisual();
367
367
 
368
368
  const transitions = this.options.transitions;
369
+ const motionConfig = this.options.motion || {};
370
+ const motion = {
371
+ enabled: motionConfig.enabled
372
+ };
369
373
  if (transitions !== false) {
370
374
  model.traverse(function(element) {
371
375
  if (element.animation) {
372
376
  const loading = (transitions && transitions !== true) ? transitions.loading : transitions;
373
- element.animation.options = Object.assign({}, element.animation.options, loading);
377
+ element.animation.options = Object.assign({}, element.animation.options, loading, {motionConfig, motion});
374
378
  element.animation.setup();
375
379
  }
376
380
  });
@@ -477,6 +481,75 @@ class Chart {
477
481
  return visual;
478
482
  }
479
483
 
484
+ _toggleCategoryHighlight(index, axis) {
485
+ const plotArea = this._plotArea;
486
+ const categoryAxis = axis || plotArea.categoryAxis;
487
+
488
+ if (this._categoryHighlightIndex === index && this._categoryHighlightAxis === categoryAxis) {
489
+ return;
490
+ }
491
+
492
+ this._categoryHighlightIndex = index;
493
+ this._categoryHighlightAxis = categoryAxis;
494
+
495
+ const highlightOptions = (categoryAxis.options || {}).highlight;
496
+
497
+ if (!highlightOptions || !highlightOptions.visible || index === null) {
498
+ if (this._categoryHighlight) {
499
+ this._categoryHighlight.visible(false);
500
+ }
501
+ return;
502
+ }
503
+
504
+ if (!this._categoryHighlight) {
505
+ this._categoryHighlight = new draw.Path({
506
+ fill: {
507
+ color: highlightOptions.color,
508
+ opacity: highlightOptions.opacity
509
+ },
510
+ stroke: highlightOptions.border || null,
511
+ closed: true
512
+ });
513
+
514
+ this.surface.draw(this._categoryHighlight);
515
+ } else {
516
+ const options = this._categoryHighlight.options;
517
+ options.set("fill", {
518
+ color: highlightOptions.color,
519
+ opacity: highlightOptions.opacity
520
+ });
521
+ options.set("stroke", highlightOptions.border);
522
+ }
523
+
524
+ const slot = categoryAxis.getSlot(index);
525
+ const rect = slot.toRect();
526
+ let plotBox = plotArea.box;
527
+
528
+ if (categoryAxis.pane) {
529
+ plotBox = categoryAxis.pane.chartsBox();
530
+ }
531
+
532
+ if (categoryAxis.options.vertical) {
533
+ rect.origin.x = plotBox.x1;
534
+ rect.size.width = plotBox.width();
535
+ } else {
536
+ rect.origin.y = plotBox.y1;
537
+ rect.size.height = plotBox.height();
538
+ }
539
+
540
+ const newPath = draw.Path.fromRect(rect);
541
+ this._categoryHighlight.segments.elements(newPath.segments.elements());
542
+ this._categoryHighlight.visible(true);
543
+ }
544
+
545
+ _categoryHighlightEnabled() {
546
+ const plotArea = this._plotArea;
547
+ const categoryAxis = plotArea.categoryAxis || {};
548
+ const highlightOptions = (categoryAxis.options || {}).highlight;
549
+
550
+ return plotArea instanceof CategoricalPlotArea && highlightOptions && highlightOptions.visible;
551
+ }
552
+
480
553
  _sharedTooltip() {
481
554
  return this._plotArea instanceof CategoricalPlotArea && this.options.tooltip && this.options.tooltip.shared;
482
555
  }
@@ -1792,6 +1865,22 @@ class Chart {
1792
1865
  if (this._sharedTooltip()) {
1793
1866
  this._trackSharedTooltip(coords, e);
1794
1867
  }
1868
+
1869
+ if (this._categoryHighlightEnabled()) {
1870
+ this._trackCategoryHighlight(coords);
1871
+ }
1872
+ }
1873
+
1874
+ _trackCategoryHighlight(coords) {
1875
+ const { _plotArea: plotArea } = this;
1876
+ const categoryAxis = plotArea.categoryAxisByPoint(coords) || plotArea.categoryAxis;
1877
+
1878
+ if (plotArea.backgroundContainsPoint(coords)) {
1879
+ const index = categoryAxis.pointCategoryIndex(coords);
1880
+ this._toggleCategoryHighlight(index, categoryAxis);
1881
+ } else {
1882
+ this._toggleCategoryHighlight(null);
1883
+ }
1795
1884
  }
1796
1885
 
1797
1886
  _trackCrosshairs(coords) {
@@ -1858,6 +1947,8 @@ class Chart {
1858
1947
  plotArea.hideCrosshairs();
1859
1948
 
1860
1949
  this._unsetActivePoint(options);
1950
+
1951
+ this._toggleCategoryHighlight(null);
1861
1952
  }
1862
1953
 
1863
1954
  _unsetActivePoint(options) {
@@ -1997,7 +2088,7 @@ class Chart {
1997
2088
  }
1998
2089
 
1999
2090
  _shouldAttachMouseMove() {
2000
- return this._plotArea.crosshairs.length || (this._tooltip && this._sharedTooltip()) || this.requiresHandlers([ PLOT_AREA_HOVER, PLOT_AREA_LEAVE ]);
2091
+ return this._plotArea.crosshairs.length || (this._tooltip && this._sharedTooltip()) || this._categoryHighlightEnabled() || this.requiresHandlers([ PLOT_AREA_HOVER, PLOT_AREA_LEAVE ]);
2001
2092
  }
2002
2093
 
2003
2094
  updateMouseMoveHandler() {
@@ -2311,6 +2402,9 @@ function triggerPaneRender(panes) {
2311
2402
 
2312
2403
  setDefaultOptions(Chart, {
2313
2404
  renderAs: "",
2405
+ motion: {
2406
+ enabled: false,
2407
+ },
2314
2408
  chartArea: {},
2315
2409
  legend: {
2316
2410
  visible: true,
@@ -2324,7 +2418,12 @@ setDefaultOptions(Chart, {
2324
2418
  }
2325
2419
  }
2326
2420
  },
2327
- categoryAxis: {},
2421
+ categoryAxis: {
2422
+ highlight: {
2423
+ visible: false,
2424
+ border: null
2425
+ }
2426
+ },
2328
2427
  seriesDefaults: {
2329
2428
  type: COLUMN,
2330
2429
  data: [],
@@ -883,6 +883,21 @@ class CategoricalPlotArea extends PlotAreaBase {
883
883
 
884
884
  return sortableSeries || stackableSeries && point.options.isStacked;
885
885
  }
886
+
887
+ categoryAxisByPoint(point) {
888
+ const axes = this.axes;
889
+ let axis;
890
+
891
+ for (let i = 0; i < axes.length; i++) {
892
+ const current = axes[i];
893
+ if (current instanceof CategoryAxis && current.pane.chartsBox().containsPoint(point)) {
894
+ axis = current;
895
+ break;
896
+ }
897
+ }
898
+
899
+ return axis;
900
+ }
886
901
  }
887
902
 
888
903
  function updateAxisOptions(targetOptions, axis, options) {
@@ -51,6 +51,42 @@ const getSeriesColors = (element) => {
51
51
  return result;
52
52
  };
53
53
 
54
+ const parseToMS = (value) => {
55
+ // Handle CSS time values (e.g., "200ms", "0.5s", ".3s")
56
+ if (typeof value === 'string' && value.endsWith('ms')) {
57
+ return parseFloat(value);
58
+ } else if (typeof value === 'string' && value.endsWith('s')) {
59
+ return parseFloat(value) * 1000;
60
+ }
61
+ return parseFloat(value);
62
+ };
63
+
64
+ function parseCubicBezier(input) {
65
+ const s = input.trim();
66
+ const m = s.match(
67
+ /cubic-bezier\s*\(\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*\)\s*/i
68
+ );
69
+
70
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])] : [0, 0, 1, 1];
71
+ }
72
+
73
+ function parseEasing(input) {
74
+ switch (input) {
75
+ case "linear":
76
+ return [0, 0, 1, 1];
77
+ case "ease":
78
+ return [0.25, 0.1, 0.25, 1];
79
+ case "ease-in":
80
+ return [0.42, 0, 1, 1];
81
+ case "ease-out":
82
+ return [0, 0, 0.58, 1];
83
+ case "ease-in-out":
84
+ return [0.42, 0, 0.58, 1];
85
+ default:
86
+ return input ? parseCubicBezier(input) : [0, 0, 1, 1];
87
+ }
88
+ }
89
+
54
90
  // -----------------------------------------------------------------------------
55
91
  // CSS Variable Groups (shared + component specific)
56
92
  // -----------------------------------------------------------------------------
@@ -84,7 +120,11 @@ const chartVariables = {
84
120
  notesBg: "--kendo-chart-notes-bg",
85
121
  notesBorder: "--kendo-chart-notes-border",
86
122
  notesLines: "--kendo-chart-notes-lines",
87
- inactive: "--kendo-chart-inactive"
123
+ inactive: "--kendo-chart-inactive",
124
+ initialAnimationDuration: '--kendo-duration-steady',
125
+ fadeInAnimationDuration: '--kendo-duration-rapid',
126
+ elasticEasing: '--kendo-easing-elastic',
127
+ linearEasing: '--kendo-easing-linear',
88
128
  };
89
129
 
90
130
  // Sankey-specific variables (in addition to shared)
@@ -252,6 +292,13 @@ export const chartTheme = (element) => {
252
292
  },
253
293
  });
254
294
 
295
+ const motion = {
296
+ initialDuration: parseToMS(getProp(element, vars.initialAnimationDuration)),
297
+ fadeInDuration: parseToMS(getProp(element, vars.fadeInAnimationDuration)),
298
+ elasticEasing: parseEasing(getProp(element, vars.elasticEasing)),
299
+ linearEasing: parseEasing(getProp(element, vars.linearEasing)),
300
+ };
301
+
255
302
  return {
256
303
  axisDefaults: {
257
304
  crosshair: {
@@ -276,6 +323,12 @@ export const chartTheme = (element) => {
276
323
  font: defaultFont(element),
277
324
  }
278
325
  },
326
+ categoryAxis: {
327
+ highlight: {
328
+ color: "#8C9FD9",
329
+ opacity: 0.15
330
+ }
331
+ },
279
332
  chartArea: {
280
333
  background: chartBg,
281
334
  },
@@ -329,6 +382,7 @@ export const chartTheme = (element) => {
329
382
  title: {
330
383
  font: paneTitleFont(element),
331
384
  }
332
- }
385
+ },
386
+ motion
333
387
  };
334
388
  };
@@ -4,11 +4,19 @@ import { INITIAL_ANIMATION_DURATION, BAR, START_SCALE } from '../constants';
4
4
 
5
5
  import { X, Y } from '../../common/constants';
6
6
  import { interpolateValue, setDefaultOptions } from '../../common';
7
+ import { easingMap } from './easingMap';
7
8
 
8
9
  class BarChartAnimation extends draw.Animation {
9
10
 
10
11
  setup() {
11
12
  const { element, options } = this;
13
+ if (options.motion && options.motion.enabled) {
14
+ options.motion.duration = options.motionConfig.initialDuration;
15
+ if (options.easing && easingMap[options.easing]) {
16
+ options.motion.easing = easingMap[options.easing];
17
+ }
18
+ }
19
+
12
20
  const bbox = element.bbox();
13
21
 
14
22
  if (bbox) {
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class BubbleAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
11
+ this.options.motion.easing = this.options.motionConfig.elasticEasing;
12
+ }
9
13
  const center = this.center = this.element.bbox().center();
10
14
  this.element.transform(geom.transform()
11
15
  .scale(START_SCALE, START_SCALE, center)
@@ -3,9 +3,16 @@ import { drawing as draw } from '@progress/kendo-drawing';
3
3
  import { INITIAL_ANIMATION_DURATION } from '../constants';
4
4
 
5
5
  import { interpolateValue, setDefaultOptions } from '../../common';
6
+ import { easingMap } from './easingMap';
6
7
 
7
8
  class ClipAnimation extends draw.Animation {
8
9
  setup() {
10
+ if (this.options.motion && this.options.motion.enabled) {
11
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
12
+ if (this.options.easing && easingMap[this.options.easing]) {
13
+ this.options.motion.easing = easingMap[this.options.easing];
14
+ }
15
+ }
9
16
  this._setEnd(this.options.box.x1);
10
17
  }
11
18
 
@@ -0,0 +1,8 @@
1
+ export const easingMap = {
2
+ swing: [0.3583, 0, 0.6422, 1],
3
+ linear: [0, 0, 1, 1],
4
+ ease: [0.25, 0.1, 0.25, 1],
5
+ "ease-in": [0.42, 0, 1, 1],
6
+ "ease-out": [0, 0, 0.58, 1],
7
+ "ease-in-out": [0.42, 0, 0.58, 1]
8
+ };
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class FadeInAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.fadeInDuration;
11
+ this.options.motion.easing = this.options.motionConfig.linearEasing;
12
+ }
9
13
  this.fadeTo = this.element.opacity();
10
14
  this.element.opacity(0);
11
15
  }
@@ -6,6 +6,10 @@ import { setDefaultOptions } from '../../common';
6
6
 
7
7
  class PieAnimation extends draw.Animation {
8
8
  setup() {
9
+ if (this.options.motion && this.options.motion.enabled) {
10
+ this.options.motion.duration = this.options.motionConfig.initialDuration;
11
+ this.options.motion.easing = this.options.motionConfig.elasticEasing;
12
+ }
9
13
  this.element.transform(geom.transform()
10
14
  .scale(START_SCALE, START_SCALE, this.options.center)
11
15
  );
@@ -366,11 +366,15 @@ class Chart {
366
366
  model.renderVisual();
367
367
 
368
368
  const transitions = this.options.transitions;
369
+ const motionConfig = this.options.motion || {};
370
+ const motion = {
371
+ enabled: motionConfig.enabled
372
+ };
369
373
  if (transitions !== false) {
370
374
  model.traverse(function(element) {
371
375
  if (element.animation) {
372
376
  const loading = (transitions && transitions !== true) ? transitions.loading : transitions;
373
- element.animation.options = Object.assign({}, element.animation.options, loading);
377
+ element.animation.options = Object.assign({}, element.animation.options, loading, {motionConfig, motion});
374
378
  element.animation.setup();
375
379
  }
376
380
  });
@@ -477,6 +481,75 @@ class Chart {
477
481
  return visual;
478
482
  }
479
483
 
484
+ _toggleCategoryHighlight(index, axis) {
485
+ const plotArea = this._plotArea;
486
+ const categoryAxis = axis || plotArea.categoryAxis;
487
+
488
+ if (this._categoryHighlightIndex === index && this._categoryHighlightAxis === categoryAxis) {
489
+ return;
490
+ }
491
+
492
+ this._categoryHighlightIndex = index;
493
+ this._categoryHighlightAxis = categoryAxis;
494
+
495
+ const highlightOptions = (categoryAxis.options || {}).highlight;
496
+
497
+ if (!highlightOptions || !highlightOptions.visible || index === null) {
498
+ if (this._categoryHighlight) {
499
+ this._categoryHighlight.visible(false);
500
+ }
501
+ return;
502
+ }
503
+
504
+ if (!this._categoryHighlight) {
505
+ this._categoryHighlight = new draw.Path({
506
+ fill: {
507
+ color: highlightOptions.color,
508
+ opacity: highlightOptions.opacity
509
+ },
510
+ stroke: highlightOptions.border || null,
511
+ closed: true
512
+ });
513
+
514
+ this.surface.draw(this._categoryHighlight);
515
+ } else {
516
+ const options = this._categoryHighlight.options;
517
+ options.set("fill", {
518
+ color: highlightOptions.color,
519
+ opacity: highlightOptions.opacity
520
+ });
521
+ options.set("stroke", highlightOptions.border);
522
+ }
523
+
524
+ const slot = categoryAxis.getSlot(index);
525
+ const rect = slot.toRect();
526
+ let plotBox = plotArea.box;
527
+
528
+ if (categoryAxis.pane) {
529
+ plotBox = categoryAxis.pane.chartsBox();
530
+ }
531
+
532
+ if (categoryAxis.options.vertical) {
533
+ rect.origin.x = plotBox.x1;
534
+ rect.size.width = plotBox.width();
535
+ } else {
536
+ rect.origin.y = plotBox.y1;
537
+ rect.size.height = plotBox.height();
538
+ }
539
+
540
+ const newPath = draw.Path.fromRect(rect);
541
+ this._categoryHighlight.segments.elements(newPath.segments.elements());
542
+ this._categoryHighlight.visible(true);
543
+ }
544
+
545
+ _categoryHighlightEnabled() {
546
+ const plotArea = this._plotArea;
547
+ const categoryAxis = plotArea.categoryAxis || {};
548
+ const highlightOptions = (categoryAxis.options || {}).highlight;
549
+
550
+ return plotArea instanceof CategoricalPlotArea && highlightOptions && highlightOptions.visible;
551
+ }
552
+
480
553
  _sharedTooltip() {
481
554
  return this._plotArea instanceof CategoricalPlotArea && this.options.tooltip && this.options.tooltip.shared;
482
555
  }
@@ -1792,6 +1865,22 @@ class Chart {
1792
1865
  if (this._sharedTooltip()) {
1793
1866
  this._trackSharedTooltip(coords, e);
1794
1867
  }
1868
+
1869
+ if (this._categoryHighlightEnabled()) {
1870
+ this._trackCategoryHighlight(coords);
1871
+ }
1872
+ }
1873
+
1874
+ _trackCategoryHighlight(coords) {
1875
+ const { _plotArea: plotArea } = this;
1876
+ const categoryAxis = plotArea.categoryAxisByPoint(coords) || plotArea.categoryAxis;
1877
+
1878
+ if (plotArea.backgroundContainsPoint(coords)) {
1879
+ const index = categoryAxis.pointCategoryIndex(coords);
1880
+ this._toggleCategoryHighlight(index, categoryAxis);
1881
+ } else {
1882
+ this._toggleCategoryHighlight(null);
1883
+ }
1795
1884
  }
1796
1885
 
1797
1886
  _trackCrosshairs(coords) {
@@ -1858,6 +1947,8 @@ class Chart {
1858
1947
  plotArea.hideCrosshairs();
1859
1948
 
1860
1949
  this._unsetActivePoint(options);
1950
+
1951
+ this._toggleCategoryHighlight(null);
1861
1952
  }
1862
1953
 
1863
1954
  _unsetActivePoint(options) {
@@ -1997,7 +2088,7 @@ class Chart {
1997
2088
  }
1998
2089
 
1999
2090
  _shouldAttachMouseMove() {
2000
- return this._plotArea.crosshairs.length || (this._tooltip && this._sharedTooltip()) || this.requiresHandlers([ PLOT_AREA_HOVER, PLOT_AREA_LEAVE ]);
2091
+ return this._plotArea.crosshairs.length || (this._tooltip && this._sharedTooltip()) || this._categoryHighlightEnabled() || this.requiresHandlers([ PLOT_AREA_HOVER, PLOT_AREA_LEAVE ]);
2001
2092
  }
2002
2093
 
2003
2094
  updateMouseMoveHandler() {
@@ -2311,6 +2402,9 @@ function triggerPaneRender(panes) {
2311
2402
 
2312
2403
  setDefaultOptions(Chart, {
2313
2404
  renderAs: "",
2405
+ motion: {
2406
+ enabled: false,
2407
+ },
2314
2408
  chartArea: {},
2315
2409
  legend: {
2316
2410
  visible: true,
@@ -2324,7 +2418,12 @@ setDefaultOptions(Chart, {
2324
2418
  }
2325
2419
  }
2326
2420
  },
2327
- categoryAxis: {},
2421
+ categoryAxis: {
2422
+ highlight: {
2423
+ visible: false,
2424
+ border: null
2425
+ }
2426
+ },
2328
2427
  seriesDefaults: {
2329
2428
  type: COLUMN,
2330
2429
  data: [],
@@ -883,6 +883,21 @@ class CategoricalPlotArea extends PlotAreaBase {
883
883
 
884
884
  return sortableSeries || stackableSeries && point.options.isStacked;
885
885
  }
886
+
887
+ categoryAxisByPoint(point) {
888
+ const axes = this.axes;
889
+ let axis;
890
+
891
+ for (let i = 0; i < axes.length; i++) {
892
+ const current = axes[i];
893
+ if (current instanceof CategoryAxis && current.pane.chartsBox().containsPoint(point)) {
894
+ axis = current;
895
+ break;
896
+ }
897
+ }
898
+
899
+ return axis;
900
+ }
886
901
  }
887
902
 
888
903
  function updateAxisOptions(targetOptions, axis, options) {