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
@@ -84,7 +84,6 @@ export default class Tooltip {
84
84
  this.e = null
85
85
  this.isBarShared = !w.globals.isBarHorizontal && this.tConfig.shared
86
86
  this.lastHoverTime = Date.now()
87
- this.dimensionUpdateScheduled = false
88
87
 
89
88
  // Properties set in drawTooltip() / create() / event handlers
90
89
  /** @type {import('../../types/internal').XYRatios | null} */
@@ -117,7 +116,7 @@ export default class Tooltip {
117
116
  /**
118
117
  * The hover listeners addSVGEvents put on the svg itself (`hoverArea`),
119
118
  * kept so the next call can take them off: see detachHoverAreaListeners.
120
- * @type {Array<{ el: Element, ev: string, handler: (e: any) => void }>}
119
+ * @type {Array<{ el: Element, ev: string, handler: (e: any) => void, opt: any }>}
121
120
  */
122
121
  this.hoverAreaListeners = []
123
122
  /** @type {ReturnType<typeof setTimeout> | undefined} */
@@ -157,79 +156,25 @@ export default class Tooltip {
157
156
  return this.w.config.tooltip
158
157
  }
159
158
 
160
- setupDimensionCache() {
161
- const w = this.w
159
+ /**
160
+ * The tooltip's last measured size, measured again once it is a second
161
+ * old. Only a first guess: Labels measures again once it has written the
162
+ * rows, before anything is placed off it.
163
+ * @returns {{ ttWidth: number, ttHeight: number }}
164
+ */
165
+ getCachedDimensions() {
166
+ const dims = this.w.globals.dimensionCache
162
167
  const tooltipEl = this.getElTooltip()
163
-
164
- if (!tooltipEl) return
165
-
166
- // No initial measurement on purpose. Reading the rect here forced a
167
- // synchronous layout of the chart that was just (re)built, on every render
168
- // AND every update, to measure a tooltip that is still empty and hidden --
169
- // a size superseded the moment any content is written into it. It is not
170
- // needed either: `getCachedDimensions()` measures live when the cache is
171
- // missing or older than a second, and every consumer of it runs at
172
- // interaction time, by which point the ResizeObserver below has normally
173
- // filled the cache anyway.
174
-
175
- // Setup ResizeObserver for automatic dimension updates
176
- if (typeof ResizeObserver !== 'undefined' && !w.globals.resizeObserver) {
177
- w.globals.resizeObserver = new ResizeObserver(() => {
178
- if (!this.dimensionUpdateScheduled) {
179
- this.dimensionUpdateScheduled = true
180
- requestAnimationFrame(() => {
181
- this.updateDimensionCache()
182
- this.dimensionUpdateScheduled = false
183
- })
184
- }
168
+ let cache = /** @type {any} */ (dims.tooltip)
169
+ if (tooltipEl && !(Date.now() - cache?.lastUpdate < 1000)) {
170
+ const rect = tooltipEl.getBoundingClientRect()
171
+ cache = dims.tooltip = /** @type {any} */ ({
172
+ width: rect.width,
173
+ height: rect.height,
174
+ lastUpdate: Date.now(),
185
175
  })
186
- w.globals.resizeObserver.observe(tooltipEl)
187
- }
188
- }
189
-
190
- updateDimensionCache() {
191
- const w = this.w
192
- const tooltipEl = this.getElTooltip()
193
-
194
- if (!tooltipEl) return
195
-
196
- const rect = tooltipEl.getBoundingClientRect()
197
- w.globals.dimensionCache.tooltip = /** @type {any} */ ({
198
- width: rect.width,
199
- height: rect.height,
200
- lastUpdate: Date.now(),
201
- })
202
- }
203
-
204
- getCachedDimensions() {
205
- const w = this.w
206
-
207
- // Return cached dimensions if available and fresh (< 1 second old)
208
- if (w.globals.dimensionCache.tooltip) {
209
- const cache = /** @type {Record<string,any>} */ (
210
- w.globals.dimensionCache.tooltip
211
- )
212
- const age = Date.now() - cache.lastUpdate
213
-
214
- if (age < 1000) {
215
- return {
216
- ttWidth: cache.width,
217
- ttHeight: cache.height,
218
- }
219
- }
220
176
  }
221
-
222
- // Fallback to live measurement and update cache
223
- this.updateDimensionCache()
224
- const cache = /** @type {Record<string,any>} */ (
225
- w.globals.dimensionCache.tooltip
226
- )
227
- return cache
228
- ? {
229
- ttWidth: cache.width,
230
- ttHeight: cache.height,
231
- }
232
- : { ttWidth: 0, ttHeight: 0 }
177
+ return { ttWidth: cache?.width || 0, ttHeight: cache?.height || 0 }
233
178
  }
234
179
 
235
180
  /**
@@ -244,9 +189,9 @@ export default class Tooltip {
244
189
  * 700-series chart showed dominating hover time. Validity is checked with
245
190
  * `baseEl.contains()` — a short walk up the ancestors, not a tree scan.
246
191
  *
247
- * That check is what respects `fastUpdate`: `drawTooltip` DETACHES the
248
- * existing tooltip before building a replacement (see its comment on the
249
- * fastUpdate path), so a rebuilt element leaves the cached node outside
192
+ * That check is what respects a rebuild: `drawTooltip` DETACHES the
193
+ * existing tooltip before building a replacement (a data-only fastUpdate
194
+ * keeps it), so a rebuilt element leaves the cached node outside
250
195
  * `baseEl`, the check fails, and the selector runs again. A full re-render
251
196
  * replaces `baseEl`'s subtree with the same consequence.
252
197
  *
@@ -342,141 +287,156 @@ export default class Tooltip {
342
287
  // axes), so the nodes cached from the last one are detached.
343
288
  this.yaxisTTEls = null
344
289
  this.hoverBars = []
290
+ // What the last render's hovers learnt about its marks and legend.
291
+ /** @type {any} */
292
+ this._facts = null
293
+ /** @type {string | null} */
294
+ this.legendKey = null
345
295
 
346
296
  if (!w.globals.axisCharts) {
347
297
  this.showTooltipTitle = false
348
298
  }
349
299
 
350
- // Remove any existing tooltip element to avoid duplicates on re-draws
351
- // (e.g. fastUpdate path calls drawTooltip without full DOM teardown)
352
- const existingTooltip = this.getElTooltip()
353
- if (existingTooltip?.parentNode) {
354
- existingTooltip.parentNode.removeChild(existingTooltip)
355
- }
356
- this.tooltipTitle = null
357
-
358
- const tooltipEl = BrowserAPIs.createElementNS(
359
- 'http://www.w3.org/1999/xhtml',
360
- 'div',
361
- )
362
- tooltipEl.classList.add('apexcharts-tooltip')
363
- if (this.tConfig.interactive) {
364
- tooltipEl.classList.add('apexcharts-tooltip-interactive')
365
- tooltipEl.addEventListener(
366
- 'mouseenter',
367
- () => {
368
- clearTimeout(this.interactiveHideTimeout)
369
- this.cancelOffMarkHide()
370
- },
371
- { passive: true },
372
- )
373
- tooltipEl.addEventListener(
374
- 'mouseleave',
375
- () => {
376
- clearTimeout(this.interactiveHideTimeout)
377
- this.handleMouseOut({ tooltipEl })
378
- if (w.config.chart.group) {
379
- this.ctx.getGroupedCharts().forEach((ch) => {
380
- const el = this.getElTooltip(ch)
381
- if (el) ch.w.globals.tooltip.handleMouseOut({ tooltipEl: el })
382
- })
383
- }
384
- },
385
- { passive: true },
300
+ // A data-only fast update keeps elWrap (a render builds a new one), and
301
+ // with it this box. Nothing in the box depends on the data, only on the
302
+ // config, which such an update leaves alone (a hover writes the row
303
+ // colours), so it is kept, rows and all, and only the hover is bound
304
+ // again: rebuilding cost a row of nodes and an SVG parse per series per
305
+ // update, and closed an open tooltip on every update. A custom tooltip
306
+ // that replaced the rows rebuilds them.
307
+ /** @type {any} */
308
+ let tooltipEl = this.getElTooltip()
309
+ const keep =
310
+ tooltipEl?.parentNode === w.dom.elWrap &&
311
+ this.ttItems?.[0]?.parentNode === tooltipEl
312
+ if (!keep) {
313
+ // Remove any existing tooltip element to avoid duplicates on re-draws
314
+ tooltipEl?.remove()
315
+ this.tooltipTitle = null
316
+ tooltipEl = BrowserAPIs.createElementNS(
317
+ 'http://www.w3.org/1999/xhtml',
318
+ 'div',
386
319
  )
387
- }
388
- if (w.config.tooltip.cssClass) {
389
- // A space-separated list is a valid class attribute, but classList.add
390
- // throws on a token with whitespace in it.
320
+ tooltipEl.classList.add('apexcharts-tooltip')
321
+ if (this.tConfig.interactive) {
322
+ tooltipEl.classList.add('apexcharts-tooltip-interactive')
323
+ tooltipEl.addEventListener(
324
+ 'mouseenter',
325
+ () => {
326
+ clearTimeout(this.interactiveHideTimeout)
327
+ this.cancelOffMarkHide()
328
+ },
329
+ { passive: true },
330
+ )
331
+ tooltipEl.addEventListener(
332
+ 'mouseleave',
333
+ () => {
334
+ clearTimeout(this.interactiveHideTimeout)
335
+ this.handleMouseOut({ tooltipEl })
336
+ if (w.config.chart.group) {
337
+ this.ctx.getGroupedCharts().forEach((ch) => {
338
+ const el = this.getElTooltip(ch)
339
+ if (el) ch.w.globals.tooltip.handleMouseOut({ tooltipEl: el })
340
+ })
341
+ }
342
+ },
343
+ { passive: true },
344
+ )
345
+ }
346
+ if (w.config.tooltip.cssClass) {
347
+ // A space-separated list is a valid class attribute, but classList.add
348
+ // throws on a token with whitespace in it.
349
+ tooltipEl.classList.add(
350
+ ...String(w.config.tooltip.cssClass).split(/\s+/).filter(Boolean),
351
+ )
352
+ }
391
353
  tooltipEl.classList.add(
392
- ...String(w.config.tooltip.cssClass).split(/\s+/).filter(Boolean),
354
+ `apexcharts-theme-${this.tConfig.theme || 'light'}`,
393
355
  )
394
- }
395
- tooltipEl.classList.add(`apexcharts-theme-${this.tConfig.theme || 'light'}`)
396
-
397
- // `fillSeriesColor` paints each series-group with its series colour.
398
- // Tag the tooltip so the CSS can drop the glass body (transparent bg,
399
- // no border, no backdrop-filter) and clip the coloured series-group(s)
400
- // to the body's rounded corners — otherwise the colour block sits
401
- // inside a glass shell with visible padding/border around it.
402
- if (this.tConfig.fillSeriesColor) {
403
- tooltipEl.classList.add('apexcharts-tooltip-fill-series')
404
- }
405
356
 
406
- // `compact`: one tight line instead of a card. The x label loses its own
407
- // title bar and sits inline before the value, the marker goes, and the
408
- // padding and font shrink. Meant for panels a normal card would cover
409
- // (small multiples, sparklines, dashboard tiles). With a SINGLE series the
410
- // series-name label goes too, because in a one-series panel it repeats
411
- // what the panel header already says; with several series the names are
412
- // the only thing distinguishing the rows, so they stay.
413
- if (this.tConfig.compact) {
414
- tooltipEl.classList.add('apexcharts-tooltip-compact')
415
- if (w.config.series.length === 1) {
416
- tooltipEl.classList.add('apexcharts-tooltip-value-only')
357
+ // `fillSeriesColor` paints each series-group with its series colour.
358
+ // Tag the tooltip so the CSS can drop the glass body (transparent bg,
359
+ // no border, no backdrop-filter) and clip the coloured series-group(s)
360
+ // to the body's rounded corners; otherwise the colour block sits
361
+ // inside a glass shell with visible padding/border around it.
362
+ if (this.tConfig.fillSeriesColor) {
363
+ tooltipEl.classList.add('apexcharts-tooltip-fill-series')
417
364
  }
418
- }
419
365
 
420
- // Optional user-supplied solid background — set on the CSS variable so
421
- // the rest of the glass theme (border, shadow) still applies cleanly.
422
- // An opaque value also visually disables the backdrop blur.
423
- if (this.tConfig.style && this.tConfig.style.background) {
424
- tooltipEl.style.setProperty(
425
- '--apx-tt-bg',
426
- this.tConfig.style.background,
427
- )
428
- }
366
+ // `compact`: one tight line instead of a card. The x label loses its own
367
+ // title bar and sits inline before the value, the marker goes, and the
368
+ // padding and font shrink. Meant for panels a normal card would cover
369
+ // (small multiples, sparklines, dashboard tiles). With a SINGLE series the
370
+ // series-name label goes too, because in a one-series panel it repeats
371
+ // what the panel header already says; with several series the names are
372
+ // the only thing distinguishing the rows, so they stay.
373
+ if (this.tConfig.compact) {
374
+ tooltipEl.classList.add('apexcharts-tooltip-compact')
375
+ if (w.config.series.length === 1) {
376
+ tooltipEl.classList.add('apexcharts-tooltip-value-only')
377
+ }
378
+ }
429
379
 
430
- // Arrow connector (default on via `tooltip.arrow`). Positioned by
431
- // Position.applyTooltipPosition() via the `--apx-tt-arrow-y` CSS var.
432
- // Skip the arrow in these cases:
433
- // - `followCursor`: the cursor IS the anchor; arrow would be redundant.
434
- // - `fixed.enabled`: tooltip is pinned to a chart corner, not a point,
435
- // so an arrow would point nowhere.
436
- // - shared tooltip on a multi-series chart: there's no single point to
437
- // anchor to, so an arrow would mislead. Exceptions: horizontal-bar
438
- // charts (a single row of bars per category — tooltip goes above/below
439
- // the row union) and heatmaps (the hover resolves a single cell —
440
- // tooltip goes above/below that cell). Both keep a meaningful arrow
441
- // target, so they're allowed an arrow despite `shared` being on.
442
- // - non-axis charts (pie/donut/radialBar/polarArea): slices radiate
443
- // from a centre, so there's no single "anchor edge" for the arrow.
444
- // - `fillSeriesColor`: the glass arrow would not match the solid
445
- // series-colour body. Better to drop it than render a colour clash.
446
- const isSharedMulti =
447
- this.tConfig.shared &&
448
- w.config.series.length > 1 &&
449
- !w.globals.isBarHorizontal &&
450
- w.config.chart.type !== 'heatmap'
451
- const shouldDrawArrow =
452
- this.tConfig.arrow &&
453
- !Utils.isFollowCursor(this.w) &&
454
- !this.tConfig.fixed.enabled &&
455
- !isSharedMulti &&
456
- !this.tConfig.fillSeriesColor &&
457
- w.globals.axisCharts
458
- if (shouldDrawArrow) {
459
- const arrowEl = BrowserAPIs.createElementNS(
460
- 'http://www.w3.org/1999/xhtml',
461
- 'div',
462
- )
463
- arrowEl.classList.add('apexcharts-tooltip-arrow')
464
- tooltipEl.appendChild(arrowEl)
465
- }
380
+ // Optional user-supplied solid background, set on the CSS variable so
381
+ // the rest of the glass theme (border, shadow) still applies cleanly.
382
+ // An opaque value also visually disables the backdrop blur.
383
+ if (this.tConfig.style && this.tConfig.style.background) {
384
+ tooltipEl.style.setProperty(
385
+ '--apx-tt-bg',
386
+ this.tConfig.style.background,
387
+ )
388
+ }
466
389
 
467
- // accessibility attributes
468
- if (
469
- w.config.chart.accessibility.enabled &&
470
- w.config.chart.accessibility.announcements.enabled
471
- ) {
472
- tooltipEl.setAttribute('role', 'tooltip')
473
- tooltipEl.setAttribute('aria-live', 'polite')
474
- tooltipEl.setAttribute('aria-atomic', 'true')
475
- tooltipEl.setAttribute('aria-hidden', 'true')
476
- }
390
+ // Arrow connector (default on via `tooltip.arrow`). Positioned by
391
+ // Position.applyTooltipPosition() via the `--apx-tt-arrow-y` CSS var.
392
+ // Skip the arrow in these cases:
393
+ // - `followCursor`: the cursor IS the anchor; arrow would be redundant.
394
+ // - `fixed.enabled`: tooltip is pinned to a chart corner, not a point,
395
+ // so an arrow would point nowhere.
396
+ // - shared tooltip on a multi-series chart: there's no single point to
397
+ // anchor to, so an arrow would mislead. Exceptions: horizontal-bar
398
+ // charts (a single row of bars per category: tooltip goes above/below
399
+ // the row union) and heatmaps (the hover resolves a single cell:
400
+ // tooltip goes above/below that cell). Both keep a meaningful arrow
401
+ // target, so they're allowed an arrow despite `shared` being on.
402
+ // - non-axis charts (pie/donut/radialBar/polarArea): slices radiate
403
+ // from a centre, so there's no single "anchor edge" for the arrow.
404
+ // - `fillSeriesColor`: the glass arrow would not match the solid
405
+ // series-colour body. Better to drop it than render a colour clash.
406
+ const isSharedMulti =
407
+ this.tConfig.shared &&
408
+ w.config.series.length > 1 &&
409
+ !w.globals.isBarHorizontal &&
410
+ w.config.chart.type !== 'heatmap'
411
+ const shouldDrawArrow =
412
+ this.tConfig.arrow &&
413
+ !Utils.isFollowCursor(this.w) &&
414
+ !this.tConfig.fixed.enabled &&
415
+ !isSharedMulti &&
416
+ !this.tConfig.fillSeriesColor &&
417
+ w.globals.axisCharts
418
+ if (shouldDrawArrow) {
419
+ const arrowEl = BrowserAPIs.createElementNS(
420
+ 'http://www.w3.org/1999/xhtml',
421
+ 'div',
422
+ )
423
+ arrowEl.classList.add('apexcharts-tooltip-arrow')
424
+ tooltipEl.appendChild(arrowEl)
425
+ }
477
426
 
478
- w.dom.elWrap.appendChild(tooltipEl)
427
+ // accessibility attributes
428
+ if (
429
+ w.config.chart.accessibility.enabled &&
430
+ w.config.chart.accessibility.announcements.enabled
431
+ ) {
432
+ tooltipEl.setAttribute('role', 'tooltip')
433
+ tooltipEl.setAttribute('aria-live', 'polite')
434
+ tooltipEl.setAttribute('aria-atomic', 'true')
435
+ tooltipEl.setAttribute('aria-hidden', 'true')
436
+ }
479
437
 
438
+ w.dom.elWrap.appendChild(tooltipEl)
439
+ }
480
440
 
481
441
  if (w.globals.axisCharts) {
482
442
  this.axesTooltip.drawXaxisTooltip()
@@ -515,18 +475,6 @@ export default class Tooltip {
515
475
  this.dataPointsDividedHeight = w.layout.gridHeight / w.globals.dataPoints
516
476
  this.dataPointsDividedWidth = w.layout.gridWidth / w.globals.dataPoints
517
477
 
518
- if (this.showTooltipTitle) {
519
- this.tooltipTitle = BrowserAPIs.createElementNS(
520
- 'http://www.w3.org/1999/xhtml',
521
- 'div',
522
- )
523
- this.tooltipTitle.classList.add('apexcharts-tooltip-title')
524
- this.tooltipTitle.style.fontFamily =
525
- this.tConfig.style.fontFamily || w.config.chart.fontFamily
526
- this.tooltipTitle.style.fontSize = this.tConfig.style.fontSize
527
- tooltipEl.appendChild(this.tooltipTitle)
528
- }
529
-
530
478
  // A row per series is only ever DISPLAYED by a shared tooltip: every
531
479
  // non-shared path activates exactly one group (see
532
480
  // Labels.toggleActiveInactiveSeries) and writes the hovered series' values
@@ -545,12 +493,54 @@ export default class Tooltip {
545
493
  : 1
546
494
  }
547
495
 
548
- this.legendLabels = w.dom.baseEl.querySelectorAll('.apexcharts-legend-text')
496
+ if (!keep) {
497
+ if (this.showTooltipTitle) {
498
+ this.tooltipTitle = BrowserAPIs.createElementNS(
499
+ 'http://www.w3.org/1999/xhtml',
500
+ 'div',
501
+ )
502
+ this.tooltipTitle.classList.add('apexcharts-tooltip-title')
503
+ this.tooltipTitle.style.fontFamily =
504
+ this.tConfig.style.fontFamily || w.config.chart.fontFamily
505
+ this.tooltipTitle.style.fontSize = this.tConfig.style.fontSize
506
+ tooltipEl.appendChild(this.tooltipTitle)
507
+ }
508
+ this.ttItems = this.createTTElements(ttItemsCnt)
509
+ }
549
510
 
550
- this.ttItems = this.createTTElements(ttItemsCnt)
511
+ this.legendLabels = w.dom.baseEl.querySelectorAll('.apexcharts-legend-text')
551
512
  this.addSVGEvents()
552
513
 
553
- this.setupDimensionCache()
514
+ // Kept open across the update: shown again for the new data at the
515
+ // same pointer where that is all the hover reads (the plot-wide
516
+ // listener), else closed, its marks having been redrawn under it. The
517
+ // replay waits for the update's tween to land: a hover dot drawn now
518
+ // would sit at the new value while the line still eases towards it. A
519
+ // later bind or a mouseout drops the wait. Meanwhile the marker ease is
520
+ // off (markers riding the tween would lag under it), and the replay is
521
+ // placed as a first show is, or the new dots glide in from the corner.
522
+ // Without a frame clock (a DOM shim without one) it shows at once.
523
+ if (keep && tooltipEl.classList.contains('apexcharts-active')) {
524
+ const h = this.hoverAreaListeners[0]
525
+ if (h && /move/.test(this.e?.type) && !w.config.chart.group) {
526
+ let n = 600
527
+ const replay = () => {
528
+ if (h !== this.hoverAreaListeners[0] || w.globals.isDestroyed) return
529
+ if (
530
+ !w.globals.animationEnded &&
531
+ n-- &&
532
+ typeof requestAnimationFrame == 'function'
533
+ )
534
+ requestAnimationFrame(replay)
535
+ else if (tooltipEl.classList.contains('apexcharts-active')) {
536
+ delete tooltipEl.dataset.positioned
537
+ this.seriesHover(h.opt, this.e)
538
+ }
539
+ }
540
+ w.dom.baseEl.classList.remove('apexcharts-tooltip-active')
541
+ replay()
542
+ } else this.handleMouseOut({ tooltipEl })
543
+ }
554
544
  }
555
545
 
556
546
  /**
@@ -702,45 +692,13 @@ export default class Tooltip {
702
692
  // synchronous layout of the chart that was just rebuilt: a profile of 30
703
693
  // updateSeries calls put 20% of all update time in this one dead read.
704
694
 
705
- /** @type {any[]} */
706
- const tooltipY = []
707
- /** @type {any[]} */
708
- const tooltipX = []
709
-
710
695
  const seriesHoverParams = {
711
696
  hoverArea,
712
697
  elGrid,
713
698
  tooltipEl,
714
- tooltipY,
715
- tooltipX,
716
699
  ttItems: this.ttItems,
717
700
  }
718
701
 
719
- let points
720
-
721
- if (w.globals.axisCharts) {
722
- if (chartWithmarkers) {
723
- points = w.dom.baseEl.querySelectorAll(
724
- ".apexcharts-series[data\\:longestSeries='true'] .apexcharts-marker",
725
- )
726
- } else if (commonBar) {
727
- points = w.dom.baseEl.querySelectorAll(
728
- '.apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-violin-area, .apexcharts-series .apexcharts-rangebar-area',
729
- )
730
- } else if (type === 'heatmap' || type === 'treemap') {
731
- points = w.dom.baseEl.querySelectorAll(
732
- '.apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap',
733
- )
734
- }
735
-
736
- if (points && points.length) {
737
- for (let p = 0; p < points.length; p++) {
738
- tooltipY.push(points[p].getAttribute('cy'))
739
- tooltipX.push(points[p].getAttribute('cx'))
740
- }
741
- }
742
- }
743
-
744
702
  const validSharedChartTypes =
745
703
  (w.globals.xyCharts && !this.showOnIntersect) ||
746
704
  (w.globals.comboCharts && !this.showOnIntersect) ||
@@ -918,8 +876,6 @@ export default class Tooltip {
918
876
  const extendedOpts = {
919
877
  paths: paths[p],
920
878
  tooltipEl: opts.tooltipEl,
921
- tooltipY: opts.tooltipY,
922
- tooltipX: opts.tooltipX,
923
879
  elGrid: opts.elGrid,
924
880
  hoverArea: opts.hoverArea,
925
881
  ttItems: opts.ttItems,
@@ -936,7 +892,12 @@ export default class Tooltip {
936
892
  events.map((ev) => {
937
893
  const handler = self.onSeriesHover.bind(self, extendedOpts)
938
894
  if (paths[p] === opts.hoverArea) {
939
- self.hoverAreaListeners.push({ el: paths[p], ev, handler })
895
+ self.hoverAreaListeners.push({
896
+ el: paths[p],
897
+ ev,
898
+ handler,
899
+ opt: extendedOpts,
900
+ })
940
901
  }
941
902
  return paths[p].addEventListener(ev, handler, {
942
903
  capture: false,
@@ -1122,8 +1083,6 @@ export default class Tooltip {
1122
1083
  const newOpts = {
1123
1084
  paths: opt.paths,
1124
1085
  tooltipEl,
1125
- tooltipY: opt.tooltipY,
1126
- tooltipX: opt.tooltipX,
1127
1086
  elGrid: elGrid || opt.elGrid,
1128
1087
  hoverArea: opt.hoverArea,
1129
1088
  ttItems: ch.w.globals.tooltip.ttItems,
@@ -1677,6 +1636,11 @@ export default class Tooltip {
1677
1636
  */
1678
1637
  applyLegendHoverText(series, j, shared) {
1679
1638
  const w = this.w
1639
+ // Every move in the same column, and every hide, asks for what is
1640
+ // already written: the next render or data update asks again.
1641
+ const key = `${shared || series},${j}`
1642
+ if (key === this.legendKey) return
1643
+ this.legendKey = key
1680
1644
  const els = this.legendLabels ?? []
1681
1645
  for (let k = 0; k < els.length; k++) {
1682
1646
  const l = /** @type {HTMLElement} */ (els[k])
@@ -1961,6 +1925,9 @@ export default class Tooltip {
1961
1925
  const xcrosshairs = this.getElXCrosshairs()
1962
1926
  w.dom.baseEl.classList.remove('apexcharts-tooltip-active')
1963
1927
 
1928
+ // Did anything show or grow since the last hide? This runs per move
1929
+ // over the axis margins, and per marker left.
1930
+ const shown = opt.tooltipEl.classList.contains('apexcharts-active')
1964
1931
  opt.tooltipEl.classList.remove('apexcharts-active')
1965
1932
  // Clear the first-paint marker so the next show repositions without
1966
1933
  // animating from the now-stale prior coords. Paired with the
@@ -1975,7 +1942,10 @@ export default class Tooltip {
1975
1942
  this.deactivateHoverFilter()
1976
1943
  // A canvas heatmap's dataPointMouseLeave for the cell it was over.
1977
1944
  this.intersect.hoverCell(null)
1978
- if (w.config.chart.type !== 'bubble') {
1945
+ if (
1946
+ w.config.chart.type !== 'bubble' &&
1947
+ (shown || this.marker.dirty || this.marker.enlargedPoints.size)
1948
+ ) {
1979
1949
  this.marker.resetPointsSize()
1980
1950
  }
1981
1951
  if (xcrosshairs !== null) {
@@ -2098,8 +2068,6 @@ export default class Tooltip {
2098
2068
  this._hasCustomSeries()
2099
2069
  const dynamicPoints = canvasNonBar || canvasPointSeries || marksMode
2100
2070
 
2101
- const bars = this.tooltipUtil.getElBars()
2102
-
2103
2071
  const handlePoints = () => {
2104
2072
  if (
2105
2073
  w.globals.markers.largestSize > 0 &&
@@ -2137,16 +2105,30 @@ export default class Tooltip {
2137
2105
  shared: this.showOnIntersect ? false : this.tConfig.shared,
2138
2106
  })
2139
2107
 
2140
- if (hasMarkers || dynamicPoints) {
2108
+ // A shared tooltip speaks for every series at `j`, so in a combo the
2109
+ // line's hover dots and the columns' hover state both follow it,
2110
+ // whichever series the pointer is nearest. Only the box goes to the
2111
+ // captured series' mark. Gating the bars on the captured series having
2112
+ // no markers left the dots on the last column the line was nearest to.
2113
+ const capturedPoints = hasMarkers || dynamicPoints
2114
+ if (
2115
+ capturedPoints ||
2116
+ (w.globals.comboCharts &&
2117
+ !canvasMode &&
2118
+ this.tooltipUtil.hasVisibleMarkers())
2119
+ ) {
2141
2120
  handlePoints()
2142
- } else if (this.tooltipUtil.hasBars()) {
2121
+ }
2122
+ if (this.tooltipUtil.hasBars()) {
2143
2123
  if (canvasMode) {
2144
2124
  // canvas: bars/candles paint to the bitmap, so the paths /
2145
2125
  // hover-filter machinery below is DOM-only. Position straight off
2146
2126
  // the cached bar coords.
2147
- ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2127
+ if (!capturedPoints) {
2128
+ ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2129
+ }
2148
2130
  } else if (
2149
- [...bars].some((/** @type {Element} */ b) => b.querySelector('path'))
2131
+ [...this.tooltipUtil.getElBars()].some((b) => b.querySelector('path'))
2150
2132
  ) {
2151
2133
  // Any drawn bar mark. A DOM probe, not a getBBox, so a move does
2152
2134
  // not force a layout.
@@ -2157,7 +2139,9 @@ export default class Tooltip {
2157
2139
  // de-activate first
2158
2140
  this.deactivateHoverFilter()
2159
2141
 
2160
- ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2142
+ if (!capturedPoints) {
2143
+ ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
2144
+ }
2161
2145
 
2162
2146
  for (let b = 0; b < paths.length; b++) {
2163
2147
  graphics.pathMouseEnter(
@@ -2186,5 +2170,8 @@ export default class Tooltip {
2186
2170
  ttCtx.tooltipPosition.moveDynamicPointOnHover(j, capturedSeries)
2187
2171
  }
2188
2172
  }
2173
+ // The markers and dots moved above are this hover's: handleMouseOut
2174
+ // puts them back even if the box was never shown.
2175
+ ttCtx.marker.dirty = true
2189
2176
  }
2190
2177
  }