apexcharts 8.2.0 → 8.3.1

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 (256) hide show
  1. package/README.md +7 -7
  2. package/dist/apexcharts.common.js +2 -2
  3. package/dist/apexcharts.core.js +19793 -19713
  4. package/dist/apexcharts.core.min.js +2 -2
  5. package/dist/apexcharts.css +38 -8
  6. package/dist/apexcharts.esm.js +1882 -1625
  7. package/dist/apexcharts.full.js +22044 -21634
  8. package/dist/apexcharts.full.min.js +2 -2
  9. package/dist/apexcharts.js +21414 -21124
  10. package/dist/apexcharts.min.js +2 -2
  11. package/dist/apexcharts.ssr.common.js +2 -2
  12. package/dist/apexcharts.ssr.esm.js +21413 -21123
  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 +478 -406
  17. package/dist/bar.js +2 -2
  18. package/dist/boxPlot.common.js +2 -2
  19. package/dist/boxPlot.esm.js +409 -334
  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 +409 -334
  24. package/dist/candlestick.js +2 -2
  25. package/dist/column.common.js +2 -2
  26. package/dist/column.esm.js +478 -406
  27. package/dist/core.common.js +2 -2
  28. package/dist/core.esm.js +19793 -19713
  29. package/dist/donut.common.js +2 -2
  30. package/dist/donut.esm.js +3 -9
  31. package/dist/dumbbell.common.js +2 -2
  32. package/dist/dumbbell.esm.js +478 -406
  33. package/dist/features/all.common.js +2 -2
  34. package/dist/features/all.esm.js +442 -324
  35. package/dist/features/annotations.common.js +2 -2
  36. package/dist/features/annotations.esm.js +65 -48
  37. package/dist/features/annotations.js +2 -2
  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 +2 -2
  48. package/dist/features/exports.esm.js +10 -4
  49. package/dist/features/exports.js +2 -2
  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 +64 -44
  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 +2 -2
  78. package/dist/features/morph.esm.js +9 -5
  79. package/dist/features/morph.js +2 -2
  80. package/dist/features/perspectives.common.js +2 -2
  81. package/dist/features/perspectives.esm.js +4 -1
  82. package/dist/features/perspectives.js +2 -2
  83. package/dist/features/raincloud.common.js +2 -2
  84. package/dist/features/raincloud.esm.js +26 -27
  85. package/dist/features/raincloud.js +2 -2
  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 +2 -2
  90. package/dist/features/stats.esm.js +52 -59
  91. package/dist/features/stats.js +2 -2
  92. package/dist/features/storyboard.common.js +2 -2
  93. package/dist/features/storyboard.esm.js +4 -1
  94. package/dist/features/storyboard.js +2 -2
  95. package/dist/features/streamgraph.common.js +2 -2
  96. package/dist/features/streamgraph.esm.js +48 -14
  97. package/dist/features/streamgraph.js +2 -2
  98. package/dist/features/toolbar.common.js +2 -2
  99. package/dist/features/toolbar.esm.js +148 -18
  100. package/dist/features/toolbar.js +2 -2
  101. package/dist/features/trellis.common.js +2 -2
  102. package/dist/features/trellis.esm.js +150 -63
  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 +2 -2
  108. package/dist/features/weave.esm.js +13 -12
  109. package/dist/features/weave.js +2 -2
  110. package/dist/full.common.js +2 -2
  111. package/dist/full.esm.js +1068 -842
  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 +529 -464
  117. package/dist/icicle.common.js +2 -2
  118. package/dist/icicle.esm.js +80 -46
  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 +2 -2
  129. package/dist/pie.esm.js +3 -9
  130. package/dist/pie.js +2 -2
  131. package/dist/polarArea.common.js +2 -2
  132. package/dist/polarArea.esm.js +3 -9
  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 +2 -2
  137. package/dist/radialBar.esm.js +5 -12
  138. package/dist/radialBar.js +2 -2
  139. package/dist/raincloud.common.js +2 -2
  140. package/dist/raincloud.esm.js +97 -52
  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 +478 -406
  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 +138 -53
  149. package/dist/sunburst.common.js +2 -2
  150. package/dist/sunburst.esm.js +116 -94
  151. package/dist/sunburst.js +2 -2
  152. package/dist/treemap.common.js +2 -2
  153. package/dist/treemap.esm.js +138 -104
  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 +477 -405
  166. package/package.json +1 -1
  167. package/src/apexcharts.js +40 -59
  168. package/src/assets/apexcharts.css +38 -8
  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 +68 -47
  173. package/src/charts/Line.js +118 -42
  174. package/src/charts/Pie.js +2 -8
  175. package/src/charts/Radar.js +5 -7
  176. package/src/charts/Radial.js +2 -6
  177. package/src/charts/RangeBar.js +47 -43
  178. package/src/charts/Sunburst.js +101 -98
  179. package/src/charts/Treemap.js +150 -88
  180. package/src/charts/Unit.js +368 -250
  181. package/src/charts/Violin.js +48 -30
  182. package/src/charts/common/Breadcrumb.js +2 -2
  183. package/src/charts/common/Hierarchy.js +32 -11
  184. package/src/charts/common/Jitter.js +32 -7
  185. package/src/charts/common/Stats.js +30 -23
  186. package/src/charts/common/bar/DataLabels.js +76 -81
  187. package/src/charts/common/bar/Helpers.js +43 -7
  188. package/src/charts/common/line/Helpers.js +12 -15
  189. package/src/charts/common/partition/NodeTooltip.js +29 -7
  190. package/src/charts/common/partition/Partition.js +33 -1
  191. package/src/charts/common/treemap/Helpers.js +3 -11
  192. package/src/charts/common/treemap/Nested.js +2 -3
  193. package/src/features/raincloud.js +16 -10
  194. package/src/features/stats.js +67 -62
  195. package/src/locales/en.json +2 -1
  196. package/src/locales/ro.json +3 -2
  197. package/src/modules/Animations.js +102 -95
  198. package/src/modules/AxisMapping.js +47 -12
  199. package/src/modules/Core.js +4 -0
  200. package/src/modules/CoreUtils.js +18 -42
  201. package/src/modules/Data.js +23 -24
  202. package/src/modules/DataLabels.js +6 -6
  203. package/src/modules/Exports.js +12 -5
  204. package/src/modules/Fill.js +17 -8
  205. package/src/modules/Filters.js +50 -50
  206. package/src/modules/Graphics.js +69 -51
  207. package/src/modules/Markers.js +3 -1
  208. package/src/modules/MorphTypeChange.js +13 -7
  209. package/src/modules/Range.js +44 -29
  210. package/src/modules/RendererController.js +17 -3
  211. package/src/modules/Scales.js +32 -9
  212. package/src/modules/Series.js +41 -19
  213. package/src/modules/Theme.js +1 -1
  214. package/src/modules/Toolbar.js +3 -2
  215. package/src/modules/UnitMarkRegistry.js +0 -5
  216. package/src/modules/ZoomPanSelection.js +123 -2
  217. package/src/modules/accessibility/KeyboardNavigation.js +114 -186
  218. package/src/modules/animations/Easing.js +2 -1
  219. package/src/modules/animations/LengthTransition.js +22 -2
  220. package/src/modules/annotations/Helpers.js +68 -52
  221. package/src/modules/axes/Grid.js +21 -7
  222. package/src/modules/axes/YAxis.js +8 -9
  223. package/src/modules/dimensions/Dimensions.js +9 -3
  224. package/src/modules/dimensions/Grid.js +2 -2
  225. package/src/modules/dimensions/XAxis.js +2 -5
  226. package/src/modules/drilldown/Breadcrumb.js +5 -2
  227. package/src/modules/helpers/Destroy.js +1 -1
  228. package/src/modules/helpers/UpdateHelpers.js +33 -15
  229. package/src/modules/highlightFilter/HighlightFilter.js +8 -9
  230. package/src/modules/highlightFilter/TreemapPart.js +2 -2
  231. package/src/modules/legend/HeatmapGradientLegend.js +76 -45
  232. package/src/modules/legend/Helpers.js +8 -1
  233. package/src/modules/legend/Legend.js +11 -12
  234. package/src/modules/license/LicenseEnforcer.js +5 -0
  235. package/src/modules/settings/Defaults.js +28 -56
  236. package/src/modules/settings/Globals.js +5 -0
  237. package/src/modules/settings/Options.js +7 -7
  238. package/src/modules/settings/TypeAliases.js +33 -16
  239. package/src/modules/streamgraph/StreamLabels.js +1 -0
  240. package/src/modules/tooltip/Labels.js +73 -76
  241. package/src/modules/tooltip/Marker.js +25 -21
  242. package/src/modules/tooltip/Position.js +88 -51
  243. package/src/modules/tooltip/Tooltip.js +242 -255
  244. package/src/modules/tooltip/Utils.js +71 -130
  245. package/src/modules/tooltip/constants.js +5 -5
  246. package/src/modules/trellis/Trellis.js +6 -0
  247. package/src/modules/trellis/TrellisChrome.js +18 -7
  248. package/src/modules/trellis/TrellisSync.js +2 -0
  249. package/src/modules/trellis/TrellisTooltip.js +2 -11
  250. package/src/modules/weave/WeaveHost.js +18 -12
  251. package/src/renderers/canvas/CanvasGraphics.js +1 -0
  252. package/src/ssr/DOMShim.js +10 -0
  253. package/src/svg/SVGFilter.js +26 -17
  254. package/src/types/internal.d.ts +9 -3
  255. package/src/utils/Utils.js +0 -14
  256. package/types/apexcharts.d.ts +33 -12
@@ -22,13 +22,18 @@ export default class Labels {
22
22
  this.w = tooltipContext.w
23
23
  this.ttCtx = tooltipContext
24
24
  this.tooltipUtil = new Utils(tooltipContext)
25
+ // Stateless helpers a datetime hover used to build afresh on every move.
26
+ this.xFormat = new Formatters(this.w)
27
+ this.dateTime = new DateTime(this.w)
25
28
  }
26
29
 
27
30
  /** @param {{ shared?: boolean, ttItems?: any, i?: number, j?: any, y1?: any, y2?: any, e?: any }} opts */
28
31
  drawSeriesTexts({ shared = true, ttItems, i = 0, j = null, y1, y2, e }) {
29
32
  const w = this.w
33
+ const custom = w.config.tooltip.custom !== undefined
34
+ const tooltipEl = this.ttCtx.getElTooltip()
30
35
 
31
- if (w.config.tooltip.custom !== undefined) {
36
+ if (custom) {
32
37
  this.handleCustomTooltip({ i, j, y1, y2, w })
33
38
  } else {
34
39
  this.toggleActiveInactiveSeries(shared, i)
@@ -46,11 +51,12 @@ export default class Labels {
46
51
  ttItems,
47
52
  shared,
48
53
  e,
54
+ // Markup from tooltip.custom has replaced the built-in rows: formatting
55
+ // them anyway ran every formatter into nodes no one sees.
56
+ rows: !custom || tooltipEl?.contains(ttItems?.[0]),
49
57
  })
50
58
 
51
59
  // Re-calculate tooltip dimensions now that we have drawn the text
52
- const tooltipEl = this.ttCtx.getElTooltip()
53
-
54
60
  if (tooltipEl) {
55
61
  // One rect read, not two: each call forces a synchronous layout of the
56
62
  // whole tooltip, which on a shared tooltip with a row per series is the
@@ -61,12 +67,28 @@ export default class Labels {
61
67
  }
62
68
  }
63
69
 
64
- /** @param {{i: any, j: any, values: any, ttItems: any, shared: any, e: any}} opts */
65
- printLabels({ i, j, values, ttItems, shared, e }) {
70
+ /** @param {{i: any, j: any, values: any, ttItems: any, shared: any, e: any, rows?: boolean}} opts */
71
+ printLabels({ i, j, values, ttItems, shared, e, rows = true }) {
66
72
  const w = this.w
73
+ const ttCtx = this.ttCtx
67
74
  const { xVal, zVal, xAxisTTVal } = values
68
75
  const seriesLen = w.seriesData.series.length
69
76
 
77
+ // The title and the x-axis label read the same for every row, so they are
78
+ // written once a hover, not once per series.
79
+ if (ttItems?.[0]) {
80
+ if (rows && ttCtx.showTooltipTitle) {
81
+ ttCtx.tooltipTitle ??= w.dom.baseEl.querySelector(
82
+ '.apexcharts-tooltip-title',
83
+ )
84
+ if (ttCtx.tooltipTitle) ttCtx.tooltipTitle.innerHTML = xVal
85
+ }
86
+ if (ttCtx.isXAxisTooltipEnabled && ttCtx.xaxisTooltipText) {
87
+ ttCtx.xaxisTooltipText.innerHTML = xAxisTTVal !== '' ? xAxisTTVal : xVal
88
+ }
89
+ }
90
+ if (!rows) return
91
+
70
92
  const basePColor =
71
93
  j !== null && w.config.plotOptions.bar.distributed
72
94
  ? w.globals.colors[j]
@@ -103,8 +125,6 @@ export default class Labels {
103
125
  values: {
104
126
  val: row.val,
105
127
  goalVals: row.goalVals,
106
- xVal,
107
- xAxisTTVal,
108
128
  zVal,
109
129
  },
110
130
  seriesName: row.seriesName,
@@ -122,38 +142,28 @@ export default class Labels {
122
142
  */
123
143
  computeSeriesRow({ i, j, tIndex, shared, e, basePColor }) {
124
144
  const w = this.w
125
- let f = this.getFormatters(i)
145
+ // A shared row speaks for series tIndex. Its formatters and name are taken
146
+ // once, not the hovered series' first and then thrown away, which ran the
147
+ // title formatter twice per row.
148
+ const own = w.globals.axisCharts && shared ? tIndex : i
149
+ const f = this.getFormatters(own)
126
150
  let pColor = basePColor
127
151
  let val
128
152
  let goalVals = /** @type {any[]} */ ([])
129
153
 
130
- let seriesName =
131
- w.config.chart.type === 'treemap'
132
- ? f.yLbTitleFormatter(
133
- String(/** @type {any} */ (w.config.series[i]).data[j].x),
134
- {
135
- series: w.seriesData.series,
136
- seriesIndex: i,
137
- dataPointIndex: j,
138
- w,
139
- },
140
- )
141
- : this.getSeriesName({
142
- fn: f.yLbTitleFormatter,
143
- index: i,
144
- seriesIndex: i,
145
- j,
146
- })
154
+ const seriesName = this.getSeriesName({
155
+ fn: f.yLbTitleFormatter,
156
+ index: own,
157
+ seriesIndex: i,
158
+ j,
159
+ leaf:
160
+ w.config.chart.type === 'treemap'
161
+ ? String(/** @type {any} */ (w.config.series[i]).data[j].x)
162
+ : null,
163
+ })
147
164
 
148
165
  if (w.globals.axisCharts) {
149
166
  if (shared) {
150
- f = this.getFormatters(tIndex)
151
- seriesName = this.getSeriesName({
152
- fn: f.yLbTitleFormatter,
153
- index: tIndex,
154
- seriesIndex: i,
155
- j,
156
- })
157
167
  pColor = w.globals.colors[tIndex]
158
168
  val = this.formatYValue(f, tIndex, j)
159
169
  goalVals = this.formatGoalVals(f, tIndex, j)
@@ -358,15 +368,22 @@ export default class Labels {
358
368
  }
359
369
  }
360
370
 
361
- /** @param {{fn: any, index: any, seriesIndex: any, j: any}} opts */
362
- getSeriesName({ fn, index, seriesIndex, j }) {
371
+ /**
372
+ * A treemap row is named by its leaf, not its series.
373
+ * @param {{fn: any, index: any, seriesIndex: any, j: any, leaf?: string | null}} opts
374
+ */
375
+ getSeriesName({ fn, index, seriesIndex, j, leaf }) {
363
376
  const w = this.w
364
- return fn(String(w.seriesData.seriesNames[index]), {
377
+ const s = fn(leaf ?? String(w.seriesData.seriesNames[index]), {
365
378
  series: w.seriesData.series,
366
379
  seriesIndex,
367
380
  dataPointIndex: j,
368
381
  w,
369
382
  })
383
+ // A leaf's name is data and the title is written as HTML. The default
384
+ // title is the name plus ': ', so that shape gets the name's '<' escaped
385
+ // (enough in element text); markup a title formatter adds stays markup.
386
+ return leaf && s === leaf + ': ' ? leaf.replace(/</g, '&lt;') + ': ' : s
370
387
  }
371
388
 
372
389
  /**
@@ -397,7 +414,7 @@ export default class Labels {
397
414
  const w = this.w
398
415
  const ttCtx = this.ttCtx
399
416
 
400
- const { val, goalVals, xVal, xAxisTTVal, zVal } = values
417
+ const { val, goalVals, zVal } = values
401
418
 
402
419
  // A hover processed mid-render (e.g. during a cross-type updateOptions, where
403
420
  // the new type's series count can momentarily outpace the rebuilt tooltip
@@ -417,25 +434,6 @@ export default class Labels {
417
434
  ttItemsChildren[0].style.display = 'none'
418
435
  }
419
436
 
420
- if (ttCtx.showTooltipTitle) {
421
- if (ttCtx.tooltipTitle === null) {
422
- // get it once if null, and store it in class property
423
- ttCtx.tooltipTitle = w.dom.baseEl.querySelector(
424
- '.apexcharts-tooltip-title',
425
- )
426
- }
427
- if (ttCtx.tooltipTitle) {
428
- ttCtx.tooltipTitle.innerHTML = xVal
429
- }
430
- }
431
-
432
- // if xaxis tooltip is constructed, we need to replace the innerHTML
433
- if (ttCtx.isXAxisTooltipEnabled) {
434
- if (ttCtx.xaxisTooltipText) {
435
- ttCtx.xaxisTooltipText.innerHTML = xAxisTTVal !== '' ? xAxisTTVal : xVal
436
- }
437
- }
438
-
439
437
  const ttYLabel = refs.yLabel
440
438
  if (ttYLabel) {
441
439
  ttYLabel.innerHTML = seriesName ? seriesName : ''
@@ -580,12 +578,10 @@ export default class Labels {
580
578
  // Rows are indexed by series index, except where only one was allocated
581
579
  // (the non-shared cartesian case, see Tooltip.createTTElements) and
582
580
  // every hovered series writes into row 0.
583
- const rowIndex = this.ttCtx.ttItems?.length === 1 ? 0 : i
584
-
585
- // enable the first tooltip text group
586
- const firstTooltipSeriesGroup = w.dom.baseEl.querySelector(
587
- `.apexcharts-tooltip-series-group-${rowIndex}`,
588
- )
581
+ const rows = this.ttCtx.ttItems
582
+ // enable the first tooltip text group, the row already held, not
583
+ // queried for on every move
584
+ const firstTooltipSeriesGroup = rows?.[rows.length === 1 ? 0 : i]
589
585
 
590
586
  if (firstTooltipSeriesGroup) {
591
587
  const ftsGroup = /** @type {HTMLElement} */ (firstTooltipSeriesGroup)
@@ -598,12 +594,7 @@ export default class Labels {
598
594
  /** @param {{i: any, j: any}} opts */
599
595
  getValuesToPrint({ i, j }) {
600
596
  const w = this.w
601
- /**
602
- * @param {any[]} ser
603
- */
604
- const filteredSeriesX = w.seriesData.seriesX.map(
605
- (/** @type {any} */ ser) => (ser.length > 0 ? ser : []),
606
- )
597
+ const seriesX = /** @type {any[][]} */ (w.seriesData.seriesX)
607
598
 
608
599
  let xVal = ''
609
600
  let xAxisTTVal = ''
@@ -623,16 +614,23 @@ export default class Labels {
623
614
  val = w.seriesData.series[i]
624
615
  } else {
625
616
  if (w.axisFlags.isXNumeric && w.config.chart.type !== 'treemap') {
626
- xVal = filteredSeriesX[i][j]
627
- if (filteredSeriesX[i].length === 0) {
617
+ xVal = seriesX[i][j]
618
+ if (seriesX[i].length === 0) {
628
619
  // a series (possibly the first one) might be collapsed, so get the next active index
629
620
  const firstActiveSeriesIndex =
630
- this.tooltipUtil.getFirstActiveXArray(filteredSeriesX)
631
- xVal = filteredSeriesX[firstActiveSeriesIndex][j]
621
+ this.tooltipUtil.getFirstActiveXArray(seriesX)
622
+ xVal = seriesX[firstActiveSeriesIndex][j]
632
623
  }
633
624
  } else {
634
- const dataFormat = new Data(this.w)
635
- if (dataFormat.isFormatXY()) {
625
+ // Asked once a render (drawTooltip starts a new one): asking per move
626
+ // built a Data and scanned the series, and on [{x, y}, null] data
627
+ // logged its mixed-format warning on every move.
628
+ const gen = this.ttCtx.allTooltipSeriesGroups
629
+ if (this._xyGen !== gen) {
630
+ this._xyGen = gen
631
+ this._isXY = new Data(this.w).isFormatXY()
632
+ }
633
+ if (this._isXY) {
636
634
  // a null entry is a gap in the data, with no x of its own
637
635
  const datum = /** @type {any} */ (w.config.series[i]).data[j]
638
636
  xVal = datum == null ? '' : datum.x
@@ -648,14 +646,13 @@ export default class Labels {
648
646
  const bufferXVal = xVal
649
647
 
650
648
  if (w.axisFlags.isXNumeric && w.config.xaxis.type === 'datetime') {
651
- const xFormat = new Formatters(this.w)
652
- xVal = xFormat.xLabelFormat(
649
+ xVal = this.xFormat.xLabelFormat(
653
650
  /** @type {Function} */ (w.formatters.ttKeyFormatter),
654
651
  bufferXVal,
655
652
  bufferXVal,
656
653
  {
657
654
  i: undefined,
658
- dateFormatter: new DateTime(this.w).formatDate,
655
+ dateFormatter: this.dateTime.formatDate,
659
656
  w: this.w,
660
657
  },
661
658
  )
@@ -2,7 +2,6 @@
2
2
  import Graphics from '../Graphics'
3
3
  import Position from './Position'
4
4
  import Markers from '../../modules/Markers'
5
- import Utils from '../../utils/Utils'
6
5
  import TooltipUtils from './Utils'
7
6
  import { BrowserAPIs } from '../../ssr/BrowserAPIs.js'
8
7
  import { SVGNS } from '../../svg/math'
@@ -76,6 +75,8 @@ export default class Marker {
76
75
  // ones it actually touched. See enlargePoints().
77
76
  /** @type {Set<any>} */
78
77
  this.enlargedPoints = new Set()
78
+ // A hover may have moved a hover dot since the last resetPointsSize.
79
+ this.dirty = false
79
80
  }
80
81
 
81
82
  drawDynamicPoints() {
@@ -204,6 +205,7 @@ export default class Marker {
204
205
  enlargePaintedPoint(marker, x, y) {
205
206
  const w = this.w
206
207
  this.resetPointsSize()
208
+ this.dirty = true
207
209
 
208
210
  // A bubble's radius is its value, so enlargeCurrentPoint leaves an SVG
209
211
  // bubble as it is and clears only `markers.hover.size`; a painted one is
@@ -442,27 +444,29 @@ export default class Marker {
442
444
  this.enlargedPoints.delete(point)
443
445
  }
444
446
 
447
+ /**
448
+ * Put every marker a hover changed back as drawn: the ones newPointSize
449
+ * grew (tracked) and each visible series' hover dot, the only nodes the
450
+ * dot writers touch. This used to re-path every marker in the chart, on
451
+ * every hide.
452
+ */
445
453
  resetPointsSize() {
446
- const w = this.w
447
-
448
- // This is the chart-wide sweep, so nothing is left enlarged afterwards.
449
- this.enlargedPoints.clear()
450
-
451
- const points = w.dom.baseEl.querySelectorAll(
452
- '.apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker',
453
- )
454
-
455
- for (let p = 0; p < points.length; p++) {
456
- const size = parseFloat(
457
- points[p].getAttribute('default-marker-size') ?? '0',
458
- )
459
-
460
- if (Utils.isNumber(size) && size > 0) {
461
- const path = this.ttCtx.tooltipUtil.getPathFromPoint(points[p], size)
462
- points[p].setAttribute('d', path)
463
- } else {
464
- points[p].setAttribute('d', 'M0,0')
454
+ const collapsed = this.w.globals.collapsedSeriesIndices
455
+ this.dirty = false
456
+ this.resetEnlargedPoints()
457
+ this.ttCtx.tooltipUtil.markerWraps().forEach(([i, ...dots]) => {
458
+ if (collapsed.indexOf(i) < 0) {
459
+ dots.forEach((p) => {
460
+ if (!p) return
461
+ const size = parseFloat(p.getAttribute('default-marker-size'))
462
+ p.setAttribute(
463
+ 'd',
464
+ size > 0
465
+ ? this.ttCtx.tooltipUtil.getPathFromPoint(p, size)
466
+ : 'M0,0',
467
+ )
468
+ })
465
469
  }
466
- }
470
+ })
467
471
  }
468
472
  }
@@ -27,6 +27,24 @@ import {
27
27
  */
28
28
  const CLIP_CACHE_TTL = 1000
29
29
 
30
+ /**
31
+ * The bar-like series groups `group` (an attribute test) names, as one
32
+ * selector, down to the path at `j` when given: a violin's body, never the
33
+ * jitter that shares its `j`.
34
+ * @param {string} group
35
+ * @param {number} [j]
36
+ */
37
+ const barSel = (group, j) =>
38
+ ['bar', 'candlestick', 'boxPlot', 'violin', 'rangebar']
39
+ .map(
40
+ (t) =>
41
+ `.apexcharts-${t}-series .apexcharts-series[${group}]` +
42
+ (j == null
43
+ ? ''
44
+ : ` path${t === 'violin' ? '.apexcharts-violin-area' : ''}[j='${j}']`),
45
+ )
46
+ .join()
47
+
30
48
  /**
31
49
  * ApexCharts Tooltip.Position Class to move the tooltip based on x and y position.
32
50
  *
@@ -53,6 +71,11 @@ export default class Position {
53
71
  * @type {{ clippers?: Array<{ node: Element, x: boolean, y: boolean }> | null, at?: number }}
54
72
  */
55
73
  this.clipCache = { clippers: null, at: 0 }
74
+ /**
75
+ * Hover lookups by selector, for one drawTooltip (see `_marks`).
76
+ * @type {any}
77
+ */
78
+ this._memo = null
56
79
  }
57
80
 
58
81
  /**
@@ -505,11 +528,9 @@ export default class Position {
505
528
  // grid. Falls back to `translateYAxisX` when the labels group can't
506
529
  // be measured (e.g. yaxis.show=false but tooltip still drawn).
507
530
  let cx
508
- const labelsGroup = /** @type {SVGGElement | null} */ (
509
- w.dom.baseEl.querySelector(
510
- `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-texts-g`,
511
- )
512
- )
531
+ const labelsGroup = this._marks(
532
+ `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-texts-g`,
533
+ )[0]
513
534
  const elWrapRect = w.dom.elWrap.getBoundingClientRect()
514
535
  if (labelsGroup) {
515
536
  const lr = labelsGroup.getBoundingClientRect()
@@ -733,12 +754,14 @@ export default class Position {
733
754
  // WCAG 2.4.11 Focus Not Obscured: when keyboard nav drives the tooltip,
734
755
  // make sure the tooltip box doesn't sit on top of the focused data
735
756
  // point. If the tooltip's vertical extent overlaps the point, push it
736
- // above the point by enough margin to clear the focus stroke.
757
+ // above the point by enough margin to clear the focus stroke. The focused
758
+ // node is read off KeyboardNavigation: a query for its class walked the
759
+ // whole chart on every move, with nothing focused just as with something.
737
760
  const a11y = w.config?.chart?.accessibility
738
761
  if (
739
762
  a11y?.enabled &&
740
763
  a11y?.keyboard?.navigation?.enabled &&
741
- w.dom?.baseEl?.querySelector?.('.apexcharts-keyboard-focused')
764
+ this.ttCtx.ctx?._keyboardNavigation?._focusedEl?.isConnected
742
765
  ) {
743
766
  // Same coord space as `y`: elWrap.
744
767
  const refPointY = pointY
@@ -818,14 +841,19 @@ export default class Position {
818
841
  }
819
842
  if (firstPaint) {
820
843
  // Force layout so the position above is committed with transitions
821
- // disabled; then in a microtask (after the active class is added by
822
- // the caller in the same tick) clear the override so subsequent
823
- // moves between data points animate smoothly.
844
+ // disabled; then on the next frame (after the active class is added
845
+ // by the caller in the same tick) clear the override so subsequent
846
+ // moves between data points animate smoothly. Where there is no
847
+ // frame clock (server rendering, a DOM shim without one) it clears
848
+ // at once.
824
849
  void tooltipEl.offsetWidth
825
850
  tooltipEl.dataset.positioned = 'true'
826
- requestAnimationFrame(() => {
851
+ const clear = () => {
827
852
  tooltipEl.style.transitionProperty = ''
828
- })
853
+ }
854
+ if (typeof requestAnimationFrame == 'function')
855
+ requestAnimationFrame(clear)
856
+ else clear()
829
857
  }
830
858
  }
831
859
 
@@ -840,23 +868,22 @@ export default class Position {
840
868
  // batched markers are one path per series, so there is no per-point node
841
869
  // to find by `rel` and enlarge: fall through to the dynamic point
842
870
  if (w.globals.markers.size[i] > 0 && !w.globals.markers.batched) {
843
- const allPoints = w.dom.baseEl.querySelectorAll(
844
- ` .apexcharts-series[data\\:realIndex='${i}'] .apexcharts-marker`,
871
+ // The one marker at `j`, found by its rel: reading rel off every marker
872
+ // of the series was linear in its points on every move.
873
+ const point = w.dom.baseEl.querySelector(
874
+ ` .apexcharts-series[data\\:realIndex='${i}'] .apexcharts-marker[rel='${j}']`,
845
875
  )
846
- for (let p = 0; p < allPoints.length; p++) {
847
- if (parseInt(allPoints[p].getAttribute('rel') ?? '0', 10) === j) {
848
- // Put back only what we previously grew. resetPointsSize() re-paths
849
- // EVERY marker in the chart, which on this (non-shared) path ran per
850
- // hover and made it cost more than the shared tooltip it is usually
851
- // reached for to avoid. Everything that enlarges a marker goes
852
- // through Marker.newPointSize, so the tracked set is complete; the
853
- // zero-size `M0,0` branch resetPointsSize also handles only applies
854
- // to dynamic points, which exist solely when largestSize is 0 or
855
- // markers are batched — neither of which reaches this branch.
856
- ttCtx.marker.resetEnlargedPoints()
857
- ttCtx.marker.enlargeCurrentPoint(j, allPoints[p])
858
- break
859
- }
876
+ if (point) {
877
+ // Put back only what we previously grew. resetPointsSize() re-paths
878
+ // EVERY marker in the chart, which on this (non-shared) path ran per
879
+ // hover and made it cost more than the shared tooltip it is usually
880
+ // reached for to avoid. Everything that enlarges a marker goes
881
+ // through Marker.newPointSize, so the tracked set is complete; the
882
+ // zero-size `M0,0` branch resetPointsSize also handles only applies
883
+ // to dynamic points, which exist solely when largestSize is 0 or
884
+ // markers are batched, neither of which reaches this branch.
885
+ ttCtx.marker.resetEnlargedPoints()
886
+ ttCtx.marker.enlargeCurrentPoint(j, point)
860
887
  }
861
888
  } else {
862
889
  ttCtx.marker.resetPointsSize()
@@ -1100,20 +1127,10 @@ export default class Position {
1100
1127
  const painted = !!(/** @type {any} */ (w.globals).barCanvasCoords)
1101
1128
  // A violin's mark is its body (`.apexcharts-violin-area`), never the
1102
1129
  // jitter path that shares its `j`.
1103
- let jBar = painted
1104
- ? null
1105
- : w.dom.baseEl.querySelector(
1106
- `.apexcharts-bar-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-candlestick-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-boxPlot-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-violin-series .apexcharts-series[rel='${i}'] path.apexcharts-violin-area[j='${j}'], .apexcharts-rangebar-series .apexcharts-series[rel='${i}'] path[j='${j}']`,
1107
- )
1130
+ let jBar = painted ? null : this._marks(barSel(`rel='${i}'`, j))[0]
1108
1131
  if (!jBar && !painted && typeof capturedSeries === 'number') {
1109
1132
  // Try with captured series index
1110
- jBar = w.dom.baseEl.querySelector(
1111
- `.apexcharts-bar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
1112
- .apexcharts-candlestick-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
1113
- .apexcharts-boxPlot-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
1114
- .apexcharts-violin-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path.apexcharts-violin-area[j='${j}'],
1115
- .apexcharts-rangebar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}']`,
1116
- )
1133
+ jBar = this._marks(barSel(`data\\:realIndex='${capturedSeries}'`, j))[0]
1117
1134
  }
1118
1135
  const bc = painted ? this._paintedBarFor(i, j, capturedSeries) : null
1119
1136
 
@@ -1242,6 +1259,30 @@ export default class Position {
1242
1259
  }
1243
1260
  }
1244
1261
 
1262
+ /**
1263
+ * The nodes `sel` finds, none included, kept until the tooltip is drawn
1264
+ * again (every render and update does that) or one of them leaves the DOM.
1265
+ * A move along one column re-ran the same whole-chart attribute scans on
1266
+ * every mousemove. Rects are still read per move: the marks can be
1267
+ * mid-animation.
1268
+ * @param {string} sel
1269
+ * @returns {Element[]}
1270
+ */
1271
+ _marks(sel) {
1272
+ const gen = this.ttCtx.allTooltipSeriesGroups
1273
+ let m = this._memo
1274
+ if (!m || m.gen !== gen || m.size > 99) {
1275
+ m = this._memo = new Map()
1276
+ m.gen = gen
1277
+ }
1278
+ /** @type {Element[]} */
1279
+ let hit = m.get(sel)
1280
+ if (!hit || hit.some((n) => !n.isConnected)) {
1281
+ m.set(sel, (hit = Array.from(this.w.dom.baseEl.querySelectorAll(sel))))
1282
+ }
1283
+ return hit
1284
+ }
1285
+
1245
1286
  /**
1246
1287
  * The coords cached for the bar-like mark at index `j` painted to canvas
1247
1288
  * (Bar.renderSeries), found the way `moveStickyTooltipOverBars` finds the
@@ -1259,11 +1300,7 @@ export default class Position {
1259
1300
  const w = this.w
1260
1301
  const bcc = /** @type {any} */ (w.globals).barCanvasCoords
1261
1302
  if (!bcc) return null
1262
- const groups = w.dom.baseEl.querySelectorAll(
1263
- ['bar', 'candlestick', 'boxPlot', 'violin', 'rangebar']
1264
- .map((t) => `.apexcharts-${t}-series .apexcharts-series[rel='${rel}']`)
1265
- .join(', '),
1266
- )
1303
+ const groups = this._marks(barSel(`rel='${rel}'`))
1267
1304
  for (let g = 0; g < groups.length; g++) {
1268
1305
  const realIndex = groups[g].getAttribute('data:realIndex')
1269
1306
  if (realIndex !== null && bcc[realIndex]?.[j]) return bcc[realIndex][j]
@@ -1352,7 +1389,7 @@ export default class Position {
1352
1389
  if (c?.type === 'violin' && !hidden.includes(Number(key))) take(c.cx)
1353
1390
  }
1354
1391
  } else {
1355
- const bodies = w.dom.baseEl.querySelectorAll(
1392
+ const bodies = this._marks(
1356
1393
  `.apexcharts-violin-series .apexcharts-series:not(.apexcharts-series-collapsed) path.apexcharts-violin-area[j='${j}']`,
1357
1394
  )
1358
1395
  for (let k = 0; k < bodies.length; k++) {
@@ -1374,7 +1411,7 @@ export default class Position {
1374
1411
  */
1375
1412
  _barsExtentInGrid(j) {
1376
1413
  const w = this.w
1377
- const marks = w.dom.baseEl.querySelectorAll(
1414
+ const marks = this._marks(
1378
1415
  `.apexcharts-bar-series path[j='${j}'],` +
1379
1416
  `.apexcharts-rangebar-series path[j='${j}'],` +
1380
1417
  `.apexcharts-candlestick-series path[j='${j}'],` +
@@ -1425,7 +1462,7 @@ export default class Position {
1425
1462
  */
1426
1463
  _datapointCenterXFromBars(j) {
1427
1464
  const w = this.w
1428
- const bars = w.dom.baseEl.querySelectorAll(
1465
+ const bars = this._marks(
1429
1466
  `.apexcharts-bar-series path[j='${j}'],` +
1430
1467
  `.apexcharts-rangebar-series path[j='${j}']`,
1431
1468
  )
@@ -1435,8 +1472,8 @@ export default class Position {
1435
1472
 
1436
1473
  let unionLeft = Infinity
1437
1474
  let unionRight = -Infinity
1475
+ const plot = TooltipUtils.plotRect(w)
1438
1476
  if (painted) {
1439
- const plot = TooltipUtils.plotRect(w)
1440
1477
  unionLeft = plot.left + painted.left * plot.zoom
1441
1478
  unionRight = plot.left + painted.right * plot.zoom
1442
1479
  }
@@ -1452,7 +1489,7 @@ export default class Position {
1452
1489
  // Convert to data-area-local x from the plot origin, the space `cx` and the
1453
1490
  // crosshair are already in. Measuring from the `.apexcharts-grid` element
1454
1491
  // rect instead only works while the grid draws nothing outside its own box.
1455
- return AxisMapping.screenXToPlotPx(w, (unionLeft + unionRight) / 2)
1492
+ return AxisMapping.screenXToPlotPx(w, (unionLeft + unionRight) / 2, plot)
1456
1493
  }
1457
1494
 
1458
1495
  /**
@@ -1476,7 +1513,7 @@ export default class Position {
1476
1513
  // config-time, but the selector is harmless), and violin, read as its
1477
1514
  // whole glyph: body, box lane and jitter or rain, so the box clears a
1478
1515
  // raincloud's lanes as well as its cloud.
1479
- const bars = w.dom.baseEl.querySelectorAll(
1516
+ const bars = this._marks(
1480
1517
  `.apexcharts-bar-series path[j='${j}'],` +
1481
1518
  `.apexcharts-rangebar-series path[j='${j}'],` +
1482
1519
  `.apexcharts-boxPlot-series path[j='${j}'],` +