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
@@ -48,6 +48,8 @@ export default class HeatmapGradientLegend {
48
48
  this._max = 0
49
49
  /** @type {any} */
50
50
  this._geom = null
51
+ /** @type {any} */
52
+ this._grad = null
51
53
  /** @type {any[]} */
52
54
  this._bandHitEls = []
53
55
  /** @type {number} Currently highlighted band index (-1 = none). */
@@ -58,6 +60,8 @@ export default class HeatmapGradientLegend {
58
60
  * @type {HTMLElement|null}
59
61
  */
60
62
  this._targetEl = null
63
+ /** @type {(() => any[])|null} detached mode: every panel's w */
64
+ this.panelsW = null
61
65
  this._onCellEnter = this._onCellEnter.bind(this)
62
66
  this._onCellLeave = this._onCellLeave.bind(this)
63
67
  this._onBandEnter = this._onBandEnter.bind(this)
@@ -217,15 +221,8 @@ export default class HeatmapGradientLegend {
217
221
  linearGrad.setAttribute('y2', '0')
218
222
  }
219
223
 
220
- const { min, max, stops, bands } = this._computeStops()
221
- this._min = min
222
- this._max = max
223
- stops.forEach((s) => {
224
- const stopEl = BrowserAPIs.createElementNS(SVG_NS, 'stop')
225
- stopEl.setAttribute('offset', `${(s.percent * 100).toFixed(2)}%`)
226
- stopEl.setAttribute('stop-color', s.color)
227
- linearGrad.appendChild(stopEl)
228
- })
224
+ this._grad = linearGrad
225
+ const { min, max, bands } = this._paintStops()
229
226
  defs.appendChild(linearGrad)
230
227
  svg.appendChild(defs)
231
228
 
@@ -498,6 +495,9 @@ export default class HeatmapGradientLegend {
498
495
  * Honors `legend.offsetX/offsetY` for user nudging. Safe to call repeatedly.
499
496
  */
500
497
  repositionToPlot() {
498
+ // The cells' y extent and hasNegs exist only now (draw runs before
499
+ // coreCalculations), so shade the strip again against them.
500
+ if (this._grad) this._paintStops()
501
501
  if (!Environment.isBrowser()) return
502
502
  // Detached mode (trellis): the host slot owns layout entirely.
503
503
  if (this._targetEl) return
@@ -672,7 +672,7 @@ export default class HeatmapGradientLegend {
672
672
  if (idx < 0 || idx === this._activeBandIndex) return
673
673
  this._activeBandIndex = idx
674
674
  this.ctx?.events?.fireEvent?.('legendHover', [this.ctx, idx, w])
675
- new Series(w).highlightRangeInSeries(idx, 'highlight')
675
+ this._highlightRange(idx, 'highlight')
676
676
  }
677
677
 
678
678
  /** Leaving a band clears the highlight. */
@@ -680,7 +680,18 @@ export default class HeatmapGradientLegend {
680
680
  if (this._activeBandIndex < 0) return
681
681
  const idx = this._activeBandIndex
682
682
  this._activeBandIndex = -1
683
- new Series(this.w).highlightRangeInSeries(idx, 'reset')
683
+ this._highlightRange(idx, 'reset')
684
+ }
685
+
686
+ /**
687
+ * A shared trellis strip sets `panelsW` so a band reaches every panel.
688
+ * @param {number} idx
689
+ * @param {'highlight'|'reset'} action
690
+ */
691
+ _highlightRange(idx, action) {
692
+ ;(this.panelsW?.() || [this.w]).forEach((/** @type {any} */ w) =>
693
+ new Series(w).highlightRangeInSeries(idx, action),
694
+ )
684
695
  }
685
696
 
686
697
  _attachHoverListeners() {
@@ -702,11 +713,12 @@ export default class HeatmapGradientLegend {
702
713
  * @param {...any} args
703
714
  */
704
715
  _onCellEnter(...args) {
705
- const w = this.w
706
716
  if (!this.arrowEl) return
707
717
  // The opts object is the last argument.
708
718
  const opts = args[args.length - 1]
709
719
  if (!opts || typeof opts !== 'object') return
720
+ // the hovered chart: a shared trellis strip hears every panel
721
+ const w = opts.w || this.w
710
722
  const i = opts.seriesIndex
711
723
  const j = opts.dataPointIndex
712
724
  if (typeof i !== 'number' || typeof j !== 'number') return
@@ -845,6 +857,22 @@ export default class HeatmapGradientLegend {
845
857
  return polygon
846
858
  }
847
859
 
860
+ /** (Re)write the strip's stops; returns `_computeStops()`. */
861
+ _paintStops() {
862
+ const r = this._computeStops()
863
+ const grad = this._grad
864
+ this._min = r.min
865
+ this._max = r.max
866
+ grad.innerHTML = ''
867
+ r.stops.forEach((s) => {
868
+ const stopEl = BrowserAPIs.createElementNS(SVG_NS, 'stop')
869
+ stopEl.setAttribute('offset', `${(s.percent * 100).toFixed(2)}%`)
870
+ stopEl.setAttribute('stop-color', s.color)
871
+ grad.appendChild(stopEl)
872
+ })
873
+ return r
874
+ }
875
+
848
876
  /**
849
877
  * Build gradient stops + return effective min/max.
850
878
  * - If `colorScale.ranges` is set, stops are placed at each range boundary
@@ -898,7 +926,9 @@ export default class HeatmapGradientLegend {
898
926
  // one-sided = the historical expand-not-clamp semantics.
899
927
  let min = dataMin
900
928
  let max = dataMax
901
- if (typeof cs.min !== 'undefined' && typeof cs.max !== 'undefined' && cs.max > cs.min) {
929
+ const csWindow =
930
+ typeof cs.min !== 'undefined' && typeof cs.max !== 'undefined' && cs.max > cs.min
931
+ if (csWindow) {
902
932
  min = cs.min
903
933
  max = cs.max
904
934
  } else {
@@ -956,45 +986,46 @@ export default class HeatmapGradientLegend {
956
986
  // is the documented behavior).
957
987
  const baseColor = w.globals.colors[0] || '#008FFB'
958
988
  const utils = new Utils()
959
- const plot = w.config.plotOptions[w.config.chart.type] || {}
989
+ const type = w.config.chart.type
990
+ const plot = w.config.plotOptions[type] || {}
960
991
  const shadeIntensity = plot.shadeIntensity ?? 0.5
961
- const hasNegs = /** @type {any} */ (w.globals).hasNegs
992
+ const g = /** @type {any} */ (w.globals)
993
+ const hasNegs = g.hasNegs
994
+ // A non-distributed heatmap's cells shade against the niced y extent
995
+ // and clamp into it (determineColor); use it once coreCalculations ran.
996
+ // A one-sided colorScale.max past maxY is ignored there, so cap at maxY.
997
+ const yExt =
998
+ type === 'heatmap' && !plot.distributed && !csWindow && g.maxY >= g.minY
999
+ const hi = yExt ? g.maxY : max
1000
+ const total =
1001
+ Math.abs(hi) + Math.abs(yExt ? (cs.min < g.minY ? cs.min : g.minY) : min)
1002
+ const k = shadeIntensity * 1.25
962
1003
  const n = Math.max(2, cfg.stops || 16)
963
1004
  for (let s = 0; s < n; s++) {
964
1005
  const t = s / (n - 1) // 0..1
965
- // Map t to the shade percent the cell at that t would receive.
1006
+ // Map t to the shade fraction the cell at that t would receive.
966
1007
  // determineColor computes percent = 100*val/(|min|+|max|), so the cell
967
- // sitting at value v = min + t*(max-min) gets percent_v.
968
- const v = min + t * (max - min)
969
- const total = Math.abs(max) + Math.abs(min)
970
- const percent_v = total === 0 ? 0 : (100 * v) / total
971
- let colorShadePercent
972
- if (hasNegs) {
973
- if (plot.reverseNegativeShade) {
974
- colorShadePercent =
975
- percent_v < 0
976
- ? (percent_v / 100) * (shadeIntensity * 1.25)
977
- : (1 - percent_v / 100) * (shadeIntensity * 1.25)
978
- } else {
979
- colorShadePercent =
980
- percent_v <= 0
981
- ? 1 - (1 + percent_v / 100) * shadeIntensity
982
- : (1 - percent_v / 100) * shadeIntensity
983
- }
984
- } else {
985
- colorShadePercent = 1 - percent_v / 100
986
- }
987
- // Clamp to a reasonable range to avoid color blowout.
988
- if (colorShadePercent > 1) colorShadePercent = 1
989
- if (colorShadePercent < -1) colorShadePercent = -1
1008
+ // sitting at value v = min + t*(max-min) gets p = v / total.
1009
+ const p =
1010
+ total === 0 ? 0 : Math.min(min + t * (max - min), hi) / total
1011
+ // Clamp to a reasonable range to avoid color blowout. Treemap tiles
1012
+ // scale the plain shade too (getShadeColor).
1013
+ const c = Math.max(
1014
+ -1,
1015
+ Math.min(
1016
+ 1,
1017
+ hasNegs
1018
+ ? plot.reverseNegativeShade
1019
+ ? (p < 0 ? p : 1 - p) * k
1020
+ : p <= 0
1021
+ ? 1 - (1 + p) * shadeIntensity
1022
+ : (1 - p) * shadeIntensity
1023
+ : (1 - p) * (type === 'treemap' ? k : 1),
1024
+ ),
1025
+ )
990
1026
 
991
1027
  const shaded = plot.enableShades
992
- ? utils.shadeColor(
993
- w.config.theme.mode === 'dark'
994
- ? colorShadePercent * -1
995
- : colorShadePercent,
996
- baseColor,
997
- )
1028
+ ? utils.shadeColor(w.config.theme.mode === 'dark' ? -c : c, baseColor)
998
1029
  : baseColor
999
1030
  stops.push({ percent: t, color: shaded })
1000
1031
  }
@@ -55,6 +55,8 @@ export default class Helpers {
55
55
  */
56
56
  toggleDataSeries(seriesCnt, isHidden) {
57
57
  const w = this.w
58
+ // a legendClick handler may have destroyed the chart
59
+ if (!w.dom.baseEl) return
58
60
  if (w.globals.axisCharts || w.config.chart.type === 'radialBar') {
59
61
  w.globals.resized = true // we don't want initial animations again
60
62
 
@@ -74,6 +74,11 @@ class Legend {
74
74
  const gl = w.globals
75
75
  const cnf = w.config
76
76
 
77
+ // Tear down any previous gradient legend every render, so hiding the
78
+ // legend does not leave its listeners and repositioning behind.
79
+ this.heatmapGradientLegend?.destroy()
80
+ this.heatmapGradientLegend = null
81
+
77
82
  // Recompute each render: the Legend instance is reused across updates, so a
78
83
  // value cached in the constructor would go stale when updateOptions changes
79
84
  // the type / distributed flag / series count (e.g. a distributed single-series
@@ -106,12 +111,6 @@ class Legend {
106
111
  elLegendWrap.removeChild(elLegendWrap.firstChild)
107
112
  }
108
113
 
109
- // Tear down any previous gradient-legend listeners before re-rendering.
110
- if (this.heatmapGradientLegend) {
111
- this.heatmapGradientLegend.destroy()
112
- this.heatmapGradientLegend = null
113
- }
114
-
115
114
  if (HeatmapGradientLegend.isEnabled(w)) {
116
115
  this.heatmapGradientLegend = new HeatmapGradientLegend(w, this.ctx)
117
116
  this.heatmapGradientLegend.draw()
@@ -381,7 +380,8 @@ class Legend {
381
380
  // by index: groups may reuse series names, and an unnamed series has
382
381
  // none to match
383
382
  const lg = legendGroups[w.labelData.seriesGroupIndex[i]]
384
- if (lg) elLegendWrap.appendChild(lg)
383
+ // once: re-appending moves the group, reordering interleaved groups
384
+ if (lg && !lg.parentNode) elLegendWrap.appendChild(lg)
385
385
  ;(lg || elLegendWrap).appendChild(elLegend)
386
386
 
387
387
  elLegendWrap.classList.add(
@@ -537,10 +537,8 @@ class Legend {
537
537
  const hoverOverLegend = isLegendItem(target)
538
538
 
539
539
  if (w.config.chart.type !== 'heatmap' && !this.isBarsDistributed) {
540
- if (
541
- !target.classList.contains('apexcharts-inactive-legend') &&
542
- hoverOverLegend
543
- ) {
540
+ // closest: the text span of a hidden item carries no inactive class
541
+ if (hoverOverLegend && !target.closest('.apexcharts-inactive-legend')) {
544
542
  const series = new Series(this.ctx.w)
545
543
  series.toggleSeriesOnHover(e, target)
546
544
  }
@@ -585,7 +583,8 @@ class Legend {
585
583
  // can keep toggling without having to re-tab to the legend.
586
584
  if (rel !== null && w.config.legend.onItemClick.toggleDataSeries) {
587
585
  requestAnimationFrame(() => {
588
- const restored = w.dom.baseEl.querySelector(
586
+ // the chart may have been destroyed before this frame
587
+ const restored = w.dom.baseEl?.querySelector(
589
588
  `.apexcharts-legend-series[rel="${rel}"]`,
590
589
  )
591
590
  if (restored) /** @type {HTMLElement} */ (restored).focus()
@@ -788,7 +788,14 @@ export default class Options {
788
788
  // asked for and nobody can undo is a trap. Set either to true to
789
789
  // force the gesture on regardless (for a page that supplies its own
790
790
  // reset control), or false to turn it off outright.
791
+ //
792
+ // The 'auto' wheel is also cooperative: a bare wheel scrolls the
793
+ // page, and Ctrl/Cmd + wheel (or a trackpad pinch) zooms, so a page
794
+ // of charts scrolls past them. `true` zooms on every wheel.
791
795
  allowMouseWheelZoom: 'auto',
796
+ // With the cooperative wheel, a bare scroll over the chart briefly
797
+ // shows how to zoom it ("Use Ctrl + scroll to zoom").
798
+ wheelHint: true,
792
799
  // Momentum: two-finger pinch-zoom on touch devices. Zooms the x-axis
793
800
  // around the pinch centroid (matching the x-only wheel/toolbar zoom),
794
801
  // frame-by-frame rather than the 400ms wheel throttle.
@@ -276,6 +276,7 @@ export default class StreamLabels {
276
276
  maxWidth: span,
277
277
  fontSize: `${px}px`,
278
278
  fontFamily,
279
+ fontWeight,
279
280
  })
280
281
  if (!text || text === '...') continue
281
282
  const drawnWidth =
@@ -2137,14 +2137,28 @@ export default class Tooltip {
2137
2137
  shared: this.showOnIntersect ? false : this.tConfig.shared,
2138
2138
  })
2139
2139
 
2140
- if (hasMarkers || dynamicPoints) {
2140
+ // A shared tooltip speaks for every series at `j`, so in a combo the
2141
+ // line's hover dots and the columns' hover state both follow it,
2142
+ // whichever series the pointer is nearest. Only the box goes to the
2143
+ // captured series' mark. Gating the bars on the captured series having
2144
+ // no markers left the dots on the last column the line was nearest to.
2145
+ const capturedPoints = hasMarkers || dynamicPoints
2146
+ if (
2147
+ capturedPoints ||
2148
+ (w.globals.comboCharts &&
2149
+ !canvasMode &&
2150
+ this.tooltipUtil.hasVisibleMarkers())
2151
+ ) {
2141
2152
  handlePoints()
2142
- } else if (this.tooltipUtil.hasBars()) {
2153
+ }
2154
+ if (this.tooltipUtil.hasBars()) {
2143
2155
  if (canvasMode) {
2144
2156
  // canvas: bars/candles paint to the bitmap, so the paths /
2145
2157
  // hover-filter machinery below is DOM-only. Position straight off
2146
2158
  // the cached bar coords.
2147
- ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2159
+ if (!capturedPoints) {
2160
+ ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2161
+ }
2148
2162
  } else if (
2149
2163
  [...bars].some((/** @type {Element} */ b) => b.querySelector('path'))
2150
2164
  ) {
@@ -2157,7 +2171,9 @@ export default class Tooltip {
2157
2171
  // de-activate first
2158
2172
  this.deactivateHoverFilter()
2159
2173
 
2160
- ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2174
+ if (!capturedPoints) {
2175
+ ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2176
+ }
2161
2177
 
2162
2178
  for (let b = 0; b < paths.length; b++) {
2163
2179
  graphics.pathMouseEnter(
@@ -714,6 +714,14 @@ export default class Utils {
714
714
  return markers.length > 0
715
715
  }
716
716
 
717
+ /**
718
+ * Whether any visible series has a marker node, its points' or its hover
719
+ * dot's.
720
+ */
721
+ hasVisibleMarkers() {
722
+ return this.getAllMarkers(true).some(Boolean)
723
+ }
724
+
717
725
  /**
718
726
  * @param {any} point
719
727
  * @param {number} size
@@ -1032,8 +1032,14 @@ export default class Trellis {
1032
1032
  // gap is the breathing room here).
1033
1033
  parentHeightOffset: 0,
1034
1034
  toolbar: { show: false },
1035
+ // the shared toolbar owns the one reset (TrellisSync.resetAll); with
1036
+ // toolbar 'none' a panel's own reset is the only way back, unless the
1037
+ // page turned it off
1035
1038
  zoom: {
1036
1039
  enabled: this.cfg.zoom !== 'none',
1040
+ resetControl:
1041
+ this.cfg.toolbar == 'none' &&
1042
+ w.config.chart.zoom.resetControl !== false,
1037
1043
  },
1038
1044
  ...(animationsOff ? { animations: { enabled: false } } : {}),
1039
1045
  // Grid-level canvas auto-selection (P2): a uniform explicit backend,
@@ -44,6 +44,8 @@ export default class TrellisChrome {
44
44
  this.gradientLegend = null
45
45
  /** @type {(() => void)|null} unsubscribe for a deferred gradient build */
46
46
  this._gradPending = null
47
+ /** @type {any} the strip's panel hover wiring (and panelMounted hook) */
48
+ this._gradWire = null
47
49
  }
48
50
 
49
51
  /**
@@ -299,17 +301,24 @@ export default class TrellisChrome {
299
301
  const legend = new HeatmapGradientLegend(chart.w, chart.ctx)
300
302
  legend.draw(slot)
301
303
  this.gradientLegend = legend
304
+ // a band highlights its range in every mounted panel
305
+ legend.panelsW = () =>
306
+ t.panels.filter((p) => p.chart).map((p) => /** @type {any} */ (p.chart).w)
302
307
 
303
308
  // The instance's own hover wiring only hears ITS panel; mirror the same
304
- // handlers onto every other mounted panel so the arrow follows the
305
- // cursor anywhere in the grid.
306
- t.panels.forEach((p) => {
307
- if (!p.chart || p === mounted) return
308
- const ev = /** @type {any} */ (p.chart).ctx?.events
309
- if (!ev?.addEventListener) return
309
+ // handlers onto every other panel, mounted now or later, so the arrow
310
+ // follows the cursor anywhere in the grid. Doubles as the host's
311
+ // panelMounted listener (ctx, { chart }).
312
+ /** @type {any} */
313
+ const wire = (/** @type {any} */ _, /** @type {any} */ p) => {
314
+ const ev = p.chart !== chart && p.chart?.ctx.events
315
+ if (!ev) return
310
316
  ev.addEventListener('dataPointMouseEnter', legend._onCellEnter)
311
317
  ev.addEventListener('dataPointMouseLeave', legend._onCellLeave)
312
- })
318
+ }
319
+ t.panels.forEach((p) => wire(0, p))
320
+ t.ctx.events.addEventListener('panelMounted', wire)
321
+ this._gradWire = wire
313
322
  }
314
323
 
315
324
  /** Tear down the shared gradient strip (P5). */
@@ -319,6 +328,8 @@ export default class TrellisChrome {
319
328
  t.ctx.events.removeEventListener?.('panelMounted', this._gradPending)
320
329
  this._gradPending = null
321
330
  }
331
+ // the panels' own buses die with them right after (teardown)
332
+ t.ctx.events.removeEventListener('panelMounted', this._gradWire)
322
333
  if (this.gradientLegend) {
323
334
  try {
324
335
  this.gradientLegend.destroy()
@@ -310,6 +310,8 @@ export default class TrellisSync {
310
310
  })
311
311
  }
312
312
  const loop = () => {
313
+ // a teardown (rebuild or destroy) under a resting pointer ends the loop
314
+ if (elGrid !== this.trellis.elGrid) return (raf = 0)
313
315
  apply()
314
316
  raf = hoverTarget ? requestAnimationFrame(loop) : 0
315
317
  }
@@ -1023,6 +1023,7 @@ export default class CanvasGraphics {
1023
1023
  cmd.strokeWidth = opts.strokeWidth
1024
1024
  cmd.fill = opts.fill
1025
1025
  cmd.lineCap = opts.strokeLinecap
1026
+ cmd.strokeDash = this._g.seriesDash(opts.realIndex)
1026
1027
  cmd.si = opts.realIndex
1027
1028
  // Data point identity. Only a mark that stands for one point (a bar-like:
1028
1029
  // Bar.renderSeries) passes `j`; a line or area path stands for the whole
@@ -59,6 +59,16 @@ class SSRElement {
59
59
  this.attributes.set(name, value)
60
60
  }
61
61
 
62
+ /**
63
+ * Namespaced attributes serialize by their qualified name.
64
+ * @param {any} _ns
65
+ * @param {string} name
66
+ * @param {any} value
67
+ */
68
+ setAttributeNS(_ns, name, value) {
69
+ this.setAttribute(name, value)
70
+ }
71
+
62
72
  /**
63
73
  * @param {string} name
64
74
  */
@@ -110,6 +110,12 @@ export interface InteractState {
110
110
  rafId: number | null
111
111
  endTimer: any
112
112
  }
113
+ // Cooperative wheel: fade-out timer for the "Ctrl + scroll to zoom" hint.
114
+ wheelHintTimer?: any
115
+ // Keyboard nav handing focus across an update that replaces the SVG:
116
+ // [seriesIndex, dataPointIndex, active, tooltipDismissed], set on destroy,
117
+ // taken on init.
118
+ keyNav?: [number, number, boolean, boolean] | null
113
119
  }
114
120
 
115
121
  /** Label formatter functions — lives on `w.formatters` */
@@ -569,9 +575,9 @@ export interface ChartGlobals
569
575
  seriesYvalues: number[][]
570
576
  seriesYAxisMap: number[][]
571
577
  seriesYAxisReverseMap: number[]
572
- barGroups: string[]
573
- lineGroups: string[]
574
- areaGroups: string[]
578
+ barGroups: number[]
579
+ lineGroups: number[]
580
+ areaGroups: number[]
575
581
  hasSeriesGroups: boolean
576
582
 
577
583
  // ── Layout extras (ephemeral) ─────────────────────────────────────────────
@@ -336,11 +336,11 @@ declare class ApexCharts {
336
336
  /** Returns the sum of all data points whose x value falls within [minX, maxX]. */
337
337
  getSeriesTotalXRange(minX: number, maxX: number): number[]
338
338
 
339
- /** Returns the highest y value in the specified series. */
340
- getHighestValueInSeries(seriesIndex?: number): number
339
+ /** Returns the highest y value in the specified series, or undefined for an index with no series. */
340
+ getHighestValueInSeries(seriesIndex?: number): number | undefined
341
341
 
342
- /** Returns the lowest y value in the specified series. */
343
- getLowestValueInSeries(seriesIndex?: number): number
342
+ /** Returns the lowest y value in the specified series, or undefined for an index with no series. */
343
+ getLowestValueInSeries(seriesIndex?: number): number | undefined
344
344
 
345
345
  /** Returns the sum of each series (totals used for percentage calculations). */
346
346
  getSeriesTotal(): number[]
@@ -2236,13 +2236,21 @@ type ApexChart = {
2236
2236
  type?: 'x' | 'y' | 'xy'
2237
2237
  autoScaleYaxis?: boolean
2238
2238
  /**
2239
- * Cursor-anchored zoom on mouse wheel / trackpad. `'auto'` enables it only
2240
- * when the toolbar's reset button is present, so an unintended scroll-zoom
2241
- * is always undoable; `true` forces it on even with the toolbar hidden.
2242
- * Requires `enabled: true`.
2239
+ * Cursor-anchored zoom on mouse wheel / trackpad. `'auto'` is cooperative:
2240
+ * a bare wheel scrolls the page, and Ctrl/Cmd + wheel (or a trackpad
2241
+ * pinch) zooms. It is offered only when the toolbar's reset button is
2242
+ * present, so a zoom is always undoable. `true` zooms on every wheel, even
2243
+ * with the toolbar hidden. Requires `enabled: true`.
2243
2244
  * @default 'auto'
2244
2245
  */
2245
2246
  allowMouseWheelZoom?: boolean | 'auto'
2247
+ /**
2248
+ * With the cooperative `'auto'` wheel, a bare scroll over the chart
2249
+ * briefly shows how to zoom it. The text is the locale's
2250
+ * `toolbar.wheelZoomHint`, where `{key}` becomes Ctrl or ⌘.
2251
+ * @default true
2252
+ */
2253
+ wheelHint?: boolean
2246
2254
  /**
2247
2255
  * Momentum: enable two-finger pinch-zoom on touch devices. Zooms the x-axis
2248
2256
  * around the pinch centroid, frame-by-frame. `'auto'` enables it only when
@@ -2869,6 +2877,8 @@ type ApexLocale = {
2869
2877
  exportToSVG?: string
2870
2878
  exportToPNG?: string
2871
2879
  exportToCSV?: string
2880
+ /** The cooperative wheel's hint; `{key}` becomes Ctrl or ⌘. Default 'Use {key} + scroll to zoom'. */
2881
+ wheelZoomHint?: string
2872
2882
  }
2873
2883
  /** The highlight filter add-on's words. */
2874
2884
  highlightFilter?: {
@@ -3781,7 +3791,8 @@ type ApexPlotOptions = {
3781
3791
  * recolours) across a regroup - the circles-to-bars transition.
3782
3792
  * 'identity': keyed by each datum's `id`/`name`, so a SPECIFIC unit migrates
3783
3793
  * across any regroup or relayout keeping its colour and size. Requires the
3784
- * per-unit object form with unique ids/names.
3794
+ * per-unit object form with unique ids/names (a repeated one is tracked by
3795
+ * its order of appearance).
3785
3796
  */
3786
3797
  transition?: 'group' | 'flow' | 'identity'
3787
3798
  /**
@@ -3919,7 +3930,10 @@ type ApexPlotOptions = {
3919
3930
  sizeField?: string
3920
3931
  /** `[minRadius, maxRadius]` in px: turns dots into area-scaled bubbles. */
3921
3932
  sizeRange?: [number, number]
3922
- /** Left-gutter width reserved for lane (category) labels (lanes mode). */
3933
+ /**
3934
+ * Left-gutter width reserved for lane (category) labels (lanes mode).
3935
+ * Defaults to the widest label, up to a third of the plot.
3936
+ */
3923
3937
  laneLabelWidth?: number
3924
3938
  /** Draw the faint gridlines. Defaults to true. */
3925
3939
  gridlines?: boolean
@@ -3991,7 +4005,11 @@ type ApexPlotOptions = {
3991
4005
  borderRadius?: number
3992
4006
  /** 1 dot represents this many units of value (waffle scaling). */
3993
4007
  unitValue?: number
3994
- /** Safety cap on total dots; counts scale down proportionally above it. */
4008
+ /**
4009
+ * Safety cap on total dots; counts scale down proportionally above it (a
4010
+ * scatter keeps an even sample of each category). A split grid draws a
4011
+ * fixed number of cells per tile and is not capped.
4012
+ */
3995
4013
  maxUnits?: number
3996
4014
  /** Packed layout: order categories smallest-first (minority centred). */
3997
4015
  sortByGroup?: boolean
@@ -5327,7 +5345,8 @@ interface ApexUnitObject {
5327
5345
  * Stable identity. The datum's own `id`/`name` when the per-unit object form
5328
5346
  * supplies one, so a provider can address a specific unit ("Texas",
5329
5347
  * "employee 41") rather than a positional slot; otherwise
5330
- * `"<seriesIndex>:<dataPointIndex>"`.
5348
+ * `"<seriesIndex>:<dataPointIndex>"`. A repeated one is suffixed by its
5349
+ * order of appearance (`"Alex#2"`).
5331
5350
  */
5332
5351
  id: string
5333
5352
  /** Global draw order across every category. */