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
package/dist/unit.esm.js CHANGED
@@ -18,7 +18,7 @@ var __spreadValues = (a, b) => {
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
  /*!
21
- * ApexCharts v8.2.0
21
+ * ApexCharts v8.3.0
22
22
  * (c) 2018-2026 ApexCharts
23
23
  */
24
24
  import * as _core from "apexcharts/core";
@@ -217,7 +217,22 @@ function spaceOutLabels(items, minGap, maxY, minY) {
217
217
  }
218
218
  }
219
219
  }
220
+ function hash01(n) {
221
+ let h = (n ^ 2654435769) >>> 0;
222
+ h = Math.imul(h ^ h >>> 16, 73244475);
223
+ h = Math.imul(h ^ h >>> 16, 73244475);
224
+ return ((h ^ h >>> 16) >>> 0) / 4294967296;
225
+ }
220
226
  const GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
227
+ const uniqueId = (seen, id) => {
228
+ const n = (seen.get(id) || 0) + 1;
229
+ seen.set(id, n);
230
+ return n > 1 ? id + "#" + n : id;
231
+ };
232
+ const toNum = (v) => {
233
+ if (typeof v === "string") v = parseFloat(v);
234
+ return typeof v === "number" && isFinite(v) ? v : null;
235
+ };
221
236
  function easeOutCubic(t) {
222
237
  return 1 - Math.pow(1 - t, 3);
223
238
  }
@@ -263,6 +278,9 @@ class Unit {
263
278
  const w = this.w;
264
279
  const graphics = new Graphics(w, this.ctx);
265
280
  const ret = graphics.group({ class: "apexcharts-unit" });
281
+ BrowserAPIs.cancelAnimationFrame(w.globals.unitGatherRAF || 0);
282
+ BrowserAPIs.cancelAnimationFrame(w.globals.unitExitRAF || 0);
283
+ w.globals.unitGatherRAF = w.globals.unitExitRAF = null;
266
284
  if (w.globals.noData || !Array.isArray(series) || series.length === 0) {
267
285
  return ret;
268
286
  }
@@ -272,15 +290,16 @@ class Unit {
272
290
  const flow = transition === "flow";
273
291
  const identity = transition === "identity";
274
292
  const unitValue = opts.unitValue > 0 ? opts.unitValue : 1;
293
+ const gridSplit = layout === "grid" && !!(opts.grid && opts.grid.split);
275
294
  let counts = series.map((v) => {
276
295
  const n = Math.abs(Utils.parseNumber(v)) / unitValue;
277
- return n > 0 ? Math.max(1, Math.round(n)) : 0;
296
+ return gridSplit ? n : n > 0 ? Math.max(1, Math.round(n)) : 0;
278
297
  });
279
- counts = this._applyMaxUnits(counts, opts.maxUnits);
298
+ if (!gridSplit && layout !== "scatter") {
299
+ counts = this._applyMaxUnits(counts, opts.maxUnits);
300
+ }
280
301
  const total = counts.reduce((a, b) => a + b, 0);
281
302
  const clusters = layout === "packed" ? this._layoutPacked(counts, opts) : layout === "columns" ? this._layoutColumns(counts, opts) : layout === "grid" ? this._layoutGrid(counts, opts) : layout === "scatter" ? this._layoutScatter(opts) : layout === "arc" ? this._layoutArc(counts, opts) : layout === "custom" ? this._layoutCustom(counts, opts) : this._layoutGrouped(counts, opts);
282
- const gridSplit = layout === "grid" && !!(opts.grid && opts.grid.split);
283
- if (gridSplit) this._drawGridTrack(ret, graphics, opts);
284
303
  if (layout === "scatter") this._drawScatterAxes(ret, graphics);
285
304
  const dotR = this._lastDotR;
286
305
  const animate = this._shouldAnimate();
@@ -311,7 +330,49 @@ class Unit {
311
330
  const unitData = w.seriesData.unitData || [];
312
331
  const sizeStats = this._bubbleStats(unitData, opts, dotR);
313
332
  let gIndex = 0;
314
- clusters.forEach((cluster) => {
333
+ const seen = /* @__PURE__ */ new Map();
334
+ const track = gridSplit && this._gridTrack && this._gridTrack.cells || [];
335
+ const trackG = graphics.group({ class: "apexcharts-unit-track" });
336
+ if (track.length) ret.add(trackG);
337
+ const trackFill = opts.grid && opts.grid.trackColor || "rgba(128,128,128,0.14)";
338
+ const trackSpec = this._baseSpec({ shape: opts.shape === "square" && "square" }, dotR);
339
+ const perTile = track.length / Math.max(1, clusters.length);
340
+ const live = this.ctx && this.ctx._unitSprings;
341
+ const glide = (node, spec, x, y, key, r, at, pair) => {
342
+ const from = prev && prev.get(key);
343
+ const s = live && live.get(key);
344
+ const still = from && from.x === x && from.y === y && from.r === r && (!s || s.x.value === x && s.y.value === y);
345
+ nextPrev.set(key, { x, y, fill: trackFill, r, spec });
346
+ if (prev && animate && !pieceTakeover && !still) {
347
+ const cx0 = from ? from.x : x;
348
+ const cy0 = from ? from.y : y;
349
+ node.style.opacity = from ? "1" : "0";
350
+ this._place(node, spec, cx0, cy0);
351
+ animDots.splice(at, 0, {
352
+ node,
353
+ spec,
354
+ x,
355
+ y,
356
+ cx0,
357
+ cy0,
358
+ key,
359
+ r0: from && from.r != null ? from.r : r,
360
+ r1: r,
361
+ delay: 0,
362
+ isEnter: !from,
363
+ pair
364
+ });
365
+ } else {
366
+ this._place(node, spec, x, y);
367
+ }
368
+ };
369
+ const ride = (k, pair) => {
370
+ const el = this._drawDot(graphics, opts, dotR, trackFill, -1, 0, trackSpec);
371
+ el.node.classList.add("apexcharts-unit-track-cell");
372
+ trackG.add(el);
373
+ glide(el.node, trackSpec, track[k].x, track[k].y, "trk:" + k, dotR, animDots.length, pair);
374
+ };
375
+ clusters.forEach((cluster, ci) => {
315
376
  const color = w.globals.colors[cluster.i] || w.globals.colors[0] || "#008FFB";
316
377
  const elSeries = graphics.group({
317
378
  class: "apexcharts-series",
@@ -323,7 +384,9 @@ class Unit {
323
384
  });
324
385
  const burstCount = cluster.dots.length;
325
386
  const catData = unitData[cluster.i];
387
+ const at = animDots.length;
326
388
  cluster.dots.forEach((d, jj) => {
389
+ if (track.length) ride(ci * perTile + jj, true);
327
390
  const j = d.j != null ? d.j : jj;
328
391
  const datum = catData ? catData[j] : void 0;
329
392
  const dotFill = datum && typeof datum === "object" && datum.fillColor ? datum.fillColor : color;
@@ -334,7 +397,7 @@ class Unit {
334
397
  let key;
335
398
  if (identity) {
336
399
  const id = datum && typeof datum === "object" ? datum.id != null ? datum.id : datum.name : void 0;
337
- key = id != null ? `id:${id}` : `g:${gIndex}`;
400
+ key = id != null ? `id:${uniqueId(seen, String(id))}` : `g:${gIndex}`;
338
401
  } else if (flow) {
339
402
  key = String(gIndex);
340
403
  } else if (d.slot != null) {
@@ -354,8 +417,9 @@ class Unit {
354
417
  const anchor = from || (morphActive ? morph.getInitialSlotFor(cluster.i, j, burstCount) : null);
355
418
  const enter = opts.gather && opts.gather.enter || "burst";
356
419
  const inPlace = gridSplit || enter === "fade" || enter === "rise";
357
- const cx0 = anchor ? anchor.x : inPlace ? d.x : cluster.cx;
358
- const cy0 = anchor ? anchor.y : enter === "rise" && !gridSplit ? d.y + 14 : inPlace ? d.y : cluster.cy;
420
+ const start = anchor || gridSplit && prev && prev.get("trk:" + d.slot);
421
+ const cx0 = start ? start.x : inPlace ? d.x : cluster.cx;
422
+ const cy0 = start ? start.y : enter === "rise" && !gridSplit ? d.y + 14 : inPlace ? d.y : cluster.cy;
359
423
  el.node.style.opacity = anchor ? "1" : "0";
360
424
  this._place(el.node, spec, cx0, cy0);
361
425
  animDots.push({
@@ -385,9 +449,16 @@ class Unit {
385
449
  this._place(el.node, spec, d.x, d.y);
386
450
  }
387
451
  });
452
+ for (let k = cluster.dots.length; k < perTile; k++) ride(ci * perTile + k, false);
388
453
  if ((layout === "grouped" || layout === "columns" || gridSplit) && opts.clusterLabels && opts.clusterLabels.show && counts[cluster.i] > 0) {
389
454
  const labelTotal = gridSplit ? this._gridDenom : total;
390
- this._drawClusterLabel(elSeries, cluster, counts[cluster.i], labelTotal, opts, color);
455
+ const t = this._drawClusterLabel(elSeries, cluster, counts[cluster.i], labelTotal, opts, color);
456
+ if (t && gridSplit) {
457
+ const spec = { pk: PK_CORNER, hx: 0, hy: 0, r: 0 };
458
+ const lx = +(t.getAttribute("x") || 0);
459
+ const ly = +(t.getAttribute("y") || 0);
460
+ glide(t, spec, lx, ly, "lbl:" + cluster.i, 0, at, animDots.length > at);
461
+ }
391
462
  }
392
463
  ret.add(elSeries);
393
464
  });
@@ -422,9 +493,13 @@ class Unit {
422
493
  const total = counts.reduce((a, b) => a + b, 0);
423
494
  if (!maxUnits || maxUnits <= 0 || total <= maxUnits) return counts;
424
495
  const scale = maxUnits / total;
425
- console.warn(
426
- `[ApexCharts] unit chart: ${total} dots exceeds maxUnits (${maxUnits}); counts were scaled down proportionally. Raise plotOptions.unit.maxUnits or use plotOptions.unit.unitValue to represent more units per dot.`
427
- );
496
+ const ctx = this.ctx || {};
497
+ if (!ctx._unitCapWarned) {
498
+ ctx._unitCapWarned = true;
499
+ console.warn(
500
+ `[ApexCharts] unit chart: ${total} dots exceeds maxUnits (${maxUnits}); counts were scaled down proportionally. Raise plotOptions.unit.maxUnits or use plotOptions.unit.unitValue to represent more units per dot.`
501
+ );
502
+ }
428
503
  return counts.map((c) => c > 0 ? Math.max(1, Math.round(c * scale)) : 0);
429
504
  }
430
505
  /**
@@ -534,7 +609,8 @@ class Unit {
534
609
  *
535
610
  * `id` is the datum's own id/name where the per-unit object form supplies
536
611
  * one, so a provider can address a specific unit ("Texas", "employee 41")
537
- * rather than a positional slot. It falls back to `"<category>:<index>"`.
612
+ * rather than a positional slot. It falls back to `"<category>:<index>"`, and
613
+ * a repeated one is suffixed (`"Alex#2"`).
538
614
  *
539
615
  * @param {number[]} counts
540
616
  * @param {number} dotR the radius the engine would use, so a provider that
@@ -546,13 +622,17 @@ class Unit {
546
622
  const unitData = w.seriesData.unitData || [];
547
623
  const names = w.seriesData.seriesNames || [];
548
624
  const objects = [];
625
+ const seen = /* @__PURE__ */ new Map();
549
626
  let index = 0;
550
627
  counts.forEach((n, i) => {
551
628
  var _a;
552
629
  const catData = unitData[i];
553
630
  for (let j = 0; j < n; j++) {
554
631
  const datum = catData ? catData[j] : void 0;
555
- const id = datum && typeof datum === "object" && (datum.id != null || datum.name != null) ? String(datum.id != null ? datum.id : datum.name) : `${i}:${j}`;
632
+ const id = uniqueId(
633
+ seen,
634
+ datum && typeof datum === "object" && (datum.id != null || datum.name != null) ? String(datum.id != null ? datum.id : datum.name) : `${i}:${j}`
635
+ );
556
636
  objects.push({
557
637
  id,
558
638
  index,
@@ -736,11 +816,18 @@ class Unit {
736
816
  const pad = Math.min(gw, gh) * 0.04;
737
817
  const boxW = Math.max(1e-6, b.maxX - b.minX);
738
818
  const boxH = Math.max(1e-6, b.maxY - b.minY);
739
- const r1 = Math.max(4, Math.min((gw - 2 * pad) / boxW, (gh - 2 * pad) / boxH));
740
- const r0 = r1 * innerRatio;
819
+ const fit = (inset) => Math.max(4, Math.min((gw - 2 * pad - inset) / boxW, (gh - 2 * pad - inset) / boxH));
820
+ const seat = (r) => fit(2 * this._arcAllocate(total, r * innerRatio, r, span, opts).dotR);
821
+ let hi = fit(0);
822
+ let r1 = seat(hi);
823
+ for (let k = 0; k < 6; k++) {
824
+ const m = (r1 + hi) / 2;
825
+ if (m <= seat(m)) r1 = m;
826
+ else hi = m;
827
+ }
741
828
  const cx = gw / 2 - (b.minX + b.maxX) / 2 * r1;
742
829
  const cy = gh / 2 - (b.minY + b.maxY) / 2 * r1;
743
- const alloc = this._arcAllocate(total, r0, r1, span, opts);
830
+ const alloc = this._arcAllocate(total, r1 * innerRatio, r1, span, opts);
744
831
  this._lastDotR = alloc.dotR;
745
832
  const seats = [];
746
833
  for (let r = 0; r < alloc.R; r++) {
@@ -1024,7 +1111,7 @@ class Unit {
1024
1111
  * value over a denominator (`grid.max`, else the largest count so the leader
1025
1112
  * fills its tile and every other tile stays proportionally full - no empty
1026
1113
  * tiles for arbitrary data). The unfilled cells are drawn as a faint TRACK
1027
- * backdrop (see _drawGridTrack) so each tile reads as a part-to-whole "of N".
1114
+ * backdrop (drawn by draw()) so each tile reads as a part-to-whole "of N".
1028
1115
  * Only VISIBLE (non-zero) categories claim a tile, so a legend hide drops the
1029
1116
  * tile and the rest re-flow. Each filled cell is keyed by a physical
1030
1117
  * `tile*cells + localCell` slot, so a value change grows/shrinks a tile's fill
@@ -1042,7 +1129,7 @@ class Unit {
1042
1129
  const rowsPerTile = Math.max(1, Math.ceil(cellsPerTile / cols));
1043
1130
  const visible = counts.map((_2, i) => i).filter((i) => counts[i] > 0);
1044
1131
  const K2 = Math.max(1, visible.length);
1045
- const denom = gcfg.max > 0 ? gcfg.max : Math.max(1, ...counts);
1132
+ const denom = gcfg.max > 0 ? gcfg.max : Math.max(...counts) || 1;
1046
1133
  const tileCols = Math.max(
1047
1134
  1,
1048
1135
  Math.round(gcfg.tileColumns > 0 ? gcfg.tileColumns : Math.ceil(Math.sqrt(K2)))
@@ -1107,40 +1194,6 @@ class Unit {
1107
1194
  this._gridTrack = { cells: track };
1108
1195
  return clusters;
1109
1196
  }
1110
- /**
1111
- * Draw the faint "track" backdrop for the small-multiple grid: every cell of
1112
- * every tile's full lattice, so the filled (coloured) cells drawn on top read
1113
- * as a fraction of the whole. Static (redrawn each render, never animated);
1114
- * painted BEHIND the series groups. `grid.trackColor` overrides the default
1115
- * theme-neutral grey.
1116
- * @param {any} ret @param {Graphics} graphics @param {any} opts
1117
- */
1118
- _drawGridTrack(ret, graphics, opts) {
1119
- const track = this._gridTrack;
1120
- if (!track || !track.cells || !track.cells.length) return;
1121
- const r = this._lastDotR;
1122
- const gcfg = opts.grid || {};
1123
- const trackColor = gcfg.trackColor || "rgba(128,128,128,0.14)";
1124
- const g = graphics.group({ class: "apexcharts-unit-track" });
1125
- track.cells.forEach((c) => {
1126
- let el;
1127
- if (opts.shape === "square") {
1128
- const side = r * 2;
1129
- el = graphics.drawRect(0, 0, side, side, opts.borderRadius || 0, trackColor, 1, 0, "none");
1130
- el.node.setAttribute("fill", trackColor);
1131
- el.node.setAttribute("x", String(c.x - r));
1132
- el.node.setAttribute("y", String(c.y - r));
1133
- } else {
1134
- el = graphics.drawCircle(r, { fill: trackColor, "stroke-width": 0, stroke: "none" });
1135
- el.node.setAttribute("fill", trackColor);
1136
- el.node.setAttribute("cx", String(c.x));
1137
- el.node.setAttribute("cy", String(c.y));
1138
- }
1139
- el.node.classList.add("apexcharts-unit-track-cell");
1140
- g.add(el);
1141
- });
1142
- ret.add(g);
1143
- }
1144
1197
  /**
1145
1198
  * Scatter / beeswarm layout: position every unit on a real numeric X value
1146
1199
  * axis by its own value (`_unitValueOf`), laned by category on Y. Within a
@@ -1155,11 +1208,14 @@ class Unit {
1155
1208
  _layoutScatter(opts) {
1156
1209
  const w = this.w;
1157
1210
  const scfg = opts.scatter || {};
1158
- if (scfg.y === "value") return this._layoutScatter2D(opts);
1159
- if (scfg.orientation === "vertical") return this._layoutScatterVertical(opts);
1211
+ const unitData = w.seriesData.unitData || [];
1212
+ const lens = unitData.map((c) => c ? c.length : 0);
1213
+ const caps = this._applyMaxUnits(lens, opts.maxUnits);
1214
+ const keep = (ci, j) => Math.floor((j + 1) * caps[ci] / lens[ci]) > Math.floor(j * caps[ci] / lens[ci]);
1215
+ if (scfg.y === "value") return this._layoutScatter2D(opts, keep);
1216
+ if (scfg.orientation === "vertical") return this._layoutScatterVertical(opts, keep);
1160
1217
  const gw = w.layout.gridWidth;
1161
1218
  const gh = w.layout.gridHeight;
1162
- const unitData = w.seriesData.unitData || [];
1163
1219
  const names = w.seriesData.seriesNames || [];
1164
1220
  const valueOf = (d) => this._unitValueOf(d);
1165
1221
  const sizeStats = this._scatterSizeStats(scfg, unitData);
@@ -1184,11 +1240,13 @@ class Unit {
1184
1240
  vmax = 1;
1185
1241
  }
1186
1242
  const tickAmount = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
1187
- const domain = this._scatterValueDomain(scfg, vmin, vmax, tickAmount);
1243
+ const domain = this._scatterValueDomain(scfg.xMin, scfg.xMax, vmin, vmax, tickAmount);
1188
1244
  const xMin = domain.min;
1189
1245
  const xMax = domain.max;
1190
1246
  const xSpan = xMax - xMin || 1;
1191
- const laneW = scfg.laneLabelWidth != null ? Math.max(0, scfg.laneLabelWidth) : Kv > 1 ? 92 : 8;
1247
+ const font = { fontFamily: w.config.chart.fontFamily, fontWeight: 600 };
1248
+ const laneName = (i) => names[i] || `series-${i + 1}`;
1249
+ const laneW = scfg.laneLabelWidth != null ? Math.max(0, scfg.laneLabelWidth) : Kv > 1 ? Math.min(gw / 3, measureLabelWidth(w, visible.map(laneName), font) + 14) : 8;
1192
1250
  const bottomGutter = 30 + (scfg.xTitle ? 20 : 0);
1193
1251
  const plotL = laneW;
1194
1252
  const plotR = gw - 8;
@@ -1222,19 +1280,24 @@ class Unit {
1222
1280
  const maxR = sizeStats ? sizeStats.rMax : r;
1223
1281
  visible.forEach((ci, slot) => {
1224
1282
  const cy = laneCy(slot);
1225
- lanes.push({ i: ci, cy, name: names[ci] || `series-${ci + 1}` });
1226
- const cat = unitData[ci] || [];
1227
- const pts = cat.map((d, j) => {
1283
+ let name = laneName(ci);
1284
+ const wid = laneW > 12 ? measureLabelWidth(w, [name], font) : 0;
1285
+ if (wid > laneW - 14 && wid) {
1286
+ name = name.slice(0, Math.max(1, Math.floor(name.length * (laneW - 14) / wid) - 1)) + "…";
1287
+ }
1288
+ lanes.push({ i: ci, cy, name });
1289
+ const pts = [];
1290
+ (unitData[ci] || []).forEach((d, j) => {
1228
1291
  const v = valueOf(d);
1229
- const p = { j, px: plotX(isNum(v) ? v : xMin), y: cy };
1292
+ if (!isNum(v) || !keep(ci, j)) return;
1293
+ const p = { j, px: plotX(v), y: cy };
1230
1294
  if (sizeStats) p.r = this._scatterRadius(d, sizeStats, r);
1231
- return p;
1295
+ pts.push(p);
1232
1296
  });
1233
1297
  if (jitter) {
1234
1298
  const halfLane = Math.max(maxR, laneH / 2 - maxR);
1235
- pts.forEach((p, k) => {
1236
- const t = (k * 9301 + 49297) % 233280 / 233280;
1237
- p.y = cy + (t * 2 - 1) * halfLane;
1299
+ pts.forEach((p) => {
1300
+ p.y = cy + (hash01(ci * 7919 + p.j) * 2 - 1) * halfLane;
1238
1301
  });
1239
1302
  } else {
1240
1303
  this._beeswarm(pts, cy, r, step, maxR);
@@ -1244,7 +1307,7 @@ class Unit {
1244
1307
  cx: (plotL + plotR) / 2,
1245
1308
  cy,
1246
1309
  outerR: laneH / 2,
1247
- dots: pts.map((p) => ({ x: p.px, y: p.y, r: p.r }))
1310
+ dots: pts.map((p) => ({ x: p.px, y: p.y, r: p.r, j: p.j }))
1248
1311
  });
1249
1312
  });
1250
1313
  const ticks = domain.ticks;
@@ -1271,9 +1334,9 @@ class Unit {
1271
1334
  * dots horizontally off each column's centre line. The value-axis config keys
1272
1335
  * (`xMin`/`xMax`/`xTitle`/`xFormatter`/`tickAmount`) still describe the value
1273
1336
  * axis (now Y), so flipping `orientation` keeps the same value settings.
1274
- * @param {any} opts
1337
+ * @param {any} opts @param {(ci:number, j:number) => boolean} keep
1275
1338
  */
1276
- _layoutScatterVertical(opts) {
1339
+ _layoutScatterVertical(opts, keep) {
1277
1340
  const w = this.w;
1278
1341
  const scfg = opts.scatter || {};
1279
1342
  const gw = w.layout.gridWidth;
@@ -1303,7 +1366,7 @@ class Unit {
1303
1366
  vmax = 1;
1304
1367
  }
1305
1368
  const tickAmount = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
1306
- const domain = this._scatterValueDomain(scfg, vmin, vmax, tickAmount);
1369
+ const domain = this._scatterValueDomain(scfg.xMin, scfg.xMax, vmin, vmax, tickAmount);
1307
1370
  const vMin = domain.min;
1308
1371
  const vMax = domain.max;
1309
1372
  const vSpan = vMax - vMin || 1;
@@ -1342,18 +1405,18 @@ class Unit {
1342
1405
  visible.forEach((ci, slot) => {
1343
1406
  const cx = laneCx(slot);
1344
1407
  lanes.push({ i: ci, cx, name: names[ci] || `series-${ci + 1}` });
1345
- const cat = unitData[ci] || [];
1346
- const pts = cat.map((d, j) => {
1408
+ const pts = [];
1409
+ (unitData[ci] || []).forEach((d, j) => {
1347
1410
  const v = valueOf(d);
1348
- const p = { j, py: plotY(isNum(v) ? v : vMin), x: cx };
1411
+ if (!isNum(v) || !keep(ci, j)) return;
1412
+ const p = { j, py: plotY(v), x: cx };
1349
1413
  if (sizeStats) p.r = this._scatterRadius(d, sizeStats, r);
1350
- return p;
1414
+ pts.push(p);
1351
1415
  });
1352
1416
  if (jitter) {
1353
1417
  const halfLane = Math.max(maxR, laneW / 2 - maxR);
1354
- pts.forEach((p, k) => {
1355
- const t = (k * 9301 + 49297) % 233280 / 233280;
1356
- p.x = cx + (t * 2 - 1) * halfLane;
1418
+ pts.forEach((p) => {
1419
+ p.x = cx + (hash01(ci * 7919 + p.j) * 2 - 1) * halfLane;
1357
1420
  });
1358
1421
  } else {
1359
1422
  this._beeswarm(pts, cx, r, step, maxR, true);
@@ -1363,7 +1426,7 @@ class Unit {
1363
1426
  cx,
1364
1427
  cy: (plotT + plotB) / 2,
1365
1428
  outerR: laneW / 2,
1366
- dots: pts.map((p) => ({ x: p.x, y: p.py, r: p.r }))
1429
+ dots: pts.map((p) => ({ x: p.x, y: p.py, r: p.r, j: p.j }))
1367
1430
  });
1368
1431
  });
1369
1432
  const ticks = domain.ticks;
@@ -1392,17 +1455,17 @@ class Unit {
1392
1455
  * per category). With `scatter.sizeRange` set, each dot is a BUBBLE scaled (by
1393
1456
  * area) from its `sizeField` (default 'z'). Needs the object form with numeric
1394
1457
  * `x` + `y`.
1395
- * @param {any} opts
1458
+ * @param {any} opts @param {(ci:number, j:number) => boolean} keep
1396
1459
  */
1397
- _layoutScatter2D(opts) {
1460
+ _layoutScatter2D(opts, keep) {
1398
1461
  const w = this.w;
1399
1462
  const gw = w.layout.gridWidth;
1400
1463
  const gh = w.layout.gridHeight;
1401
1464
  const scfg = opts.scatter || {};
1402
1465
  const unitData = w.seriesData.unitData || [];
1403
- const isNum = (v) => typeof v === "number" && isFinite(v);
1404
- const xOf = (d) => d && typeof d === "object" ? d.x : null;
1405
- const yOf = (d) => d && typeof d === "object" ? d.y != null ? d.y : d.value : null;
1466
+ const isNum = (v) => v != null;
1467
+ const xOf = (d) => d && typeof d === "object" ? toNum(d.x) : null;
1468
+ const yOf = (d) => d && typeof d === "object" ? toNum(d.y != null ? d.y : d.value) : null;
1406
1469
  const visible = unitData.map((_2, i) => i).filter(
1407
1470
  (i) => (unitData[i] || []).some((d) => isNum(xOf(d)) && isNum(yOf(d)))
1408
1471
  );
@@ -1428,22 +1491,24 @@ class Unit {
1428
1491
  ymn = 0;
1429
1492
  ymx = 1;
1430
1493
  }
1431
- const xTicksN = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
1432
- const yTicksN = Math.max(2, Math.round(scfg.yTickAmount > 0 ? scfg.yTickAmount : 5));
1433
- const nx = this._niceScale(
1434
- scfg.xMin != null ? scfg.xMin : xmn,
1435
- scfg.xMax != null ? scfg.xMax : xmx,
1436
- xTicksN
1494
+ const dx = this._scatterValueDomain(
1495
+ scfg.xMin,
1496
+ scfg.xMax,
1497
+ xmn,
1498
+ xmx,
1499
+ Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5))
1437
1500
  );
1438
- const ny = this._niceScale(
1439
- scfg.yMin != null ? scfg.yMin : ymn,
1440
- scfg.yMax != null ? scfg.yMax : ymx,
1441
- yTicksN
1501
+ const dy = this._scatterValueDomain(
1502
+ scfg.yMin,
1503
+ scfg.yMax,
1504
+ ymn,
1505
+ ymx,
1506
+ Math.max(2, Math.round(scfg.yTickAmount > 0 ? scfg.yTickAmount : 5))
1442
1507
  );
1443
- const xMin = scfg.xMin != null ? scfg.xMin : nx.min;
1444
- const xMax = scfg.xMax != null ? scfg.xMax : nx.max;
1445
- const yMin = scfg.yMin != null ? scfg.yMin : ny.min;
1446
- const yMax = scfg.yMax != null ? scfg.yMax : ny.max;
1508
+ const xMin = dx.min;
1509
+ const xMax = dx.max;
1510
+ const yMin = dy.min;
1511
+ const yMax = dy.max;
1447
1512
  const xSpan = xMax - xMin || 1;
1448
1513
  const ySpan = yMax - yMin || 1;
1449
1514
  const leftGutter = 46 + (scfg.yTitle ? 18 : 0);
@@ -1461,15 +1526,17 @@ class Unit {
1461
1526
  this._lastDotR = baseR;
1462
1527
  const clusters = [];
1463
1528
  visible.forEach((ci) => {
1464
- const cat = unitData[ci] || [];
1465
- const dots = cat.map((d) => {
1529
+ const dots = [];
1530
+ (unitData[ci] || []).forEach((d, j) => {
1466
1531
  const x = xOf(d);
1467
1532
  const y = yOf(d);
1468
- return {
1469
- x: plotX(isNum(x) ? x : xMin),
1470
- y: plotY(isNum(y) ? y : yMin),
1471
- r: sizeStats ? this._scatterRadius(d, sizeStats, baseR) : void 0
1472
- };
1533
+ if (x == null || y == null || !keep(ci, j)) return;
1534
+ dots.push({
1535
+ x: plotX(x),
1536
+ y: plotY(y),
1537
+ r: sizeStats ? this._scatterRadius(d, sizeStats, baseR) : void 0,
1538
+ j
1539
+ });
1473
1540
  });
1474
1541
  clusters.push({
1475
1542
  i: ci,
@@ -1479,18 +1546,6 @@ class Unit {
1479
1546
  dots
1480
1547
  });
1481
1548
  });
1482
- const mkTicks = (lo, hi, span, spacing, pinned, n) => {
1483
- const out = [];
1484
- if (pinned) {
1485
- for (let k = 0; k < n; k++) out.push(lo + span * k / (n - 1));
1486
- } else {
1487
- const sp = spacing || span / Math.max(1, n - 1);
1488
- for (let v = lo; v <= hi + sp * 0.5; v += sp) {
1489
- out.push(Math.abs(v) < sp * 1e-9 ? 0 : v);
1490
- }
1491
- }
1492
- return out;
1493
- };
1494
1549
  this._scatterAxis = {
1495
1550
  mode: "2d",
1496
1551
  plotL,
@@ -1499,22 +1554,8 @@ class Unit {
1499
1554
  plotB,
1500
1555
  plotX,
1501
1556
  plotY,
1502
- xTicks: mkTicks(
1503
- xMin,
1504
- xMax,
1505
- xSpan,
1506
- nx.spacing,
1507
- scfg.xMin != null || scfg.xMax != null,
1508
- xTicksN
1509
- ),
1510
- yTicks: mkTicks(
1511
- yMin,
1512
- yMax,
1513
- ySpan,
1514
- ny.spacing,
1515
- scfg.yMin != null || scfg.yMax != null,
1516
- yTicksN
1517
- ),
1557
+ xTicks: dx.ticks,
1558
+ yTicks: dy.ticks,
1518
1559
  xTitle: scfg.xTitle,
1519
1560
  yTitle: scfg.yTitle,
1520
1561
  xFormatter: typeof scfg.xFormatter === "function" ? scfg.xFormatter : null,
@@ -1621,32 +1662,43 @@ class Unit {
1621
1662
  });
1622
1663
  }
1623
1664
  /**
1624
- * Value-axis domain + ticks for a 1D beeswarm. The domain ALWAYS contains
1665
+ * Value-axis domain + ticks for a scatter axis. The domain ALWAYS contains
1625
1666
  * every datum: a swarm that clips a dot outside the plot box is a bug, so an
1626
- * explicit `xMin`/`xMax` only FRAMES the axis and is extended by whole
1667
+ * explicit `lo`/`hi` only FRAMES the axis and is extended by whole
1627
1668
  * tick-steps whenever the data would otherwise overflow. Orientation-agnostic:
1628
1669
  * the same value axis is X for a horizontal swarm and Y for a vertical one.
1629
- * @param {any} scfg scatter config
1670
+ * @param {number|undefined} lo pinned minimum @param {number|undefined} hi
1671
+ * pinned maximum
1630
1672
  * @param {number} vmin data minimum @param {number} vmax data maximum
1631
1673
  * @param {number} tickAmount desired tick count
1632
1674
  * @returns {{ min:number, max:number, ticks:number[] }}
1633
1675
  */
1634
- _scatterValueDomain(scfg, vmin, vmax, tickAmount) {
1676
+ _scatterValueDomain(lo, hi, vmin, vmax, tickAmount) {
1635
1677
  const buildTicks = (min, max, spacing2) => {
1636
1678
  const ticks = [];
1637
- for (let v = min; v <= max + spacing2 * 0.5; v += spacing2) {
1679
+ for (let k = 0, v = min; v <= max + spacing2 * 0.5; v = min + ++k * spacing2) {
1638
1680
  ticks.push(Math.abs(v) < spacing2 * 1e-9 ? 0 : v);
1639
1681
  }
1640
1682
  return ticks;
1641
1683
  };
1642
- if (scfg.xMin != null || scfg.xMax != null) {
1643
- let min = scfg.xMin != null ? scfg.xMin : vmin;
1644
- let max = scfg.xMax != null ? scfg.xMax : vmax;
1645
- if (!(max > min)) max = min + 1;
1646
- const spacing2 = (max - min) / Math.max(1, tickAmount - 1);
1684
+ if (lo != null || hi != null) {
1685
+ let min = (
1686
+ /** @type {number} */
1687
+ lo != null ? lo : hi
1688
+ );
1689
+ let max = (
1690
+ /** @type {number} */
1691
+ hi != null ? hi : lo
1692
+ );
1693
+ const spacing2 = max > min ? (max - min) / Math.max(1, tickAmount - 1) : this._niceScale(Math.min(min, vmin), Math.max(max, vmax), tickAmount).spacing;
1647
1694
  if (vmin < min) min -= Math.ceil((min - vmin) / spacing2) * spacing2;
1648
1695
  if (vmax > max) max += Math.ceil((vmax - max) / spacing2) * spacing2;
1649
- return { min, max, ticks: buildTicks(min, max, spacing2) };
1696
+ if (!(max > min)) max = min + spacing2;
1697
+ if ((max - min) / spacing2 <= 2 * tickAmount) {
1698
+ return { min, max, ticks: buildTicks(min, max, spacing2) };
1699
+ }
1700
+ vmin = min;
1701
+ vmax = max;
1650
1702
  }
1651
1703
  const nice = this._niceScale(vmin, vmax, tickAmount);
1652
1704
  const spacing = nice.spacing || (nice.max - nice.min) / Math.max(1, tickAmount - 1);
@@ -1737,13 +1789,13 @@ class Unit {
1737
1789
  ax.yTicks.forEach((v) => {
1738
1790
  const y = ax.plotY(v);
1739
1791
  if (ax.gridlines) line(ax.plotL, y, ax.plotR, y, gridColor);
1740
- const label = ax.yFormatter ? String(ax.yFormatter(v)) : this._formatTick(v);
1792
+ const label = ax.yFormatter ? String(ax.yFormatter(v)) : this._formatTick(v, ax.yTicks);
1741
1793
  text(label, ax.plotL - 8, y, "end", labelColor, 11, 400, "apexcharts-unit-tick");
1742
1794
  });
1743
1795
  ax.xTicks.forEach((v) => {
1744
1796
  const x = ax.plotX(v);
1745
1797
  if (ax.gridlines) line(x, ax.plotT, x, ax.plotB, gridColor);
1746
- const label = ax.xFormatter ? String(ax.xFormatter(v)) : this._formatTick(v);
1798
+ const label = ax.xFormatter ? String(ax.xFormatter(v)) : this._formatTick(v, ax.xTicks);
1747
1799
  text(label, x, ax.plotB + 14, "middle", labelColor, 11, 400, "apexcharts-unit-tick");
1748
1800
  });
1749
1801
  line(ax.plotL, ax.plotB, ax.plotR, ax.plotB, axisColor);
@@ -1783,7 +1835,7 @@ class Unit {
1783
1835
  ax.ticks.forEach((v) => {
1784
1836
  const y = ax.plotY(v);
1785
1837
  if (ax.gridlines) line(ax.plotL, y, ax.plotR, y, gridColor);
1786
- const label = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v);
1838
+ const label = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v, ax.ticks);
1787
1839
  text(label, ax.plotL - 8, y, "end", labelColor, 11, 400, "apexcharts-unit-tick");
1788
1840
  });
1789
1841
  line(ax.plotL, ax.plotT, ax.plotL, ax.plotB, axisColor);
@@ -1813,7 +1865,7 @@ class Unit {
1813
1865
  ax.ticks.forEach((v, idx) => {
1814
1866
  const x = ax.plotX(v);
1815
1867
  if (ax.gridlines) line(x, ax.plotT, x, ax.plotB, gridColor);
1816
- const label = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v);
1868
+ const label = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v, ax.ticks);
1817
1869
  const anchor = idx === 0 ? "start" : idx === ax.ticks.length - 1 ? "end" : "middle";
1818
1870
  text(label, x, ax.plotB + 14, anchor, labelColor, 11, 400, "apexcharts-unit-tick");
1819
1871
  });
@@ -1839,17 +1891,19 @@ class Unit {
1839
1891
  ret.add(g);
1840
1892
  }
1841
1893
  /**
1842
- * Compact tick-value formatting: integers as-is, otherwise trimmed to a short
1843
- * decimal; large magnitudes get a k/M suffix.
1844
- * @param {number} v @returns {string}
1894
+ * Compact tick-value formatting: integers as-is, otherwise trimmed to enough
1895
+ * decimals to tell neighbouring ticks apart; large magnitudes get a k/M suffix.
1896
+ * @param {number} v @param {number[]} ticks the axis ticks (their step sets
1897
+ * the decimals) @returns {string}
1845
1898
  */
1846
- _formatTick(v) {
1899
+ _formatTick(v, ticks) {
1847
1900
  if (!isFinite(v)) return "";
1848
1901
  const a = Math.abs(v);
1849
1902
  if (a >= 1e6) return `${+(v / 1e6).toFixed(1)}M`;
1850
1903
  if (a >= 1e4) return `${+(v / 1e3).toFixed(1)}k`;
1851
1904
  if (Number.isInteger(v)) return String(v);
1852
- return String(+v.toFixed(2));
1905
+ const step = Math.abs(ticks[1] - ticks[0]) || 1;
1906
+ return String(+v.toFixed(Math.min(20, Math.max(2, 1 - Math.floor(Math.log10(step))))));
1853
1907
  }
1854
1908
  /**
1855
1909
  * Distribute `total` whole cells across `counts` in proportion to each value,
@@ -1914,17 +1968,12 @@ class Unit {
1914
1968
  return !!(sbv && sbv.enabled && opts.shape !== "image" && opts.shape !== "square");
1915
1969
  }
1916
1970
  /**
1917
- * This datum's numeric value for sizing / tooltip: the number itself, or an
1918
- * object's `value` / `y`. Null when there is no usable number.
1971
+ * This datum's numeric value for sizing / tooltip: the number (or numeric
1972
+ * string) itself, or an object's `value` / `y`. Null when there is none.
1919
1973
  * @param {any} d @returns {number | null}
1920
1974
  */
1921
1975
  _unitValueOf(d) {
1922
- if (typeof d === "number") return d;
1923
- if (d && typeof d === "object") {
1924
- const v = d.value != null ? d.value : d.y;
1925
- return typeof v === "number" ? v : null;
1926
- }
1927
- return null;
1976
+ return toNum(d && typeof d === "object" ? d.value != null ? d.value : d.y : d);
1928
1977
  }
1929
1978
  /**
1930
1979
  * Radius for one bubble given the value stats. Default 'area' scaling makes
@@ -2066,8 +2115,9 @@ class Unit {
2066
2115
  *
2067
2116
  * @param {SVGElement} node @param {UnitMarkSpec} spec
2068
2117
  * @param {number} x @param {number} y
2118
+ * @param {number} [k] scale against the spec's size, while a size tweens
2069
2119
  */
2070
- _place(node, spec, x, y) {
2120
+ _place(node, spec, x, y, k = 1) {
2071
2121
  const s = (
2072
2122
  /** @type {any} */
2073
2123
  spec
@@ -2075,16 +2125,31 @@ class Unit {
2075
2125
  if (s.pk === PK_GLYPH) {
2076
2126
  node.setAttribute(
2077
2127
  "transform",
2078
- "translate(" + (x - s.ox) + "," + (y - s.oy) + s.tail
2128
+ "translate(" + (x - s.ox * k) + "," + (y - s.oy * k) + (k === 1 ? s.tail : ") scale(" + s.s * k + ")")
2079
2129
  );
2080
2130
  } else if (s.pk === PK_CORNER) {
2081
- node.setAttribute("x", String(x - s.hx));
2082
- node.setAttribute("y", String(y - s.hy));
2131
+ node.setAttribute("x", String(x - s.hx * k));
2132
+ node.setAttribute("y", String(y - s.hy * k));
2083
2133
  } else {
2084
2134
  node.setAttribute("cx", String(x));
2085
2135
  node.setAttribute("cy", String(y));
2086
2136
  }
2087
2137
  }
2138
+ /**
2139
+ * Size one mark at `k` times its spec's size (a glyph scales in `_place`).
2140
+ * @param {SVGElement} node @param {UnitMarkSpec} spec @param {number} k
2141
+ */
2142
+ _size(node, spec, k) {
2143
+ const s = (
2144
+ /** @type {any} */
2145
+ spec
2146
+ );
2147
+ if (s.pk === PK_CIRCLE) node.setAttribute("r", String(s.r * k));
2148
+ else if (s.pk === PK_CORNER) {
2149
+ node.setAttribute("width", String(2 * s.hx * k));
2150
+ node.setAttribute("height", String(2 * s.hy * k));
2151
+ }
2152
+ }
2088
2153
  /**
2089
2154
  * The spec for the chart-wide shape (no pictogram, no per-mark radius).
2090
2155
  * @param {any} opts @param {number} [r]
@@ -2143,7 +2208,7 @@ class Unit {
2143
2208
  */
2144
2209
  _glyphSpec(mark, dotR, pcfg) {
2145
2210
  const qr = Math.round(dotR * 10) / 10;
2146
- const key = mark.name + "|" + mark.path.length + "|" + qr;
2211
+ const key = mark.name + mark.path + mark.viewBox + mark.fillRule + qr;
2147
2212
  const hit = this._specCache.get(key);
2148
2213
  if (hit) return hit;
2149
2214
  const vb = mark.viewBox || [0, 0, 100, 100];
@@ -2192,9 +2257,9 @@ class Unit {
2192
2257
  * Draw one dot (circle, square, or image icon) with the category fill +
2193
2258
  * stroke, tagged so the shared non-axis tooltip and hover reuse work.
2194
2259
  * @param {Graphics} graphics @param {any} opts @param {number} dotR
2195
- * @param {string} color @param {number} i @param {number} j
2196
- * @param {UnitMarkSpec} [spec] this mark's resolved spec; defaults to the
2197
- * chart-wide shape
2260
+ * @param {string} color @param {number} i @param {number} j (i < 0: an exit
2261
+ * ghost, left untagged so tooltips and morph capture skip it)
2262
+ * @param {UnitMarkSpec} spec this mark's resolved spec
2198
2263
  * @returns {any}
2199
2264
  */
2200
2265
  _drawDot(graphics, opts, dotR, color, i, j, spec) {
@@ -2203,7 +2268,7 @@ class Unit {
2203
2268
  const strokeColor = Array.isArray(w.globals.stroke.colors) ? w.globals.stroke.colors[i] || "none" : "none";
2204
2269
  const fillOpacity = typeof w.config.fill.opacity === "number" ? w.config.fill.opacity : 1;
2205
2270
  let el;
2206
- if (spec && spec.pk === PK_GLYPH) {
2271
+ if (spec.pk === PK_GLYPH) {
2207
2272
  el = w.dom.Paper.path(spec.d);
2208
2273
  el.node.setAttribute("fill", color);
2209
2274
  if (spec.fillRule === "evenodd") {
@@ -2211,7 +2276,7 @@ class Unit {
2211
2276
  }
2212
2277
  if (fillOpacity < 1) el.node.setAttribute("fill-opacity", String(fillOpacity));
2213
2278
  el.node.setAttribute("data:r", String(spec.r));
2214
- } else if (opts.shape === "image" && opts.image && opts.image.src) {
2279
+ } else if (spec.pk === PK_CORNER && opts.shape === "image" && opts.image && opts.image.src) {
2215
2280
  const iw = opts.image.width || 20;
2216
2281
  const ih = opts.image.height || 20;
2217
2282
  el = w.dom.Paper.image(opts.image.src);
@@ -2221,7 +2286,7 @@ class Unit {
2221
2286
  if (opts.image.tint) {
2222
2287
  el.node.setAttribute("filter", `url(#${this._tintFilter(color)})`);
2223
2288
  }
2224
- } else if (opts.shape === "square") {
2289
+ } else if (spec.pk === PK_CORNER) {
2225
2290
  const side = dotR * 2;
2226
2291
  el = graphics.drawRect(0, 0, side, side, opts.borderRadius || 0, color, 1, strokeW, strokeColor);
2227
2292
  el.node.setAttribute("fill", color);
@@ -2235,9 +2300,11 @@ class Unit {
2235
2300
  el.node.setAttribute("fill", color);
2236
2301
  if (fillOpacity < 1) el.node.setAttribute("fill-opacity", String(fillOpacity));
2237
2302
  }
2238
- el.node.classList.add("apexcharts-unit-area");
2239
- el.node.setAttribute("i", String(i));
2240
- el.node.setAttribute("j", String(j));
2303
+ if (i >= 0) {
2304
+ el.node.classList.add("apexcharts-unit-area");
2305
+ el.node.setAttribute("i", String(i));
2306
+ el.node.setAttribute("j", String(j));
2307
+ }
2241
2308
  return el;
2242
2309
  }
2243
2310
  /**
@@ -2251,10 +2318,12 @@ class Unit {
2251
2318
  _tintFilter(color) {
2252
2319
  const w = this.w;
2253
2320
  const NS = "http://www.w3.org/2000/svg";
2254
- const safe = String(color).replace(/[^a-zA-Z0-9]/g, "");
2255
- const id = `apexcharts-unit-tint-${w.globals.chartID}-${safe}`;
2321
+ const tints = this._tints || (this._tints = /* @__PURE__ */ new Map());
2322
+ let id = tints.get(color);
2323
+ if (id) return id;
2324
+ id = `apexcharts-unit-tint-${w.globals.chartID}-${tints.size}`;
2325
+ tints.set(color, id);
2256
2326
  const svg = w.dom.Paper.node;
2257
- if (svg.querySelector(`#${id}`)) return id;
2258
2327
  let defs = svg.querySelector("defs");
2259
2328
  if (!defs) {
2260
2329
  defs = BrowserAPIs.createElementNS(NS, "defs");
@@ -2377,6 +2446,14 @@ class Unit {
2377
2446
  }
2378
2447
  if (this.ctx) this.ctx._unitSprings = springs;
2379
2448
  }
2449
+ /**
2450
+ * True once a loop's node no longer belongs to this chart's live SVG.
2451
+ * @param {Element} node @returns {boolean}
2452
+ */
2453
+ _stale(node) {
2454
+ const P = this.w.dom.Paper;
2455
+ return !!this.w.globals.isDestroyed || !P || !P.node.contains(node);
2456
+ }
2380
2457
  /**
2381
2458
  * One rAF loop that tweens every dot from its start (cx0/cy0 - either the
2382
2459
  * cluster centre on first mount / for entering dots, or its previous slot on
@@ -2399,8 +2476,8 @@ class Unit {
2399
2476
  const speed = Math.max(1, w.config.chart.animations.speed || 800);
2400
2477
  const maxDelay = Math.min(speed * 0.6, 450);
2401
2478
  const n = dots.length;
2402
- for (let k = 0; k < n; k++) {
2403
- dots[k].delay = n > 1 ? k / (n - 1) * maxDelay : 0;
2479
+ for (let k = n; k--; ) {
2480
+ dots[k].delay = dots[k].pair ? dots[k + 1].delay : n > 1 ? k / (n - 1) * maxDelay : 0;
2404
2481
  }
2405
2482
  const gcfg = opts.gather || {};
2406
2483
  const motion = gcfg.motion || "auto";
@@ -2409,8 +2486,9 @@ class Unit {
2409
2486
  else if (this.ctx) this.ctx._unitSprings = null;
2410
2487
  for (let k = 0; k < n; k++) {
2411
2488
  const d = dots[k];
2412
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
2413
- d.node.setAttribute("r", String(d.r0));
2489
+ if (d.r0 !== d.r1) {
2490
+ this._size(d.node, d.spec, d.r0 / d.r1);
2491
+ this._place(d.node, d.spec, d.cx0, d.cy0, d.r0 / d.r1);
2414
2492
  }
2415
2493
  }
2416
2494
  for (let k = 0; k < n; k++) {
@@ -2421,16 +2499,11 @@ class Unit {
2421
2499
  }
2422
2500
  }
2423
2501
  const easePos = gcfg.easing === "outBack" ? easeOutBack(typeof gcfg.overshoot === "number" ? gcfg.overshoot : 1.70158) : gcfg.easing === "inOutCubic" ? easeInOutCubic : easeOutCubic;
2424
- if (this.w.globals.unitGatherRAF != null) {
2425
- BrowserAPIs.cancelAnimationFrame(this.w.globals.unitGatherRAF);
2426
- this.w.globals.unitGatherRAF = null;
2427
- }
2428
2502
  const start = performance.now();
2429
2503
  let last = start;
2430
2504
  const stepFn = (now) => {
2431
- if (this.w.globals.isDestroyed) {
2505
+ if (this._stale(dots[0].node)) {
2432
2506
  this.w.globals.unitGatherRAF = null;
2433
- this.w.globals.animationEnded = true;
2434
2507
  return;
2435
2508
  }
2436
2509
  const dt = Math.min(MAX_FRAME_STEP, Math.max(0, (now - last) / 1e3));
@@ -2458,7 +2531,12 @@ class Unit {
2458
2531
  cx = d.cx0 + (d.x - d.cx0) * e;
2459
2532
  cy = d.cy0 + (d.y - d.cy0) * e;
2460
2533
  }
2461
- this._place(d.node, d.spec, cx, cy);
2534
+ let sk = 1;
2535
+ if (d.r0 !== d.r1) {
2536
+ sk = (d.r0 + (d.r1 - d.r0) * ec) / d.r1;
2537
+ this._size(d.node, d.spec, sk);
2538
+ }
2539
+ this._place(d.node, d.spec, cx, cy, sk);
2462
2540
  if (d.isEnter) d.node.style.opacity = String(Math.min(1, t * 2.5));
2463
2541
  if (d._c0 && d._c1) {
2464
2542
  const cr = Math.round(d._c0[0] + (d._c1[0] - d._c0[0]) * ec);
@@ -2466,9 +2544,6 @@ class Unit {
2466
2544
  const cb = Math.round(d._c0[2] + (d._c1[2] - d._c0[2]) * ec);
2467
2545
  d.node.setAttribute("fill", `rgb(${cr},${cg},${cb})`);
2468
2546
  }
2469
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
2470
- d.node.setAttribute("r", String(d.r0 + (d.r1 - d.r0) * ec));
2471
- }
2472
2547
  if (t < 1) done = false;
2473
2548
  }
2474
2549
  if (done) {
@@ -2476,8 +2551,9 @@ class Unit {
2476
2551
  const d = dots[k];
2477
2552
  d.node.style.opacity = "";
2478
2553
  if (d._c1 && d.fill1) d.node.setAttribute("fill", d.fill1);
2479
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
2480
- d.node.setAttribute("r", String(d.r1));
2554
+ if (d.r0 !== d.r1) {
2555
+ this._size(d.node, d.spec, 1);
2556
+ this._place(d.node, d.spec, d.x, d.y);
2481
2557
  }
2482
2558
  }
2483
2559
  this.w.globals.unitGatherRAF = null;
@@ -2500,7 +2576,7 @@ class Unit {
2500
2576
  const cap = Math.max(0, opts.maxUnits || 5e3);
2501
2577
  const exits = [];
2502
2578
  for (const [key, slot] of prev) {
2503
- if (!nextPrev.has(key)) {
2579
+ if (!nextPrev.has(key) && key[0] !== "l") {
2504
2580
  exits.push(slot);
2505
2581
  if (exits.length >= cap) break;
2506
2582
  }
@@ -2527,7 +2603,7 @@ class Unit {
2527
2603
  exits.forEach((slot) => {
2528
2604
  const r = slot.r != null ? slot.r : dotR;
2529
2605
  const spec = slot.spec || this._baseSpec(opts, r);
2530
- const el = this._drawDot(graphics, opts, r, slot.fill, 0, 0, spec);
2606
+ const el = this._drawDot(graphics, opts, r, slot.fill, -1, 0, spec);
2531
2607
  el.node.classList.add("apexcharts-unit-exit");
2532
2608
  this._place(el.node, spec, slot.x, slot.y);
2533
2609
  group.add(el);
@@ -2538,13 +2614,9 @@ class Unit {
2538
2614
  return;
2539
2615
  }
2540
2616
  const speed = Math.max(1, w.config.chart.animations.speed || 800);
2541
- if (this.w.globals.unitExitRAF != null) {
2542
- BrowserAPIs.cancelAnimationFrame(this.w.globals.unitExitRAF);
2543
- this.w.globals.unitExitRAF = null;
2544
- }
2545
2617
  const start = performance.now();
2546
2618
  const stepFn = (now) => {
2547
- if (this.w.globals.isDestroyed) {
2619
+ if (this._stale(group.node)) {
2548
2620
  this.w.globals.unitExitRAF = null;
2549
2621
  return;
2550
2622
  }
@@ -2615,7 +2687,8 @@ ${percent.toFixed(1)}%`;
2615
2687
  if (!lines.length) return 0;
2616
2688
  const width = measureLabelWidth(w, lines, {
2617
2689
  fontSize: cfg.fontSize,
2618
- fontFamily: cfg.fontFamily || w.config.chart.fontFamily
2690
+ fontFamily: cfg.fontFamily || w.config.chart.fontFamily,
2691
+ fontWeight: cfg.fontWeight
2619
2692
  });
2620
2693
  const gap = conn.gap != null ? conn.gap : 8;
2621
2694
  const length = conn.length != null ? conn.length : 22;
@@ -2752,6 +2825,7 @@ ${percent.toFixed(1)}%`;
2752
2825
  * the blob in either direction.
2753
2826
  * @param {any} elSeries @param {{ i:number, cx:number, cy:number, outerR:number, flat?:boolean }} cluster
2754
2827
  * @param {number} value @param {number} total @param {any} opts @param {string} color
2828
+ * @returns {SVGElement|undefined}
2755
2829
  */
2756
2830
  _drawClusterLabel(elSeries, cluster, value, total, opts, color) {
2757
2831
  const w = this.w;
@@ -2809,6 +2883,10 @@ ${percent.toFixed(1)}%`;
2809
2883
  textEl.textContent = str;
2810
2884
  }
2811
2885
  elSeries.node.appendChild(textEl);
2886
+ return (
2887
+ /** @type {any} */
2888
+ textEl
2889
+ );
2812
2890
  }
2813
2891
  }
2814
2892
  _core__default.use({