apexcharts 8.2.0 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +7 -7
  2. package/dist/apexcharts.common.js +2 -2
  3. package/dist/apexcharts.core.js +271 -173
  4. package/dist/apexcharts.core.min.js +2 -2
  5. package/dist/apexcharts.css +35 -0
  6. package/dist/apexcharts.esm.js +1722 -1527
  7. package/dist/apexcharts.full.js +1459 -1075
  8. package/dist/apexcharts.full.min.js +2 -2
  9. package/dist/apexcharts.js +994 -715
  10. package/dist/apexcharts.min.js +2 -2
  11. package/dist/apexcharts.ssr.common.js +2 -2
  12. package/dist/apexcharts.ssr.esm.js +994 -715
  13. package/dist/area.common.js +2 -2
  14. package/dist/area.esm.js +91 -40
  15. package/dist/bar.common.js +2 -2
  16. package/dist/bar.esm.js +433 -394
  17. package/dist/bar.js +2 -2
  18. package/dist/boxPlot.common.js +2 -2
  19. package/dist/boxPlot.esm.js +364 -322
  20. package/dist/bubble.common.js +2 -2
  21. package/dist/bubble.esm.js +91 -40
  22. package/dist/candlestick.common.js +2 -2
  23. package/dist/candlestick.esm.js +364 -322
  24. package/dist/candlestick.js +2 -2
  25. package/dist/column.common.js +2 -2
  26. package/dist/column.esm.js +433 -394
  27. package/dist/core.common.js +2 -2
  28. package/dist/core.esm.js +271 -173
  29. package/dist/donut.common.js +1 -1
  30. package/dist/donut.esm.js +1 -1
  31. package/dist/dumbbell.common.js +2 -2
  32. package/dist/dumbbell.esm.js +433 -394
  33. package/dist/features/all.common.js +2 -2
  34. package/dist/features/all.esm.js +250 -187
  35. package/dist/features/annotations.common.js +1 -1
  36. package/dist/features/annotations.esm.js +1 -1
  37. package/dist/features/annotations.js +1 -1
  38. package/dist/features/context-menu.common.js +1 -1
  39. package/dist/features/context-menu.esm.js +1 -1
  40. package/dist/features/context-menu.js +1 -1
  41. package/dist/features/drilldown.common.js +2 -2
  42. package/dist/features/drilldown.esm.js +4 -2
  43. package/dist/features/drilldown.js +2 -2
  44. package/dist/features/dumbbell.common.js +1 -1
  45. package/dist/features/dumbbell.esm.js +1 -1
  46. package/dist/features/dumbbell.js +1 -1
  47. package/dist/features/exports.common.js +1 -1
  48. package/dist/features/exports.esm.js +1 -1
  49. package/dist/features/exports.js +1 -1
  50. package/dist/features/facet.common.js +1 -1
  51. package/dist/features/facet.esm.js +1 -1
  52. package/dist/features/facet.js +1 -1
  53. package/dist/features/highlight-filter.common.js +2 -2
  54. package/dist/features/highlight-filter.esm.js +14 -9
  55. package/dist/features/highlight-filter.js +2 -2
  56. package/dist/features/history.common.js +1 -1
  57. package/dist/features/history.esm.js +1 -1
  58. package/dist/features/history.js +1 -1
  59. package/dist/features/ink.common.js +1 -1
  60. package/dist/features/ink.esm.js +1 -1
  61. package/dist/features/ink.js +1 -1
  62. package/dist/features/keyboard.common.js +2 -2
  63. package/dist/features/keyboard.esm.js +97 -145
  64. package/dist/features/keyboard.js +2 -2
  65. package/dist/features/legend.common.js +2 -2
  66. package/dist/features/legend.esm.js +63 -43
  67. package/dist/features/legend.js +2 -2
  68. package/dist/features/link.common.js +1 -1
  69. package/dist/features/link.esm.js +1 -1
  70. package/dist/features/link.js +1 -1
  71. package/dist/features/marks.common.js +1 -1
  72. package/dist/features/marks.esm.js +1 -1
  73. package/dist/features/marks.js +1 -1
  74. package/dist/features/measure.common.js +1 -1
  75. package/dist/features/measure.esm.js +1 -1
  76. package/dist/features/measure.js +1 -1
  77. package/dist/features/morph.common.js +1 -1
  78. package/dist/features/morph.esm.js +1 -1
  79. package/dist/features/morph.js +1 -1
  80. package/dist/features/perspectives.common.js +1 -1
  81. package/dist/features/perspectives.esm.js +1 -1
  82. package/dist/features/perspectives.js +1 -1
  83. package/dist/features/raincloud.common.js +1 -1
  84. package/dist/features/raincloud.esm.js +1 -1
  85. package/dist/features/raincloud.js +1 -1
  86. package/dist/features/renderer-canvas.common.js +2 -2
  87. package/dist/features/renderer-canvas.esm.js +2 -1
  88. package/dist/features/renderer-canvas.js +2 -2
  89. package/dist/features/stats.common.js +1 -1
  90. package/dist/features/stats.esm.js +1 -1
  91. package/dist/features/stats.js +1 -1
  92. package/dist/features/storyboard.common.js +1 -1
  93. package/dist/features/storyboard.esm.js +1 -1
  94. package/dist/features/storyboard.js +1 -1
  95. package/dist/features/streamgraph.common.js +2 -2
  96. package/dist/features/streamgraph.esm.js +3 -2
  97. package/dist/features/streamgraph.js +2 -2
  98. package/dist/features/toolbar.common.js +2 -2
  99. package/dist/features/toolbar.esm.js +92 -2
  100. package/dist/features/toolbar.js +2 -2
  101. package/dist/features/trellis.common.js +2 -2
  102. package/dist/features/trellis.esm.js +83 -47
  103. package/dist/features/trellis.js +2 -2
  104. package/dist/features/waterfall.common.js +1 -1
  105. package/dist/features/waterfall.esm.js +1 -1
  106. package/dist/features/waterfall.js +1 -1
  107. package/dist/features/weave.common.js +1 -1
  108. package/dist/features/weave.esm.js +1 -1
  109. package/dist/features/weave.js +1 -1
  110. package/dist/full.common.js +2 -2
  111. package/dist/full.esm.js +950 -795
  112. package/dist/heatmap.common.js +2 -2
  113. package/dist/heatmap.esm.js +3 -17
  114. package/dist/heatmap.js +2 -2
  115. package/dist/histogram.common.js +2 -2
  116. package/dist/histogram.esm.js +433 -394
  117. package/dist/icicle.common.js +2 -2
  118. package/dist/icicle.esm.js +54 -38
  119. package/dist/icicle.js +2 -2
  120. package/dist/line.common.js +2 -2
  121. package/dist/line.esm.js +91 -40
  122. package/dist/line.js +2 -2
  123. package/dist/locales/en.json +2 -1
  124. package/dist/locales/ro.json +3 -2
  125. package/dist/pictograms.common.js +1 -1
  126. package/dist/pictograms.esm.js +1 -1
  127. package/dist/pictograms.js +1 -1
  128. package/dist/pie.common.js +1 -1
  129. package/dist/pie.esm.js +1 -1
  130. package/dist/pie.js +1 -1
  131. package/dist/polarArea.common.js +1 -1
  132. package/dist/polarArea.esm.js +1 -1
  133. package/dist/radar.common.js +2 -2
  134. package/dist/radar.esm.js +5 -4
  135. package/dist/radar.js +2 -2
  136. package/dist/radialBar.common.js +1 -1
  137. package/dist/radialBar.esm.js +1 -1
  138. package/dist/radialBar.js +1 -1
  139. package/dist/raincloud.common.js +2 -2
  140. package/dist/raincloud.esm.js +72 -26
  141. package/dist/rangeArea.common.js +2 -2
  142. package/dist/rangeArea.esm.js +91 -40
  143. package/dist/rangeBar.common.js +2 -2
  144. package/dist/rangeBar.esm.js +433 -394
  145. package/dist/scatter.common.js +2 -2
  146. package/dist/scatter.esm.js +91 -40
  147. package/dist/streamgraph.common.js +2 -2
  148. package/dist/streamgraph.esm.js +93 -41
  149. package/dist/sunburst.common.js +2 -2
  150. package/dist/sunburst.esm.js +103 -93
  151. package/dist/sunburst.js +2 -2
  152. package/dist/treemap.common.js +2 -2
  153. package/dist/treemap.esm.js +105 -86
  154. package/dist/treemap.js +2 -2
  155. package/dist/unit-shapes.common.js +1 -1
  156. package/dist/unit-shapes.esm.js +1 -1
  157. package/dist/unit-shapes.js +1 -1
  158. package/dist/unit.common.js +2 -2
  159. package/dist/unit.esm.js +280 -202
  160. package/dist/unit.js +2 -2
  161. package/dist/violin.common.js +2 -2
  162. package/dist/violin.esm.js +72 -26
  163. package/dist/violin.js +2 -2
  164. package/dist/waterfall.common.js +2 -2
  165. package/dist/waterfall.esm.js +432 -393
  166. package/package.json +1 -1
  167. package/src/apexcharts.js +13 -6
  168. package/src/assets/apexcharts.css +35 -0
  169. package/src/charts/Bar.js +62 -55
  170. package/src/charts/BarStacked.js +80 -88
  171. package/src/charts/BoxCandleStick.js +28 -31
  172. package/src/charts/Icicle.js +37 -37
  173. package/src/charts/Line.js +118 -42
  174. package/src/charts/Radar.js +5 -7
  175. package/src/charts/RangeBar.js +47 -43
  176. package/src/charts/Sunburst.js +101 -98
  177. package/src/charts/Treemap.js +108 -66
  178. package/src/charts/Unit.js +368 -250
  179. package/src/charts/Violin.js +48 -30
  180. package/src/charts/common/Breadcrumb.js +2 -2
  181. package/src/charts/common/Hierarchy.js +32 -11
  182. package/src/charts/common/Jitter.js +32 -7
  183. package/src/charts/common/bar/DataLabels.js +76 -81
  184. package/src/charts/common/bar/Helpers.js +43 -7
  185. package/src/charts/common/line/Helpers.js +12 -15
  186. package/src/charts/common/partition/NodeTooltip.js +8 -1
  187. package/src/charts/common/partition/Partition.js +33 -1
  188. package/src/charts/common/treemap/Helpers.js +3 -11
  189. package/src/locales/en.json +2 -1
  190. package/src/locales/ro.json +3 -2
  191. package/src/modules/Core.js +4 -0
  192. package/src/modules/CoreUtils.js +18 -42
  193. package/src/modules/Data.js +23 -24
  194. package/src/modules/DataLabels.js +6 -6
  195. package/src/modules/Graphics.js +62 -49
  196. package/src/modules/Markers.js +3 -1
  197. package/src/modules/Range.js +44 -29
  198. package/src/modules/RendererController.js +17 -3
  199. package/src/modules/Scales.js +32 -9
  200. package/src/modules/Series.js +39 -18
  201. package/src/modules/Theme.js +1 -1
  202. package/src/modules/ZoomPanSelection.js +119 -2
  203. package/src/modules/accessibility/KeyboardNavigation.js +114 -186
  204. package/src/modules/animations/LengthTransition.js +22 -2
  205. package/src/modules/drilldown/Breadcrumb.js +5 -2
  206. package/src/modules/helpers/Destroy.js +1 -1
  207. package/src/modules/highlightFilter/HighlightFilter.js +8 -9
  208. package/src/modules/highlightFilter/TreemapPart.js +2 -2
  209. package/src/modules/legend/HeatmapGradientLegend.js +76 -45
  210. package/src/modules/legend/Helpers.js +2 -0
  211. package/src/modules/legend/Legend.js +11 -12
  212. package/src/modules/settings/Options.js +7 -0
  213. package/src/modules/streamgraph/StreamLabels.js +1 -0
  214. package/src/modules/tooltip/Tooltip.js +20 -4
  215. package/src/modules/tooltip/Utils.js +8 -0
  216. package/src/modules/trellis/Trellis.js +6 -0
  217. package/src/modules/trellis/TrellisChrome.js +18 -7
  218. package/src/modules/trellis/TrellisSync.js +2 -0
  219. package/src/renderers/canvas/CanvasGraphics.js +1 -0
  220. package/src/ssr/DOMShim.js +10 -0
  221. package/src/types/internal.d.ts +9 -3
  222. package/types/apexcharts.d.ts +31 -12
@@ -4,9 +4,10 @@ import { getChartClass } from '../modules/ChartFactory'
4
4
  import Fill from '../modules/Fill'
5
5
  import Graphics from '../modules/Graphics'
6
6
  import Series from '../modules/Series'
7
+ import { datumKey } from '../modules/animations/LengthTransition'
7
8
  import Utils from '../utils/Utils'
8
9
  import { spline, svgPath } from '../libs/monotone-cubic'
9
- import { buildJitterGroups, renderJitter } from './common/Jitter'
10
+ import { buildJitterGroups, jitterExtent, renderJitter } from './common/Jitter'
10
11
 
11
12
  /**
12
13
  * ApexCharts Violin Class — draws a density curve per category (the
@@ -67,6 +68,7 @@ class Violin extends BarBase {
67
68
 
68
69
  this.violinOptions = w.config.plotOptions.violin
69
70
  this.pointsOptions = this.violinOptions.points
71
+ this.jx = jitterExtent(w.violinData.seriesViolinPoints, w)
70
72
  this.boxOptions = this.violinOptions.box || {}
71
73
  // Cross-axis signs: which side the density is drawn on (0 = symmetric)
72
74
  // and which side the jitter lane sits on (0 = centered on the slot).
@@ -93,7 +95,7 @@ class Violin extends BarBase {
93
95
  this.series = series
94
96
  this.yRatio = this.coreUtils.getLogYRatios(this.yRatio)
95
97
 
96
- this.barHelpers.initVariables(series)
98
+ this.barHelpers.initVariables(series, seriesIndex)
97
99
 
98
100
  const ret = graphics.group({
99
101
  class: 'apexcharts-violin-series apexcharts-plot-series',
@@ -285,6 +287,7 @@ class Violin extends BarBase {
285
287
  visibleSeries: this.visibleI,
286
288
  type: 'violin',
287
289
  classes: 'apexcharts-raincloud-box',
290
+ noLabel: true,
288
291
  })
289
292
  })
290
293
  }
@@ -338,15 +341,18 @@ class Violin extends BarBase {
338
341
  drawVerticalViolin({ indexes, x, xDivision, barWidth, zeroH }) {
339
342
  const w = this.w
340
343
  const { realIndex, j, translationsIndex } = indexes
341
- const yRatio = this.yRatio[translationsIndex]
344
+ const yRatio = this.yRatio[translationsIndex] * (this.isReversed ? -1 : 1)
342
345
 
346
+ // the group of violins at a numeric x is centred on it, as candles are
347
+ let barXPosition = x + barWidth * this.visibleI
343
348
  if (w.axisFlags.isXNumeric) {
344
- x =
345
- (w.seriesData.seriesX[realIndex][j] - w.globals.minX) / this.xRatio -
346
- barWidth / 2
349
+ ;({ x, barXPosition } = this.getBarXForNumericXAxis({
350
+ x,
351
+ barWidth,
352
+ realIndex,
353
+ j,
354
+ }))
347
355
  }
348
-
349
- const barXPosition = x + barWidth * this.visibleI
350
356
  const center = barXPosition + barWidth / 2
351
357
  const halfExtent = barWidth / 2
352
358
 
@@ -383,9 +389,7 @@ class Violin extends BarBase {
383
389
  if (morphFrom) {
384
390
  pathFrom = morphFrom
385
391
  } else if (w.globals.previousPaths.length > 0) {
386
- // Keyed survivor → morph; shape-changed → snap; entering → null
387
- // (falls through to the collapsed-centerline enter below).
388
- pathFrom = this.getPreviousPath(realIndex, j, pathTo)
392
+ pathFrom = this._prevBody(realIndex, j, pathTo)
389
393
  }
390
394
  if (pathFrom == null) {
391
395
  pathFrom = this.buildBodyPath({
@@ -426,7 +430,7 @@ class Violin extends BarBase {
426
430
  alongFn,
427
431
  vertical: true,
428
432
  }),
429
- alongRepresentative: alongFn(this.series[indexes.i][j] ?? 0),
433
+ alongRepresentative: alongFn(w.seriesData.series[realIndex][j] ?? 0),
430
434
  }
431
435
  }
432
436
 
@@ -434,7 +438,7 @@ class Violin extends BarBase {
434
438
  drawHorizontalViolin({ indexes, y, yDivision, barHeight, zeroW }) {
435
439
  const w = this.w
436
440
  const { realIndex, j } = indexes
437
- const yRatio = this.invertedYRatio
441
+ const yRatio = this.invertedYRatio * (this.isReversed ? -1 : 1)
438
442
 
439
443
  // One band per row on its side, a date or number x included: see
440
444
  // Bar.drawBarPaths.
@@ -475,9 +479,7 @@ class Violin extends BarBase {
475
479
  if (morphFrom) {
476
480
  pathFrom = morphFrom
477
481
  } else if (w.globals.previousPaths.length > 0) {
478
- // Keyed survivor → morph; shape-changed → snap; entering → null
479
- // (falls through to the collapsed-centerline enter below).
480
- pathFrom = this.getPreviousPath(realIndex, j, pathTo)
482
+ pathFrom = this._prevBody(realIndex, j, pathTo)
481
483
  }
482
484
  if (pathFrom == null) {
483
485
  pathFrom = this.buildBodyPath({
@@ -516,7 +518,7 @@ class Violin extends BarBase {
516
518
  alongFn,
517
519
  vertical: false,
518
520
  }),
519
- alongRepresentative: alongFn(this.series[indexes.i][j] ?? 0),
521
+ alongRepresentative: alongFn(w.seriesData.series[realIndex][j] ?? 0),
520
522
  }
521
523
  }
522
524
 
@@ -631,6 +633,25 @@ class Violin extends BarBase {
631
633
  )
632
634
  }
633
635
 
636
+ /**
637
+ * The survivor's previous body when it can morph to `pathTo`, else null so
638
+ * the violin regrows from its centreline (a changed density node count
639
+ * cannot tween, and bar corner reconciliation does not apply here).
640
+ *
641
+ * @param {number} realIndex
642
+ * @param {number} j
643
+ * @param {string} pathTo
644
+ * @returns {string | null}
645
+ */
646
+ _prevBody(realIndex, j, pathTo) {
647
+ const d = this._prevKeyedPaths(realIndex)?.get(
648
+ datumKey(this.w, realIndex, j),
649
+ )?.d
650
+ return d && Violin.pathCommandCount(d) === Violin.pathCommandCount(pathTo)
651
+ ? d
652
+ : null
653
+ }
654
+
634
655
  /**
635
656
  * Build the five-number box sub-paths for one category — the raincloud
636
657
  * "umbrella", or a violin box overlay. Two sub-paths so each can carry its
@@ -698,26 +719,23 @@ class Violin extends BarBase {
698
719
  const full = build(capHalf, boxHalfPx)
699
720
  const collapsed = build(0, 0)
700
721
 
701
- /** @param {string} pathTo @param {string} collapsedFrom */
702
- const fromFor = (pathTo, collapsedFrom) => {
703
- let pathFrom = null
704
- if (w.globals.previousPaths.length > 0) {
705
- // Keyed survivor → morph; shape-changed → snap; entering → null.
706
- // (No morphTypeChange here: the body path claims the morph target.)
707
- pathFrom = this.getPreviousPath(realIndex, j, pathTo)
708
- }
709
- return pathFrom == null ? collapsedFrom : pathFrom
710
- }
722
+ // The box lane rides on its body's captured entry (see
723
+ // Series.capturePreviousPaths), matched by datum key like the body. An
724
+ // entering box grows out of the collapsed lane.
725
+ const keyed = w.globals.previousPaths.length
726
+ ? this._prevKeyedPaths(realIndex)
727
+ : null
728
+ const prevBox = keyed?.get(datumKey(w, realIndex, j))?.box || []
711
729
 
712
730
  return [
713
731
  {
714
732
  pathTo: full.whiskers,
715
- pathFrom: fromFor(full.whiskers, collapsed.whiskers),
733
+ pathFrom: prevBox[0] || collapsed.whiskers,
716
734
  filled: false,
717
735
  },
718
736
  {
719
737
  pathTo: full.box,
720
- pathFrom: fromFor(full.box, collapsed.box),
738
+ pathFrom: prevBox[1] || collapsed.box,
721
739
  filled: true,
722
740
  },
723
741
  ]
@@ -809,8 +827,8 @@ class Violin extends BarBase {
809
827
  ? cloudMaxPx
810
828
  : halfExtent
811
829
  return buildJitterGroups({
812
- w: this.w,
813
830
  points: this.w.violinData.seriesViolinPoints[realIndex]?.[j],
831
+ extent: /** @type {number[]} */ (this.jx),
814
832
  seedA: realIndex,
815
833
  seedB: j,
816
834
  center: scatterCenter,
@@ -191,7 +191,7 @@ export function clearBreadcrumb(w) {
191
191
  * @param {any} w
192
192
  * @param {{
193
193
  * crumbs: Array<{ label: string, data?: any }>,
194
- * onNavigate: (index: number, crumb: any) => void,
194
+ * onNavigate: (index: number, crumb: any, e: MouseEvent) => void,
195
195
  * ariaLabel?: string,
196
196
  * config?: any,
197
197
  * compact?: boolean,
@@ -270,7 +270,7 @@ export function renderBreadcrumb(w, opts) {
270
270
  text.setAttribute('class', 'apexcharts-breadcrumb-label')
271
271
  text.textContent = String(label)
272
272
  btn.appendChild(text)
273
- btn.addEventListener('click', () => opts.onNavigate(i, crumb))
273
+ btn.addEventListener('click', (e) => opts.onNavigate(i, crumb, e))
274
274
  nav.appendChild(btn)
275
275
  })
276
276
 
@@ -12,8 +12,9 @@
12
12
  * a nested view does not pull the feature in)
13
13
  *
14
14
  * A node is `{ name, value, color?, _key, children? }`. `_key` is the node's
15
- * identity across data updates: the path of names, indexed so same-named
16
- * siblings stay distinct, which is what update animations match on.
15
+ * identity across data updates: the path of names, each counted among its
16
+ * same-named siblings so duplicates stay distinct while an inserted or removed
17
+ * sibling leaves the others alone. Update animations match on it.
17
18
  *
18
19
  * `value` is left as authored here; call `fillValues` to roll a missing parent
19
20
  * value up from its children.
@@ -38,6 +39,20 @@ export function drilldownById(w, id) {
38
39
  return list.find((/** @type {any} */ s) => s && s.id === id)
39
40
  }
40
41
 
42
+ /**
43
+ * One key segment: the name ('/' escaped, it is the separator) prefixed by how
44
+ * many earlier siblings share it.
45
+ * @param {any} name
46
+ * @param {Map<string, number>} seen per sibling list
47
+ * @returns {string}
48
+ */
49
+ function segment(name, seen) {
50
+ const n = String(name).replace(/\//g, '%2F')
51
+ const o = (seen.get(n) ?? -1) + 1
52
+ seen.set(n, o)
53
+ return `${o}:${n}`
54
+ }
55
+
41
56
  /**
42
57
  * Resolve one datum (and everything under it) into a node.
43
58
  *
@@ -56,6 +71,7 @@ export function drilldownById(w, id) {
56
71
  * The treemap turns this off unless asked: there, a `drilldown` id has always
57
72
  * meant "descend on click" via the drilldown feature, and silently rendering
58
73
  * those levels nested instead would change what existing charts draw.
74
+ * @param {Map<string, number>} [seen] names already keyed among the siblings
59
75
  * @returns {any}
60
76
  */
61
77
  export function toNode(
@@ -66,28 +82,30 @@ export function toNode(
66
82
  parentKey,
67
83
  seenIds = null,
68
84
  opts = {},
85
+ seen = new Map(),
69
86
  ) {
70
87
  const isObj = d && typeof d === 'object'
71
88
  const name = isObj ? (d.x ?? d.name ?? '') : ''
72
- const value = isObj ? Number(d.y ?? d.value) : Number(d)
89
+ // null / '' is a missing value (rolled up by fillValues), not a zero.
90
+ const raw = isObj ? (d.y ?? d.value) : d
91
+ const value = raw == null || raw === '' ? NaN : Number(raw)
73
92
  /** @type {any} */
74
93
  const node = {
75
94
  name: String(name),
76
95
  value: isNaN(value) ? null : value,
77
96
  color: isObj && d.color ? d.color : undefined,
78
- // Identity across data updates: the path of names (indexed so same-named
79
- // siblings stay distinct). Update animations morph matched keys in place.
80
- _key: `${parentKey}/${i}:${name}`,
97
+ _key: `${parentKey}/${segment(name, seen)}`,
81
98
  }
82
99
  if (paletteFromParent && !node.color) {
83
100
  node.color = paletteFromParent[i % paletteFromParent.length]
84
101
  }
85
102
  if (opts.keepDatum) node._datum = d
86
103
 
104
+ const kids = new Map()
87
105
  if (isObj && Array.isArray(d.children) && d.children.length) {
88
106
  node.children = d.children.map(
89
107
  (/** @type {any} */ c, /** @type {number} */ j) =>
90
- toNode(w, c, j, null, node._key, seenIds, opts),
108
+ toNode(w, c, j, null, node._key, seenIds, opts, kids),
91
109
  )
92
110
  } else if (isObj && d.drilldown != null && opts.expandDrilldown !== false) {
93
111
  // Drilldown ids resolve indirectly, so a self- or mutually-referential id
@@ -102,7 +120,7 @@ export function toNode(
102
120
  const palette = Array.isArray(dd.colors) ? dd.colors : null
103
121
  node.children = dd.data.map(
104
122
  (/** @type {any} */ c, /** @type {number} */ j) =>
105
- toNode(w, c, j, palette, node._key, nextSeen, opts),
123
+ toNode(w, c, j, palette, node._key, nextSeen, opts, kids),
106
124
  )
107
125
  }
108
126
  }
@@ -123,8 +141,9 @@ export function buildHierarchy(w, opts = {}) {
123
141
  const first = cfgSeries && cfgSeries[0]
124
142
  const data = first && Array.isArray(first.data) ? first.data : cfgSeries
125
143
  if (!Array.isArray(data)) return []
144
+ const seen = new Map()
126
145
  return data.map((/** @type {any} */ d, /** @type {number} */ i) =>
127
- toNode(w, d, i, null, '', null, opts),
146
+ toNode(w, d, i, null, '', null, opts, seen),
128
147
  )
129
148
  }
130
149
 
@@ -142,9 +161,11 @@ export function buildHierarchy(w, opts = {}) {
142
161
  export function buildSeriesRoots(w, series, opts = {}) {
143
162
  const cfgSeries = /** @type {any} */ (series || w.config.series)
144
163
  if (!Array.isArray(cfgSeries)) return []
164
+ const seen = new Map()
145
165
  return cfgSeries.map((/** @type {any} */ s, /** @type {number} */ i) => {
146
166
  const data = s && Array.isArray(s.data) ? s.data : []
147
- const key = `${i}:${s?.name ?? ''}`
167
+ const key = segment(s?.name ?? '', seen)
168
+ const kids = new Map()
148
169
  /** @type {any} */
149
170
  const root = {
150
171
  name: String(s?.name ?? ''),
@@ -153,7 +174,7 @@ export function buildSeriesRoots(w, series, opts = {}) {
153
174
  _key: key,
154
175
  _seriesIndex: i,
155
176
  children: data.map((/** @type {any} */ d, /** @type {number} */ j) =>
156
- toNode(w, d, j, null, key, null, opts),
177
+ toNode(w, d, j, null, key, null, opts, kids),
157
178
  ),
158
179
  }
159
180
  return root
@@ -14,6 +14,28 @@ import Utils from '../../utils/Utils'
14
14
  * @module common/Jitter
15
15
  **/
16
16
 
17
+ /**
18
+ * The value range of the drawn series' observations, for a colorScale ramp.
19
+ * Once per draw; stale slots past the series count and non-numbers are skipped.
20
+ * @param {any[]} pool per-series, per-datum observations
21
+ * @param {any} w
22
+ * @returns {number[]}
23
+ */
24
+ export function jitterExtent(pool, w) {
25
+ let lo = Infinity
26
+ let hi = -Infinity
27
+ pool
28
+ .slice(0, w.seriesData.series.length)
29
+ .flat(2)
30
+ .forEach((v) => {
31
+ if (v === +v) {
32
+ if (v < lo) lo = v
33
+ if (v > hi) hi = v
34
+ }
35
+ })
36
+ return [lo, hi]
37
+ }
38
+
17
39
  /**
18
40
  * Build the jitter sub-path groups for ONE category. Returns `[]` when points
19
41
  * are hidden/absent. Normally one group (single dot colour); with
@@ -22,8 +44,8 @@ import Utils from '../../utils/Utils'
22
44
  * caller's resolved default colour.
23
45
  *
24
46
  * @param {{
25
- * w: any,
26
47
  * points: number[] | undefined,
48
+ * extent: number[],
27
49
  * seedA: number,
28
50
  * seedB: number,
29
51
  * center: number,
@@ -37,8 +59,8 @@ import Utils from '../../utils/Utils'
37
59
  * @returns {{fill:string|null, d:string}[]}
38
60
  */
39
61
  export function buildJitterGroups({
40
- w,
41
62
  points,
63
+ extent,
42
64
  seedA,
43
65
  seedB,
44
66
  center,
@@ -67,8 +89,10 @@ export function buildJitterGroups({
67
89
  const useScale =
68
90
  scale && Array.isArray(scale.colors) && scale.colors.length > 0
69
91
  const steps = useScale ? Math.max(2, scale.steps || 24) : 1
70
- const sMin = useScale && scale.min != null ? scale.min : w.globals.minY
71
- const sMax = useScale && scale.max != null ? scale.max : w.globals.maxY
92
+ // The ramp spans the observations (jitterExtent) unless colorScale says
93
+ // otherwise: an axis's nice scale overshoots them, so the ends never showed.
94
+ const sMin = scale?.min ?? extent[0]
95
+ const sMax = scale?.max ?? extent[1]
72
96
  const span = sMax - sMin || 1
73
97
  /** @type {string[]} */
74
98
  const buckets = useScale ? new Array(steps).fill('') : ['']
@@ -206,13 +230,14 @@ export function renderJitter({
206
230
  /**
207
231
  * Darken a colour toward black by `amount` (0 = unchanged, 1 = black),
208
232
  * returning an `rgb(...)` string. Falls back to the input when it can't be
209
- * parsed as hex.
210
- * @param {string} color hex colour
233
+ * parsed (hex, rgb()/rgba(), or a named colour the browser resolves).
234
+ * @param {string} color
211
235
  * @param {number} amount 0..1
212
236
  * @returns {string}
213
237
  */
214
238
  export function darkenColor(color, amount) {
215
- const rgb = Utils.parseHex(color)
239
+ const rgb =
240
+ Utils.parseColor(color) || Utils.parseColor(Utils.resolveColor(color))
216
241
  if (!rgb) return color
217
242
  const f = Math.max(0, 1 - amount)
218
243
  return `rgb(${Math.round(rgb[0] * f)},${Math.round(rgb[1] * f)},${Math.round(rgb[2] * f)})`