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
@@ -4,8 +4,8 @@ import Graphics from '../modules/Graphics'
4
4
  import Series from '../modules/Series'
5
5
  import Utils from '../utils/Utils'
6
6
  import {
7
- datumKey,
8
7
  renderBarExitGhosts,
8
+ uniquifyKeys,
9
9
  } from '../modules/animations/LengthTransition'
10
10
 
11
11
  /**
@@ -29,7 +29,7 @@ class RangeBar extends Bar {
29
29
  this.seriesRangeStart = w.rangeData.seriesRangeStart
30
30
  this.seriesRangeEnd = w.rangeData.seriesRangeEnd
31
31
 
32
- this.barHelpers.initVariables(series)
32
+ this.barHelpers.initVariables(series, seriesIndex)
33
33
 
34
34
  const ret = graphics.group({
35
35
  class: 'apexcharts-rangebar-series apexcharts-plot-series',
@@ -58,6 +58,10 @@ class RangeBar extends Bar {
58
58
  }
59
59
 
60
60
  const translationsIndex = this.setSeriesAxis(realIndex)
61
+ // a time axis's labels never reverse, so its bars may not either
62
+ if (this.isHorizontal && w.labelData.timescaleLabels.length) {
63
+ this.isReversed = false
64
+ }
61
65
 
62
66
  const initPositions = this.barHelpers.initialPositions(realIndex)
63
67
  const {
@@ -66,8 +70,6 @@ class RangeBar extends Bar {
66
70
  x: initX,
67
71
  zeroH, // zeroH is the baseline where 0 meets y axis
68
72
  } = initPositions
69
- let barWidth = initPositions.barWidth ?? 0
70
- let barHeight = initPositions.barHeight ?? 0
71
73
  const yDivision = initPositions.yDivision ?? 0
72
74
  const xDivision = initPositions.xDivision ?? 0
73
75
 
@@ -84,11 +86,16 @@ class RangeBar extends Bar {
84
86
  class: 'apexcharts-rangebar-goals-markers',
85
87
  })
86
88
 
89
+ const data = /** @type {any} */ (w.config.series[realIndex]).data
90
+
87
91
  for (let j = 0; j < w.globals.dataPoints; j++) {
88
92
  const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex)
93
+ // per datum: an overlap row shrinks only its own bars
94
+ let barWidth = initPositions.barWidth ?? 0
95
+ let barHeight = initPositions.barHeight ?? 0
89
96
 
90
- const y1 = this.seriesRangeStart[i][j]
91
- const y2 = this.seriesRangeEnd[i][j]
97
+ const y1 = this.seriesRangeStart[realIndex][j]
98
+ const y2 = this.seriesRangeEnd[realIndex][j]
92
99
 
93
100
  let paths = /** @type {any} */ (null)
94
101
  let barXPosition = null
@@ -100,10 +107,7 @@ class RangeBar extends Bar {
100
107
  seriesLen = 1
101
108
  }
102
109
 
103
- if (
104
- typeof /** @type {Record<string,any>} */ (w.config.series[i]).data?.[j] ===
105
- 'undefined'
106
- ) {
110
+ if (typeof data?.[j] === 'undefined') {
107
111
  // no data exists for further indexes, hence we need to get out the innr loop.
108
112
  // As we are iterating over total datapoints, there is a possiblity the series might not have data for j index
109
113
  break
@@ -114,9 +118,9 @@ class RangeBar extends Bar {
114
118
 
115
119
  const srty = (yDivision - barHeight * seriesLen) / 2
116
120
 
117
- if (/** @type {Record<string,any>} */ (w.config.series[i]).data?.[j]?.x) {
121
+ if (data[j]?.x != null) {
118
122
  const positions = this.detectOverlappingBars({
119
- i,
123
+ i: realIndex,
120
124
  j,
121
125
  barYPosition,
122
126
  srty,
@@ -144,7 +148,8 @@ class RangeBar extends Bar {
144
148
  } else {
145
149
  if (w.axisFlags.isXNumeric) {
146
150
  x =
147
- (w.seriesData.seriesX[i][j] - w.globals.minX) / this.xRatio -
151
+ (w.seriesData.seriesX[realIndex][j] - w.globals.minX) /
152
+ this.xRatio -
148
153
  barWidth / 2
149
154
  }
150
155
 
@@ -152,9 +157,9 @@ class RangeBar extends Bar {
152
157
 
153
158
  const srtx = (xDivision - barWidth * seriesLen) / 2
154
159
 
155
- if (/** @type {Record<string,any>} */ (w.config.series[i]).data?.[j]?.x) {
160
+ if (data[j]?.x != null) {
156
161
  const positions = this.detectOverlappingBars({
157
- i,
162
+ i: realIndex,
158
163
  j,
159
164
  barXPosition,
160
165
  srtx,
@@ -177,10 +182,11 @@ class RangeBar extends Bar {
177
182
  })
178
183
 
179
184
  barHeight = paths.barHeight
185
+ barXPosition = paths.barXPosition
180
186
  }
181
187
 
182
188
  const barGoalLine = this.barHelpers.drawGoalLine({
183
- barXPosition: paths.barXPosition,
189
+ barXPosition,
184
190
  barYPosition,
185
191
  goalX: paths.goalX,
186
192
  goalY: paths.goalY,
@@ -236,16 +242,11 @@ class RangeBar extends Bar {
236
242
  // overrides draw() and never made this call, so a series hidden from the
237
243
  // legend (no datums left) dropped every bar on the click.
238
244
  if (w.globals.previousPaths.length > 0) {
239
- const data = /** @type {Record<string,any>} */ (w.config.series[i]).data || []
240
- const newKeys = []
241
- for (let j = 0; j < data.length; j++) {
242
- newKeys.push(datumKey(w, realIndex, j))
243
- }
244
245
  renderBarExitGhosts({
245
246
  w,
246
247
  elSeries,
247
248
  record: this._prevRecord(realIndex),
248
- newKeys,
249
+ newKeys: this._keys(realIndex),
249
250
  isHorizontal: this.isHorizontal,
250
251
  speed: w.config.chart.animations.dynamicAnimation.speed,
251
252
  })
@@ -308,18 +309,11 @@ class RangeBar extends Bar {
308
309
 
309
310
  if (overlaps.indexOf(rangeName) > -1) {
310
311
  barHeight = initPositions.barHeight / overlaps.length
311
-
312
- barYPosition =
313
- barHeight * this.visibleI +
314
- (yDivision * (100 - parseInt(this.barOptions.barHeight, 10))) /
315
- 100 /
316
- 2 +
317
- barHeight * (this.visibleI + overlaps.indexOf(rangeName)) +
318
- yDivision * rowIndex
312
+ barYPosition += barHeight * overlaps.indexOf(rangeName)
319
313
  }
320
314
  }
321
315
  } else {
322
- if (rowIndex > -1 && !w.labelData.timescaleLabels.length) {
316
+ if (rowIndex > -1 && !w.axisFlags.isXNumeric) {
323
317
  if (w.config.plotOptions.bar.rangeBarGroupRows) {
324
318
  barXPosition = srtx + xDivision * rowIndex
325
319
  } else {
@@ -335,14 +329,7 @@ class RangeBar extends Bar {
335
329
 
336
330
  if (overlaps.indexOf(rangeName) > -1) {
337
331
  barWidth = initPositions.barWidth / overlaps.length
338
-
339
- barXPosition =
340
- barWidth * this.visibleI +
341
- (xDivision * (100 - parseInt(this.barOptions.barWidth, 10))) /
342
- 100 /
343
- 2 +
344
- barWidth * (this.visibleI + overlaps.indexOf(rangeName)) +
345
- xDivision * rowIndex
332
+ barXPosition += barWidth * overlaps.indexOf(rangeName)
346
333
  }
347
334
  }
348
335
  }
@@ -368,7 +355,7 @@ class RangeBar extends Bar {
368
355
 
369
356
  const { i, j, realIndex, translationsIndex } = indexes
370
357
 
371
- const yRatio = this.yRatio[translationsIndex]
358
+ const yRatio = this.yRatio[translationsIndex] * (this.isReversed ? -1 : 1)
372
359
 
373
360
  const range = this.getRangeValue(realIndex, j)
374
361
 
@@ -381,7 +368,7 @@ class RangeBar extends Bar {
381
368
  )
382
369
 
383
370
  if (!hasValue) {
384
- y1 = zeroH
371
+ y1 = y2 = zeroH
385
372
  } else {
386
373
  y1 = zeroH - y1 / yRatio
387
374
  y2 = zeroH - y2 / yRatio
@@ -448,6 +435,22 @@ class RangeBar extends Bar {
448
435
  }
449
436
  }
450
437
 
438
+ /**
439
+ * Timeline datums key by their own row, not by the j-th label: a row can
440
+ * hold several bars, so labels[j] names some other datum's row.
441
+ * @param {number} realIndex
442
+ * @returns {string[]}
443
+ */
444
+ _keys(realIndex) {
445
+ const c = (/** @type {any} */ (this)._keyCache ||= {})
446
+ const data = /** @type {any} */ (this.w.config.series[realIndex]).data
447
+ return (c[realIndex] ||= uniquifyKeys(
448
+ (data || []).map(
449
+ (/** @type {any} */ d, /** @type {number} */ j) => 'c:' + (d?.x ?? j),
450
+ ),
451
+ ))
452
+ }
453
+
451
454
  /**
452
455
  * @param {number} val
453
456
  */
@@ -479,8 +482,9 @@ class RangeBar extends Bar {
479
482
 
480
483
  const { realIndex, j } = indexes
481
484
 
482
- const x1 = this.preventBarOverflow(zeroW + y1 / this.invertedYRatio)
483
- const x2 = this.preventBarOverflow(zeroW + y2 / this.invertedYRatio)
485
+ const ratio = this.invertedYRatio * (this.isReversed ? -1 : 1)
486
+ const x1 = this.preventBarOverflow(zeroW + y1 / ratio)
487
+ const x2 = this.preventBarOverflow(zeroW + y2 / ratio)
484
488
 
485
489
  const range = this.getRangeValue(realIndex, j)
486
490
 
@@ -33,10 +33,12 @@ import { avoidChromeOverlap } from './common/Breadcrumb'
33
33
  import {
34
34
  bandScale,
35
35
  farEdge,
36
+ focusByKey,
36
37
  focusChain,
37
38
  parseSize,
38
39
  placeTree,
39
40
  resolveFocus,
41
+ setZoomCursor,
40
42
  validateStrict,
41
43
  } from './common/partition/Partition'
42
44
  import { colorPass } from './common/partition/Tint'
@@ -129,15 +131,22 @@ export default class SunburstChart {
129
131
  (!w.config.chart.sparkline.enabled ? w.config.chart.dropShadow.blur : 0)
130
132
  if (this.maxRadius < 5) return g
131
133
 
132
- // Build + colour the hierarchy once (angles/radii are recomputed per zoom).
133
- this._roots = this._buildHierarchy()
134
+ // Build + colour the hierarchy once (angles/radii are recomputed per zoom),
135
+ // through the helpers shared with the treemap and icicle: a node's explicit
136
+ // `color` wins, else it takes its parent's tinted lighter.
137
+ this._roots = buildHierarchy(w)
134
138
  if (!this._roots.length) return g
135
- this._roots.forEach((r) => this._fillValues(r))
139
+ this._roots.forEach(fillValues)
136
140
  this._validateStrict()
137
141
  this._nodesAll = []
138
142
  const colors = w.globals.colors || []
139
143
  this._roots.forEach((r, i) => {
140
- this._colorPass(r, r.color || colors[i % colors.length] || '#008FFB')
144
+ colorPass(
145
+ r,
146
+ r.color || colors[i % colors.length] || '#008FFB',
147
+ this.cfg.tint,
148
+ this._nodesAll,
149
+ )
141
150
  })
142
151
  this.total = this._roots.reduce((s, r) => s + Math.max(0, r.value), 0) || 1
143
152
  this._tooltip.total = this.total
@@ -147,7 +156,12 @@ export default class SunburstChart {
147
156
  g.add(this._ringsG)
148
157
  g.add(this._labelsG)
149
158
 
150
- this._focus = null
159
+ // The focus outlives this instance by key (_relayout clears a stale one).
160
+ this._focus = focusByKey(
161
+ w.globals.sunburstFocusKey,
162
+ this._nodesAll,
163
+ this._roots,
164
+ )
151
165
  this._relayout(this._focus)
152
166
 
153
167
  // Same gating as Pie: angular clock-sweep intro on first render, a morph
@@ -158,7 +172,7 @@ export default class SunburstChart {
158
172
  let mode = 'none'
159
173
  if (anims.enabled) {
160
174
  if (w.globals.dataChanged) {
161
- if (anims.dynamicAnimation.enabled) mode = 'update'
175
+ if (w.globals.shouldAnimate !== false) mode = 'update'
162
176
  } else if (!w.globals.resized) {
163
177
  mode = 'intro'
164
178
  }
@@ -171,24 +185,6 @@ export default class SunburstChart {
171
185
 
172
186
  // ------------------------------------------------------------------ data
173
187
 
174
- /**
175
- * Resolve the config into root nodes `{ name, value, color?, children? }`.
176
- * Each datum may carry `children` (native) or `drilldown: '<id>'` (adapter).
177
- * Shared with the treemap - see charts/common/Hierarchy.
178
- * @returns {any[]}
179
- */
180
- _buildHierarchy() {
181
- return buildHierarchy(this.w)
182
- }
183
-
184
- /**
185
- * Fill a parent's value from its children when missing.
186
- * @param {any} node
187
- */
188
- _fillValues(node) {
189
- fillValues(node)
190
- }
191
-
192
188
  /**
193
189
  * With `partition: 'strict'`, warn (once) when a parent's value does not
194
190
  * match the sum of its children. The angles are still normalized to fill the
@@ -203,17 +199,6 @@ export default class SunburstChart {
203
199
  })
204
200
  }
205
201
 
206
- /**
207
- * Assign a colour to every node (explicit `color` wins, else the parent's
208
- * colour tinted lighter). Done once so zoom preserves colours.
209
- * Shared with the icicle - see charts/common/partition/Tint.
210
- * @param {any} node
211
- * @param {string} color
212
- */
213
- _colorPass(node, color) {
214
- colorPass(node, color, this.cfg.tint, this._nodesAll)
215
- }
216
-
217
202
  // ---------------------------------------------------------------- layout
218
203
 
219
204
  /**
@@ -221,6 +206,7 @@ export default class SunburstChart {
221
206
  * @param {any} focus
222
207
  */
223
208
  _relayout(focus) {
209
+ this.w.globals.sunburstFocusKey = focus && focus._key
224
210
  // The angular partition. `_t0`/`_t1` come back in degrees because that is
225
211
  // the interval handed in; the icicle passes pixels to the same recursion.
226
212
  this._focusMaxDepth = placeTree({
@@ -271,13 +257,15 @@ export default class SunburstChart {
271
257
  _applyLayout(mode) {
272
258
  const w = this.w
273
259
  const anims = w.config.chart.animations
274
- const dur = !anims.enabled
275
- ? 0
276
- : mode === 'none'
260
+ const dyn = anims.dynamicAnimation
261
+ const dur =
262
+ !anims.enabled || mode === 'none'
277
263
  ? 0
278
- : mode === 'update'
279
- ? anims.dynamicAnimation.speed || 350
280
- : anims.speed || 500
264
+ : mode === 'intro'
265
+ ? (anims.speed ?? 500)
266
+ : dyn.enabled
267
+ ? (dyn.speed ?? 350)
268
+ : 0
281
269
 
282
270
  // Supersede any in-flight zoom: stale frames from the previous pass check
283
271
  // this and stop, so a fast re-click can't leave two animations fighting.
@@ -299,10 +287,19 @@ export default class SunburstChart {
299
287
  const morphDur = morphActive ? morph.getSpeed() : 0
300
288
  this._morphLeafIndex = 0
301
289
 
302
- // Geometry of the PREVIOUS render (stashed on the persistent chart ctx —
303
- // this module is re-instantiated on every data update).
290
+ // The PREVIOUS render's nodes (stashed on the persistent chart ctx, as
291
+ // this module is re-instantiated on every data update), read live so an
292
+ // update landing mid-tween starts where each arc actually is.
304
293
  const prev =
305
294
  mode === 'update' ? /** @type {any} */ (this.ctx)._sunburstPrevGeoms : null
295
+ /** @param {any} n */
296
+ const geo = (n) =>
297
+ n._cur || { a0: n._t0, a1: n._t1, iR: n._iR, oR: n._oR }
298
+ /** @param {any} c the arc pinched to nothing at its mid angle */
299
+ const pinch = (c) => {
300
+ const m = (c.a0 + c.a1) / 2
301
+ return { a0: m, a1: m, iR: c.iR, oR: c.iR }
302
+ }
306
303
 
307
304
  this._nodesAll.forEach((node) => {
308
305
  if (node._show) {
@@ -313,6 +310,7 @@ export default class SunburstChart {
313
310
  oR: node._oR,
314
311
  }
315
312
  if (!node._el) node._el = this._createArcEl(node)
313
+ setZoomCursor(this, node)
316
314
 
317
315
  // Cross-type morph (treemap -> sunburst): a leaf starts as the tile
318
316
  // that stood for the same row and unrolls into its arc. Interior rings
@@ -325,36 +323,58 @@ export default class SunburstChart {
325
323
  } else if ((mode === 'intro' || morphActive) && dur > 0) {
326
324
  this._sweepArc(node, target, morphActive ? morphDur : dur, gen)
327
325
  } else {
328
- let from
329
- let isNew = false
330
- if (node._cur) {
331
- // `_cur` is the arc's LIVE geometry (updated each frame), so an
332
- // interrupted zoom continues smoothly from where it actually is.
333
- from = node._cur
334
- } else if (prev && prev.get(node._key)) {
335
- from = prev.get(node._key) // survived a data update: morph in place
336
- } else {
337
- const mid = (target.a0 + target.a1) / 2 // new arc: unfurl from its slot
338
- from = { a0: mid, a1: mid, iR: target.iR, oR: target.iR }
339
- isNew = true
340
- }
341
- // Only genuinely new arcs fade in; arcs morphing from a previous
342
- // geometry stay fully opaque while their shape tweens.
343
- this._animateArc(node, from, target, dur, false, isNew, gen)
326
+ const p = prev && prev.get(node._key)
327
+ // `_cur` is the arc's LIVE geometry (updated each frame), so an
328
+ // interrupted zoom continues smoothly from where it actually is; a
329
+ // survivor of a data update morphs in place; a new arc unfurls from
330
+ // its slot and is the only one that fades in.
331
+ const from = node._cur || (p ? geo(p) : pinch(target))
332
+ this._animateArc(node, from, target, dur, false, !node._cur && !p, gen)
344
333
  }
345
334
  } else if (node._el && node._cur) {
346
- const mid = (node._cur.a0 + node._cur.a1) / 2
347
- const target = { a0: mid, a1: mid, iR: node._cur.iR, oR: node._cur.iR }
348
- this._animateArc(node, node._cur, target, dur, true, false, gen)
335
+ this._animateArc(node, node._cur, pinch(node._cur), dur, true, false, gen)
349
336
  }
350
337
  })
351
338
 
339
+ // An arc the update dropped (or no longer places) collapses out with the
340
+ // morph, as a hidden one does, instead of vanishing at frame 0. The ghost
341
+ // joins _nodesAll (never shown), so a zoom or the next update picks it up
342
+ // mid-collapse like any other arc.
343
+ if (prev && dur && !morphActive) {
344
+ const keys = new Set(this._nodesAll.map((n) => n._show && n._key))
345
+ prev.forEach((/** @type {any} */ p, /** @type {string} */ k) => {
346
+ if (keys.has(k) || !(p._show || p._cur)) return
347
+ const el = this._graphics.drawPath({
348
+ fill: p._color,
349
+ stroke: this.strokeColor,
350
+ strokeWidth: this.strokeWidth,
351
+ classes: 'apexcharts-sunburst-arc',
352
+ })
353
+ el.node.setAttribute('data:name', p.name)
354
+ this._ringsG.add(el.attr({ opacity: p._el.attr('opacity') }))
355
+ const c = geo(p)
356
+ const g = { _el: el, _color: p._color, name: p.name, _key: k, _cur: c }
357
+ this._nodesAll.push(g)
358
+ this._animateArc(g, c, pinch(c), dur, true, false, gen)
359
+ })
360
+ }
361
+
362
+ // Over when the last arc lands (a newer layout takes that over), or now
363
+ // when nothing moves.
364
+ const end = Math.max(dur, morphDur)
365
+ if (end) {
366
+ this._ringsG.animate(end).after(() => {
367
+ if (this._zoomGen === gen && this._ringsG.node.isConnected)
368
+ new Animations(w, /** @type {any} */ (this.ctx)).animationCompleted(
369
+ this._ringsG.node,
370
+ )
371
+ })
372
+ } else w.globals.animationEnded = true
373
+
352
374
  // Stash what is now on screen so the NEXT data update can morph from it.
353
375
  const geoms = new Map()
354
376
  this._nodesAll.forEach((n) => {
355
- if (n._show) {
356
- geoms.set(n._key, { a0: n._t0, a1: n._t1, iR: n._iR, oR: n._oR })
357
- }
377
+ if (n._show || n._cur) geoms.set(n._key, n)
358
378
  })
359
379
  ;/** @type {any} */ (this.ctx)._sunburstPrevGeoms = geoms
360
380
 
@@ -480,11 +500,9 @@ export default class SunburstChart {
480
500
  */
481
501
  _createArcEl(node) {
482
502
  const path = this._graphics.drawPath({
483
- d: '',
484
503
  fill: node._color,
485
504
  stroke: this.strokeColor,
486
505
  strokeWidth: this.strokeWidth,
487
- fillOpacity: 1,
488
506
  classes: 'apexcharts-sunburst-arc',
489
507
  })
490
508
  const el = path.node
@@ -501,9 +519,8 @@ export default class SunburstChart {
501
519
  String(!(node.children && node.children.length)),
502
520
  )
503
521
  this._tooltip.attach(el, node)
504
- if (Environment.isBrowser()) {
522
+ if (Environment.isBrowser() && this.cfg.zoomOnClick !== false) {
505
523
  el.addEventListener('click', () => this._zoomTo(node))
506
- el.style.cursor = 'pointer'
507
524
  }
508
525
  this._ringsG.add(path)
509
526
  return path
@@ -579,11 +596,14 @@ export default class SunburstChart {
579
596
  this._renderCurvedLabel(node)
580
597
  })
581
598
 
599
+ // Gen-checked: a superseded zoom's fade must not reveal labels over arcs
600
+ // the newer zoom is still moving.
601
+ const gen = this._zoomGen
602
+ labelsG.attr({ opacity: dur > 0 ? 0 : 1 })
582
603
  if (dur > 0) {
583
- labelsG.attr({ opacity: 0 })
584
- labelsG.animate(250, dur).attr({ opacity: 1 })
585
- } else {
586
- labelsG.attr({ opacity: 1 })
604
+ labelsG.animate(250, dur).during((/** @type {number} */ p) => {
605
+ if (this._zoomGen === gen) labelsG.attr({ opacity: p })
606
+ })
587
607
  }
588
608
  }
589
609
 
@@ -593,11 +613,11 @@ export default class SunburstChart {
593
613
  * @param {any} node
594
614
  */
595
615
  _renderCurvedLabel(node) {
596
- if (!Environment.isBrowser()) return
597
616
  const style = this.cfg.dataLabels.style
598
617
  const w = this.w
599
618
  const r = (node._iR + node._oR) / 2
600
- const mid = (node._t0 + node._t1) / 2
619
+ // Normalised, so a negative or past-360 startAngle flips the same half.
620
+ const mid = ((((node._t0 + node._t1) / 2) % 360) + 360) % 360
601
621
  const flip = mid > 90 && mid < 270
602
622
 
603
623
  // Small angular padding so text does not touch the radial edges.
@@ -667,7 +687,7 @@ export default class SunburstChart {
667
687
  */
668
688
  _zoomTo(node) {
669
689
  if (this.cfg.zoomOnClick === false) return
670
- const next = resolveFocus(node, this._focus)
690
+ const next = resolveFocus(node, this._focus, this._roots)
671
691
  if (!next.changed) return
672
692
  this._focus = next.focus
673
693
  this._relayout(this._focus)
@@ -675,11 +695,6 @@ export default class SunburstChart {
675
695
  this._renderBreadcrumb()
676
696
  }
677
697
 
678
- /** Root -> focus chain of nodes. */
679
- _focusChain() {
680
- return focusChain(this._focus)
681
- }
682
-
683
698
  /**
684
699
  * Minimal self-contained breadcrumb (reuses the shared `.apexcharts-breadcrumb`
685
700
  * CSS classes, but does NOT depend on the drilldown feature).
@@ -702,7 +717,7 @@ export default class SunburstChart {
702
717
  nav.style.left = '0px'
703
718
 
704
719
  const crumbs = [{ name: 'All', node: null }].concat(
705
- this._focusChain().map((n) => ({ name: n.name, node: n })),
720
+ focusChain(this._focus).map((n) => ({ name: n.name, node: n })),
706
721
  )
707
722
 
708
723
  crumbs.forEach((crumb, i) => {
@@ -746,22 +761,10 @@ export default class SunburstChart {
746
761
  })
747
762
 
748
763
  elWrap.appendChild(nav)
749
- this._avoidChromeOverlap(nav)
750
- }
751
-
752
- /**
753
- * The breadcrumb is an absolute overlay at top-left, so it can sit on top of
754
- * a left-aligned title (or subtitle). After mounting, push it below any chart
755
- * chrome it intersects. (Duplicated from drilldown's Breadcrumb on purpose —
756
- * sunburst must not import the drilldown feature.)
757
- * @param {any} nav
758
- */
759
- /**
760
- * Shared with the treemap - see charts/common/Breadcrumb.
761
- * @param {any} nav
762
- */
763
- _avoidChromeOverlap(nav) {
764
- avoidChromeOverlap(this.w, nav)
764
+ // The breadcrumb is an absolute overlay at top-left: push it below any
765
+ // chart chrome it intersects (shared with the treemap, see
766
+ // charts/common/Breadcrumb).
767
+ avoidChromeOverlap(w, nav)
765
768
  }
766
769
 
767
770
  // ------------------------------------------------------------ geometry