apexcharts 8.2.0 → 8.3.0

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.
Files changed (222) hide show
  1. package/README.md +7 -7
  2. package/dist/apexcharts.common.js +2 -2
  3. package/dist/apexcharts.core.js +271 -173
  4. package/dist/apexcharts.core.min.js +2 -2
  5. package/dist/apexcharts.css +35 -0
  6. package/dist/apexcharts.esm.js +1722 -1527
  7. package/dist/apexcharts.full.js +1459 -1075
  8. package/dist/apexcharts.full.min.js +2 -2
  9. package/dist/apexcharts.js +994 -715
  10. package/dist/apexcharts.min.js +2 -2
  11. package/dist/apexcharts.ssr.common.js +2 -2
  12. package/dist/apexcharts.ssr.esm.js +994 -715
  13. package/dist/area.common.js +2 -2
  14. package/dist/area.esm.js +91 -40
  15. package/dist/bar.common.js +2 -2
  16. package/dist/bar.esm.js +433 -394
  17. package/dist/bar.js +2 -2
  18. package/dist/boxPlot.common.js +2 -2
  19. package/dist/boxPlot.esm.js +364 -322
  20. package/dist/bubble.common.js +2 -2
  21. package/dist/bubble.esm.js +91 -40
  22. package/dist/candlestick.common.js +2 -2
  23. package/dist/candlestick.esm.js +364 -322
  24. package/dist/candlestick.js +2 -2
  25. package/dist/column.common.js +2 -2
  26. package/dist/column.esm.js +433 -394
  27. package/dist/core.common.js +2 -2
  28. package/dist/core.esm.js +271 -173
  29. package/dist/donut.common.js +1 -1
  30. package/dist/donut.esm.js +1 -1
  31. package/dist/dumbbell.common.js +2 -2
  32. package/dist/dumbbell.esm.js +433 -394
  33. package/dist/features/all.common.js +2 -2
  34. package/dist/features/all.esm.js +250 -187
  35. package/dist/features/annotations.common.js +1 -1
  36. package/dist/features/annotations.esm.js +1 -1
  37. package/dist/features/annotations.js +1 -1
  38. package/dist/features/context-menu.common.js +1 -1
  39. package/dist/features/context-menu.esm.js +1 -1
  40. package/dist/features/context-menu.js +1 -1
  41. package/dist/features/drilldown.common.js +2 -2
  42. package/dist/features/drilldown.esm.js +4 -2
  43. package/dist/features/drilldown.js +2 -2
  44. package/dist/features/dumbbell.common.js +1 -1
  45. package/dist/features/dumbbell.esm.js +1 -1
  46. package/dist/features/dumbbell.js +1 -1
  47. package/dist/features/exports.common.js +1 -1
  48. package/dist/features/exports.esm.js +1 -1
  49. package/dist/features/exports.js +1 -1
  50. package/dist/features/facet.common.js +1 -1
  51. package/dist/features/facet.esm.js +1 -1
  52. package/dist/features/facet.js +1 -1
  53. package/dist/features/highlight-filter.common.js +2 -2
  54. package/dist/features/highlight-filter.esm.js +14 -9
  55. package/dist/features/highlight-filter.js +2 -2
  56. package/dist/features/history.common.js +1 -1
  57. package/dist/features/history.esm.js +1 -1
  58. package/dist/features/history.js +1 -1
  59. package/dist/features/ink.common.js +1 -1
  60. package/dist/features/ink.esm.js +1 -1
  61. package/dist/features/ink.js +1 -1
  62. package/dist/features/keyboard.common.js +2 -2
  63. package/dist/features/keyboard.esm.js +97 -145
  64. package/dist/features/keyboard.js +2 -2
  65. package/dist/features/legend.common.js +2 -2
  66. package/dist/features/legend.esm.js +63 -43
  67. package/dist/features/legend.js +2 -2
  68. package/dist/features/link.common.js +1 -1
  69. package/dist/features/link.esm.js +1 -1
  70. package/dist/features/link.js +1 -1
  71. package/dist/features/marks.common.js +1 -1
  72. package/dist/features/marks.esm.js +1 -1
  73. package/dist/features/marks.js +1 -1
  74. package/dist/features/measure.common.js +1 -1
  75. package/dist/features/measure.esm.js +1 -1
  76. package/dist/features/measure.js +1 -1
  77. package/dist/features/morph.common.js +1 -1
  78. package/dist/features/morph.esm.js +1 -1
  79. package/dist/features/morph.js +1 -1
  80. package/dist/features/perspectives.common.js +1 -1
  81. package/dist/features/perspectives.esm.js +1 -1
  82. package/dist/features/perspectives.js +1 -1
  83. package/dist/features/raincloud.common.js +1 -1
  84. package/dist/features/raincloud.esm.js +1 -1
  85. package/dist/features/raincloud.js +1 -1
  86. package/dist/features/renderer-canvas.common.js +2 -2
  87. package/dist/features/renderer-canvas.esm.js +2 -1
  88. package/dist/features/renderer-canvas.js +2 -2
  89. package/dist/features/stats.common.js +1 -1
  90. package/dist/features/stats.esm.js +1 -1
  91. package/dist/features/stats.js +1 -1
  92. package/dist/features/storyboard.common.js +1 -1
  93. package/dist/features/storyboard.esm.js +1 -1
  94. package/dist/features/storyboard.js +1 -1
  95. package/dist/features/streamgraph.common.js +2 -2
  96. package/dist/features/streamgraph.esm.js +3 -2
  97. package/dist/features/streamgraph.js +2 -2
  98. package/dist/features/toolbar.common.js +2 -2
  99. package/dist/features/toolbar.esm.js +92 -2
  100. package/dist/features/toolbar.js +2 -2
  101. package/dist/features/trellis.common.js +2 -2
  102. package/dist/features/trellis.esm.js +83 -47
  103. package/dist/features/trellis.js +2 -2
  104. package/dist/features/waterfall.common.js +1 -1
  105. package/dist/features/waterfall.esm.js +1 -1
  106. package/dist/features/waterfall.js +1 -1
  107. package/dist/features/weave.common.js +1 -1
  108. package/dist/features/weave.esm.js +1 -1
  109. package/dist/features/weave.js +1 -1
  110. package/dist/full.common.js +2 -2
  111. package/dist/full.esm.js +950 -795
  112. package/dist/heatmap.common.js +2 -2
  113. package/dist/heatmap.esm.js +3 -17
  114. package/dist/heatmap.js +2 -2
  115. package/dist/histogram.common.js +2 -2
  116. package/dist/histogram.esm.js +433 -394
  117. package/dist/icicle.common.js +2 -2
  118. package/dist/icicle.esm.js +54 -38
  119. package/dist/icicle.js +2 -2
  120. package/dist/line.common.js +2 -2
  121. package/dist/line.esm.js +91 -40
  122. package/dist/line.js +2 -2
  123. package/dist/locales/en.json +2 -1
  124. package/dist/locales/ro.json +3 -2
  125. package/dist/pictograms.common.js +1 -1
  126. package/dist/pictograms.esm.js +1 -1
  127. package/dist/pictograms.js +1 -1
  128. package/dist/pie.common.js +1 -1
  129. package/dist/pie.esm.js +1 -1
  130. package/dist/pie.js +1 -1
  131. package/dist/polarArea.common.js +1 -1
  132. package/dist/polarArea.esm.js +1 -1
  133. package/dist/radar.common.js +2 -2
  134. package/dist/radar.esm.js +5 -4
  135. package/dist/radar.js +2 -2
  136. package/dist/radialBar.common.js +1 -1
  137. package/dist/radialBar.esm.js +1 -1
  138. package/dist/radialBar.js +1 -1
  139. package/dist/raincloud.common.js +2 -2
  140. package/dist/raincloud.esm.js +72 -26
  141. package/dist/rangeArea.common.js +2 -2
  142. package/dist/rangeArea.esm.js +91 -40
  143. package/dist/rangeBar.common.js +2 -2
  144. package/dist/rangeBar.esm.js +433 -394
  145. package/dist/scatter.common.js +2 -2
  146. package/dist/scatter.esm.js +91 -40
  147. package/dist/streamgraph.common.js +2 -2
  148. package/dist/streamgraph.esm.js +93 -41
  149. package/dist/sunburst.common.js +2 -2
  150. package/dist/sunburst.esm.js +103 -93
  151. package/dist/sunburst.js +2 -2
  152. package/dist/treemap.common.js +2 -2
  153. package/dist/treemap.esm.js +105 -86
  154. package/dist/treemap.js +2 -2
  155. package/dist/unit-shapes.common.js +1 -1
  156. package/dist/unit-shapes.esm.js +1 -1
  157. package/dist/unit-shapes.js +1 -1
  158. package/dist/unit.common.js +2 -2
  159. package/dist/unit.esm.js +280 -202
  160. package/dist/unit.js +2 -2
  161. package/dist/violin.common.js +2 -2
  162. package/dist/violin.esm.js +72 -26
  163. package/dist/violin.js +2 -2
  164. package/dist/waterfall.common.js +2 -2
  165. package/dist/waterfall.esm.js +432 -393
  166. package/package.json +1 -1
  167. package/src/apexcharts.js +13 -6
  168. package/src/assets/apexcharts.css +35 -0
  169. package/src/charts/Bar.js +62 -55
  170. package/src/charts/BarStacked.js +80 -88
  171. package/src/charts/BoxCandleStick.js +28 -31
  172. package/src/charts/Icicle.js +37 -37
  173. package/src/charts/Line.js +118 -42
  174. package/src/charts/Radar.js +5 -7
  175. package/src/charts/RangeBar.js +47 -43
  176. package/src/charts/Sunburst.js +101 -98
  177. package/src/charts/Treemap.js +108 -66
  178. package/src/charts/Unit.js +368 -250
  179. package/src/charts/Violin.js +48 -30
  180. package/src/charts/common/Breadcrumb.js +2 -2
  181. package/src/charts/common/Hierarchy.js +32 -11
  182. package/src/charts/common/Jitter.js +32 -7
  183. package/src/charts/common/bar/DataLabels.js +76 -81
  184. package/src/charts/common/bar/Helpers.js +43 -7
  185. package/src/charts/common/line/Helpers.js +12 -15
  186. package/src/charts/common/partition/NodeTooltip.js +8 -1
  187. package/src/charts/common/partition/Partition.js +33 -1
  188. package/src/charts/common/treemap/Helpers.js +3 -11
  189. package/src/locales/en.json +2 -1
  190. package/src/locales/ro.json +3 -2
  191. package/src/modules/Core.js +4 -0
  192. package/src/modules/CoreUtils.js +18 -42
  193. package/src/modules/Data.js +23 -24
  194. package/src/modules/DataLabels.js +6 -6
  195. package/src/modules/Graphics.js +62 -49
  196. package/src/modules/Markers.js +3 -1
  197. package/src/modules/Range.js +44 -29
  198. package/src/modules/RendererController.js +17 -3
  199. package/src/modules/Scales.js +32 -9
  200. package/src/modules/Series.js +39 -18
  201. package/src/modules/Theme.js +1 -1
  202. package/src/modules/ZoomPanSelection.js +119 -2
  203. package/src/modules/accessibility/KeyboardNavigation.js +114 -186
  204. package/src/modules/animations/LengthTransition.js +22 -2
  205. package/src/modules/drilldown/Breadcrumb.js +5 -2
  206. package/src/modules/helpers/Destroy.js +1 -1
  207. package/src/modules/highlightFilter/HighlightFilter.js +8 -9
  208. package/src/modules/highlightFilter/TreemapPart.js +2 -2
  209. package/src/modules/legend/HeatmapGradientLegend.js +76 -45
  210. package/src/modules/legend/Helpers.js +2 -0
  211. package/src/modules/legend/Legend.js +11 -12
  212. package/src/modules/settings/Options.js +7 -0
  213. package/src/modules/streamgraph/StreamLabels.js +1 -0
  214. package/src/modules/tooltip/Tooltip.js +20 -4
  215. package/src/modules/tooltip/Utils.js +8 -0
  216. package/src/modules/trellis/Trellis.js +6 -0
  217. package/src/modules/trellis/TrellisChrome.js +18 -7
  218. package/src/modules/trellis/TrellisSync.js +2 -0
  219. package/src/renderers/canvas/CanvasGraphics.js +1 -0
  220. package/src/ssr/DOMShim.js +10 -0
  221. package/src/types/internal.d.ts +9 -3
  222. package/types/apexcharts.d.ts +31 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apexcharts",
3
- "version": "8.2.0",
3
+ "version": "8.3.0",
4
4
  "description": "A JavaScript Chart Library",
5
5
  "repository": {
6
6
  "type": "git",
package/src/apexcharts.js CHANGED
@@ -862,6 +862,9 @@ export default class ApexCharts {
862
862
  ) {
863
863
  me.keyboardNavigation?.init()
864
864
  }
865
+ // The focus note is for this render only: one that nav was off for
866
+ // must not pull focus back when a later update turns nav on
867
+ w.interact.keyNav = null
865
868
 
866
869
  if (
867
870
  Environment.isBrowser() &&
@@ -2609,22 +2612,26 @@ export default class ApexCharts {
2609
2612
  * Returns the highest y value in the specified series.
2610
2613
  *
2611
2614
  * @param {number} [seriesIndex=0]
2612
- * @returns {number}
2615
+ * @returns {number | undefined}
2613
2616
  */
2614
2617
  getHighestValueInSeries(seriesIndex = 0) {
2615
- const range = new Range(this.w)
2616
- return range.getMinYMaxY(seriesIndex).highestY
2618
+ return (
2619
+ this.w.seriesData.series[seriesIndex] &&
2620
+ new Range(this.w).getMinYMaxY(seriesIndex).highestY
2621
+ )
2617
2622
  }
2618
2623
 
2619
2624
  /**
2620
2625
  * Returns the lowest y value in the specified series.
2621
2626
  *
2622
2627
  * @param {number} [seriesIndex=0]
2623
- * @returns {number}
2628
+ * @returns {number | undefined}
2624
2629
  */
2625
2630
  getLowestValueInSeries(seriesIndex = 0) {
2626
- const range = new Range(this.w)
2627
- return range.getMinYMaxY(seriesIndex).lowestY
2631
+ return (
2632
+ this.w.seriesData.series[seriesIndex] &&
2633
+ new Range(this.w).getMinYMaxY(seriesIndex).lowestY
2634
+ )
2628
2635
  }
2629
2636
 
2630
2637
  /**
@@ -1040,6 +1040,41 @@ rect.legend-mouseover-inactive,
1040
1040
  cursor: move
1041
1041
  }
1042
1042
 
1043
+ .apexcharts-wheel-hint {
1044
+ position: absolute;
1045
+ inset: 0;
1046
+ z-index: 12;
1047
+ display: flex;
1048
+ align-items: center;
1049
+ justify-content: center;
1050
+ pointer-events: none;
1051
+ opacity: 0;
1052
+ transition: opacity 0.2s ease;
1053
+ }
1054
+
1055
+ .apexcharts-wheel-hint-visible {
1056
+ opacity: 1;
1057
+ }
1058
+
1059
+ .apexcharts-wheel-hint span {
1060
+ padding: 6px 12px;
1061
+ border-radius: 8px;
1062
+ font-size: 13px;
1063
+ color: #fff;
1064
+ background: rgba(28, 28, 31, 0.82);
1065
+ }
1066
+
1067
+ .apexcharts-theme-dark .apexcharts-wheel-hint span {
1068
+ color: #1c1c1f;
1069
+ background: rgba(255, 255, 255, 0.9);
1070
+ }
1071
+
1072
+ @media (prefers-reduced-motion: reduce) {
1073
+ .apexcharts-wheel-hint {
1074
+ transition: none;
1075
+ }
1076
+ }
1077
+
1043
1078
  .apexcharts-menu-icon,
1044
1079
  .apexcharts-measure-icon,
1045
1080
  .apexcharts-pan-icon,
package/src/charts/Bar.js CHANGED
@@ -7,10 +7,10 @@ import Filters from '../modules/Filters'
7
7
  import Graphics from '../modules/Graphics'
8
8
  import Animations, { computeStagger } from '../modules/Animations'
9
9
  import {
10
- datumKey,
11
10
  lengthTransitionEnabled,
12
11
  renderBarExitGhosts,
13
12
  seriesJoin,
13
+ seriesKeys,
14
14
  } from '../modules/animations/LengthTransition'
15
15
  import Series from '../modules/Series'
16
16
  import AxisMapping from '../modules/AxisMapping'
@@ -136,7 +136,7 @@ class Bar {
136
136
  /**
137
137
  * Point the per-series axis state at this series' y axis. Every bar-family
138
138
  * renderer calls it before initialPositions, which reads the baseline by
139
- * this.translationsIndex.
139
+ * this.translationsIndex and flips it for a reversed axis.
140
140
  * @param {number} realIndex
141
141
  * @return {number} translationsIndex
142
142
  */
@@ -145,6 +145,7 @@ class Bar {
145
145
  this.yaxisIndex = this.w.globals.seriesYAxisReverseMap[realIndex]
146
146
  this.translationsIndex = realIndex
147
147
  }
148
+ this.isReversed = this.w.config.yaxis[this.yaxisIndex]?.reversed
148
149
  return this.translationsIndex
149
150
  }
150
151
 
@@ -163,7 +164,7 @@ class Bar {
163
164
  this.series = series
164
165
  this.yRatio = coreUtils.getLogYRatios(this.yRatio)
165
166
 
166
- this.barHelpers.initVariables(series)
167
+ this.barHelpers.initVariables(series, seriesIndex)
167
168
 
168
169
  const ret = graphics.group({
169
170
  class: 'apexcharts-bar-series apexcharts-plot-series',
@@ -210,10 +211,6 @@ class Bar {
210
211
 
211
212
  const translationsIndex = this.setSeriesAxis(realIndex)
212
213
 
213
- this.isReversed =
214
- w.config.yaxis[this.yaxisIndex] &&
215
- w.config.yaxis[this.yaxisIndex].reversed
216
-
217
214
  if (this.isPyramid) {
218
215
  // Recompute the per-segment trapezoid layout once per series, before
219
216
  // drawing any of its segments. Each entry yields the segment's top
@@ -322,6 +319,9 @@ class Bar {
322
319
  if (this.isHorizontal) {
323
320
  paths = this.drawBarPaths({
324
321
  ...pathsParams,
322
+ ...(this.barHelpers.rows && {
323
+ y: initY + this.barHelpers.barRow(realIndex, j) * yDivision,
324
+ }),
325
325
  barHeight,
326
326
  zeroW,
327
327
  yDivision,
@@ -426,6 +426,7 @@ class Bar {
426
426
  series,
427
427
  barHeight: Math.abs(paths.barHeight ? paths.barHeight : barHeight),
428
428
  barWidth: Math.abs(paths.barWidth ? paths.barWidth : barWidth),
429
+ barXPosition: paths.barXPosition,
429
430
  elDataLabelsWrap,
430
431
  elGoalsMarkers,
431
432
  elBarShadows,
@@ -441,15 +442,11 @@ class Bar {
441
442
  // Exit ghosts: previous datums whose keys are gone shrink to their
442
443
  // baseline edge and fade under the reflowing survivors.
443
444
  if (w.globals.previousPaths.length > 0) {
444
- const newKeys = []
445
- for (let j = 0; j < series[i].length; j++) {
446
- newKeys.push(datumKey(w, realIndex, j))
447
- }
448
445
  renderBarExitGhosts({
449
446
  w,
450
447
  elSeries,
451
448
  record: this._prevRecord(realIndex),
452
- newKeys,
449
+ newKeys: this._keys(realIndex),
453
450
  isHorizontal: this.isHorizontal,
454
451
  speed: w.config.chart.animations.dynamicAnimation.speed,
455
452
  })
@@ -461,7 +458,7 @@ class Bar {
461
458
  return ret
462
459
  }
463
460
 
464
- /** @param {{ hl?: any, realIndex?: any, pathFill?: any, lineFill?: any, j?: any, i?: any, columnGroupIndex?: any, pathFrom?: any, pathTo?: any, strokeWidth?: any, elSeries?: any, x?: any, y?: any, y1?: any, y2?: any, series?: any, barHeight?: any, barWidth?: any, barXPosition?: any, barYPosition?: any, elDataLabelsWrap?: any, elGoalsMarkers?: any, elBarShadows?: any, visibleSeries?: any, type?: any, classes?: any, fadeReveal?: boolean }} opts */
461
+ /** @param {{ hl?: any, realIndex?: any, pathFill?: any, lineFill?: any, j?: any, i?: any, columnGroupIndex?: any, pathFrom?: any, pathTo?: any, strokeWidth?: any, elSeries?: any, x?: any, y?: any, y1?: any, y2?: any, series?: any, barHeight?: any, barWidth?: any, barXPosition?: any, barYPosition?: any, elDataLabelsWrap?: any, elGoalsMarkers?: any, elBarShadows?: any, visibleSeries?: any, type?: any, classes?: any, fadeReveal?: boolean, noLabel?: boolean }} opts */
465
462
  renderSeries({
466
463
  hl,
467
464
  realIndex,
@@ -490,6 +487,7 @@ class Bar {
490
487
  type,
491
488
  classes,
492
489
  fadeReveal = false,
490
+ noLabel, // a datum's extra paths (box halves, box lane) label nothing
493
491
  }) {
494
492
  const w = this.w
495
493
  const graphics = new Graphics(this.w, this.ctx)
@@ -619,6 +617,7 @@ class Bar {
619
617
  barYPosition,
620
618
  visibleSeries,
621
619
  hl: hl?.label,
620
+ noLabel,
622
621
  })
623
622
  )
624
623
 
@@ -640,28 +639,23 @@ class Bar {
640
639
 
641
640
  // Drawn only to slide out of view (see _wasOnScreen): removed once the
642
641
  // update has landed, so nothing is left lying off-screen.
642
+ // Culled on the bar's own centre: the label x carries dataLabels.offsetX,
643
+ // which dropped the first or last columns even with labels off.
643
644
  let leaving = false
644
645
  if (!w.globals.isBarHorizontal) {
645
- if (
646
- dataLabelsObj.dataLabelsPos.dataLabelsX +
647
- Math.max(barWidth, w.globals.barPadForNumericAxis) <
648
- 0 ||
649
- dataLabelsObj.dataLabelsPos.dataLabelsX -
650
- Math.max(barWidth, w.globals.barPadForNumericAxis) >
651
- w.layout.gridWidth
652
- ) {
646
+ const cx =
647
+ barXPosition === undefined
648
+ ? dataLabelsObj.dataLabelsPos.dataLabelsX
649
+ : barXPosition + barWidth / 2
650
+ const pad = Math.max(barWidth, w.globals.barPadForNumericAxis)
651
+ if (cx + pad < 0 || cx - pad > w.layout.gridWidth) {
653
652
  if (this._wasOnScreen(realIndex, j)) leaving = true
654
653
  else skipDrawing = true
655
654
  }
656
655
  }
657
656
 
658
- if (
659
- /** @type {Record<string,any>} */ (w.config.series[i]).data[j] &&
660
- /** @type {Record<string,any>} */ (w.config.series[i]).data[j].strokeColor
661
- ) {
662
- lineFill = /** @type {Record<string,any>} */ (w.config.series[i]).data[j]
663
- .strokeColor
664
- }
657
+ const datum = /** @type {any} */ (w.config.series[realIndex]).data[j]
658
+ if (datum && datum.strokeColor) lineFill = datum.strokeColor
665
659
 
666
660
  const fill = pathFill
667
661
 
@@ -711,7 +705,7 @@ class Bar {
711
705
  pathTo,
712
706
  stroke: lineFill,
713
707
  strokeWidth,
714
- strokeLineCap: w.config.stroke.lineCap,
708
+ strokeLinecap: w.config.stroke.lineCap,
715
709
  fill: pathFill,
716
710
  pathToInterp,
717
711
  animationDelay: delay,
@@ -777,12 +771,12 @@ class Bar {
777
771
  cy: dataLabelsObj.dataLabelsPos.bcy,
778
772
  cx: dataLabelsObj.dataLabelsPos.bcx,
779
773
  j,
780
- val: w.seriesData.series[i][j],
774
+ val: w.seriesData.series[realIndex][j],
781
775
  barHeight,
782
776
  barWidth,
783
777
  // Datum identity for the next update's keyed join (see
784
778
  // LengthTransition): survivors match by key, not array position.
785
- 'data:pathKey': datumKey(w, realIndex, j),
779
+ 'data:pathKey': this._keys(realIndex)[j],
786
780
  })
787
781
 
788
782
  // Highlight filter: fade this whole and draw its part above it (below
@@ -886,7 +880,10 @@ class Bar {
886
880
  // band.
887
881
  if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
888
882
  const { nonZeroColumns, zeroEncounters } =
889
- this.barHelpers.getZeroValueEncounters({ i, j })
883
+ this.barHelpers.getZeroValueEncounters({
884
+ i: indexes.realIndex,
885
+ j,
886
+ })
890
887
 
891
888
  if (nonZeroColumns > 0) {
892
889
  barHeight = (this.seriesLen * barHeight) / nonZeroColumns
@@ -1054,7 +1051,7 @@ class Bar {
1054
1051
  } else {
1055
1052
  if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
1056
1053
  const { nonZeroColumns, zeroEncounters } =
1057
- this.barHelpers.getZeroValueEncounters({ i, j })
1054
+ this.barHelpers.getZeroValueEncounters({ i: realIndex, j })
1058
1055
 
1059
1056
  if (nonZeroColumns > 0) {
1060
1057
  barWidth = (this.seriesLen * barWidth) / nonZeroColumns
@@ -1122,6 +1119,7 @@ class Bar {
1122
1119
  /** @param {{x: any, barWidth: any, realIndex: any, j: any}} opts */
1123
1120
  getBarXForNumericXAxis({ x, barWidth, realIndex, j }) {
1124
1121
  const w = this.w
1122
+ const overlay = isHistogramOverlay(w)
1125
1123
  let sxI = realIndex
1126
1124
  if (!w.seriesData.seriesX[realIndex].length) {
1127
1125
  sxI = w.globals.maxValsInArrayIndex
@@ -1133,15 +1131,15 @@ class Bar {
1133
1131
  // ranges cannot drift apart.
1134
1132
  x =
1135
1133
  AxisMapping.dataXToPx(w, w.seriesData.seriesX[sxI][j]) -
1136
- (barWidth * this.seriesLen) / 2
1134
+ (barWidth * (overlay ? 1 : this.seriesLen)) / 2
1137
1135
  }
1138
1136
 
1139
1137
  // Overlaid histogram series all sit on the bin, rather than stepping across
1140
- // it one series-width at a time. `seriesLen` is already forced to 1 above,
1141
- // so `barWidth` is the whole bin and stepping would march each series clear
1142
- // of the last.
1138
+ // it one series-width at a time. `barWidth` is already the whole bin, so
1139
+ // centring on seriesLen bars or stepping would march each series clear of
1140
+ // the last.
1143
1141
  return {
1144
- barXPosition: x + (isHistogramOverlay(w) ? 0 : barWidth * this.visibleI),
1142
+ barXPosition: x + (overlay ? 0 : barWidth * this.visibleI),
1145
1143
  x,
1146
1144
  }
1147
1145
  }
@@ -1163,7 +1161,18 @@ class Bar {
1163
1161
  _wasOnScreen(realIndex, j) {
1164
1162
  if (!lengthTransitionEnabled(this.w)) return false
1165
1163
  const keyed = this._prevKeyedPaths(realIndex)
1166
- return !!keyed && keyed.has(datumKey(this.w, realIndex, j))
1164
+ return !!keyed && keyed.has(this._keys(realIndex)[j])
1165
+ }
1166
+
1167
+ /**
1168
+ * This render's datum keys for a series (see seriesKeys), as stamped on
1169
+ * each bar and looked up in the previous render's.
1170
+ * @param {number} realIndex
1171
+ * @returns {string[]}
1172
+ */
1173
+ _keys(realIndex) {
1174
+ const c = (/** @type {any} */ (this)._keyCache ||= {})
1175
+ return (c[realIndex] ||= seriesKeys(this.w, realIndex))
1167
1176
  }
1168
1177
 
1169
1178
  /**
@@ -1235,20 +1244,21 @@ class Bar {
1235
1244
  if (lengthTransitionEnabled(w) && w.globals.previousPaths.length > 0) {
1236
1245
  const record = this._prevRecord(realIndex)
1237
1246
  const dataLen = w.seriesData.series[realIndex]?.length ?? 0
1247
+ const keyed = record && this._prevKeyedPaths(realIndex)
1238
1248
  if (!record) {
1239
1249
  result = dataLen > 0 // a brand-new series entering
1240
- } else if (record.paths.length !== dataLen) {
1241
- result = true
1250
+ } else if (keyed) {
1251
+ // Off-screen bars are not drawn, so a zoomed series has fewer paths
1252
+ // than points and comparing counts called every value update a
1253
+ // layout change. The datum count comes from the previous frame; a
1254
+ // drawn datum whose key is gone has left.
1255
+ const now = new Set(this._keys(realIndex))
1256
+ result =
1257
+ (w.globals.prevStreamFrame?.seriesY?.[realIndex]?.length ??
1258
+ dataLen) !== dataLen ||
1259
+ [...keyed.keys()].some((k) => !now.has(k))
1242
1260
  } else {
1243
- const keyed = this._prevKeyedPaths(realIndex)
1244
- if (keyed) {
1245
- for (let j = 0; j < dataLen; j++) {
1246
- if (!keyed.has(datumKey(w, realIndex, j))) {
1247
- result = true
1248
- break
1249
- }
1250
- }
1251
- }
1261
+ result = record.paths.length !== dataLen
1252
1262
  }
1253
1263
  }
1254
1264
  this._ltCache[realIndex] = result
@@ -1317,7 +1327,7 @@ class Bar {
1317
1327
  let isNewDatum = false
1318
1328
  const keyed = this._prevKeyedPaths(realIndex)
1319
1329
  if (keyed) {
1320
- const prev = keyed.get(datumKey(w, realIndex, j))
1330
+ const prev = keyed.get(this._keys(realIndex)[j])
1321
1331
  if (prev) {
1322
1332
  oldD = prev.d
1323
1333
  } else {
@@ -1535,14 +1545,11 @@ class Bar {
1535
1545
  * @returns {boolean | null} null when there is no previous record to consult
1536
1546
  */
1537
1547
  getPreviousFlip(realIndex, j) {
1538
- const w = this.w
1539
1548
  const record = this._prevRecord(realIndex)
1540
1549
  if (!record) return null
1541
1550
 
1542
1551
  const keyed = this._prevKeyedPaths(realIndex)
1543
- const prev = keyed
1544
- ? keyed.get(datumKey(w, realIndex, j))
1545
- : record.paths[j]
1552
+ const prev = keyed ? keyed.get(this._keys(realIndex)[j]) : record.paths[j]
1546
1553
  return prev ? !!prev.flip : null
1547
1554
  }
1548
1555
 
@@ -27,7 +27,7 @@ class BarStacked extends Bar {
27
27
  series = coreUtils.getLogSeries(series)
28
28
  this.yRatio = coreUtils.getLogYRatios(this.yRatio)
29
29
 
30
- this.barHelpers.initVariables(series)
30
+ this.barHelpers.initVariables(series, seriesIndex)
31
31
 
32
32
  if (w.config.chart.stackType === '100%') {
33
33
  series = w.globals.comboCharts
@@ -74,10 +74,6 @@ class BarStacked extends Bar {
74
74
 
75
75
  const translationsIndex = this.setSeriesAxis(realIndex)
76
76
 
77
- this.isReversed =
78
- w.config.yaxis[this.yaxisIndex] &&
79
- w.config.yaxis[this.yaxisIndex].reversed
80
-
81
77
  // el to which series will be drawn
82
78
  let elSeries = this.graphics.group({
83
79
  class: `apexcharts-series`,
@@ -160,6 +156,11 @@ class BarStacked extends Bar {
160
156
  if (this.isHorizontal) {
161
157
  paths = this.drawStackedBarPaths({
162
158
  ...commonPathOpts,
159
+ ...(this.barHelpers.rows && {
160
+ y:
161
+ initPositions.y +
162
+ this.barHelpers.barRow(realIndex, j) * yDivision,
163
+ }),
163
164
  zeroW,
164
165
  barHeight,
165
166
  yDivision,
@@ -263,6 +264,7 @@ class BarStacked extends Bar {
263
264
  series,
264
265
  barHeight,
265
266
  barWidth,
267
+ barXPosition: paths.barXPosition,
266
268
  elDataLabelsWrap,
267
269
  elGoalsMarkers,
268
270
  type: 'bar',
@@ -286,7 +288,7 @@ class BarStacked extends Bar {
286
288
  ret.add(elSeries)
287
289
  }
288
290
 
289
- if (heldMirrors) this.settleHeldMirrors()
291
+ if (heldMirrors) this.settleHeldMirrors(ret)
290
292
 
291
293
  return ret
292
294
  }
@@ -310,8 +312,12 @@ class BarStacked extends Bar {
310
312
  * 'bottom' → 'top', where the endpoint really is rounded at the other end;
311
313
  * everywhere else the mirror is an exact identity on the settled shape, so
312
314
  * removing it changes nothing on screen.
315
+ *
316
+ * The marks are collected now, not when the timer fires: a query then also
317
+ * stripped the mirrors a LATER update was still holding mid-tween.
318
+ * @param {any} [ret] this draw's series group (default: the whole chart)
313
319
  */
314
- settleHeldMirrors() {
320
+ settleHeldMirrors(ret) {
315
321
  const w = this.w
316
322
  if (!Environment.isBrowser()) return
317
323
 
@@ -319,18 +325,18 @@ class BarStacked extends Bar {
319
325
  // Matches the collapsing-series hold: morphSVG applies the stagger delay
320
326
  // twice, so the real tail runs past dynamicAnimation.speed on its own.
321
327
  const hold = (anim.dynamicAnimation.speed || 0) + (anim.speed || 0) + 100
328
+ const held = (ret ? ret.node : w.dom.baseEl).querySelectorAll(
329
+ '.apexcharts-flip-held',
330
+ )
322
331
 
323
332
  setTimeout(() => {
324
- if (w.globals.isDestroyed || !Utils.elementExists(w.dom.baseEl)) return
325
- w.dom.baseEl
326
- .querySelectorAll('.apexcharts-flip-held')
327
- .forEach((/** @type {Element} */ el) => {
328
- el.classList.remove(
329
- 'apexcharts-flip-held',
330
- 'apexcharts-flip-y',
331
- 'apexcharts-flip-x',
332
- )
333
- })
333
+ held.forEach((/** @type {Element} */ el) => {
334
+ el.classList.remove(
335
+ 'apexcharts-flip-held',
336
+ 'apexcharts-flip-y',
337
+ 'apexcharts-flip-x',
338
+ )
339
+ })
334
340
  }, hold)
335
341
  }
336
342
 
@@ -454,53 +460,34 @@ class BarStacked extends Bar {
454
460
  const j = indexes.j
455
461
  const realIndex = indexes.realIndex
456
462
  const translationsIndex = indexes.translationsIndex
463
+ // past the end of a short series reads as null, as a padded one would
464
+ const v = /** @type {any} */ (this.series)[i]?.[j] ?? null
457
465
 
458
- let prevBarW = 0
459
- for (let k = 0; k < this.groupCtx.prevXF.length; k++) {
460
- prevBarW = prevBarW + this.groupCtx.prevXF[k][j]
461
- }
462
-
463
- // Where this series sits in its group's stack. See the note in
464
- // drawStackedColumnPaths: the index has to count the series this renderer
465
- // has already drawn for the group, which is exactly what prevX holds.
466
- const gsi = this.groupCtx.prevX.length
467
-
468
- if (gsi > 0) {
469
- let bXP = zeroW
470
-
471
- if (this.groupCtx.prevXVal[gsi - 1][j] < 0) {
472
- bXP =
473
- /** @type {any} */ (this.series)[i]?.[j] >= 0
474
- ? this.groupCtx.prevX[gsi - 1][j] +
475
- prevBarW -
476
- (this.isReversed ? prevBarW : 0) * 2
477
- : this.groupCtx.prevX[gsi - 1][j]
478
- } else if (this.groupCtx.prevXVal[gsi - 1][j] >= 0) {
479
- bXP =
480
- /** @type {any} */ (this.series)[i]?.[j] >= 0
481
- ? this.groupCtx.prevX[gsi - 1][j]
482
- : this.groupCtx.prevX[gsi - 1][j] -
483
- prevBarW +
484
- (this.isReversed ? prevBarW : 0) * 2
485
- }
486
-
487
- barXPosition = bXP
466
+ // The layer below is found by x on a numeric axis, as for columns (see
467
+ // drawStackedColumnPaths): a series without a row's x leaves that row's
468
+ // stack as it was.
469
+ const g = this.groupCtx
470
+ const key = w.axisFlags.isXNumeric ? w.seriesData.seriesX[realIndex][j] : j
471
+ const top = g.tops.get(key)
472
+ if (top) {
473
+ const [prevX, prevVal, prevBarW] = top
474
+ const flip = (this.isReversed ? prevBarW : 0) * 2
475
+ barXPosition =
476
+ prevVal < 0
477
+ ? v >= 0
478
+ ? prevX + prevBarW - flip
479
+ : prevX
480
+ : v >= 0
481
+ ? prevX
482
+ : prevX - prevBarW + flip
488
483
  } else {
489
- // the first series will not have prevX values
484
+ // nothing is stacked in this row yet
490
485
  barXPosition = zeroW
491
486
  }
492
487
 
493
- if (/** @type {any} */ (this.series)[i]?.[j] === null) {
494
- x = barXPosition
495
- } else {
496
- x =
497
- barXPosition +
498
- /** @type {any} */ (this.series)[i]?.[j] / this.invertedYRatio -
499
- (this.isReversed
500
- ? /** @type {any} */ (this.series)[i]?.[j] / this.invertedYRatio
501
- : 0) *
502
- 2
503
- }
488
+ x =
489
+ barXPosition +
490
+ (v / this.invertedYRatio) * (this.isReversed ? -1 : 1)
504
491
 
505
492
  const paths = this.barHelpers.getBarpaths({
506
493
  barYPosition,
@@ -516,6 +503,8 @@ class BarStacked extends Bar {
516
503
  w,
517
504
  })
518
505
 
506
+ this._setTop(key, top, g.xArrj[j], g.xArrjVal[j], g.xArrjF[j], realIndex, j)
507
+
519
508
  this.barHelpers.barBackground({
520
509
  j,
521
510
  i,
@@ -561,6 +550,7 @@ class BarStacked extends Bar {
561
550
  const bc = indexes.bc
562
551
  const realIndex = indexes.realIndex
563
552
  const translationsIndex = indexes.translationsIndex
553
+ const v = /** @type {any} */ (this.series)[i]?.[j] ?? null
564
554
 
565
555
  if (w.axisFlags.isXNumeric) {
566
556
  let seriesVal = w.seriesData.seriesX[realIndex][j]
@@ -589,12 +579,12 @@ class BarStacked extends Bar {
589
579
  let bYP
590
580
  if (prevVal < 0) {
591
581
  bYP =
592
- /** @type {any} */ (this.series)[i]?.[j] >= 0
582
+ v >= 0
593
583
  ? prevYValue - prevBarH + (this.isReversed ? prevBarH : 0) * 2
594
584
  : prevYValue
595
585
  } else if (prevVal >= 0) {
596
586
  bYP =
597
- /** @type {any} */ (this.series)[i]?.[j] >= 0
587
+ v >= 0
598
588
  ? prevYValue
599
589
  : prevYValue + prevBarH - (this.isReversed ? prevBarH : 0) * 2
600
590
  }
@@ -621,20 +611,10 @@ class BarStacked extends Bar {
621
611
  barYPosition = zeroH
622
612
  }
623
613
 
624
- if (/** @type {any} */ (this.series)[i]?.[j]) {
625
- y =
626
- barYPosition -
627
- /** @type {any} */ (this.series)[i]?.[j] /
628
- this.yRatio[translationsIndex] +
629
- (this.isReversed
630
- ? /** @type {any} */ (this.series)[i]?.[j] /
631
- this.yRatio[translationsIndex]
632
- : 0) *
633
- 2
634
- } else {
635
- // fixes #3610
636
- y = barYPosition
637
- }
614
+ // a null stays on its layer below (#3610)
615
+ y =
616
+ barYPosition -
617
+ (v / this.yRatio[translationsIndex]) * (this.isReversed ? -1 : 1)
638
618
 
639
619
  const paths = this.barHelpers.getColumnPaths({
640
620
  barXPosition,
@@ -642,7 +622,9 @@ class BarStacked extends Bar {
642
622
  y1: barYPosition,
643
623
  y2: y,
644
624
  yRatio: this.yRatio[translationsIndex],
645
- strokeWidth: this.strokeWidth,
625
+ // the series' width even for an empty segment, so a hidden layer keeps
626
+ // its place, but no inset at all when no stroke is drawn
627
+ strokeWidth: w.config.stroke.show ? this.strokeWidth : 0,
646
628
  isReversed: this.isReversed,
647
629
  series: this.series,
648
630
  realIndex: indexes.realIndex,
@@ -651,17 +633,7 @@ class BarStacked extends Bar {
651
633
  w,
652
634
  })
653
635
 
654
- const t = g.yArrj[j]
655
- if (!isNaN(t)) {
656
- g.tops.set(key, [
657
- t,
658
- g.yArrjVal[j],
659
- (top ? top[2] : 0) + g.yArrjF[j],
660
- (top ? top[3] : 0) + (w.seriesData.series[realIndex][j] || 0),
661
- top ? Math.min(top[0], top[4]) : 1 / 0,
662
- top ? Math.max(top[0], top[5]) : -1 / 0,
663
- ])
664
- }
636
+ this._setTop(key, top, g.yArrj[j], g.yArrjVal[j], g.yArrjF[j], realIndex, j)
665
637
 
666
638
  this.barHelpers.barBackground({
667
639
  bc,
@@ -681,13 +653,33 @@ class BarStacked extends Bar {
681
653
  zeroH,
682
654
  realIndex,
683
655
  j,
684
- 0,
656
+ translationsIndex,
685
657
  ),
686
658
  barXPosition,
687
659
  x: w.axisFlags.isXNumeric ? x : x + xDivision,
688
660
  y,
689
661
  }
690
662
  }
663
+
664
+ /**
665
+ * Record the segment just drawn as the top of its stack. Per key it holds
666
+ * the top and value of the last segment drawn there, the summed lengths and
667
+ * raw values of every segment so far (the total label's value), and the
668
+ * extreme tops below that segment (the total label's position).
669
+ * @param {any} key @param {any} top @param {number} t @param {any} val
670
+ * @param {number} len @param {number} realIndex @param {number} j
671
+ */
672
+ _setTop(key, top, t, val, len, realIndex, j) {
673
+ if (isNaN(t)) return
674
+ this.groupCtx.tops.set(key, [
675
+ t,
676
+ val,
677
+ (top ? top[2] : 0) + len,
678
+ (top ? top[3] : 0) + (this.w.seriesData.series[realIndex][j] || 0),
679
+ top ? Math.min(top[0], top[4]) : 1 / 0,
680
+ top ? Math.max(top[0], top[5]) : -1 / 0,
681
+ ])
682
+ }
691
683
  }
692
684
 
693
685
  export default BarStacked