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/full.esm.js CHANGED
@@ -50,7 +50,7 @@ var __async = (__this, __arguments, generator) => {
50
50
  });
51
51
  };
52
52
  /*!
53
- * ApexCharts v8.2.0
53
+ * ApexCharts v8.3.0
54
54
  * (c) 2018-2026 ApexCharts
55
55
  */
56
56
  import ApexCharts$1 from "apexcharts";
@@ -62,6 +62,285 @@ const getChartClass = ApexCharts.__apex_ChartFactory_getChartClass;
62
62
  const Fill = ApexCharts.__apex_Fill;
63
63
  const Graphics = ApexCharts.__apex_Graphics;
64
64
  const Series = ApexCharts.__apex_Series;
65
+ const Animations = ApexCharts.__apex_Animations;
66
+ const prefersReducedMotion = ApexCharts.__apex_Animations_prefersReducedMotion;
67
+ const BrowserAPIs = ApexCharts.__apex_BrowserAPIs_BrowserAPIs;
68
+ const Environment = ApexCharts.__apex_Environment_Environment;
69
+ const resolveEasing = ApexCharts.__apex_Easing_resolveEasing;
70
+ const parsePath = ApexCharts.__apex_PathMorphing_parsePath;
71
+ const arrayToPath = ApexCharts.__apex_PathMorphing_arrayToPath;
72
+ function buildUnionEntries(join, oldN) {
73
+ const exitSet = new Set(join.exits);
74
+ const entries = [];
75
+ let oi = 0;
76
+ for (let nj = 0; nj < join.toOld.length; nj++) {
77
+ const oj = join.toOld[nj];
78
+ if (oj !== -1) {
79
+ while (oi < oj) {
80
+ if (exitSet.has(oi)) entries.push({ oldJ: oi, newJ: -1 });
81
+ oi++;
82
+ }
83
+ entries.push({ oldJ: oj, newJ: nj });
84
+ oi = oj + 1;
85
+ } else {
86
+ entries.push({ oldJ: -1, newJ: nj });
87
+ }
88
+ }
89
+ while (oi < oldN) {
90
+ if (exitSet.has(oi)) entries.push({ oldJ: oi, newJ: -1 });
91
+ oi++;
92
+ }
93
+ return entries;
94
+ }
95
+ function analyzeSeriesPath(d, expectedAnchors, isArea) {
96
+ if (!d || typeof d !== "string") return null;
97
+ const cmds = parsePath(d);
98
+ if (!cmds.length || cmds[0][0] !== "M") return null;
99
+ for (let i2 = 1; i2 < cmds.length; i2++) {
100
+ if (cmds[i2][0] === "M") return null;
101
+ }
102
+ let body = cmds;
103
+ let closing = null;
104
+ if (isArea) {
105
+ if (cmds.length < 5) return null;
106
+ closing = cmds.slice(-3);
107
+ body = cmds.slice(0, -3);
108
+ if (closing[2][0] !== "Z") return null;
109
+ if (closing[1][0] !== "L") return null;
110
+ if (closing[0][0] !== "L" && closing[0][0] !== "C") return null;
111
+ } else if (cmds[cmds.length - 1][0] === "Z") {
112
+ return null;
113
+ }
114
+ if (body.length !== expectedAnchors || body.length < 2) return null;
115
+ const segType = body[1][0];
116
+ if (segType !== "L" && segType !== "C") return null;
117
+ for (let i2 = 2; i2 < body.length; i2++) {
118
+ if (body[i2][0] !== segType) return null;
119
+ }
120
+ const anchors = body.map(
121
+ (c) => c[0] === "C" ? [Number(c[5]), Number(c[6])] : [Number(c[1]), Number(c[2])]
122
+ );
123
+ for (const a2 of anchors) {
124
+ if (!isFinite(a2[0]) || !isFinite(a2[1])) return null;
125
+ }
126
+ return { body, closing, segType, anchors };
127
+ }
128
+ function lerpPt(a2, b, t2) {
129
+ return [a2[0] + (b[0] - a2[0]) * t2, a2[1] + (b[1] - a2[1]) * t2];
130
+ }
131
+ function splitCubic(s2, cmd, t2) {
132
+ const p0 = s2;
133
+ const p1 = [cmd[1], cmd[2]];
134
+ const p2 = [cmd[3], cmd[4]];
135
+ const p3 = [cmd[5], cmd[6]];
136
+ const p01 = lerpPt(p0, p1, t2);
137
+ const p12 = lerpPt(p1, p2, t2);
138
+ const p23 = lerpPt(p2, p3, t2);
139
+ const p012 = lerpPt(p01, p12, t2);
140
+ const p123 = lerpPt(p12, p23, t2);
141
+ const mid = lerpPt(p012, p123, t2);
142
+ return {
143
+ first: ["C", p01[0], p01[1], p012[0], p012[1], mid[0], mid[1]],
144
+ second: ["C", p123[0], p123[1], p23[0], p23[1], p3[0], p3[1]],
145
+ mid
146
+ };
147
+ }
148
+ function splitLine(s2, cmd, t2) {
149
+ const e2 = [cmd[1], cmd[2]];
150
+ const mid = lerpPt(s2, e2, t2);
151
+ return {
152
+ first: ["L", mid[0], mid[1]],
153
+ second: ["L", e2[0], e2[1]],
154
+ mid
155
+ };
156
+ }
157
+ function expandPath(analysis, ownIdx) {
158
+ const { body, closing, segType, anchors } = analysis;
159
+ const n2 = anchors.length;
160
+ const m = ownIdx.length;
161
+ const degen = (p) => segType === "C" ? ["C", p[0], p[1], p[0], p[1], p[0], p[1]] : ["L", p[0], p[1]];
162
+ const out = [["M", anchors[0][0], anchors[0][1]]];
163
+ let firstOwn = 0;
164
+ while (firstOwn < m && ownIdx[firstOwn] !== 0) firstOwn++;
165
+ for (let q2 = 1; q2 <= firstOwn; q2++) out.push(degen(anchors[0]));
166
+ let entry = firstOwn + 1;
167
+ for (let s2 = 0; s2 < n2 - 1; s2++) {
168
+ let interior = 0;
169
+ while (entry + interior < m && ownIdx[entry + interior] === -1) interior++;
170
+ const cmd = body[s2 + 1];
171
+ if (!interior) {
172
+ out.push(cmd.slice());
173
+ } else {
174
+ const totalParts = interior + 1;
175
+ let start = anchors[s2];
176
+ let rest = cmd;
177
+ for (let q2 = 0; q2 < interior; q2++) {
178
+ const t2 = 1 / (totalParts - q2);
179
+ const sp = segType === "C" ? splitCubic(start, rest, t2) : splitLine(start, rest, t2);
180
+ out.push(sp.first);
181
+ start = sp.mid;
182
+ rest = sp.second;
183
+ }
184
+ out.push(rest);
185
+ }
186
+ entry += interior + 1;
187
+ }
188
+ while (entry < m) {
189
+ out.push(degen(anchors[n2 - 1]));
190
+ entry++;
191
+ }
192
+ if (closing) closing.forEach((c) => out.push(c.slice()));
193
+ return out;
194
+ }
195
+ function reconcilePathPair(fromD, toD, entries, oldN, newN, isArea) {
196
+ const fromAnalysis = analyzeSeriesPath(fromD, oldN, isArea);
197
+ if (!fromAnalysis) return null;
198
+ const toAnalysis = analyzeSeriesPath(toD, newN, isArea);
199
+ if (!toAnalysis) return null;
200
+ if (fromAnalysis.segType !== toAnalysis.segType) return null;
201
+ if ((fromAnalysis.closing || toAnalysis.closing) && (!fromAnalysis.closing || !toAnalysis.closing || fromAnalysis.closing[0][0] !== toAnalysis.closing[0][0])) {
202
+ return null;
203
+ }
204
+ const fromOwn = entries.map((e2) => e2.oldJ);
205
+ const toOwn = entries.map((e2) => e2.newJ);
206
+ return {
207
+ from: arrayToPath(expandPath(fromAnalysis, fromOwn)),
208
+ toInterp: arrayToPath(expandPath(toAnalysis, toOwn))
209
+ };
210
+ }
211
+ function lengthTransitionEnabled(w) {
212
+ var _a;
213
+ const anim = w.config.chart.animations;
214
+ if (!anim || anim.enabled === false) return false;
215
+ if (!anim.dynamicAnimation || anim.dynamicAnimation.enabled === false) {
216
+ return false;
217
+ }
218
+ const largeThreshold = (_a = anim.largeDatasetThreshold) != null ? _a : 0;
219
+ if (largeThreshold > 0 && w.globals.dataPoints > largeThreshold) return false;
220
+ return !!(Environment.isBrowser() && w.globals.dataChanged && w.globals.shouldAnimate);
221
+ }
222
+ function datumKey(w, realIndex, j2) {
223
+ var _a, _b, _c, _d;
224
+ if ((_a = w.axisFlags) == null ? void 0 : _a.isXNumeric) {
225
+ const sx = (_c = (_b = w.seriesData) == null ? void 0 : _b.seriesX) == null ? void 0 : _c[realIndex];
226
+ if (sx && sx.length && sx[j2] != null) return "x:" + sx[j2];
227
+ }
228
+ const lbl = (_d = w.globals.labels) == null ? void 0 : _d[j2];
229
+ if (lbl != null && String(lbl) !== "") {
230
+ return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl));
231
+ }
232
+ return "j:" + j2;
233
+ }
234
+ function frameDatumKey(frame, realIndex, j2) {
235
+ var _a, _b;
236
+ if (frame.isXNumeric) {
237
+ const sx = (_a = frame.seriesX) == null ? void 0 : _a[realIndex];
238
+ if (sx && sx.length && sx[j2] != null) return "x:" + sx[j2];
239
+ }
240
+ const lbl = (_b = frame.labels) == null ? void 0 : _b[j2];
241
+ if (lbl != null && String(lbl) !== "") {
242
+ return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl));
243
+ }
244
+ return "j:" + j2;
245
+ }
246
+ function joinKeys(oldKeys, newKeys) {
247
+ const oldIndex = /* @__PURE__ */ new Map();
248
+ oldKeys.forEach((k2, i2) => {
249
+ if (!oldIndex.has(k2)) oldIndex.set(k2, i2);
250
+ });
251
+ const toOld = new Array(newKeys.length);
252
+ const usedOld = /* @__PURE__ */ new Set();
253
+ let prev = -1;
254
+ let ordered = true;
255
+ let identity = oldKeys.length === newKeys.length;
256
+ newKeys.forEach((k2, i2) => {
257
+ const oi = oldIndex.has(k2) && !usedOld.has(oldIndex.get(k2)) ? oldIndex.get(k2) : -1;
258
+ toOld[i2] = oi;
259
+ if (oi !== -1) {
260
+ usedOld.add(oi);
261
+ if (oi < prev) ordered = false;
262
+ prev = oi;
263
+ }
264
+ if (oi !== i2) identity = false;
265
+ });
266
+ const exits = [];
267
+ for (let i2 = 0; i2 < oldKeys.length; i2++) {
268
+ if (!usedOld.has(i2)) exits.push(i2);
269
+ }
270
+ return { toOld, exits, ordered, changed: !identity };
271
+ }
272
+ function uniquifyKeys(keys) {
273
+ const seen = /* @__PURE__ */ new Map();
274
+ return keys.map((k2) => {
275
+ const count = seen.get(k2) || 0;
276
+ seen.set(k2, count + 1);
277
+ return count === 0 ? k2 : `${k2}#${count}`;
278
+ });
279
+ }
280
+ function seriesKeys(w, realIndex, n2 = ((_a) => (_a = w.seriesData.series[realIndex]) == null ? void 0 : _a.length)() | 0) {
281
+ return uniquifyKeys(
282
+ Array.from({ length: n2 }, (_2, j2) => datumKey(w, realIndex, j2))
283
+ );
284
+ }
285
+ function seriesJoin(w, realIndex, includeIdentity = false, allowReorder = false) {
286
+ var _a, _b;
287
+ if (!lengthTransitionEnabled(w)) return null;
288
+ const frame = w.globals.prevStreamFrame;
289
+ if (!frame) return null;
290
+ const oldY = (_a = frame.seriesY) == null ? void 0 : _a[realIndex];
291
+ const newY = (_b = w.seriesData.series) == null ? void 0 : _b[realIndex];
292
+ if (!Array.isArray(oldY) || !Array.isArray(newY)) return null;
293
+ if (!oldY.length || !newY.length) return null;
294
+ const oldKeys = uniquifyKeys(
295
+ oldY.map((_2, j2) => frameDatumKey(frame, realIndex, j2))
296
+ );
297
+ const newKeys = seriesKeys(w, realIndex);
298
+ const join = joinKeys(oldKeys, newKeys);
299
+ if (!join.ordered && !allowReorder) return null;
300
+ if (!join.changed && !includeIdentity) return null;
301
+ return { join, oldKeys, newKeys };
302
+ }
303
+ function morphEasing(w) {
304
+ var _a, _b;
305
+ const anim = w.config.chart.animations;
306
+ return resolveEasing((_b = (_a = anim.dynamicAnimation) == null ? void 0 : _a.easing) != null ? _b : anim.easing);
307
+ }
308
+ function reconcileSeriesPaths(w, { type, realIndex, pathFromLine, pathFromArea, linePaths, areaPaths }) {
309
+ var _a, _b;
310
+ const sj = seriesJoin(w, realIndex);
311
+ if (!sj) return null;
312
+ const { join, oldKeys, newKeys } = sj;
313
+ const frame = w.globals.prevStreamFrame;
314
+ if (!frame) return null;
315
+ const oldY = (_a = frame.seriesY) == null ? void 0 : _a[realIndex];
316
+ const newY = (_b = w.seriesData.series) == null ? void 0 : _b[realIndex];
317
+ if (oldY.length < 2 || newY.length < 2) return null;
318
+ if (oldY.some((v) => v === null) || newY.some((v) => v === null)) return null;
319
+ const entries = buildUnionEntries(join, oldKeys.length);
320
+ const out = {};
321
+ if (Array.isArray(linePaths) && linePaths.length === 1 && pathFromLine) {
322
+ out.line = reconcilePathPair(
323
+ pathFromLine,
324
+ linePaths[0],
325
+ entries,
326
+ oldKeys.length,
327
+ newKeys.length,
328
+ false
329
+ );
330
+ }
331
+ if (type === "area" && Array.isArray(areaPaths) && areaPaths.length === 1 && pathFromArea) {
332
+ out.area = reconcilePathPair(
333
+ pathFromArea,
334
+ areaPaths[0],
335
+ entries,
336
+ oldKeys.length,
337
+ newKeys.length,
338
+ true
339
+ );
340
+ }
341
+ if (!out.line && !out.area) return null;
342
+ return out;
343
+ }
65
344
  const Utils = ApexCharts.__apex_Utils;
66
345
  const tangents = (points) => {
67
346
  const m = finiteDifferences(points);
@@ -173,9 +452,20 @@ function finiteDifferences(points) {
173
452
  m[i2] = d;
174
453
  return m;
175
454
  }
455
+ function jitterExtent(pool, w) {
456
+ let lo = Infinity;
457
+ let hi = -Infinity;
458
+ pool.slice(0, w.seriesData.series.length).flat(2).forEach((v) => {
459
+ if (v === +v) {
460
+ if (v < lo) lo = v;
461
+ if (v > hi) hi = v;
462
+ }
463
+ });
464
+ return [lo, hi];
465
+ }
176
466
  function buildJitterGroups({
177
- w,
178
467
  points,
468
+ extent,
179
469
  seedA,
180
470
  seedB,
181
471
  center,
@@ -186,6 +476,7 @@ function buildJitterGroups({
186
476
  clampAt,
187
477
  sideSign
188
478
  }) {
479
+ var _a, _b;
189
480
  const opts = options;
190
481
  if (!opts || opts.show === false) return [];
191
482
  if (!points || !points.length) return [];
@@ -199,8 +490,8 @@ function buildJitterGroups({
199
490
  const scale = opts.colorScale;
200
491
  const useScale = scale && Array.isArray(scale.colors) && scale.colors.length > 0;
201
492
  const steps = useScale ? Math.max(2, scale.steps || 24) : 1;
202
- const sMin = useScale && scale.min != null ? scale.min : w.globals.minY;
203
- const sMax = useScale && scale.max != null ? scale.max : w.globals.maxY;
493
+ const sMin = (_a = scale == null ? void 0 : scale.min) != null ? _a : extent[0];
494
+ const sMax = (_b = scale == null ? void 0 : scale.max) != null ? _b : extent[1];
204
495
  const span = sMax - sMin || 1;
205
496
  const buckets = useScale ? new Array(steps).fill("") : [""];
206
497
  for (let k2 = 0; k2 < points.length; k2 += stride) {
@@ -286,7 +577,7 @@ function renderJitter({
286
577
  elSeries.add(elPointsWrap);
287
578
  }
288
579
  function darkenColor(color, amount) {
289
- const rgb = Utils.parseHex(color);
580
+ const rgb = Utils.parseColor(color) || Utils.parseColor(Utils.resolveColor(color));
290
581
  if (!rgb) return color;
291
582
  const f = Math.max(0, 1 - amount);
292
583
  return `rgb(${Math.round(rgb[0] * f)},${Math.round(rgb[1] * f)},${Math.round(rgb[2] * f)})`;
@@ -343,6 +634,7 @@ class Violin extends BarBase {
343
634
  const fill = new Fill(this.w);
344
635
  this.violinOptions = w.config.plotOptions.violin;
345
636
  this.pointsOptions = this.violinOptions.points;
637
+ this.jx = jitterExtent(w.violinData.seriesViolinPoints, w);
346
638
  this.boxOptions = this.violinOptions.box || {};
347
639
  this.cloudSign = crossSign(this.violinOptions.side);
348
640
  this.rainSign = this.pointsOptions.show === false ? 0 : crossSign(this.pointsOptions.position);
@@ -357,7 +649,7 @@ class Violin extends BarBase {
357
649
  series = this.coreUtils.getLogSeries(series);
358
650
  this.series = series;
359
651
  this.yRatio = this.coreUtils.getLogYRatios(this.yRatio);
360
- this.barHelpers.initVariables(series);
652
+ this.barHelpers.initVariables(series, seriesIndex);
361
653
  const ret = graphics.group({
362
654
  class: "apexcharts-violin-series apexcharts-plot-series"
363
655
  });
@@ -504,7 +796,8 @@ class Violin extends BarBase {
504
796
  elDataLabelsWrap,
505
797
  visibleSeries: this.visibleI,
506
798
  type: "violin",
507
- classes: "apexcharts-raincloud-box"
799
+ classes: "apexcharts-raincloud-box",
800
+ noLabel: true
508
801
  });
509
802
  });
510
803
  }
@@ -541,11 +834,16 @@ class Violin extends BarBase {
541
834
  var _a, _b, _c, _d, _e, _f, _g;
542
835
  const w = this.w;
543
836
  const { realIndex, j: j2, translationsIndex } = indexes;
544
- const yRatio = this.yRatio[translationsIndex];
837
+ const yRatio = this.yRatio[translationsIndex] * (this.isReversed ? -1 : 1);
838
+ let barXPosition = x2 + barWidth * this.visibleI;
545
839
  if (w.axisFlags.isXNumeric) {
546
- x2 = (w.seriesData.seriesX[realIndex][j2] - w.globals.minX) / this.xRatio - barWidth / 2;
840
+ ({ x: x2, barXPosition } = this.getBarXForNumericXAxis({
841
+ x: x2,
842
+ barWidth,
843
+ realIndex,
844
+ j: j2
845
+ }));
547
846
  }
548
- const barXPosition = x2 + barWidth * this.visibleI;
549
847
  const center = barXPosition + barWidth / 2;
550
848
  const halfExtent = barWidth / 2;
551
849
  const density = this.getDensity(realIndex, j2);
@@ -574,7 +872,7 @@ class Violin extends BarBase {
574
872
  if (morphFrom) {
575
873
  pathFrom = morphFrom;
576
874
  } else if (w.globals.previousPaths.length > 0) {
577
- pathFrom = this.getPreviousPath(realIndex, j2, pathTo);
875
+ pathFrom = this._prevBody(realIndex, j2, pathTo);
578
876
  }
579
877
  if (pathFrom == null) {
580
878
  pathFrom = this.buildBodyPath({
@@ -613,7 +911,7 @@ class Violin extends BarBase {
613
911
  alongFn,
614
912
  vertical: true
615
913
  }),
616
- alongRepresentative: alongFn((_g = this.series[indexes.i][j2]) != null ? _g : 0)
914
+ alongRepresentative: alongFn((_g = w.seriesData.series[realIndex][j2]) != null ? _g : 0)
617
915
  };
618
916
  }
619
917
  /** @param {{indexes: any, y: any, yDivision: any, barHeight: any, zeroW: any}} opts */
@@ -621,7 +919,7 @@ class Violin extends BarBase {
621
919
  var _a, _b, _c, _d, _e, _f, _g;
622
920
  const w = this.w;
623
921
  const { realIndex, j: j2 } = indexes;
624
- const yRatio = this.invertedYRatio;
922
+ const yRatio = this.invertedYRatio * (this.isReversed ? -1 : 1);
625
923
  const barYPosition = y + barHeight * this.visibleI;
626
924
  const center = barYPosition + barHeight / 2;
627
925
  const halfExtent = barHeight / 2;
@@ -651,7 +949,7 @@ class Violin extends BarBase {
651
949
  if (morphFrom) {
652
950
  pathFrom = morphFrom;
653
951
  } else if (w.globals.previousPaths.length > 0) {
654
- pathFrom = this.getPreviousPath(realIndex, j2, pathTo);
952
+ pathFrom = this._prevBody(realIndex, j2, pathTo);
655
953
  }
656
954
  if (pathFrom == null) {
657
955
  pathFrom = this.buildBodyPath({
@@ -688,7 +986,7 @@ class Violin extends BarBase {
688
986
  alongFn,
689
987
  vertical: false
690
988
  }),
691
- alongRepresentative: alongFn((_g = this.series[indexes.i][j2]) != null ? _g : 0)
989
+ alongRepresentative: alongFn((_g = w.seriesData.series[realIndex][j2]) != null ? _g : 0)
692
990
  };
693
991
  }
694
992
  /**
@@ -779,6 +1077,23 @@ class Violin extends BarBase {
779
1077
  leftPts.reverse();
780
1078
  return this.smoothSegment(rightPts, vertical, false) + this.smoothSegment(leftPts, vertical, true) + "z";
781
1079
  }
1080
+ /**
1081
+ * The survivor's previous body when it can morph to `pathTo`, else null so
1082
+ * the violin regrows from its centreline (a changed density node count
1083
+ * cannot tween, and bar corner reconciliation does not apply here).
1084
+ *
1085
+ * @param {number} realIndex
1086
+ * @param {number} j
1087
+ * @param {string} pathTo
1088
+ * @returns {string | null}
1089
+ */
1090
+ _prevBody(realIndex, j2, pathTo) {
1091
+ var _a, _b;
1092
+ const d = (_b = (_a = this._prevKeyedPaths(realIndex)) == null ? void 0 : _a.get(
1093
+ datumKey(this.w, realIndex, j2)
1094
+ )) == null ? void 0 : _b.d;
1095
+ return d && Violin.pathCommandCount(d) === Violin.pathCommandCount(pathTo) ? d : null;
1096
+ }
782
1097
  /**
783
1098
  * Build the five-number box sub-paths for one category — the raincloud
784
1099
  * "umbrella", or a violin box overlay. Two sub-paths so each can carry its
@@ -796,7 +1111,7 @@ class Violin extends BarBase {
796
1111
  * @returns {{pathTo:string, pathFrom:string, filled:boolean}[] | null}
797
1112
  */
798
1113
  buildBoxSubPaths({ realIndex, j: j2, boxCenter, boxHalfPx, alongFn, vertical }) {
799
- var _a, _b;
1114
+ var _a, _b, _c;
800
1115
  const w = this.w;
801
1116
  if (!this.boxShown || boxHalfPx <= 0) return null;
802
1117
  const summary = (_a = w.violinData.seriesViolinSummary[realIndex]) == null ? void 0 : _a[j2];
@@ -821,22 +1136,17 @@ class Violin extends BarBase {
821
1136
  };
822
1137
  const full = build2(capHalf, boxHalfPx);
823
1138
  const collapsed = build2(0, 0);
824
- const fromFor = (pathTo, collapsedFrom) => {
825
- let pathFrom = null;
826
- if (w.globals.previousPaths.length > 0) {
827
- pathFrom = this.getPreviousPath(realIndex, j2, pathTo);
828
- }
829
- return pathFrom == null ? collapsedFrom : pathFrom;
830
- };
1139
+ const keyed = w.globals.previousPaths.length ? this._prevKeyedPaths(realIndex) : null;
1140
+ const prevBox = ((_c = keyed == null ? void 0 : keyed.get(datumKey(w, realIndex, j2))) == null ? void 0 : _c.box) || [];
831
1141
  return [
832
1142
  {
833
1143
  pathTo: full.whiskers,
834
- pathFrom: fromFor(full.whiskers, collapsed.whiskers),
1144
+ pathFrom: prevBox[0] || collapsed.whiskers,
835
1145
  filled: false
836
1146
  },
837
1147
  {
838
1148
  pathTo: full.box,
839
- pathFrom: fromFor(full.box, collapsed.box),
1149
+ pathFrom: prevBox[1] || collapsed.box,
840
1150
  filled: true
841
1151
  }
842
1152
  ];
@@ -912,8 +1222,11 @@ class Violin extends BarBase {
912
1222
  const scatterCenter = offsetLane ? rainCenter : this.cloudSign !== 0 ? cloudBase : center;
913
1223
  const scatterHalf = offsetLane ? rainHalfPx : this.cloudSign !== 0 ? cloudMaxPx : halfExtent;
914
1224
  return buildJitterGroups({
915
- w: this.w,
916
1225
  points: (_a = this.w.violinData.seriesViolinPoints[realIndex]) == null ? void 0 : _a[j2],
1226
+ extent: (
1227
+ /** @type {number[]} */
1228
+ this.jx
1229
+ ),
917
1230
  seedA: realIndex,
918
1231
  seedB: j2,
919
1232
  center: scatterCenter,
@@ -1707,10 +2020,6 @@ function getUnitMark(name) {
1707
2020
  if (!name) return null;
1708
2021
  return getMarks()[name] || null;
1709
2022
  }
1710
- const Environment = ApexCharts.__apex_Environment_Environment;
1711
- const BrowserAPIs = ApexCharts.__apex_BrowserAPIs_BrowserAPIs;
1712
- const Animations = ApexCharts.__apex_Animations;
1713
- const prefersReducedMotion = ApexCharts.__apex_Animations_prefersReducedMotion;
1714
2023
  function drawOuterLabel(w, spec) {
1715
2024
  const {
1716
2025
  lines,
@@ -1813,6 +2122,15 @@ function spaceOutLabels(items, minGap, maxY, minY) {
1813
2122
  }
1814
2123
  }
1815
2124
  const GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
2125
+ const uniqueId = (seen, id) => {
2126
+ const n2 = (seen.get(id) || 0) + 1;
2127
+ seen.set(id, n2);
2128
+ return n2 > 1 ? id + "#" + n2 : id;
2129
+ };
2130
+ const toNum = (v) => {
2131
+ if (typeof v === "string") v = parseFloat(v);
2132
+ return typeof v === "number" && isFinite(v) ? v : null;
2133
+ };
1816
2134
  function easeOutCubic(t2) {
1817
2135
  return 1 - Math.pow(1 - t2, 3);
1818
2136
  }
@@ -1858,6 +2176,9 @@ class Unit {
1858
2176
  const w = this.w;
1859
2177
  const graphics = new Graphics(w, this.ctx);
1860
2178
  const ret = graphics.group({ class: "apexcharts-unit" });
2179
+ BrowserAPIs.cancelAnimationFrame(w.globals.unitGatherRAF || 0);
2180
+ BrowserAPIs.cancelAnimationFrame(w.globals.unitExitRAF || 0);
2181
+ w.globals.unitGatherRAF = w.globals.unitExitRAF = null;
1861
2182
  if (w.globals.noData || !Array.isArray(series) || series.length === 0) {
1862
2183
  return ret;
1863
2184
  }
@@ -1867,15 +2188,16 @@ class Unit {
1867
2188
  const flow = transition === "flow";
1868
2189
  const identity = transition === "identity";
1869
2190
  const unitValue = opts.unitValue > 0 ? opts.unitValue : 1;
2191
+ const gridSplit = layout === "grid" && !!(opts.grid && opts.grid.split);
1870
2192
  let counts = series.map((v) => {
1871
2193
  const n2 = Math.abs(Utils.parseNumber(v)) / unitValue;
1872
- return n2 > 0 ? Math.max(1, Math.round(n2)) : 0;
2194
+ return gridSplit ? n2 : n2 > 0 ? Math.max(1, Math.round(n2)) : 0;
1873
2195
  });
1874
- counts = this._applyMaxUnits(counts, opts.maxUnits);
2196
+ if (!gridSplit && layout !== "scatter") {
2197
+ counts = this._applyMaxUnits(counts, opts.maxUnits);
2198
+ }
1875
2199
  const total = counts.reduce((a2, b) => a2 + b, 0);
1876
2200
  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);
1877
- const gridSplit = layout === "grid" && !!(opts.grid && opts.grid.split);
1878
- if (gridSplit) this._drawGridTrack(ret, graphics, opts);
1879
2201
  if (layout === "scatter") this._drawScatterAxes(ret, graphics);
1880
2202
  const dotR = this._lastDotR;
1881
2203
  const animate = this._shouldAnimate();
@@ -1906,7 +2228,49 @@ class Unit {
1906
2228
  const unitData = w.seriesData.unitData || [];
1907
2229
  const sizeStats = this._bubbleStats(unitData, opts, dotR);
1908
2230
  let gIndex = 0;
1909
- clusters.forEach((cluster) => {
2231
+ const seen = /* @__PURE__ */ new Map();
2232
+ const track = gridSplit && this._gridTrack && this._gridTrack.cells || [];
2233
+ const trackG = graphics.group({ class: "apexcharts-unit-track" });
2234
+ if (track.length) ret.add(trackG);
2235
+ const trackFill = opts.grid && opts.grid.trackColor || "rgba(128,128,128,0.14)";
2236
+ const trackSpec = this._baseSpec({ shape: opts.shape === "square" && "square" }, dotR);
2237
+ const perTile = track.length / Math.max(1, clusters.length);
2238
+ const live = this.ctx && this.ctx._unitSprings;
2239
+ const glide = (node, spec, x2, y, key, r2, at, pair) => {
2240
+ const from = prev && prev.get(key);
2241
+ const s2 = live && live.get(key);
2242
+ const still = from && from.x === x2 && from.y === y && from.r === r2 && (!s2 || s2.x.value === x2 && s2.y.value === y);
2243
+ nextPrev.set(key, { x: x2, y, fill: trackFill, r: r2, spec });
2244
+ if (prev && animate && !pieceTakeover && !still) {
2245
+ const cx0 = from ? from.x : x2;
2246
+ const cy0 = from ? from.y : y;
2247
+ node.style.opacity = from ? "1" : "0";
2248
+ this._place(node, spec, cx0, cy0);
2249
+ animDots.splice(at, 0, {
2250
+ node,
2251
+ spec,
2252
+ x: x2,
2253
+ y,
2254
+ cx0,
2255
+ cy0,
2256
+ key,
2257
+ r0: from && from.r != null ? from.r : r2,
2258
+ r1: r2,
2259
+ delay: 0,
2260
+ isEnter: !from,
2261
+ pair
2262
+ });
2263
+ } else {
2264
+ this._place(node, spec, x2, y);
2265
+ }
2266
+ };
2267
+ const ride2 = (k2, pair) => {
2268
+ const el = this._drawDot(graphics, opts, dotR, trackFill, -1, 0, trackSpec);
2269
+ el.node.classList.add("apexcharts-unit-track-cell");
2270
+ trackG.add(el);
2271
+ glide(el.node, trackSpec, track[k2].x, track[k2].y, "trk:" + k2, dotR, animDots.length, pair);
2272
+ };
2273
+ clusters.forEach((cluster, ci) => {
1910
2274
  const color = w.globals.colors[cluster.i] || w.globals.colors[0] || "#008FFB";
1911
2275
  const elSeries = graphics.group({
1912
2276
  class: "apexcharts-series",
@@ -1918,7 +2282,9 @@ class Unit {
1918
2282
  });
1919
2283
  const burstCount = cluster.dots.length;
1920
2284
  const catData = unitData[cluster.i];
2285
+ const at = animDots.length;
1921
2286
  cluster.dots.forEach((d, jj) => {
2287
+ if (track.length) ride2(ci * perTile + jj, true);
1922
2288
  const j2 = d.j != null ? d.j : jj;
1923
2289
  const datum = catData ? catData[j2] : void 0;
1924
2290
  const dotFill = datum && typeof datum === "object" && datum.fillColor ? datum.fillColor : color;
@@ -1929,7 +2295,7 @@ class Unit {
1929
2295
  let key;
1930
2296
  if (identity) {
1931
2297
  const id = datum && typeof datum === "object" ? datum.id != null ? datum.id : datum.name : void 0;
1932
- key = id != null ? `id:${id}` : `g:${gIndex}`;
2298
+ key = id != null ? `id:${uniqueId(seen, String(id))}` : `g:${gIndex}`;
1933
2299
  } else if (flow) {
1934
2300
  key = String(gIndex);
1935
2301
  } else if (d.slot != null) {
@@ -1949,8 +2315,9 @@ class Unit {
1949
2315
  const anchor = from || (morphActive ? morph.getInitialSlotFor(cluster.i, j2, burstCount) : null);
1950
2316
  const enter = opts.gather && opts.gather.enter || "burst";
1951
2317
  const inPlace = gridSplit || enter === "fade" || enter === "rise";
1952
- const cx0 = anchor ? anchor.x : inPlace ? d.x : cluster.cx;
1953
- const cy0 = anchor ? anchor.y : enter === "rise" && !gridSplit ? d.y + 14 : inPlace ? d.y : cluster.cy;
2318
+ const start = anchor || gridSplit && prev && prev.get("trk:" + d.slot);
2319
+ const cx0 = start ? start.x : inPlace ? d.x : cluster.cx;
2320
+ const cy0 = start ? start.y : enter === "rise" && !gridSplit ? d.y + 14 : inPlace ? d.y : cluster.cy;
1954
2321
  el.node.style.opacity = anchor ? "1" : "0";
1955
2322
  this._place(el.node, spec, cx0, cy0);
1956
2323
  animDots.push({
@@ -1980,9 +2347,16 @@ class Unit {
1980
2347
  this._place(el.node, spec, d.x, d.y);
1981
2348
  }
1982
2349
  });
2350
+ for (let k2 = cluster.dots.length; k2 < perTile; k2++) ride2(ci * perTile + k2, false);
1983
2351
  if ((layout === "grouped" || layout === "columns" || gridSplit) && opts.clusterLabels && opts.clusterLabels.show && counts[cluster.i] > 0) {
1984
2352
  const labelTotal = gridSplit ? this._gridDenom : total;
1985
- this._drawClusterLabel(elSeries, cluster, counts[cluster.i], labelTotal, opts, color);
2353
+ const t2 = this._drawClusterLabel(elSeries, cluster, counts[cluster.i], labelTotal, opts, color);
2354
+ if (t2 && gridSplit) {
2355
+ const spec = { pk: PK_CORNER, hx: 0, hy: 0, r: 0 };
2356
+ const lx = +(t2.getAttribute("x") || 0);
2357
+ const ly = +(t2.getAttribute("y") || 0);
2358
+ glide(t2, spec, lx, ly, "lbl:" + cluster.i, 0, at, animDots.length > at);
2359
+ }
1986
2360
  }
1987
2361
  ret.add(elSeries);
1988
2362
  });
@@ -2017,9 +2391,13 @@ class Unit {
2017
2391
  const total = counts.reduce((a2, b) => a2 + b, 0);
2018
2392
  if (!maxUnits || maxUnits <= 0 || total <= maxUnits) return counts;
2019
2393
  const scale = maxUnits / total;
2020
- console.warn(
2021
- `[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.`
2022
- );
2394
+ const ctx = this.ctx || {};
2395
+ if (!ctx._unitCapWarned) {
2396
+ ctx._unitCapWarned = true;
2397
+ console.warn(
2398
+ `[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.`
2399
+ );
2400
+ }
2023
2401
  return counts.map((c) => c > 0 ? Math.max(1, Math.round(c * scale)) : 0);
2024
2402
  }
2025
2403
  /**
@@ -2129,7 +2507,8 @@ class Unit {
2129
2507
  *
2130
2508
  * `id` is the datum's own id/name where the per-unit object form supplies
2131
2509
  * one, so a provider can address a specific unit ("Texas", "employee 41")
2132
- * rather than a positional slot. It falls back to `"<category>:<index>"`.
2510
+ * rather than a positional slot. It falls back to `"<category>:<index>"`, and
2511
+ * a repeated one is suffixed (`"Alex#2"`).
2133
2512
  *
2134
2513
  * @param {number[]} counts
2135
2514
  * @param {number} dotR the radius the engine would use, so a provider that
@@ -2141,13 +2520,17 @@ class Unit {
2141
2520
  const unitData = w.seriesData.unitData || [];
2142
2521
  const names = w.seriesData.seriesNames || [];
2143
2522
  const objects = [];
2523
+ const seen = /* @__PURE__ */ new Map();
2144
2524
  let index = 0;
2145
2525
  counts.forEach((n2, i2) => {
2146
2526
  var _a;
2147
2527
  const catData = unitData[i2];
2148
2528
  for (let j2 = 0; j2 < n2; j2++) {
2149
2529
  const datum = catData ? catData[j2] : void 0;
2150
- const id = datum && typeof datum === "object" && (datum.id != null || datum.name != null) ? String(datum.id != null ? datum.id : datum.name) : `${i2}:${j2}`;
2530
+ const id = uniqueId(
2531
+ seen,
2532
+ datum && typeof datum === "object" && (datum.id != null || datum.name != null) ? String(datum.id != null ? datum.id : datum.name) : `${i2}:${j2}`
2533
+ );
2151
2534
  objects.push({
2152
2535
  id,
2153
2536
  index,
@@ -2331,11 +2714,18 @@ class Unit {
2331
2714
  const pad = Math.min(gw, gh) * 0.04;
2332
2715
  const boxW = Math.max(1e-6, b.maxX - b.minX);
2333
2716
  const boxH = Math.max(1e-6, b.maxY - b.minY);
2334
- const r1 = Math.max(4, Math.min((gw - 2 * pad) / boxW, (gh - 2 * pad) / boxH));
2335
- const r0 = r1 * innerRatio;
2717
+ const fit = (inset) => Math.max(4, Math.min((gw - 2 * pad - inset) / boxW, (gh - 2 * pad - inset) / boxH));
2718
+ const seat = (r2) => fit(2 * this._arcAllocate(total, r2 * innerRatio, r2, span, opts).dotR);
2719
+ let hi = fit(0);
2720
+ let r1 = seat(hi);
2721
+ for (let k2 = 0; k2 < 6; k2++) {
2722
+ const m = (r1 + hi) / 2;
2723
+ if (m <= seat(m)) r1 = m;
2724
+ else hi = m;
2725
+ }
2336
2726
  const cx = gw / 2 - (b.minX + b.maxX) / 2 * r1;
2337
2727
  const cy = gh / 2 - (b.minY + b.maxY) / 2 * r1;
2338
- const alloc = this._arcAllocate(total, r0, r1, span, opts);
2728
+ const alloc = this._arcAllocate(total, r1 * innerRatio, r1, span, opts);
2339
2729
  this._lastDotR = alloc.dotR;
2340
2730
  const seats = [];
2341
2731
  for (let r2 = 0; r2 < alloc.R; r2++) {
@@ -2619,7 +3009,7 @@ class Unit {
2619
3009
  * value over a denominator (`grid.max`, else the largest count so the leader
2620
3010
  * fills its tile and every other tile stays proportionally full - no empty
2621
3011
  * tiles for arbitrary data). The unfilled cells are drawn as a faint TRACK
2622
- * backdrop (see _drawGridTrack) so each tile reads as a part-to-whole "of N".
3012
+ * backdrop (drawn by draw()) so each tile reads as a part-to-whole "of N".
2623
3013
  * Only VISIBLE (non-zero) categories claim a tile, so a legend hide drops the
2624
3014
  * tile and the rest re-flow. Each filled cell is keyed by a physical
2625
3015
  * `tile*cells + localCell` slot, so a value change grows/shrinks a tile's fill
@@ -2637,7 +3027,7 @@ class Unit {
2637
3027
  const rowsPerTile = Math.max(1, Math.ceil(cellsPerTile / cols));
2638
3028
  const visible = counts.map((_2, i2) => i2).filter((i2) => counts[i2] > 0);
2639
3029
  const K2 = Math.max(1, visible.length);
2640
- const denom = gcfg.max > 0 ? gcfg.max : Math.max(1, ...counts);
3030
+ const denom = gcfg.max > 0 ? gcfg.max : Math.max(...counts) || 1;
2641
3031
  const tileCols = Math.max(
2642
3032
  1,
2643
3033
  Math.round(gcfg.tileColumns > 0 ? gcfg.tileColumns : Math.ceil(Math.sqrt(K2)))
@@ -2702,40 +3092,6 @@ class Unit {
2702
3092
  this._gridTrack = { cells: track };
2703
3093
  return clusters;
2704
3094
  }
2705
- /**
2706
- * Draw the faint "track" backdrop for the small-multiple grid: every cell of
2707
- * every tile's full lattice, so the filled (coloured) cells drawn on top read
2708
- * as a fraction of the whole. Static (redrawn each render, never animated);
2709
- * painted BEHIND the series groups. `grid.trackColor` overrides the default
2710
- * theme-neutral grey.
2711
- * @param {any} ret @param {Graphics} graphics @param {any} opts
2712
- */
2713
- _drawGridTrack(ret, graphics, opts) {
2714
- const track = this._gridTrack;
2715
- if (!track || !track.cells || !track.cells.length) return;
2716
- const r2 = this._lastDotR;
2717
- const gcfg = opts.grid || {};
2718
- const trackColor = gcfg.trackColor || "rgba(128,128,128,0.14)";
2719
- const g = graphics.group({ class: "apexcharts-unit-track" });
2720
- track.cells.forEach((c) => {
2721
- let el;
2722
- if (opts.shape === "square") {
2723
- const side = r2 * 2;
2724
- el = graphics.drawRect(0, 0, side, side, opts.borderRadius || 0, trackColor, 1, 0, "none");
2725
- el.node.setAttribute("fill", trackColor);
2726
- el.node.setAttribute("x", String(c.x - r2));
2727
- el.node.setAttribute("y", String(c.y - r2));
2728
- } else {
2729
- el = graphics.drawCircle(r2, { fill: trackColor, "stroke-width": 0, stroke: "none" });
2730
- el.node.setAttribute("fill", trackColor);
2731
- el.node.setAttribute("cx", String(c.x));
2732
- el.node.setAttribute("cy", String(c.y));
2733
- }
2734
- el.node.classList.add("apexcharts-unit-track-cell");
2735
- g.add(el);
2736
- });
2737
- ret.add(g);
2738
- }
2739
3095
  /**
2740
3096
  * Scatter / beeswarm layout: position every unit on a real numeric X value
2741
3097
  * axis by its own value (`_unitValueOf`), laned by category on Y. Within a
@@ -2750,11 +3106,14 @@ class Unit {
2750
3106
  _layoutScatter(opts) {
2751
3107
  const w = this.w;
2752
3108
  const scfg = opts.scatter || {};
2753
- if (scfg.y === "value") return this._layoutScatter2D(opts);
2754
- if (scfg.orientation === "vertical") return this._layoutScatterVertical(opts);
3109
+ const unitData = w.seriesData.unitData || [];
3110
+ const lens = unitData.map((c) => c ? c.length : 0);
3111
+ const caps = this._applyMaxUnits(lens, opts.maxUnits);
3112
+ const keep = (ci, j2) => Math.floor((j2 + 1) * caps[ci] / lens[ci]) > Math.floor(j2 * caps[ci] / lens[ci]);
3113
+ if (scfg.y === "value") return this._layoutScatter2D(opts, keep);
3114
+ if (scfg.orientation === "vertical") return this._layoutScatterVertical(opts, keep);
2755
3115
  const gw = w.layout.gridWidth;
2756
3116
  const gh = w.layout.gridHeight;
2757
- const unitData = w.seriesData.unitData || [];
2758
3117
  const names = w.seriesData.seriesNames || [];
2759
3118
  const valueOf2 = (d) => this._unitValueOf(d);
2760
3119
  const sizeStats = this._scatterSizeStats(scfg, unitData);
@@ -2779,11 +3138,13 @@ class Unit {
2779
3138
  vmax = 1;
2780
3139
  }
2781
3140
  const tickAmount = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
2782
- const domain = this._scatterValueDomain(scfg, vmin, vmax, tickAmount);
3141
+ const domain = this._scatterValueDomain(scfg.xMin, scfg.xMax, vmin, vmax, tickAmount);
2783
3142
  const xMin = domain.min;
2784
3143
  const xMax = domain.max;
2785
3144
  const xSpan = xMax - xMin || 1;
2786
- const laneW = scfg.laneLabelWidth != null ? Math.max(0, scfg.laneLabelWidth) : Kv > 1 ? 92 : 8;
3145
+ const font = { fontFamily: w.config.chart.fontFamily, fontWeight: 600 };
3146
+ const laneName = (i2) => names[i2] || `series-${i2 + 1}`;
3147
+ const laneW = scfg.laneLabelWidth != null ? Math.max(0, scfg.laneLabelWidth) : Kv > 1 ? Math.min(gw / 3, measureLabelWidth(w, visible.map(laneName), font) + 14) : 8;
2787
3148
  const bottomGutter = 30 + (scfg.xTitle ? 20 : 0);
2788
3149
  const plotL = laneW;
2789
3150
  const plotR = gw - 8;
@@ -2817,19 +3178,24 @@ class Unit {
2817
3178
  const maxR = sizeStats ? sizeStats.rMax : r2;
2818
3179
  visible.forEach((ci, slot) => {
2819
3180
  const cy = laneCy(slot);
2820
- lanes2.push({ i: ci, cy, name: names[ci] || `series-${ci + 1}` });
2821
- const cat = unitData[ci] || [];
2822
- const pts = cat.map((d, j2) => {
3181
+ let name = laneName(ci);
3182
+ const wid = laneW > 12 ? measureLabelWidth(w, [name], font) : 0;
3183
+ if (wid > laneW - 14 && wid) {
3184
+ name = name.slice(0, Math.max(1, Math.floor(name.length * (laneW - 14) / wid) - 1)) + "…";
3185
+ }
3186
+ lanes2.push({ i: ci, cy, name });
3187
+ const pts = [];
3188
+ (unitData[ci] || []).forEach((d, j2) => {
2823
3189
  const v = valueOf2(d);
2824
- const p = { j: j2, px: plotX(isNum(v) ? v : xMin), y: cy };
3190
+ if (!isNum(v) || !keep(ci, j2)) return;
3191
+ const p = { j: j2, px: plotX(v), y: cy };
2825
3192
  if (sizeStats) p.r = this._scatterRadius(d, sizeStats, r2);
2826
- return p;
3193
+ pts.push(p);
2827
3194
  });
2828
3195
  if (jitter) {
2829
3196
  const halfLane = Math.max(maxR, laneH / 2 - maxR);
2830
- pts.forEach((p, k2) => {
2831
- const t2 = (k2 * 9301 + 49297) % 233280 / 233280;
2832
- p.y = cy + (t2 * 2 - 1) * halfLane;
3197
+ pts.forEach((p) => {
3198
+ p.y = cy + (hash01(ci * 7919 + p.j) * 2 - 1) * halfLane;
2833
3199
  });
2834
3200
  } else {
2835
3201
  this._beeswarm(pts, cy, r2, step, maxR);
@@ -2839,7 +3205,7 @@ class Unit {
2839
3205
  cx: (plotL + plotR) / 2,
2840
3206
  cy,
2841
3207
  outerR: laneH / 2,
2842
- dots: pts.map((p) => ({ x: p.px, y: p.y, r: p.r }))
3208
+ dots: pts.map((p) => ({ x: p.px, y: p.y, r: p.r, j: p.j }))
2843
3209
  });
2844
3210
  });
2845
3211
  const ticks = domain.ticks;
@@ -2866,9 +3232,9 @@ class Unit {
2866
3232
  * dots horizontally off each column's centre line. The value-axis config keys
2867
3233
  * (`xMin`/`xMax`/`xTitle`/`xFormatter`/`tickAmount`) still describe the value
2868
3234
  * axis (now Y), so flipping `orientation` keeps the same value settings.
2869
- * @param {any} opts
3235
+ * @param {any} opts @param {(ci:number, j:number) => boolean} keep
2870
3236
  */
2871
- _layoutScatterVertical(opts) {
3237
+ _layoutScatterVertical(opts, keep) {
2872
3238
  const w = this.w;
2873
3239
  const scfg = opts.scatter || {};
2874
3240
  const gw = w.layout.gridWidth;
@@ -2898,7 +3264,7 @@ class Unit {
2898
3264
  vmax = 1;
2899
3265
  }
2900
3266
  const tickAmount = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
2901
- const domain = this._scatterValueDomain(scfg, vmin, vmax, tickAmount);
3267
+ const domain = this._scatterValueDomain(scfg.xMin, scfg.xMax, vmin, vmax, tickAmount);
2902
3268
  const vMin = domain.min;
2903
3269
  const vMax = domain.max;
2904
3270
  const vSpan = vMax - vMin || 1;
@@ -2937,18 +3303,18 @@ class Unit {
2937
3303
  visible.forEach((ci, slot) => {
2938
3304
  const cx = laneCx(slot);
2939
3305
  lanes2.push({ i: ci, cx, name: names[ci] || `series-${ci + 1}` });
2940
- const cat = unitData[ci] || [];
2941
- const pts = cat.map((d, j2) => {
3306
+ const pts = [];
3307
+ (unitData[ci] || []).forEach((d, j2) => {
2942
3308
  const v = valueOf2(d);
2943
- const p = { j: j2, py: plotY(isNum(v) ? v : vMin), x: cx };
3309
+ if (!isNum(v) || !keep(ci, j2)) return;
3310
+ const p = { j: j2, py: plotY(v), x: cx };
2944
3311
  if (sizeStats) p.r = this._scatterRadius(d, sizeStats, r2);
2945
- return p;
3312
+ pts.push(p);
2946
3313
  });
2947
3314
  if (jitter) {
2948
3315
  const halfLane = Math.max(maxR, laneW / 2 - maxR);
2949
- pts.forEach((p, k2) => {
2950
- const t2 = (k2 * 9301 + 49297) % 233280 / 233280;
2951
- p.x = cx + (t2 * 2 - 1) * halfLane;
3316
+ pts.forEach((p) => {
3317
+ p.x = cx + (hash01(ci * 7919 + p.j) * 2 - 1) * halfLane;
2952
3318
  });
2953
3319
  } else {
2954
3320
  this._beeswarm(pts, cx, r2, step, maxR, true);
@@ -2958,7 +3324,7 @@ class Unit {
2958
3324
  cx,
2959
3325
  cy: (plotT + plotB) / 2,
2960
3326
  outerR: laneW / 2,
2961
- dots: pts.map((p) => ({ x: p.x, y: p.py, r: p.r }))
3327
+ dots: pts.map((p) => ({ x: p.x, y: p.py, r: p.r, j: p.j }))
2962
3328
  });
2963
3329
  });
2964
3330
  const ticks = domain.ticks;
@@ -2987,17 +3353,17 @@ class Unit {
2987
3353
  * per category). With `scatter.sizeRange` set, each dot is a BUBBLE scaled (by
2988
3354
  * area) from its `sizeField` (default 'z'). Needs the object form with numeric
2989
3355
  * `x` + `y`.
2990
- * @param {any} opts
3356
+ * @param {any} opts @param {(ci:number, j:number) => boolean} keep
2991
3357
  */
2992
- _layoutScatter2D(opts) {
3358
+ _layoutScatter2D(opts, keep) {
2993
3359
  const w = this.w;
2994
3360
  const gw = w.layout.gridWidth;
2995
3361
  const gh = w.layout.gridHeight;
2996
3362
  const scfg = opts.scatter || {};
2997
3363
  const unitData = w.seriesData.unitData || [];
2998
- const isNum = (v) => typeof v === "number" && isFinite(v);
2999
- const xOf = (d) => d && typeof d === "object" ? d.x : null;
3000
- const yOf = (d) => d && typeof d === "object" ? d.y != null ? d.y : d.value : null;
3364
+ const isNum = (v) => v != null;
3365
+ const xOf = (d) => d && typeof d === "object" ? toNum(d.x) : null;
3366
+ const yOf = (d) => d && typeof d === "object" ? toNum(d.y != null ? d.y : d.value) : null;
3001
3367
  const visible = unitData.map((_2, i2) => i2).filter(
3002
3368
  (i2) => (unitData[i2] || []).some((d) => isNum(xOf(d)) && isNum(yOf(d)))
3003
3369
  );
@@ -3023,22 +3389,24 @@ class Unit {
3023
3389
  ymn = 0;
3024
3390
  ymx = 1;
3025
3391
  }
3026
- const xTicksN = Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5));
3027
- const yTicksN = Math.max(2, Math.round(scfg.yTickAmount > 0 ? scfg.yTickAmount : 5));
3028
- const nx = this._niceScale(
3029
- scfg.xMin != null ? scfg.xMin : xmn,
3030
- scfg.xMax != null ? scfg.xMax : xmx,
3031
- xTicksN
3392
+ const dx = this._scatterValueDomain(
3393
+ scfg.xMin,
3394
+ scfg.xMax,
3395
+ xmn,
3396
+ xmx,
3397
+ Math.max(2, Math.round(scfg.tickAmount > 0 ? scfg.tickAmount : 5))
3032
3398
  );
3033
- const ny = this._niceScale(
3034
- scfg.yMin != null ? scfg.yMin : ymn,
3035
- scfg.yMax != null ? scfg.yMax : ymx,
3036
- yTicksN
3399
+ const dy = this._scatterValueDomain(
3400
+ scfg.yMin,
3401
+ scfg.yMax,
3402
+ ymn,
3403
+ ymx,
3404
+ Math.max(2, Math.round(scfg.yTickAmount > 0 ? scfg.yTickAmount : 5))
3037
3405
  );
3038
- const xMin = scfg.xMin != null ? scfg.xMin : nx.min;
3039
- const xMax = scfg.xMax != null ? scfg.xMax : nx.max;
3040
- const yMin = scfg.yMin != null ? scfg.yMin : ny.min;
3041
- const yMax = scfg.yMax != null ? scfg.yMax : ny.max;
3406
+ const xMin = dx.min;
3407
+ const xMax = dx.max;
3408
+ const yMin = dy.min;
3409
+ const yMax = dy.max;
3042
3410
  const xSpan = xMax - xMin || 1;
3043
3411
  const ySpan = yMax - yMin || 1;
3044
3412
  const leftGutter = 46 + (scfg.yTitle ? 18 : 0);
@@ -3056,15 +3424,17 @@ class Unit {
3056
3424
  this._lastDotR = baseR;
3057
3425
  const clusters = [];
3058
3426
  visible.forEach((ci) => {
3059
- const cat = unitData[ci] || [];
3060
- const dots = cat.map((d) => {
3427
+ const dots = [];
3428
+ (unitData[ci] || []).forEach((d, j2) => {
3061
3429
  const x2 = xOf(d);
3062
3430
  const y = yOf(d);
3063
- return {
3064
- x: plotX(isNum(x2) ? x2 : xMin),
3065
- y: plotY(isNum(y) ? y : yMin),
3066
- r: sizeStats ? this._scatterRadius(d, sizeStats, baseR) : void 0
3067
- };
3431
+ if (x2 == null || y == null || !keep(ci, j2)) return;
3432
+ dots.push({
3433
+ x: plotX(x2),
3434
+ y: plotY(y),
3435
+ r: sizeStats ? this._scatterRadius(d, sizeStats, baseR) : void 0,
3436
+ j: j2
3437
+ });
3068
3438
  });
3069
3439
  clusters.push({
3070
3440
  i: ci,
@@ -3074,18 +3444,6 @@ class Unit {
3074
3444
  dots
3075
3445
  });
3076
3446
  });
3077
- const mkTicks = (lo, hi, span, spacing, pinned, n2) => {
3078
- const out = [];
3079
- if (pinned) {
3080
- for (let k2 = 0; k2 < n2; k2++) out.push(lo + span * k2 / (n2 - 1));
3081
- } else {
3082
- const sp = spacing || span / Math.max(1, n2 - 1);
3083
- for (let v = lo; v <= hi + sp * 0.5; v += sp) {
3084
- out.push(Math.abs(v) < sp * 1e-9 ? 0 : v);
3085
- }
3086
- }
3087
- return out;
3088
- };
3089
3447
  this._scatterAxis = {
3090
3448
  mode: "2d",
3091
3449
  plotL,
@@ -3094,22 +3452,8 @@ class Unit {
3094
3452
  plotB,
3095
3453
  plotX,
3096
3454
  plotY,
3097
- xTicks: mkTicks(
3098
- xMin,
3099
- xMax,
3100
- xSpan,
3101
- nx.spacing,
3102
- scfg.xMin != null || scfg.xMax != null,
3103
- xTicksN
3104
- ),
3105
- yTicks: mkTicks(
3106
- yMin,
3107
- yMax,
3108
- ySpan,
3109
- ny.spacing,
3110
- scfg.yMin != null || scfg.yMax != null,
3111
- yTicksN
3112
- ),
3455
+ xTicks: dx.ticks,
3456
+ yTicks: dy.ticks,
3113
3457
  xTitle: scfg.xTitle,
3114
3458
  yTitle: scfg.yTitle,
3115
3459
  xFormatter: typeof scfg.xFormatter === "function" ? scfg.xFormatter : null,
@@ -3216,32 +3560,43 @@ class Unit {
3216
3560
  });
3217
3561
  }
3218
3562
  /**
3219
- * Value-axis domain + ticks for a 1D beeswarm. The domain ALWAYS contains
3563
+ * Value-axis domain + ticks for a scatter axis. The domain ALWAYS contains
3220
3564
  * every datum: a swarm that clips a dot outside the plot box is a bug, so an
3221
- * explicit `xMin`/`xMax` only FRAMES the axis and is extended by whole
3565
+ * explicit `lo`/`hi` only FRAMES the axis and is extended by whole
3222
3566
  * tick-steps whenever the data would otherwise overflow. Orientation-agnostic:
3223
3567
  * the same value axis is X for a horizontal swarm and Y for a vertical one.
3224
- * @param {any} scfg scatter config
3568
+ * @param {number|undefined} lo pinned minimum @param {number|undefined} hi
3569
+ * pinned maximum
3225
3570
  * @param {number} vmin data minimum @param {number} vmax data maximum
3226
3571
  * @param {number} tickAmount desired tick count
3227
3572
  * @returns {{ min:number, max:number, ticks:number[] }}
3228
3573
  */
3229
- _scatterValueDomain(scfg, vmin, vmax, tickAmount) {
3574
+ _scatterValueDomain(lo, hi, vmin, vmax, tickAmount) {
3230
3575
  const buildTicks = (min, max, spacing2) => {
3231
3576
  const ticks = [];
3232
- for (let v = min; v <= max + spacing2 * 0.5; v += spacing2) {
3577
+ for (let k2 = 0, v = min; v <= max + spacing2 * 0.5; v = min + ++k2 * spacing2) {
3233
3578
  ticks.push(Math.abs(v) < spacing2 * 1e-9 ? 0 : v);
3234
3579
  }
3235
3580
  return ticks;
3236
3581
  };
3237
- if (scfg.xMin != null || scfg.xMax != null) {
3238
- let min = scfg.xMin != null ? scfg.xMin : vmin;
3239
- let max = scfg.xMax != null ? scfg.xMax : vmax;
3240
- if (!(max > min)) max = min + 1;
3241
- const spacing2 = (max - min) / Math.max(1, tickAmount - 1);
3582
+ if (lo != null || hi != null) {
3583
+ let min = (
3584
+ /** @type {number} */
3585
+ lo != null ? lo : hi
3586
+ );
3587
+ let max = (
3588
+ /** @type {number} */
3589
+ hi != null ? hi : lo
3590
+ );
3591
+ const spacing2 = max > min ? (max - min) / Math.max(1, tickAmount - 1) : this._niceScale(Math.min(min, vmin), Math.max(max, vmax), tickAmount).spacing;
3242
3592
  if (vmin < min) min -= Math.ceil((min - vmin) / spacing2) * spacing2;
3243
3593
  if (vmax > max) max += Math.ceil((vmax - max) / spacing2) * spacing2;
3244
- return { min, max, ticks: buildTicks(min, max, spacing2) };
3594
+ if (!(max > min)) max = min + spacing2;
3595
+ if ((max - min) / spacing2 <= 2 * tickAmount) {
3596
+ return { min, max, ticks: buildTicks(min, max, spacing2) };
3597
+ }
3598
+ vmin = min;
3599
+ vmax = max;
3245
3600
  }
3246
3601
  const nice = this._niceScale(vmin, vmax, tickAmount);
3247
3602
  const spacing = nice.spacing || (nice.max - nice.min) / Math.max(1, tickAmount - 1);
@@ -3332,13 +3687,13 @@ class Unit {
3332
3687
  ax.yTicks.forEach((v) => {
3333
3688
  const y = ax.plotY(v);
3334
3689
  if (ax.gridlines) line(ax.plotL, y, ax.plotR, y, gridColor);
3335
- const label2 = ax.yFormatter ? String(ax.yFormatter(v)) : this._formatTick(v);
3690
+ const label2 = ax.yFormatter ? String(ax.yFormatter(v)) : this._formatTick(v, ax.yTicks);
3336
3691
  text(label2, ax.plotL - 8, y, "end", labelColor, 11, 400, "apexcharts-unit-tick");
3337
3692
  });
3338
3693
  ax.xTicks.forEach((v) => {
3339
3694
  const x2 = ax.plotX(v);
3340
3695
  if (ax.gridlines) line(x2, ax.plotT, x2, ax.plotB, gridColor);
3341
- const label2 = ax.xFormatter ? String(ax.xFormatter(v)) : this._formatTick(v);
3696
+ const label2 = ax.xFormatter ? String(ax.xFormatter(v)) : this._formatTick(v, ax.xTicks);
3342
3697
  text(label2, x2, ax.plotB + 14, "middle", labelColor, 11, 400, "apexcharts-unit-tick");
3343
3698
  });
3344
3699
  line(ax.plotL, ax.plotB, ax.plotR, ax.plotB, axisColor);
@@ -3378,7 +3733,7 @@ class Unit {
3378
3733
  ax.ticks.forEach((v) => {
3379
3734
  const y = ax.plotY(v);
3380
3735
  if (ax.gridlines) line(ax.plotL, y, ax.plotR, y, gridColor);
3381
- const label2 = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v);
3736
+ const label2 = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v, ax.ticks);
3382
3737
  text(label2, ax.plotL - 8, y, "end", labelColor, 11, 400, "apexcharts-unit-tick");
3383
3738
  });
3384
3739
  line(ax.plotL, ax.plotT, ax.plotL, ax.plotB, axisColor);
@@ -3408,7 +3763,7 @@ class Unit {
3408
3763
  ax.ticks.forEach((v, idx) => {
3409
3764
  const x2 = ax.plotX(v);
3410
3765
  if (ax.gridlines) line(x2, ax.plotT, x2, ax.plotB, gridColor);
3411
- const label2 = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v);
3766
+ const label2 = ax.formatter ? String(ax.formatter(v)) : this._formatTick(v, ax.ticks);
3412
3767
  const anchor = idx === 0 ? "start" : idx === ax.ticks.length - 1 ? "end" : "middle";
3413
3768
  text(label2, x2, ax.plotB + 14, anchor, labelColor, 11, 400, "apexcharts-unit-tick");
3414
3769
  });
@@ -3434,17 +3789,19 @@ class Unit {
3434
3789
  ret.add(g);
3435
3790
  }
3436
3791
  /**
3437
- * Compact tick-value formatting: integers as-is, otherwise trimmed to a short
3438
- * decimal; large magnitudes get a k/M suffix.
3439
- * @param {number} v @returns {string}
3792
+ * Compact tick-value formatting: integers as-is, otherwise trimmed to enough
3793
+ * decimals to tell neighbouring ticks apart; large magnitudes get a k/M suffix.
3794
+ * @param {number} v @param {number[]} ticks the axis ticks (their step sets
3795
+ * the decimals) @returns {string}
3440
3796
  */
3441
- _formatTick(v) {
3797
+ _formatTick(v, ticks) {
3442
3798
  if (!isFinite(v)) return "";
3443
3799
  const a2 = Math.abs(v);
3444
3800
  if (a2 >= 1e6) return `${+(v / 1e6).toFixed(1)}M`;
3445
3801
  if (a2 >= 1e4) return `${+(v / 1e3).toFixed(1)}k`;
3446
3802
  if (Number.isInteger(v)) return String(v);
3447
- return String(+v.toFixed(2));
3803
+ const step = Math.abs(ticks[1] - ticks[0]) || 1;
3804
+ return String(+v.toFixed(Math.min(20, Math.max(2, 1 - Math.floor(Math.log10(step))))));
3448
3805
  }
3449
3806
  /**
3450
3807
  * Distribute `total` whole cells across `counts` in proportion to each value,
@@ -3509,17 +3866,12 @@ class Unit {
3509
3866
  return !!(sbv && sbv.enabled && opts.shape !== "image" && opts.shape !== "square");
3510
3867
  }
3511
3868
  /**
3512
- * This datum's numeric value for sizing / tooltip: the number itself, or an
3513
- * object's `value` / `y`. Null when there is no usable number.
3869
+ * This datum's numeric value for sizing / tooltip: the number (or numeric
3870
+ * string) itself, or an object's `value` / `y`. Null when there is none.
3514
3871
  * @param {any} d @returns {number | null}
3515
3872
  */
3516
3873
  _unitValueOf(d) {
3517
- if (typeof d === "number") return d;
3518
- if (d && typeof d === "object") {
3519
- const v = d.value != null ? d.value : d.y;
3520
- return typeof v === "number" ? v : null;
3521
- }
3522
- return null;
3874
+ return toNum(d && typeof d === "object" ? d.value != null ? d.value : d.y : d);
3523
3875
  }
3524
3876
  /**
3525
3877
  * Radius for one bubble given the value stats. Default 'area' scaling makes
@@ -3661,8 +4013,9 @@ class Unit {
3661
4013
  *
3662
4014
  * @param {SVGElement} node @param {UnitMarkSpec} spec
3663
4015
  * @param {number} x @param {number} y
4016
+ * @param {number} [k] scale against the spec's size, while a size tweens
3664
4017
  */
3665
- _place(node, spec, x2, y) {
4018
+ _place(node, spec, x2, y, k2 = 1) {
3666
4019
  const s2 = (
3667
4020
  /** @type {any} */
3668
4021
  spec
@@ -3670,16 +4023,31 @@ class Unit {
3670
4023
  if (s2.pk === PK_GLYPH) {
3671
4024
  node.setAttribute(
3672
4025
  "transform",
3673
- "translate(" + (x2 - s2.ox) + "," + (y - s2.oy) + s2.tail
4026
+ "translate(" + (x2 - s2.ox * k2) + "," + (y - s2.oy * k2) + (k2 === 1 ? s2.tail : ") scale(" + s2.s * k2 + ")")
3674
4027
  );
3675
4028
  } else if (s2.pk === PK_CORNER) {
3676
- node.setAttribute("x", String(x2 - s2.hx));
3677
- node.setAttribute("y", String(y - s2.hy));
4029
+ node.setAttribute("x", String(x2 - s2.hx * k2));
4030
+ node.setAttribute("y", String(y - s2.hy * k2));
3678
4031
  } else {
3679
4032
  node.setAttribute("cx", String(x2));
3680
4033
  node.setAttribute("cy", String(y));
3681
4034
  }
3682
4035
  }
4036
+ /**
4037
+ * Size one mark at `k` times its spec's size (a glyph scales in `_place`).
4038
+ * @param {SVGElement} node @param {UnitMarkSpec} spec @param {number} k
4039
+ */
4040
+ _size(node, spec, k2) {
4041
+ const s2 = (
4042
+ /** @type {any} */
4043
+ spec
4044
+ );
4045
+ if (s2.pk === PK_CIRCLE) node.setAttribute("r", String(s2.r * k2));
4046
+ else if (s2.pk === PK_CORNER) {
4047
+ node.setAttribute("width", String(2 * s2.hx * k2));
4048
+ node.setAttribute("height", String(2 * s2.hy * k2));
4049
+ }
4050
+ }
3683
4051
  /**
3684
4052
  * The spec for the chart-wide shape (no pictogram, no per-mark radius).
3685
4053
  * @param {any} opts @param {number} [r]
@@ -3738,7 +4106,7 @@ class Unit {
3738
4106
  */
3739
4107
  _glyphSpec(mark, dotR, pcfg) {
3740
4108
  const qr = Math.round(dotR * 10) / 10;
3741
- const key = mark.name + "|" + mark.path.length + "|" + qr;
4109
+ const key = mark.name + mark.path + mark.viewBox + mark.fillRule + qr;
3742
4110
  const hit = this._specCache.get(key);
3743
4111
  if (hit) return hit;
3744
4112
  const vb = mark.viewBox || [0, 0, 100, 100];
@@ -3787,9 +4155,9 @@ class Unit {
3787
4155
  * Draw one dot (circle, square, or image icon) with the category fill +
3788
4156
  * stroke, tagged so the shared non-axis tooltip and hover reuse work.
3789
4157
  * @param {Graphics} graphics @param {any} opts @param {number} dotR
3790
- * @param {string} color @param {number} i @param {number} j
3791
- * @param {UnitMarkSpec} [spec] this mark's resolved spec; defaults to the
3792
- * chart-wide shape
4158
+ * @param {string} color @param {number} i @param {number} j (i < 0: an exit
4159
+ * ghost, left untagged so tooltips and morph capture skip it)
4160
+ * @param {UnitMarkSpec} spec this mark's resolved spec
3793
4161
  * @returns {any}
3794
4162
  */
3795
4163
  _drawDot(graphics, opts, dotR, color, i2, j2, spec) {
@@ -3798,7 +4166,7 @@ class Unit {
3798
4166
  const strokeColor = Array.isArray(w.globals.stroke.colors) ? w.globals.stroke.colors[i2] || "none" : "none";
3799
4167
  const fillOpacity = typeof w.config.fill.opacity === "number" ? w.config.fill.opacity : 1;
3800
4168
  let el;
3801
- if (spec && spec.pk === PK_GLYPH) {
4169
+ if (spec.pk === PK_GLYPH) {
3802
4170
  el = w.dom.Paper.path(spec.d);
3803
4171
  el.node.setAttribute("fill", color);
3804
4172
  if (spec.fillRule === "evenodd") {
@@ -3806,7 +4174,7 @@ class Unit {
3806
4174
  }
3807
4175
  if (fillOpacity < 1) el.node.setAttribute("fill-opacity", String(fillOpacity));
3808
4176
  el.node.setAttribute("data:r", String(spec.r));
3809
- } else if (opts.shape === "image" && opts.image && opts.image.src) {
4177
+ } else if (spec.pk === PK_CORNER && opts.shape === "image" && opts.image && opts.image.src) {
3810
4178
  const iw = opts.image.width || 20;
3811
4179
  const ih = opts.image.height || 20;
3812
4180
  el = w.dom.Paper.image(opts.image.src);
@@ -3816,7 +4184,7 @@ class Unit {
3816
4184
  if (opts.image.tint) {
3817
4185
  el.node.setAttribute("filter", `url(#${this._tintFilter(color)})`);
3818
4186
  }
3819
- } else if (opts.shape === "square") {
4187
+ } else if (spec.pk === PK_CORNER) {
3820
4188
  const side = dotR * 2;
3821
4189
  el = graphics.drawRect(0, 0, side, side, opts.borderRadius || 0, color, 1, strokeW, strokeColor);
3822
4190
  el.node.setAttribute("fill", color);
@@ -3830,9 +4198,11 @@ class Unit {
3830
4198
  el.node.setAttribute("fill", color);
3831
4199
  if (fillOpacity < 1) el.node.setAttribute("fill-opacity", String(fillOpacity));
3832
4200
  }
3833
- el.node.classList.add("apexcharts-unit-area");
3834
- el.node.setAttribute("i", String(i2));
3835
- el.node.setAttribute("j", String(j2));
4201
+ if (i2 >= 0) {
4202
+ el.node.classList.add("apexcharts-unit-area");
4203
+ el.node.setAttribute("i", String(i2));
4204
+ el.node.setAttribute("j", String(j2));
4205
+ }
3836
4206
  return el;
3837
4207
  }
3838
4208
  /**
@@ -3846,10 +4216,12 @@ class Unit {
3846
4216
  _tintFilter(color) {
3847
4217
  const w = this.w;
3848
4218
  const NS = "http://www.w3.org/2000/svg";
3849
- const safe = String(color).replace(/[^a-zA-Z0-9]/g, "");
3850
- const id = `apexcharts-unit-tint-${w.globals.chartID}-${safe}`;
4219
+ const tints = this._tints || (this._tints = /* @__PURE__ */ new Map());
4220
+ let id = tints.get(color);
4221
+ if (id) return id;
4222
+ id = `apexcharts-unit-tint-${w.globals.chartID}-${tints.size}`;
4223
+ tints.set(color, id);
3851
4224
  const svg = w.dom.Paper.node;
3852
- if (svg.querySelector(`#${id}`)) return id;
3853
4225
  let defs = svg.querySelector("defs");
3854
4226
  if (!defs) {
3855
4227
  defs = BrowserAPIs.createElementNS(NS, "defs");
@@ -3972,6 +4344,14 @@ class Unit {
3972
4344
  }
3973
4345
  if (this.ctx) this.ctx._unitSprings = springs;
3974
4346
  }
4347
+ /**
4348
+ * True once a loop's node no longer belongs to this chart's live SVG.
4349
+ * @param {Element} node @returns {boolean}
4350
+ */
4351
+ _stale(node) {
4352
+ const P = this.w.dom.Paper;
4353
+ return !!this.w.globals.isDestroyed || !P || !P.node.contains(node);
4354
+ }
3975
4355
  /**
3976
4356
  * One rAF loop that tweens every dot from its start (cx0/cy0 - either the
3977
4357
  * cluster centre on first mount / for entering dots, or its previous slot on
@@ -3994,8 +4374,8 @@ class Unit {
3994
4374
  const speed = Math.max(1, w.config.chart.animations.speed || 800);
3995
4375
  const maxDelay = Math.min(speed * 0.6, 450);
3996
4376
  const n2 = dots.length;
3997
- for (let k2 = 0; k2 < n2; k2++) {
3998
- dots[k2].delay = n2 > 1 ? k2 / (n2 - 1) * maxDelay : 0;
4377
+ for (let k2 = n2; k2--; ) {
4378
+ dots[k2].delay = dots[k2].pair ? dots[k2 + 1].delay : n2 > 1 ? k2 / (n2 - 1) * maxDelay : 0;
3999
4379
  }
4000
4380
  const gcfg = opts.gather || {};
4001
4381
  const motion = gcfg.motion || "auto";
@@ -4004,8 +4384,9 @@ class Unit {
4004
4384
  else if (this.ctx) this.ctx._unitSprings = null;
4005
4385
  for (let k2 = 0; k2 < n2; k2++) {
4006
4386
  const d = dots[k2];
4007
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
4008
- d.node.setAttribute("r", String(d.r0));
4387
+ if (d.r0 !== d.r1) {
4388
+ this._size(d.node, d.spec, d.r0 / d.r1);
4389
+ this._place(d.node, d.spec, d.cx0, d.cy0, d.r0 / d.r1);
4009
4390
  }
4010
4391
  }
4011
4392
  for (let k2 = 0; k2 < n2; k2++) {
@@ -4016,16 +4397,11 @@ class Unit {
4016
4397
  }
4017
4398
  }
4018
4399
  const easePos = gcfg.easing === "outBack" ? easeOutBack(typeof gcfg.overshoot === "number" ? gcfg.overshoot : 1.70158) : gcfg.easing === "inOutCubic" ? easeInOutCubic : easeOutCubic;
4019
- if (this.w.globals.unitGatherRAF != null) {
4020
- BrowserAPIs.cancelAnimationFrame(this.w.globals.unitGatherRAF);
4021
- this.w.globals.unitGatherRAF = null;
4022
- }
4023
4400
  const start = performance.now();
4024
4401
  let last = start;
4025
4402
  const stepFn = (now) => {
4026
- if (this.w.globals.isDestroyed) {
4403
+ if (this._stale(dots[0].node)) {
4027
4404
  this.w.globals.unitGatherRAF = null;
4028
- this.w.globals.animationEnded = true;
4029
4405
  return;
4030
4406
  }
4031
4407
  const dt = Math.min(MAX_FRAME_STEP, Math.max(0, (now - last) / 1e3));
@@ -4053,7 +4429,12 @@ class Unit {
4053
4429
  cx = d.cx0 + (d.x - d.cx0) * e2;
4054
4430
  cy = d.cy0 + (d.y - d.cy0) * e2;
4055
4431
  }
4056
- this._place(d.node, d.spec, cx, cy);
4432
+ let sk = 1;
4433
+ if (d.r0 !== d.r1) {
4434
+ sk = (d.r0 + (d.r1 - d.r0) * ec) / d.r1;
4435
+ this._size(d.node, d.spec, sk);
4436
+ }
4437
+ this._place(d.node, d.spec, cx, cy, sk);
4057
4438
  if (d.isEnter) d.node.style.opacity = String(Math.min(1, t2 * 2.5));
4058
4439
  if (d._c0 && d._c1) {
4059
4440
  const cr = Math.round(d._c0[0] + (d._c1[0] - d._c0[0]) * ec);
@@ -4061,9 +4442,6 @@ class Unit {
4061
4442
  const cb = Math.round(d._c0[2] + (d._c1[2] - d._c0[2]) * ec);
4062
4443
  d.node.setAttribute("fill", `rgb(${cr},${cg},${cb})`);
4063
4444
  }
4064
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
4065
- d.node.setAttribute("r", String(d.r0 + (d.r1 - d.r0) * ec));
4066
- }
4067
4445
  if (t2 < 1) done = false;
4068
4446
  }
4069
4447
  if (done) {
@@ -4071,8 +4449,9 @@ class Unit {
4071
4449
  const d = dots[k2];
4072
4450
  d.node.style.opacity = "";
4073
4451
  if (d._c1 && d.fill1) d.node.setAttribute("fill", d.fill1);
4074
- if (d.spec.pk === PK_CIRCLE && d.r0 != null && d.r1 != null && d.r0 !== d.r1) {
4075
- d.node.setAttribute("r", String(d.r1));
4452
+ if (d.r0 !== d.r1) {
4453
+ this._size(d.node, d.spec, 1);
4454
+ this._place(d.node, d.spec, d.x, d.y);
4076
4455
  }
4077
4456
  }
4078
4457
  this.w.globals.unitGatherRAF = null;
@@ -4095,7 +4474,7 @@ class Unit {
4095
4474
  const cap = Math.max(0, opts.maxUnits || 5e3);
4096
4475
  const exits = [];
4097
4476
  for (const [key, slot] of prev) {
4098
- if (!nextPrev.has(key)) {
4477
+ if (!nextPrev.has(key) && key[0] !== "l") {
4099
4478
  exits.push(slot);
4100
4479
  if (exits.length >= cap) break;
4101
4480
  }
@@ -4122,7 +4501,7 @@ class Unit {
4122
4501
  exits.forEach((slot) => {
4123
4502
  const r2 = slot.r != null ? slot.r : dotR;
4124
4503
  const spec = slot.spec || this._baseSpec(opts, r2);
4125
- const el = this._drawDot(graphics, opts, r2, slot.fill, 0, 0, spec);
4504
+ const el = this._drawDot(graphics, opts, r2, slot.fill, -1, 0, spec);
4126
4505
  el.node.classList.add("apexcharts-unit-exit");
4127
4506
  this._place(el.node, spec, slot.x, slot.y);
4128
4507
  group2.add(el);
@@ -4133,13 +4512,9 @@ class Unit {
4133
4512
  return;
4134
4513
  }
4135
4514
  const speed = Math.max(1, w.config.chart.animations.speed || 800);
4136
- if (this.w.globals.unitExitRAF != null) {
4137
- BrowserAPIs.cancelAnimationFrame(this.w.globals.unitExitRAF);
4138
- this.w.globals.unitExitRAF = null;
4139
- }
4140
4515
  const start = performance.now();
4141
4516
  const stepFn = (now) => {
4142
- if (this.w.globals.isDestroyed) {
4517
+ if (this._stale(group2.node)) {
4143
4518
  this.w.globals.unitExitRAF = null;
4144
4519
  return;
4145
4520
  }
@@ -4210,7 +4585,8 @@ ${percent2.toFixed(1)}%`;
4210
4585
  if (!lines.length) return 0;
4211
4586
  const width = measureLabelWidth(w, lines, {
4212
4587
  fontSize: cfg.fontSize,
4213
- fontFamily: cfg.fontFamily || w.config.chart.fontFamily
4588
+ fontFamily: cfg.fontFamily || w.config.chart.fontFamily,
4589
+ fontWeight: cfg.fontWeight
4214
4590
  });
4215
4591
  const gap = conn.gap != null ? conn.gap : 8;
4216
4592
  const length = conn.length != null ? conn.length : 22;
@@ -4347,6 +4723,7 @@ ${percent2.toFixed(1)}%`;
4347
4723
  * the blob in either direction.
4348
4724
  * @param {any} elSeries @param {{ i:number, cx:number, cy:number, outerR:number, flat?:boolean }} cluster
4349
4725
  * @param {number} value @param {number} total @param {any} opts @param {string} color
4726
+ * @returns {SVGElement|undefined}
4350
4727
  */
4351
4728
  _drawClusterLabel(elSeries, cluster, value, total, opts, color) {
4352
4729
  const w = this.w;
@@ -4404,6 +4781,10 @@ ${percent2.toFixed(1)}%`;
4404
4781
  textEl.textContent = str;
4405
4782
  }
4406
4783
  elSeries.node.appendChild(textEl);
4784
+ return (
4785
+ /** @type {any} */
4786
+ textEl
4787
+ );
4407
4788
  }
4408
4789
  }
4409
4790
  ApexCharts__default.use({
@@ -4506,26 +4887,33 @@ function drilldownById(w, id) {
4506
4887
  const list = dd && Array.isArray(dd.series) ? dd.series : [];
4507
4888
  return list.find((s2) => s2 && s2.id === id);
4508
4889
  }
4509
- function toNode(w, d, i2, paletteFromParent, parentKey, seenIds = null, opts = {}) {
4890
+ function segment(name, seen) {
4891
+ var _a;
4892
+ const n2 = String(name).replace(/\//g, "%2F");
4893
+ const o2 = ((_a = seen.get(n2)) != null ? _a : -1) + 1;
4894
+ seen.set(n2, o2);
4895
+ return `${o2}:${n2}`;
4896
+ }
4897
+ function toNode(w, d, i2, paletteFromParent, parentKey, seenIds = null, opts = {}, seen = /* @__PURE__ */ new Map()) {
4510
4898
  var _a, _b, _c;
4511
4899
  const isObj = d && typeof d === "object";
4512
4900
  const name = isObj ? (_b = (_a = d.x) != null ? _a : d.name) != null ? _b : "" : "";
4513
- const value = isObj ? Number((_c = d.y) != null ? _c : d.value) : Number(d);
4901
+ const raw = isObj ? (_c = d.y) != null ? _c : d.value : d;
4902
+ const value = raw == null || raw === "" ? NaN : Number(raw);
4514
4903
  const node = {
4515
4904
  name: String(name),
4516
4905
  value: isNaN(value) ? null : value,
4517
4906
  color: isObj && d.color ? d.color : void 0,
4518
- // Identity across data updates: the path of names (indexed so same-named
4519
- // siblings stay distinct). Update animations morph matched keys in place.
4520
- _key: `${parentKey}/${i2}:${name}`
4907
+ _key: `${parentKey}/${segment(name, seen)}`
4521
4908
  };
4522
4909
  if (paletteFromParent && !node.color) {
4523
4910
  node.color = paletteFromParent[i2 % paletteFromParent.length];
4524
4911
  }
4525
4912
  if (opts.keepDatum) node._datum = d;
4913
+ const kids = /* @__PURE__ */ new Map();
4526
4914
  if (isObj && Array.isArray(d.children) && d.children.length) {
4527
4915
  node.children = d.children.map(
4528
- (c, j2) => toNode(w, c, j2, null, node._key, seenIds, opts)
4916
+ (c, j2) => toNode(w, c, j2, null, node._key, seenIds, opts, kids)
4529
4917
  );
4530
4918
  } else if (isObj && d.drilldown != null && opts.expandDrilldown !== false) {
4531
4919
  const visited = seenIds || /* @__PURE__ */ new Set();
@@ -4536,7 +4924,7 @@ function toNode(w, d, i2, paletteFromParent, parentKey, seenIds = null, opts = {
4536
4924
  nextSeen.add(d.drilldown);
4537
4925
  const palette = Array.isArray(dd.colors) ? dd.colors : null;
4538
4926
  node.children = dd.data.map(
4539
- (c, j2) => toNode(w, c, j2, palette, node._key, nextSeen, opts)
4927
+ (c, j2) => toNode(w, c, j2, palette, node._key, nextSeen, opts, kids)
4540
4928
  );
4541
4929
  }
4542
4930
  }
@@ -4551,8 +4939,9 @@ function buildHierarchy(w, opts = {}) {
4551
4939
  const first = cfgSeries && cfgSeries[0];
4552
4940
  const data = first && Array.isArray(first.data) ? first.data : cfgSeries;
4553
4941
  if (!Array.isArray(data)) return [];
4942
+ const seen = /* @__PURE__ */ new Map();
4554
4943
  return data.map(
4555
- (d, i2) => toNode(w, d, i2, null, "", null, opts)
4944
+ (d, i2) => toNode(w, d, i2, null, "", null, opts, seen)
4556
4945
  );
4557
4946
  }
4558
4947
  function fillValues(node) {
@@ -4706,7 +5095,7 @@ function renderBreadcrumb(w, opts) {
4706
5095
  text.setAttribute("class", "apexcharts-breadcrumb-label");
4707
5096
  text.textContent = String(label2);
4708
5097
  btn.appendChild(text);
4709
- btn.addEventListener("click", () => opts.onNavigate(i2, crumb));
5098
+ btn.addEventListener("click", (e2) => opts.onNavigate(i2, crumb, e2));
4710
5099
  nav.appendChild(btn);
4711
5100
  });
4712
5101
  elWrap.appendChild(nav);
@@ -4826,6 +5215,15 @@ function resolveFocus(node, current, roots) {
4826
5215
  if (next === current) return { changed: false, focus: current };
4827
5216
  return { changed: true, focus: next };
4828
5217
  }
5218
+ function focusByKey(key, nodesAll, roots) {
5219
+ var _a;
5220
+ const f = key && nodesAll.find((n2) => n2._key === key);
5221
+ return f && ((_a = f.children) == null ? void 0 : _a.length) && !(roots.length === 1 && f === roots[0]) ? f : null;
5222
+ }
5223
+ function setZoomCursor(chart, node) {
5224
+ if (!Environment.isBrowser() || chart.cfg.zoomOnClick === false) return;
5225
+ node._el.node.style.cursor = resolveFocus(node, chart._focus, chart._roots).changed ? "pointer" : "default";
5226
+ }
4829
5227
  function parseSize(size, max, fallbackRatio = 0) {
4830
5228
  if (typeof size === "number") return size;
4831
5229
  const s2 = String(size).trim();
@@ -4897,6 +5295,7 @@ class NodeTooltip {
4897
5295
  * @param {any} node
4898
5296
  */
4899
5297
  show(e2, node) {
5298
+ var _a;
4900
5299
  const t2 = this._tip();
4901
5300
  if (!t2) return;
4902
5301
  const w = this.w;
@@ -4904,7 +5303,10 @@ class NodeTooltip {
4904
5303
  const parentVal = node._parent ? node._parent.value : this.total;
4905
5304
  const pctParent = parentVal > 0 ? (node.value / parentVal * 100).toFixed(1) : pctTotal;
4906
5305
  const groupBg = w.config.tooltip.fillSeriesColor ? `background-color:${node._color};` : "";
4907
- t2.innerHTML = `<div class="apexcharts-tooltip-series-group apexcharts-active" style="display:flex;${groupBg}"><span class="apexcharts-tooltip-marker" style="background-color:${node._color}"></span><div class="apexcharts-tooltip-text"><div class="apexcharts-tooltip-y-group"><span class="apexcharts-tooltip-text-y-label">${node.name}: </span><span class="apexcharts-tooltip-text-y-value">${node.value} (${pctParent}% of parent, ${pctTotal}% of total)</span></div></div></div>`;
5306
+ const ty = w.config.tooltip.y;
5307
+ const fmt = (_a = Array.isArray(ty) ? ty[0] : ty) == null ? void 0 : _a.formatter;
5308
+ const val = fmt ? fmt(node.value, { seriesIndex: 0, dataPointIndex: -1, w, node }) : +node.value.toPrecision(12);
5309
+ t2.innerHTML = `<div class="apexcharts-tooltip-series-group apexcharts-active" style="display:flex;${groupBg}"><span class="apexcharts-tooltip-marker" style="background-color:${node._color}"></span><div class="apexcharts-tooltip-text"><div class="apexcharts-tooltip-y-group"><span class="apexcharts-tooltip-text-y-label">${node.name}: </span><span class="apexcharts-tooltip-text-y-value">${val} (${pctParent}% of parent, ${pctTotal}% of total)</span></div></div></div>`;
4908
5310
  t2.classList.add("apexcharts-active");
4909
5311
  t2.style.opacity = "1";
4910
5312
  this.position(e2);
@@ -4992,14 +5394,19 @@ class SunburstChart {
4992
5394
  this.centerY = gh / 2 + (this.cfg.offsetY || 0);
4993
5395
  this.maxRadius = Math.min(gw, gh) / 2.05 - this.strokeWidth - (!w.config.chart.sparkline.enabled ? w.config.chart.dropShadow.blur : 0);
4994
5396
  if (this.maxRadius < 5) return g;
4995
- this._roots = this._buildHierarchy();
5397
+ this._roots = buildHierarchy(w);
4996
5398
  if (!this._roots.length) return g;
4997
- this._roots.forEach((r2) => this._fillValues(r2));
5399
+ this._roots.forEach(fillValues);
4998
5400
  this._validateStrict();
4999
5401
  this._nodesAll = [];
5000
5402
  const colors = w.globals.colors || [];
5001
5403
  this._roots.forEach((r2, i2) => {
5002
- this._colorPass(r2, r2.color || colors[i2 % colors.length] || "#008FFB");
5404
+ colorPass(
5405
+ r2,
5406
+ r2.color || colors[i2 % colors.length] || "#008FFB",
5407
+ this.cfg.tint,
5408
+ this._nodesAll
5409
+ );
5003
5410
  });
5004
5411
  this.total = this._roots.reduce((s2, r2) => s2 + Math.max(0, r2.value), 0) || 1;
5005
5412
  this._tooltip.total = this.total;
@@ -5007,13 +5414,17 @@ class SunburstChart {
5007
5414
  this._labelsG = graphics.group({ class: "apexcharts-sunburst-labels" });
5008
5415
  g.add(this._ringsG);
5009
5416
  g.add(this._labelsG);
5010
- this._focus = null;
5417
+ this._focus = focusByKey(
5418
+ w.globals.sunburstFocusKey,
5419
+ this._nodesAll,
5420
+ this._roots
5421
+ );
5011
5422
  this._relayout(this._focus);
5012
5423
  const anims = w.config.chart.animations;
5013
5424
  let mode = "none";
5014
5425
  if (anims.enabled) {
5015
5426
  if (w.globals.dataChanged) {
5016
- if (anims.dynamicAnimation.enabled) mode = "update";
5427
+ if (w.globals.shouldAnimate !== false) mode = "update";
5017
5428
  } else if (!w.globals.resized) {
5018
5429
  mode = "intro";
5019
5430
  }
@@ -5023,22 +5434,6 @@ class SunburstChart {
5023
5434
  return g;
5024
5435
  }
5025
5436
  // ------------------------------------------------------------------ data
5026
- /**
5027
- * Resolve the config into root nodes `{ name, value, color?, children? }`.
5028
- * Each datum may carry `children` (native) or `drilldown: '<id>'` (adapter).
5029
- * Shared with the treemap - see charts/common/Hierarchy.
5030
- * @returns {any[]}
5031
- */
5032
- _buildHierarchy() {
5033
- return buildHierarchy(this.w);
5034
- }
5035
- /**
5036
- * Fill a parent's value from its children when missing.
5037
- * @param {any} node
5038
- */
5039
- _fillValues(node) {
5040
- fillValues(node);
5041
- }
5042
5437
  /**
5043
5438
  * With `partition: 'strict'`, warn (once) when a parent's value does not
5044
5439
  * match the sum of its children. The angles are still normalized to fill the
@@ -5052,22 +5447,13 @@ class SunburstChart {
5052
5447
  remedy: "Angles are normalized to fill the wedge."
5053
5448
  });
5054
5449
  }
5055
- /**
5056
- * Assign a colour to every node (explicit `color` wins, else the parent's
5057
- * colour tinted lighter). Done once so zoom preserves colours.
5058
- * Shared with the icicle - see charts/common/partition/Tint.
5059
- * @param {any} node
5060
- * @param {string} color
5061
- */
5062
- _colorPass(node, color) {
5063
- colorPass(node, color, this.cfg.tint, this._nodesAll);
5064
- }
5065
5450
  // ---------------------------------------------------------------- layout
5066
5451
  /**
5067
5452
  * Recompute visibility + angles + radii for a focus node (null = whole tree).
5068
5453
  * @param {any} focus
5069
5454
  */
5070
5455
  _relayout(focus) {
5456
+ this.w.globals.sunburstFocusKey = focus && focus._key;
5071
5457
  this._focusMaxDepth = placeTree({
5072
5458
  roots: this._roots,
5073
5459
  nodesAll: this._nodesAll,
@@ -5108,14 +5494,15 @@ class SunburstChart {
5108
5494
  * @param {'intro'|'zoom'|'update'|'none'} mode
5109
5495
  */
5110
5496
  _applyLayout(mode) {
5111
- var _a;
5497
+ var _a, _b, _c;
5112
5498
  const w = this.w;
5113
5499
  const anims = w.config.chart.animations;
5114
- const dur = !anims.enabled ? 0 : mode === "none" ? 0 : mode === "update" ? anims.dynamicAnimation.speed || 350 : anims.speed || 500;
5500
+ const dyn = anims.dynamicAnimation;
5501
+ const dur = !anims.enabled || mode === "none" ? 0 : mode === "intro" ? (_a = anims.speed) != null ? _a : 500 : dyn.enabled ? (_b = dyn.speed) != null ? _b : 350 : 0;
5115
5502
  const gen = ++this._zoomGen;
5116
5503
  const morph = (
5117
5504
  /** @type {any} */
5118
- (_a = this.ctx) == null ? void 0 : _a.morphTypeChange
5505
+ (_c = this.ctx) == null ? void 0 : _c.morphTypeChange
5119
5506
  );
5120
5507
  const morphActive = !!morph && typeof morph.isActive === "function" && morph.isActive() && typeof morph.getInitialPathAt === "function";
5121
5508
  const morphDur = morphActive ? morph.getSpeed() : 0;
@@ -5124,6 +5511,11 @@ class SunburstChart {
5124
5511
  /** @type {any} */
5125
5512
  this.ctx._sunburstPrevGeoms
5126
5513
  ) : null;
5514
+ const geo = (n2) => n2._cur || { a0: n2._t0, a1: n2._t1, iR: n2._iR, oR: n2._oR };
5515
+ const pinch = (c) => {
5516
+ const m = (c.a0 + c.a1) / 2;
5517
+ return { a0: m, a1: m, iR: c.iR, oR: c.iR };
5518
+ };
5127
5519
  this._nodesAll.forEach((node) => {
5128
5520
  if (node._show) {
5129
5521
  const target2 = {
@@ -5133,36 +5525,55 @@ class SunburstChart {
5133
5525
  oR: node._oR
5134
5526
  };
5135
5527
  if (!node._el) node._el = this._createArcEl(node);
5528
+ setZoomCursor(this, node);
5136
5529
  const morphFrom = morphActive ? this._morphSourceFor(node) : null;
5137
5530
  if (morphFrom && morphDur > 0) {
5138
5531
  this._morphArcFrom(node, morphFrom, target2, morphDur, gen);
5139
5532
  } else if ((mode === "intro" || morphActive) && dur > 0) {
5140
5533
  this._sweepArc(node, target2, morphActive ? morphDur : dur, gen);
5141
5534
  } else {
5142
- let from;
5143
- let isNew = false;
5144
- if (node._cur) {
5145
- from = node._cur;
5146
- } else if (prev && prev.get(node._key)) {
5147
- from = prev.get(node._key);
5148
- } else {
5149
- const mid = (target2.a0 + target2.a1) / 2;
5150
- from = { a0: mid, a1: mid, iR: target2.iR, oR: target2.iR };
5151
- isNew = true;
5152
- }
5153
- this._animateArc(node, from, target2, dur, false, isNew, gen);
5535
+ const p = prev && prev.get(node._key);
5536
+ const from = node._cur || (p ? geo(p) : pinch(target2));
5537
+ this._animateArc(node, from, target2, dur, false, !node._cur && !p, gen);
5154
5538
  }
5155
5539
  } else if (node._el && node._cur) {
5156
- const mid = (node._cur.a0 + node._cur.a1) / 2;
5157
- const target2 = { a0: mid, a1: mid, iR: node._cur.iR, oR: node._cur.iR };
5158
- this._animateArc(node, node._cur, target2, dur, true, false, gen);
5540
+ this._animateArc(node, node._cur, pinch(node._cur), dur, true, false, gen);
5159
5541
  }
5160
5542
  });
5543
+ if (prev && dur && !morphActive) {
5544
+ const keys = new Set(this._nodesAll.map((n2) => n2._show && n2._key));
5545
+ prev.forEach((p, k2) => {
5546
+ if (keys.has(k2) || !(p._show || p._cur)) return;
5547
+ const el = this._graphics.drawPath({
5548
+ fill: p._color,
5549
+ stroke: this.strokeColor,
5550
+ strokeWidth: this.strokeWidth,
5551
+ classes: "apexcharts-sunburst-arc"
5552
+ });
5553
+ el.node.setAttribute("data:name", p.name);
5554
+ this._ringsG.add(el.attr({ opacity: p._el.attr("opacity") }));
5555
+ const c = geo(p);
5556
+ const g = { _el: el, _color: p._color, name: p.name, _key: k2, _cur: c };
5557
+ this._nodesAll.push(g);
5558
+ this._animateArc(g, c, pinch(c), dur, true, false, gen);
5559
+ });
5560
+ }
5561
+ const end = Math.max(dur, morphDur);
5562
+ if (end) {
5563
+ this._ringsG.animate(end).after(() => {
5564
+ if (this._zoomGen === gen && this._ringsG.node.isConnected)
5565
+ new Animations(
5566
+ w,
5567
+ /** @type {any} */
5568
+ this.ctx
5569
+ ).animationCompleted(
5570
+ this._ringsG.node
5571
+ );
5572
+ });
5573
+ } else w.globals.animationEnded = true;
5161
5574
  const geoms = /* @__PURE__ */ new Map();
5162
5575
  this._nodesAll.forEach((n2) => {
5163
- if (n2._show) {
5164
- geoms.set(n2._key, { a0: n2._t0, a1: n2._t1, iR: n2._iR, oR: n2._oR });
5165
- }
5576
+ if (n2._show || n2._cur) geoms.set(n2._key, n2);
5166
5577
  });
5167
5578
  this.ctx._sunburstPrevGeoms = geoms;
5168
5579
  this._renderLabels(dur);
@@ -5279,11 +5690,9 @@ class SunburstChart {
5279
5690
  */
5280
5691
  _createArcEl(node) {
5281
5692
  const path = this._graphics.drawPath({
5282
- d: "",
5283
5693
  fill: node._color,
5284
5694
  stroke: this.strokeColor,
5285
5695
  strokeWidth: this.strokeWidth,
5286
- fillOpacity: 1,
5287
5696
  classes: "apexcharts-sunburst-arc"
5288
5697
  });
5289
5698
  const el = path.node;
@@ -5295,9 +5704,8 @@ class SunburstChart {
5295
5704
  String(!(node.children && node.children.length))
5296
5705
  );
5297
5706
  this._tooltip.attach(el, node);
5298
- if (Environment.isBrowser()) {
5707
+ if (Environment.isBrowser() && this.cfg.zoomOnClick !== false) {
5299
5708
  el.addEventListener("click", () => this._zoomTo(node));
5300
- el.style.cursor = "pointer";
5301
5709
  }
5302
5710
  this._ringsG.add(path);
5303
5711
  return path;
@@ -5361,11 +5769,12 @@ class SunburstChart {
5361
5769
  if (node._t1 - node._t0 < this.cfg.dataLabels.minAngleToShow) return;
5362
5770
  this._renderCurvedLabel(node);
5363
5771
  });
5772
+ const gen = this._zoomGen;
5773
+ labelsG.attr({ opacity: dur > 0 ? 0 : 1 });
5364
5774
  if (dur > 0) {
5365
- labelsG.attr({ opacity: 0 });
5366
- labelsG.animate(250, dur).attr({ opacity: 1 });
5367
- } else {
5368
- labelsG.attr({ opacity: 1 });
5775
+ labelsG.animate(250, dur).during((p) => {
5776
+ if (this._zoomGen === gen) labelsG.attr({ opacity: p });
5777
+ });
5369
5778
  }
5370
5779
  }
5371
5780
  /**
@@ -5374,11 +5783,10 @@ class SunburstChart {
5374
5783
  * @param {any} node
5375
5784
  */
5376
5785
  _renderCurvedLabel(node) {
5377
- if (!Environment.isBrowser()) return;
5378
5786
  const style = this.cfg.dataLabels.style;
5379
5787
  const w = this.w;
5380
5788
  const r2 = (node._iR + node._oR) / 2;
5381
- const mid = (node._t0 + node._t1) / 2;
5789
+ const mid = ((node._t0 + node._t1) / 2 % 360 + 360) % 360;
5382
5790
  const flip = mid > 90 && mid < 270;
5383
5791
  const padDeg = Math.min(r2 > 0 ? 4 / r2 * R2D : 0, (node._t1 - node._t0) / 2);
5384
5792
  const from = flip ? node._t1 - padDeg : node._t0 + padDeg;
@@ -5439,17 +5847,13 @@ class SunburstChart {
5439
5847
  */
5440
5848
  _zoomTo(node) {
5441
5849
  if (this.cfg.zoomOnClick === false) return;
5442
- const next = resolveFocus(node, this._focus);
5850
+ const next = resolveFocus(node, this._focus, this._roots);
5443
5851
  if (!next.changed) return;
5444
5852
  this._focus = next.focus;
5445
5853
  this._relayout(this._focus);
5446
5854
  this._applyLayout("zoom");
5447
5855
  this._renderBreadcrumb();
5448
5856
  }
5449
- /** Root -> focus chain of nodes. */
5450
- _focusChain() {
5451
- return focusChain(this._focus);
5452
- }
5453
5857
  /**
5454
5858
  * Minimal self-contained breadcrumb (reuses the shared `.apexcharts-breadcrumb`
5455
5859
  * CSS classes, but does NOT depend on the drilldown feature).
@@ -5469,7 +5873,7 @@ class SunburstChart {
5469
5873
  nav.style.top = "0px";
5470
5874
  nav.style.left = "0px";
5471
5875
  const crumbs = [{ name: "All", node: null }].concat(
5472
- this._focusChain().map((n2) => ({ name: n2.name, node: n2 }))
5876
+ focusChain(this._focus).map((n2) => ({ name: n2.name, node: n2 }))
5473
5877
  );
5474
5878
  crumbs.forEach((crumb, i2) => {
5475
5879
  if (i2 > 0) {
@@ -5511,21 +5915,7 @@ class SunburstChart {
5511
5915
  }
5512
5916
  });
5513
5917
  elWrap.appendChild(nav);
5514
- this._avoidChromeOverlap(nav);
5515
- }
5516
- /**
5517
- * The breadcrumb is an absolute overlay at top-left, so it can sit on top of
5518
- * a left-aligned title (or subtitle). After mounting, push it below any chart
5519
- * chrome it intersects. (Duplicated from drilldown's Breadcrumb on purpose —
5520
- * sunburst must not import the drilldown feature.)
5521
- * @param {any} nav
5522
- */
5523
- /**
5524
- * Shared with the treemap - see charts/common/Breadcrumb.
5525
- * @param {any} nav
5526
- */
5527
- _avoidChromeOverlap(nav) {
5528
- avoidChromeOverlap(this.w, nav);
5918
+ avoidChromeOverlap(w, nav);
5529
5919
  }
5530
5920
  // ------------------------------------------------------------ geometry
5531
5921
  /**
@@ -5626,6 +6016,8 @@ class IcicleChart {
5626
6016
  this._roots = buildHierarchy(this.w);
5627
6017
  if (!this._roots.length) return g;
5628
6018
  this._roots.forEach((r2) => fillValues(r2));
6019
+ this._nodesAll = [];
6020
+ this._colorNodes();
5629
6021
  sortTree(this._roots, this.cfg.sort);
5630
6022
  if (this.cfg.partition === "strict") {
5631
6023
  validateStrict(this._roots, {
@@ -5633,15 +6025,17 @@ class IcicleChart {
5633
6025
  remedy: "Extents are normalized to fill the parent."
5634
6026
  });
5635
6027
  }
5636
- this._nodesAll = [];
5637
- this._colorNodes();
5638
6028
  this.total = this._roots.reduce((s2, r2) => s2 + Math.max(0, r2.value), 0) || 1;
5639
6029
  this._tooltip.total = this.total;
5640
6030
  this._cellsG = graphics.group({ class: "apexcharts-icicle-cells" });
5641
6031
  this._labelsG = graphics.group({ class: "apexcharts-icicle-labels" });
5642
6032
  g.add(this._cellsG);
5643
6033
  g.add(this._labelsG);
5644
- this._focus = null;
6034
+ this._focus = focusByKey(
6035
+ w.globals.icicleFocusKey,
6036
+ this._nodesAll,
6037
+ this._roots
6038
+ );
5645
6039
  this._relayout(this._focus);
5646
6040
  const anims = w.config.chart.animations;
5647
6041
  let mode = "none";
@@ -5672,7 +6066,7 @@ class IcicleChart {
5672
6066
  const fallback = colors[0] || "#008FFB";
5673
6067
  let level = this._roots;
5674
6068
  const trunk = [];
5675
- while (level.length === 1 && level[0].children && level[0].children.length > 1) {
6069
+ while (level.length === 1 && level[0].children && level[0].children.length) {
5676
6070
  trunk.push(level[0]);
5677
6071
  level = level[0].children;
5678
6072
  }
@@ -5704,6 +6098,7 @@ class IcicleChart {
5704
6098
  * @param {any} focus
5705
6099
  */
5706
6100
  _relayout(focus) {
6101
+ this.w.globals.icicleFocusKey = focus && focus._key;
5707
6102
  const extent = this._valueExtent();
5708
6103
  const maxDepth = this.cfg.maxDepth;
5709
6104
  const capLevels = typeof maxDepth === "number" && maxDepth > 0 ? maxDepth : 0;
@@ -5819,9 +6214,11 @@ class IcicleChart {
5819
6214
  * @param {'intro'|'zoom'|'update'|'none'} mode
5820
6215
  */
5821
6216
  _applyLayout(mode) {
6217
+ var _a, _b;
5822
6218
  const w = this.w;
5823
6219
  const anims = w.config.chart.animations;
5824
- const dur = !anims.enabled ? 0 : mode === "none" ? 0 : mode === "update" || mode === "zoom" ? anims.dynamicAnimation.speed || 350 : anims.speed || 500;
6220
+ const dyn = anims.dynamicAnimation;
6221
+ const dur = !anims.enabled || mode === "none" ? 0 : mode === "intro" ? (_a = anims.speed) != null ? _a : 500 : dyn.enabled ? (_b = dyn.speed) != null ? _b : 350 : 0;
5825
6222
  const gen = ++this._zoomGen;
5826
6223
  const prev = mode === "update" ? (
5827
6224
  /** @type {any} */
@@ -5832,7 +6229,7 @@ class IcicleChart {
5832
6229
  const to = this._box(node);
5833
6230
  if (!node._el) node._el = this._createCellEl(node);
5834
6231
  node._el.node.setAttribute("data:clipped", String(!!node._clipped));
5835
- this._setCursor(node);
6232
+ setZoomCursor(this, node);
5836
6233
  if (mode === "intro" && dur > 0) {
5837
6234
  this._wipeCell(node, to, dur, gen);
5838
6235
  return;
@@ -5842,7 +6239,7 @@ class IcicleChart {
5842
6239
  if (node._cur) {
5843
6240
  from = node._cur;
5844
6241
  } else if (prev && prev.get(node._key)) {
5845
- from = prev.get(node._key);
6242
+ from = prev.get(node._key)();
5846
6243
  } else {
5847
6244
  from = {
5848
6245
  x: to.x + to.w / 2,
@@ -5866,7 +6263,8 @@ class IcicleChart {
5866
6263
  });
5867
6264
  const geoms = /* @__PURE__ */ new Map();
5868
6265
  this._nodesAll.forEach((n2) => {
5869
- if (n2._show) geoms.set(n2._key, this._box(n2));
6266
+ const b = this._box(n2);
6267
+ if (n2._show) geoms.set(n2._key, () => n2._cur || b);
5870
6268
  });
5871
6269
  this.ctx._iciclePrevGeoms = geoms;
5872
6270
  this._renderLabels(dur);
@@ -6023,11 +6421,12 @@ class IcicleChart {
6023
6421
  this._renderLabel(node);
6024
6422
  if (node._clipped) this._renderMoreMark(node);
6025
6423
  });
6424
+ const gen = this._zoomGen;
6425
+ labelsG.attr({ opacity: dur > 0 ? 0 : 1 });
6026
6426
  if (dur > 0) {
6027
- labelsG.attr({ opacity: 0 });
6028
- labelsG.animate(250, dur).attr({ opacity: 1 });
6029
- } else {
6030
- labelsG.attr({ opacity: 1 });
6427
+ labelsG.animate(250, dur).during((p) => {
6428
+ if (this._zoomGen === gen) labelsG.attr({ opacity: p });
6429
+ });
6031
6430
  }
6032
6431
  }
6033
6432
  /**
@@ -6040,7 +6439,6 @@ class IcicleChart {
6040
6439
  * @param {any} node
6041
6440
  */
6042
6441
  _renderLabel(node) {
6043
- if (!Environment.isBrowser()) return;
6044
6442
  const dl = this.cfg.dataLabels;
6045
6443
  const style = dl.style;
6046
6444
  const box = this._box(node);
@@ -6053,7 +6451,7 @@ class IcicleChart {
6053
6451
  const room = along - 8;
6054
6452
  let label2 = node.name;
6055
6453
  if (dl.showValue) {
6056
- const full = `${node.name}: ${node.value}`;
6454
+ const full = `${node.name}: ${+node.value.toPrecision(12)}`;
6057
6455
  if (this._fits(full, room, fontPx)) label2 = full;
6058
6456
  }
6059
6457
  const text = this._truncate(label2, room, fontPx);
@@ -6102,7 +6500,6 @@ class IcicleChart {
6102
6500
  * @param {any} node
6103
6501
  */
6104
6502
  _renderMoreMark(node) {
6105
- if (!Environment.isBrowser()) return;
6106
6503
  const box = this._box(node);
6107
6504
  const along = this._vertical ? box.w : box.h;
6108
6505
  if (along < 18) return;
@@ -6153,18 +6550,6 @@ class IcicleChart {
6153
6550
  return name.slice(0, maxChars - 1) + "…";
6154
6551
  }
6155
6552
  // ----------------------------------------------------------------- zoom
6156
- /**
6157
- * A cell offers the pointer only while clicking it would change the view.
6158
- * A leaf under the current focus, or the single root of a one-root tree,
6159
- * resolves to the focus the reader is already on, and a pointer there
6160
- * promises a zoom that cannot happen. Asked on every layout, because a zoom
6161
- * changes the answer for every cell.
6162
- * @param {any} node
6163
- */
6164
- _setCursor(node) {
6165
- if (!Environment.isBrowser() || this.cfg.zoomOnClick === false) return;
6166
- node._el.node.style.cursor = resolveFocus(node, this._focus, this._roots).changed ? "pointer" : "default";
6167
- }
6168
6553
  /**
6169
6554
  * Focus a node (zoom in), or zoom out one level when the current focus is
6170
6555
  * clicked.
@@ -6438,7 +6823,9 @@ class Breadcrumb {
6438
6823
  const w = this.w;
6439
6824
  const elWrap = w.dom.elWrap;
6440
6825
  if (!elWrap) return;
6441
- const existing = elWrap.querySelector(".apexcharts-breadcrumb");
6826
+ const existing = elWrap.querySelector(
6827
+ ".apexcharts-breadcrumb[aria-label^=Drill]"
6828
+ );
6442
6829
  if (existing && existing.parentNode) {
6443
6830
  existing.parentNode.removeChild(existing);
6444
6831
  }
@@ -8077,7 +8464,8 @@ class StreamLabels {
8077
8464
  text: name,
8078
8465
  maxWidth: span,
8079
8466
  fontSize: `${px}px`,
8080
- fontFamily
8467
+ fontFamily,
8468
+ fontWeight
8081
8469
  });
8082
8470
  if (!text || text === "...") continue;
8083
8471
  const drawnWidth = text === name ? rect.width : rect.width * (text.length / name.length);
@@ -8928,7 +9316,8 @@ class RendererController {
8928
9316
  this._activate(desired, this._instances[desired]);
8929
9317
  return this._activeKind;
8930
9318
  }
8931
- if (mode === desired) {
9319
+ if (mode === desired && !this._warned) {
9320
+ this._warned = 1;
8932
9321
  warnMissingFeature(
8933
9322
  `\`chart.renderer: '${desired}'\``,
8934
9323
  `renderer-${desired}`,
@@ -8937,11 +9326,17 @@ class RendererController {
8937
9326
  }
8938
9327
  );
8939
9328
  }
8940
- } else if (mode === "canvas" && hasCanvasUnsupportedFeature(this.w)) {
9329
+ } else if (mode === "canvas" && !this._warned && hasCanvasUnsupportedFeature(this.w)) {
9330
+ this._warned = 1;
8941
9331
  console.warn(
8942
9332
  `[apexcharts] renderer:"canvas" requested but this chart uses a feature the canvas renderer does not render yet (gradient/pattern/image fill or a state color-matrix filter); falling back to SVG.`
8943
9333
  );
8944
9334
  }
9335
+ const prev = this._instances[this._activeKind];
9336
+ if (prev) {
9337
+ prev.destroy();
9338
+ delete this._instances[this._activeKind];
9339
+ }
8945
9340
  this._activate("svg", this.svg);
8946
9341
  return this._activeKind;
8947
9342
  }
@@ -10058,9 +10453,11 @@ class HeatmapGradientLegend {
10058
10453
  this._min = 0;
10059
10454
  this._max = 0;
10060
10455
  this._geom = null;
10456
+ this._grad = null;
10061
10457
  this._bandHitEls = [];
10062
10458
  this._activeBandIndex = -1;
10063
10459
  this._targetEl = null;
10460
+ this.panelsW = null;
10064
10461
  this._onCellEnter = this._onCellEnter.bind(this);
10065
10462
  this._onCellLeave = this._onCellLeave.bind(this);
10066
10463
  this._onBandEnter = this._onBandEnter.bind(this);
@@ -10175,15 +10572,8 @@ class HeatmapGradientLegend {
10175
10572
  linearGrad.setAttribute("x2", "1");
10176
10573
  linearGrad.setAttribute("y2", "0");
10177
10574
  }
10178
- const { min, max, stops, bands } = this._computeStops();
10179
- this._min = min;
10180
- this._max = max;
10181
- stops.forEach((s2) => {
10182
- const stopEl = BrowserAPIs.createElementNS(SVG_NS, "stop");
10183
- stopEl.setAttribute("offset", `${(s2.percent * 100).toFixed(2)}%`);
10184
- stopEl.setAttribute("stop-color", s2.color);
10185
- linearGrad.appendChild(stopEl);
10186
- });
10575
+ this._grad = linearGrad;
10576
+ const { min, max, bands } = this._paintStops();
10187
10577
  defs.appendChild(linearGrad);
10188
10578
  svg.appendChild(defs);
10189
10579
  const rect = BrowserAPIs.createElementNS(SVG_NS, "rect");
@@ -10392,6 +10782,7 @@ class HeatmapGradientLegend {
10392
10782
  */
10393
10783
  repositionToPlot() {
10394
10784
  var _a, _b;
10785
+ if (this._grad) this._paintStops();
10395
10786
  if (!Environment.isBrowser()) return;
10396
10787
  if (this._targetEl) return;
10397
10788
  const w = this.w;
@@ -10526,14 +10917,25 @@ class HeatmapGradientLegend {
10526
10917
  if (idx < 0 || idx === this._activeBandIndex) return;
10527
10918
  this._activeBandIndex = idx;
10528
10919
  (_d = (_c = (_b = this.ctx) == null ? void 0 : _b.events) == null ? void 0 : _c.fireEvent) == null ? void 0 : _d.call(_c, "legendHover", [this.ctx, idx, w]);
10529
- new Series(w).highlightRangeInSeries(idx, "highlight");
10920
+ this._highlightRange(idx, "highlight");
10530
10921
  }
10531
10922
  /** Leaving a band clears the highlight. */
10532
10923
  _onBandLeave() {
10533
10924
  if (this._activeBandIndex < 0) return;
10534
10925
  const idx = this._activeBandIndex;
10535
10926
  this._activeBandIndex = -1;
10536
- new Series(this.w).highlightRangeInSeries(idx, "reset");
10927
+ this._highlightRange(idx, "reset");
10928
+ }
10929
+ /**
10930
+ * A shared trellis strip sets `panelsW` so a band reaches every panel.
10931
+ * @param {number} idx
10932
+ * @param {'highlight'|'reset'} action
10933
+ */
10934
+ _highlightRange(idx, action) {
10935
+ var _a;
10936
+ (((_a = this.panelsW) == null ? void 0 : _a.call(this)) || [this.w]).forEach(
10937
+ (w) => new Series(w).highlightRangeInSeries(idx, action)
10938
+ );
10537
10939
  }
10538
10940
  _attachHoverListeners() {
10539
10941
  var _a, _b;
@@ -10555,10 +10957,10 @@ class HeatmapGradientLegend {
10555
10957
  */
10556
10958
  _onCellEnter(...args) {
10557
10959
  var _a, _b, _c;
10558
- const w = this.w;
10559
10960
  if (!this.arrowEl) return;
10560
10961
  const opts = args[args.length - 1];
10561
10962
  if (!opts || typeof opts !== "object") return;
10963
+ const w = opts.w || this.w;
10562
10964
  const i2 = opts.seriesIndex;
10563
10965
  const j2 = opts.dataPointIndex;
10564
10966
  if (typeof i2 !== "number" || typeof j2 !== "number") return;
@@ -10670,6 +11072,21 @@ class HeatmapGradientLegend {
10670
11072
  polygon.setAttribute("pointer-events", "none");
10671
11073
  return polygon;
10672
11074
  }
11075
+ /** (Re)write the strip's stops; returns `_computeStops()`. */
11076
+ _paintStops() {
11077
+ const r2 = this._computeStops();
11078
+ const grad = this._grad;
11079
+ this._min = r2.min;
11080
+ this._max = r2.max;
11081
+ grad.innerHTML = "";
11082
+ r2.stops.forEach((s2) => {
11083
+ const stopEl = BrowserAPIs.createElementNS(SVG_NS, "stop");
11084
+ stopEl.setAttribute("offset", `${(s2.percent * 100).toFixed(2)}%`);
11085
+ stopEl.setAttribute("stop-color", s2.color);
11086
+ grad.appendChild(stopEl);
11087
+ });
11088
+ return r2;
11089
+ }
10673
11090
  /**
10674
11091
  * Build gradient stops + return effective min/max.
10675
11092
  * - If `colorScale.ranges` is set, stops are placed at each range boundary
@@ -10711,7 +11128,8 @@ class HeatmapGradientLegend {
10711
11128
  if (!Number.isFinite(dataMax)) dataMax = 0;
10712
11129
  let min = dataMin;
10713
11130
  let max = dataMax;
10714
- if (typeof cs.min !== "undefined" && typeof cs.max !== "undefined" && cs.max > cs.min) {
11131
+ const csWindow = typeof cs.min !== "undefined" && typeof cs.max !== "undefined" && cs.max > cs.min;
11132
+ if (csWindow) {
10715
11133
  min = cs.min;
10716
11134
  max = cs.max;
10717
11135
  } else {
@@ -10742,34 +11160,30 @@ class HeatmapGradientLegend {
10742
11160
  } else {
10743
11161
  const baseColor = w.globals.colors[0] || "#008FFB";
10744
11162
  const utils = new Utils();
10745
- const plot = w.config.plotOptions[w.config.chart.type] || {};
11163
+ const type = w.config.chart.type;
11164
+ const plot = w.config.plotOptions[type] || {};
10746
11165
  const shadeIntensity = (_b = plot.shadeIntensity) != null ? _b : 0.5;
10747
- const hasNegs = (
11166
+ const g = (
10748
11167
  /** @type {any} */
10749
- w.globals.hasNegs
11168
+ w.globals
10750
11169
  );
10751
- const n2 = Math.max(2, cfg.stops || 16);
10752
- for (let s2 = 0; s2 < n2; s2++) {
10753
- const t2 = s2 / (n2 - 1);
10754
- const v = min + t2 * (max - min);
10755
- const total = Math.abs(max) + Math.abs(min);
10756
- const percent_v = total === 0 ? 0 : 100 * v / total;
10757
- let colorShadePercent;
10758
- if (hasNegs) {
10759
- if (plot.reverseNegativeShade) {
10760
- colorShadePercent = percent_v < 0 ? percent_v / 100 * (shadeIntensity * 1.25) : (1 - percent_v / 100) * (shadeIntensity * 1.25);
10761
- } else {
10762
- colorShadePercent = percent_v <= 0 ? 1 - (1 + percent_v / 100) * shadeIntensity : (1 - percent_v / 100) * shadeIntensity;
10763
- }
10764
- } else {
10765
- colorShadePercent = 1 - percent_v / 100;
10766
- }
10767
- if (colorShadePercent > 1) colorShadePercent = 1;
10768
- if (colorShadePercent < -1) colorShadePercent = -1;
10769
- const shaded = plot.enableShades ? utils.shadeColor(
10770
- w.config.theme.mode === "dark" ? colorShadePercent * -1 : colorShadePercent,
10771
- baseColor
10772
- ) : baseColor;
11170
+ const hasNegs = g.hasNegs;
11171
+ const yExt = type === "heatmap" && !plot.distributed && !csWindow && g.maxY >= g.minY;
11172
+ const hi = yExt ? g.maxY : max;
11173
+ const total = Math.abs(hi) + Math.abs(yExt ? cs.min < g.minY ? cs.min : g.minY : min);
11174
+ const k2 = shadeIntensity * 1.25;
11175
+ const n2 = Math.max(2, cfg.stops || 16);
11176
+ for (let s2 = 0; s2 < n2; s2++) {
11177
+ const t2 = s2 / (n2 - 1);
11178
+ const p = total === 0 ? 0 : Math.min(min + t2 * (max - min), hi) / total;
11179
+ const c = Math.max(
11180
+ -1,
11181
+ Math.min(
11182
+ 1,
11183
+ hasNegs ? plot.reverseNegativeShade ? (p < 0 ? p : 1 - p) * k2 : p <= 0 ? 1 - (1 + p) * shadeIntensity : (1 - p) * shadeIntensity : (1 - p) * (type === "treemap" ? k2 : 1)
11184
+ )
11185
+ );
11186
+ const shaded = plot.enableShades ? utils.shadeColor(w.config.theme.mode === "dark" ? -c : c, baseColor) : baseColor;
10773
11187
  stops.push({ percent: t2, color: shaded });
10774
11188
  }
10775
11189
  }
@@ -10794,6 +11208,7 @@ class TrellisChrome {
10794
11208
  this.elBreadcrumb = null;
10795
11209
  this.gradientLegend = null;
10796
11210
  this._gradPending = null;
11211
+ this._gradWire = null;
10797
11212
  }
10798
11213
  /**
10799
11214
  * The per-cell facet header. Built with the cell, before the panel mounts,
@@ -11024,17 +11439,20 @@ class TrellisChrome {
11024
11439
  const legend = new HeatmapGradientLegend(chart.w, chart.ctx);
11025
11440
  legend.draw(slot);
11026
11441
  this.gradientLegend = legend;
11027
- t2.panels.forEach((p) => {
11442
+ legend.panelsW = () => t2.panels.filter((p) => p.chart).map((p) => (
11443
+ /** @type {any} */
11444
+ p.chart.w
11445
+ ));
11446
+ const wire = (_2, p) => {
11028
11447
  var _a;
11029
- if (!p.chart || p === mounted) return;
11030
- const ev = (
11031
- /** @type {any} */
11032
- (_a = p.chart.ctx) == null ? void 0 : _a.events
11033
- );
11034
- if (!(ev == null ? void 0 : ev.addEventListener)) return;
11448
+ const ev = p.chart !== chart && ((_a = p.chart) == null ? void 0 : _a.ctx.events);
11449
+ if (!ev) return;
11035
11450
  ev.addEventListener("dataPointMouseEnter", legend._onCellEnter);
11036
11451
  ev.addEventListener("dataPointMouseLeave", legend._onCellLeave);
11037
- });
11452
+ };
11453
+ t2.panels.forEach((p) => wire(0, p));
11454
+ t2.ctx.events.addEventListener("panelMounted", wire);
11455
+ this._gradWire = wire;
11038
11456
  }
11039
11457
  /** Tear down the shared gradient strip (P5). */
11040
11458
  destroyGradientLegend() {
@@ -11044,6 +11462,7 @@ class TrellisChrome {
11044
11462
  (_b = (_a = t2.ctx.events).removeEventListener) == null ? void 0 : _b.call(_a, "panelMounted", this._gradPending);
11045
11463
  this._gradPending = null;
11046
11464
  }
11465
+ t2.ctx.events.removeEventListener("panelMounted", this._gradWire);
11047
11466
  if (this.gradientLegend) {
11048
11467
  try {
11049
11468
  this.gradientLegend.destroy();
@@ -11360,6 +11779,7 @@ class TrellisSync {
11360
11779
  });
11361
11780
  };
11362
11781
  const loop = () => {
11782
+ if (elGrid !== this.trellis.elGrid) return raf2 = 0;
11363
11783
  apply();
11364
11784
  raf2 = hoverTarget ? requestAnimationFrame(loop) : 0;
11365
11785
  };
@@ -13063,8 +13483,12 @@ class Trellis {
13063
13483
  // gap is the breathing room here).
13064
13484
  parentHeightOffset: 0,
13065
13485
  toolbar: { show: false },
13486
+ // the shared toolbar owns the one reset (TrellisSync.resetAll); with
13487
+ // toolbar 'none' a panel's own reset is the only way back, unless the
13488
+ // page turned it off
13066
13489
  zoom: {
13067
- enabled: this.cfg.zoom !== "none"
13490
+ enabled: this.cfg.zoom !== "none",
13491
+ resetControl: this.cfg.toolbar == "none" && w.config.chart.zoom.resetControl !== false
13068
13492
  }
13069
13493
  }, animationsOff ? { animations: { enabled: false } } : {}), this._panelRenderer ? { renderer: this._panelRenderer } : {}), {
13070
13494
  events: __spreadProps(__spreadValues({}, userEvents), {
@@ -17290,6 +17714,7 @@ class CanvasGraphics {
17290
17714
  cmd.strokeWidth = opts.strokeWidth;
17291
17715
  cmd.fill = opts.fill;
17292
17716
  cmd.lineCap = opts.strokeLinecap;
17717
+ cmd.strokeDash = this._g.seriesDash(opts.realIndex);
17293
17718
  cmd.si = opts.realIndex;
17294
17719
  if (typeof opts.j === "number") cmd.dj = opts.j;
17295
17720
  if (opts.shouldClipToGrid !== false) {
@@ -18959,405 +19384,135 @@ class History {
18959
19384
  return;
18960
19385
  }
18961
19386
  applyViewInteraction(this.ctx, cp.view);
18962
- this._refreshSettle();
18963
- this._emitChange();
18964
- }).catch(() => {
18965
- this.applying = false;
18966
- });
18967
- }
18968
- /**
18969
- * Hold `applying` true until the restore's burst of async 'updated' events
18970
- * has drained (one macrotask after the last one). Refreshed by _onUpdated.
18971
- */
18972
- _refreshSettle() {
18973
- if (!Environment.isBrowser()) {
18974
- this.applying = false;
18975
- return;
18976
- }
18977
- clearTimeout(this._settleTimer);
18978
- this._settleTimer = setTimeout(() => {
18979
- this.applying = false;
18980
- }, 0);
18981
- }
18982
- // ─── Public API ─────────────────────────────────────────────────────────
18983
- /**
18984
- * Commit a checkpoint of the current state now (a discrete undo step). Used by
18985
- * callers that mutate `w.config` without going through a full re-render (e.g.
18986
- * Ink Layer's targeted annotation redraws, which fire no 'updated' event).
18987
- * No-op when disabled or while a restore is applying.
18988
- * @param {string} [label]
18989
- */
18990
- snapshot(label2) {
18991
- if (!this.enabled || this.applying) return;
18992
- this._commit(label2 || "change");
18993
- }
18994
- /**
18995
- * @param {boolean} [animate]
18996
- */
18997
- undo(animate = true) {
18998
- if (!this.canUndo()) return;
18999
- this.pointer--;
19000
- this._restore(this.stack[this.pointer], animate);
19001
- }
19002
- /**
19003
- * @param {boolean} [animate]
19004
- */
19005
- redo(animate = true) {
19006
- if (!this.canRedo()) return;
19007
- this.pointer++;
19008
- this._restore(this.stack[this.pointer], animate);
19009
- }
19010
- canUndo() {
19011
- return this.pointer > 0;
19012
- }
19013
- canRedo() {
19014
- return this.pointer > -1 && this.pointer < this.stack.length - 1;
19015
- }
19016
- /**
19017
- * @param {string} id
19018
- * @param {boolean} [animate]
19019
- */
19020
- jump(id, animate = true) {
19021
- const idx = this.stack.findIndex((c) => c.id === id);
19022
- if (idx === -1 || idx === this.pointer) return;
19023
- this.pointer = idx;
19024
- this._restore(this.stack[idx], animate);
19025
- }
19026
- /** Clear the history, keeping the current state as the new baseline. */
19027
- clear() {
19028
- clearTimeout(this._coalesceTimer);
19029
- this._coalesceTimer = null;
19030
- const current = this.stack[this.pointer];
19031
- this.stack = current ? [current] : [];
19032
- this.pointer = this.stack.length - 1;
19033
- this._emitChange();
19034
- }
19035
- /**
19036
- * Group multiple edits into a single undo step. `fn` may be async; await your
19037
- * updateOptions/updateSeries calls inside it so the intermediate 'updated'
19038
- * events are suppressed and only one checkpoint is committed afterwards.
19039
- * @param {() => (void | Promise<any>)} fn
19040
- * @param {{ label?: string }} [opts]
19041
- * @returns {Promise<void>}
19042
- */
19043
- transaction(fn, opts = {}) {
19044
- if (typeof fn !== "function") return Promise.resolve();
19045
- const wasBatching = this._batching;
19046
- this._batching = true;
19047
- return Promise.resolve().then(() => fn()).finally(() => {
19048
- this._batching = wasBatching;
19049
- if (!wasBatching) this._commit(opts.label || "transaction", true);
19050
- });
19051
- }
19052
- /**
19053
- * @returns {{ id: string, label: string, at: number }[]}
19054
- */
19055
- entries() {
19056
- return this.stack.map((c) => ({ id: c.id, label: c.label, at: c.at }));
19057
- }
19058
- /** Lightweight state for the historyChange event / a history-rail UI. */
19059
- state() {
19060
- return {
19061
- canUndo: this.canUndo(),
19062
- canRedo: this.canRedo(),
19063
- index: this.pointer,
19064
- length: this.stack.length
19065
- };
19066
- }
19067
- _emitChange() {
19068
- this.ctx.events.fireEvent("historyChange", [this.ctx, this.state()]);
19069
- }
19070
- /** Drop the stack + detach listeners (called on full destroy). */
19071
- teardown() {
19072
- var _a, _b, _c, _d, _e, _f, _g, _h;
19073
- clearTimeout(this._coalesceTimer);
19074
- clearTimeout(this._settleTimer);
19075
- this._coalesceTimer = null;
19076
- this._settleTimer = null;
19077
- this._unwireKeyboard();
19078
- if (this._wired) {
19079
- (_b = (_a = this.ctx).removeEventListener) == null ? void 0 : _b.call(_a, "mounted", this._onMounted);
19080
- (_d = (_c = this.ctx).removeEventListener) == null ? void 0 : _d.call(_c, "updated", this._onUpdated);
19081
- (_f = (_e = this.ctx).removeEventListener) == null ? void 0 : _f.call(_e, "scrolled", this._onUpdated);
19082
- (_h = (_g = this.ctx).removeEventListener) == null ? void 0 : _h.call(_g, "dataPointSelection", this._onSelection);
19083
- }
19084
- this.stack = [];
19085
- this.pointer = -1;
19086
- this._wired = false;
19087
- }
19088
- }
19089
- ApexCharts__default.registerFeatures({ history: History });
19090
- const DataLabels = ApexCharts.__apex_DataLabels;
19091
- const resolveEasing = ApexCharts.__apex_Easing_resolveEasing;
19092
- const parsePath = ApexCharts.__apex_PathMorphing_parsePath;
19093
- const arrayToPath = ApexCharts.__apex_PathMorphing_arrayToPath;
19094
- function buildUnionEntries(join, oldN) {
19095
- const exitSet = new Set(join.exits);
19096
- const entries = [];
19097
- let oi = 0;
19098
- for (let nj = 0; nj < join.toOld.length; nj++) {
19099
- const oj = join.toOld[nj];
19100
- if (oj !== -1) {
19101
- while (oi < oj) {
19102
- if (exitSet.has(oi)) entries.push({ oldJ: oi, newJ: -1 });
19103
- oi++;
19104
- }
19105
- entries.push({ oldJ: oj, newJ: nj });
19106
- oi = oj + 1;
19107
- } else {
19108
- entries.push({ oldJ: -1, newJ: nj });
19109
- }
19110
- }
19111
- while (oi < oldN) {
19112
- if (exitSet.has(oi)) entries.push({ oldJ: oi, newJ: -1 });
19113
- oi++;
19114
- }
19115
- return entries;
19116
- }
19117
- function analyzeSeriesPath(d, expectedAnchors, isArea) {
19118
- if (!d || typeof d !== "string") return null;
19119
- const cmds = parsePath(d);
19120
- if (!cmds.length || cmds[0][0] !== "M") return null;
19121
- for (let i2 = 1; i2 < cmds.length; i2++) {
19122
- if (cmds[i2][0] === "M") return null;
19123
- }
19124
- let body = cmds;
19125
- let closing = null;
19126
- if (isArea) {
19127
- if (cmds.length < 5) return null;
19128
- closing = cmds.slice(-3);
19129
- body = cmds.slice(0, -3);
19130
- if (closing[2][0] !== "Z") return null;
19131
- if (closing[1][0] !== "L") return null;
19132
- if (closing[0][0] !== "L" && closing[0][0] !== "C") return null;
19133
- } else if (cmds[cmds.length - 1][0] === "Z") {
19134
- return null;
19135
- }
19136
- if (body.length !== expectedAnchors || body.length < 2) return null;
19137
- const segType = body[1][0];
19138
- if (segType !== "L" && segType !== "C") return null;
19139
- for (let i2 = 2; i2 < body.length; i2++) {
19140
- if (body[i2][0] !== segType) return null;
19141
- }
19142
- const anchors = body.map(
19143
- (c) => c[0] === "C" ? [Number(c[5]), Number(c[6])] : [Number(c[1]), Number(c[2])]
19144
- );
19145
- for (const a2 of anchors) {
19146
- if (!isFinite(a2[0]) || !isFinite(a2[1])) return null;
19147
- }
19148
- return { body, closing, segType, anchors };
19149
- }
19150
- function lerpPt(a2, b, t2) {
19151
- return [a2[0] + (b[0] - a2[0]) * t2, a2[1] + (b[1] - a2[1]) * t2];
19152
- }
19153
- function splitCubic(s2, cmd, t2) {
19154
- const p0 = s2;
19155
- const p1 = [cmd[1], cmd[2]];
19156
- const p2 = [cmd[3], cmd[4]];
19157
- const p3 = [cmd[5], cmd[6]];
19158
- const p01 = lerpPt(p0, p1, t2);
19159
- const p12 = lerpPt(p1, p2, t2);
19160
- const p23 = lerpPt(p2, p3, t2);
19161
- const p012 = lerpPt(p01, p12, t2);
19162
- const p123 = lerpPt(p12, p23, t2);
19163
- const mid = lerpPt(p012, p123, t2);
19164
- return {
19165
- first: ["C", p01[0], p01[1], p012[0], p012[1], mid[0], mid[1]],
19166
- second: ["C", p123[0], p123[1], p23[0], p23[1], p3[0], p3[1]],
19167
- mid
19168
- };
19169
- }
19170
- function splitLine(s2, cmd, t2) {
19171
- const e2 = [cmd[1], cmd[2]];
19172
- const mid = lerpPt(s2, e2, t2);
19173
- return {
19174
- first: ["L", mid[0], mid[1]],
19175
- second: ["L", e2[0], e2[1]],
19176
- mid
19177
- };
19178
- }
19179
- function expandPath(analysis, ownIdx) {
19180
- const { body, closing, segType, anchors } = analysis;
19181
- const n2 = anchors.length;
19182
- const m = ownIdx.length;
19183
- const degen = (p) => segType === "C" ? ["C", p[0], p[1], p[0], p[1], p[0], p[1]] : ["L", p[0], p[1]];
19184
- const out = [["M", anchors[0][0], anchors[0][1]]];
19185
- let firstOwn = 0;
19186
- while (firstOwn < m && ownIdx[firstOwn] !== 0) firstOwn++;
19187
- for (let q2 = 1; q2 <= firstOwn; q2++) out.push(degen(anchors[0]));
19188
- let entry = firstOwn + 1;
19189
- for (let s2 = 0; s2 < n2 - 1; s2++) {
19190
- let interior = 0;
19191
- while (entry + interior < m && ownIdx[entry + interior] === -1) interior++;
19192
- const cmd = body[s2 + 1];
19193
- if (!interior) {
19194
- out.push(cmd.slice());
19195
- } else {
19196
- const totalParts = interior + 1;
19197
- let start = anchors[s2];
19198
- let rest = cmd;
19199
- for (let q2 = 0; q2 < interior; q2++) {
19200
- const t2 = 1 / (totalParts - q2);
19201
- const sp = segType === "C" ? splitCubic(start, rest, t2) : splitLine(start, rest, t2);
19202
- out.push(sp.first);
19203
- start = sp.mid;
19204
- rest = sp.second;
19205
- }
19206
- out.push(rest);
19387
+ this._refreshSettle();
19388
+ this._emitChange();
19389
+ }).catch(() => {
19390
+ this.applying = false;
19391
+ });
19392
+ }
19393
+ /**
19394
+ * Hold `applying` true until the restore's burst of async 'updated' events
19395
+ * has drained (one macrotask after the last one). Refreshed by _onUpdated.
19396
+ */
19397
+ _refreshSettle() {
19398
+ if (!Environment.isBrowser()) {
19399
+ this.applying = false;
19400
+ return;
19207
19401
  }
19208
- entry += interior + 1;
19402
+ clearTimeout(this._settleTimer);
19403
+ this._settleTimer = setTimeout(() => {
19404
+ this.applying = false;
19405
+ }, 0);
19209
19406
  }
19210
- while (entry < m) {
19211
- out.push(degen(anchors[n2 - 1]));
19212
- entry++;
19407
+ // ─── Public API ─────────────────────────────────────────────────────────
19408
+ /**
19409
+ * Commit a checkpoint of the current state now (a discrete undo step). Used by
19410
+ * callers that mutate `w.config` without going through a full re-render (e.g.
19411
+ * Ink Layer's targeted annotation redraws, which fire no 'updated' event).
19412
+ * No-op when disabled or while a restore is applying.
19413
+ * @param {string} [label]
19414
+ */
19415
+ snapshot(label2) {
19416
+ if (!this.enabled || this.applying) return;
19417
+ this._commit(label2 || "change");
19213
19418
  }
19214
- if (closing) closing.forEach((c) => out.push(c.slice()));
19215
- return out;
19216
- }
19217
- function reconcilePathPair(fromD, toD, entries, oldN, newN, isArea) {
19218
- const fromAnalysis = analyzeSeriesPath(fromD, oldN, isArea);
19219
- if (!fromAnalysis) return null;
19220
- const toAnalysis = analyzeSeriesPath(toD, newN, isArea);
19221
- if (!toAnalysis) return null;
19222
- if (fromAnalysis.segType !== toAnalysis.segType) return null;
19223
- if ((fromAnalysis.closing || toAnalysis.closing) && (!fromAnalysis.closing || !toAnalysis.closing || fromAnalysis.closing[0][0] !== toAnalysis.closing[0][0])) {
19224
- return null;
19419
+ /**
19420
+ * @param {boolean} [animate]
19421
+ */
19422
+ undo(animate = true) {
19423
+ if (!this.canUndo()) return;
19424
+ this.pointer--;
19425
+ this._restore(this.stack[this.pointer], animate);
19225
19426
  }
19226
- const fromOwn = entries.map((e2) => e2.oldJ);
19227
- const toOwn = entries.map((e2) => e2.newJ);
19228
- return {
19229
- from: arrayToPath(expandPath(fromAnalysis, fromOwn)),
19230
- toInterp: arrayToPath(expandPath(toAnalysis, toOwn))
19231
- };
19232
- }
19233
- function lengthTransitionEnabled(w) {
19234
- var _a;
19235
- const anim = w.config.chart.animations;
19236
- if (!anim || anim.enabled === false) return false;
19237
- if (!anim.dynamicAnimation || anim.dynamicAnimation.enabled === false) {
19238
- return false;
19427
+ /**
19428
+ * @param {boolean} [animate]
19429
+ */
19430
+ redo(animate = true) {
19431
+ if (!this.canRedo()) return;
19432
+ this.pointer++;
19433
+ this._restore(this.stack[this.pointer], animate);
19239
19434
  }
19240
- const largeThreshold = (_a = anim.largeDatasetThreshold) != null ? _a : 0;
19241
- if (largeThreshold > 0 && w.globals.dataPoints > largeThreshold) return false;
19242
- return !!(Environment.isBrowser() && w.globals.dataChanged && w.globals.shouldAnimate);
19243
- }
19244
- function datumKey(w, realIndex, j2) {
19245
- var _a, _b, _c, _d;
19246
- if ((_a = w.axisFlags) == null ? void 0 : _a.isXNumeric) {
19247
- const sx = (_c = (_b = w.seriesData) == null ? void 0 : _b.seriesX) == null ? void 0 : _c[realIndex];
19248
- if (sx && sx.length && sx[j2] != null) return "x:" + sx[j2];
19435
+ canUndo() {
19436
+ return this.pointer > 0;
19249
19437
  }
19250
- const lbl = (_d = w.globals.labels) == null ? void 0 : _d[j2];
19251
- if (lbl != null && String(lbl) !== "") {
19252
- return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl));
19438
+ canRedo() {
19439
+ return this.pointer > -1 && this.pointer < this.stack.length - 1;
19253
19440
  }
19254
- return "j:" + j2;
19255
- }
19256
- function frameDatumKey(frame, realIndex, j2) {
19257
- var _a, _b;
19258
- if (frame.isXNumeric) {
19259
- const sx = (_a = frame.seriesX) == null ? void 0 : _a[realIndex];
19260
- if (sx && sx.length && sx[j2] != null) return "x:" + sx[j2];
19441
+ /**
19442
+ * @param {string} id
19443
+ * @param {boolean} [animate]
19444
+ */
19445
+ jump(id, animate = true) {
19446
+ const idx = this.stack.findIndex((c) => c.id === id);
19447
+ if (idx === -1 || idx === this.pointer) return;
19448
+ this.pointer = idx;
19449
+ this._restore(this.stack[idx], animate);
19261
19450
  }
19262
- const lbl = (_b = frame.labels) == null ? void 0 : _b[j2];
19263
- if (lbl != null && String(lbl) !== "") {
19264
- return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl));
19451
+ /** Clear the history, keeping the current state as the new baseline. */
19452
+ clear() {
19453
+ clearTimeout(this._coalesceTimer);
19454
+ this._coalesceTimer = null;
19455
+ const current = this.stack[this.pointer];
19456
+ this.stack = current ? [current] : [];
19457
+ this.pointer = this.stack.length - 1;
19458
+ this._emitChange();
19265
19459
  }
19266
- return "j:" + j2;
19267
- }
19268
- function joinKeys(oldKeys, newKeys) {
19269
- const oldIndex = /* @__PURE__ */ new Map();
19270
- oldKeys.forEach((k2, i2) => {
19271
- if (!oldIndex.has(k2)) oldIndex.set(k2, i2);
19272
- });
19273
- const toOld = new Array(newKeys.length);
19274
- const usedOld = /* @__PURE__ */ new Set();
19275
- let prev = -1;
19276
- let ordered = true;
19277
- let identity = oldKeys.length === newKeys.length;
19278
- newKeys.forEach((k2, i2) => {
19279
- const oi = oldIndex.has(k2) && !usedOld.has(oldIndex.get(k2)) ? oldIndex.get(k2) : -1;
19280
- toOld[i2] = oi;
19281
- if (oi !== -1) {
19282
- usedOld.add(oi);
19283
- if (oi < prev) ordered = false;
19284
- prev = oi;
19285
- }
19286
- if (oi !== i2) identity = false;
19287
- });
19288
- const exits = [];
19289
- for (let i2 = 0; i2 < oldKeys.length; i2++) {
19290
- if (!usedOld.has(i2)) exits.push(i2);
19460
+ /**
19461
+ * Group multiple edits into a single undo step. `fn` may be async; await your
19462
+ * updateOptions/updateSeries calls inside it so the intermediate 'updated'
19463
+ * events are suppressed and only one checkpoint is committed afterwards.
19464
+ * @param {() => (void | Promise<any>)} fn
19465
+ * @param {{ label?: string }} [opts]
19466
+ * @returns {Promise<void>}
19467
+ */
19468
+ transaction(fn, opts = {}) {
19469
+ if (typeof fn !== "function") return Promise.resolve();
19470
+ const wasBatching = this._batching;
19471
+ this._batching = true;
19472
+ return Promise.resolve().then(() => fn()).finally(() => {
19473
+ this._batching = wasBatching;
19474
+ if (!wasBatching) this._commit(opts.label || "transaction", true);
19475
+ });
19291
19476
  }
19292
- return { toOld, exits, ordered, changed: !identity };
19293
- }
19294
- function uniquifyKeys(keys) {
19295
- const seen = /* @__PURE__ */ new Map();
19296
- return keys.map((k2) => {
19297
- const count = seen.get(k2) || 0;
19298
- seen.set(k2, count + 1);
19299
- return count === 0 ? k2 : `${k2}#${count}`;
19300
- });
19301
- }
19302
- function seriesJoin(w, realIndex, includeIdentity = false, allowReorder = false) {
19303
- var _a, _b;
19304
- if (!lengthTransitionEnabled(w)) return null;
19305
- const frame = w.globals.prevStreamFrame;
19306
- if (!frame) return null;
19307
- const oldY = (_a = frame.seriesY) == null ? void 0 : _a[realIndex];
19308
- const newY = (_b = w.seriesData.series) == null ? void 0 : _b[realIndex];
19309
- if (!Array.isArray(oldY) || !Array.isArray(newY)) return null;
19310
- if (!oldY.length || !newY.length) return null;
19311
- const oldKeys = uniquifyKeys(
19312
- oldY.map((_2, j2) => frameDatumKey(frame, realIndex, j2))
19313
- );
19314
- const newKeys = uniquifyKeys(newY.map((_2, j2) => datumKey(w, realIndex, j2)));
19315
- const join = joinKeys(oldKeys, newKeys);
19316
- if (!join.ordered && !allowReorder) return null;
19317
- if (!join.changed && !includeIdentity) return null;
19318
- return { join, oldKeys, newKeys };
19319
- }
19320
- function morphEasing(w) {
19321
- var _a, _b;
19322
- const anim = w.config.chart.animations;
19323
- return resolveEasing((_b = (_a = anim.dynamicAnimation) == null ? void 0 : _a.easing) != null ? _b : anim.easing);
19324
- }
19325
- function reconcileSeriesPaths(w, { type, realIndex, pathFromLine, pathFromArea, linePaths, areaPaths }) {
19326
- var _a, _b;
19327
- const sj = seriesJoin(w, realIndex);
19328
- if (!sj) return null;
19329
- const { join, oldKeys, newKeys } = sj;
19330
- const frame = w.globals.prevStreamFrame;
19331
- if (!frame) return null;
19332
- const oldY = (_a = frame.seriesY) == null ? void 0 : _a[realIndex];
19333
- const newY = (_b = w.seriesData.series) == null ? void 0 : _b[realIndex];
19334
- if (oldY.length < 2 || newY.length < 2) return null;
19335
- if (oldY.some((v) => v === null) || newY.some((v) => v === null)) return null;
19336
- const entries = buildUnionEntries(join, oldKeys.length);
19337
- const out = {};
19338
- if (Array.isArray(linePaths) && linePaths.length === 1 && pathFromLine) {
19339
- out.line = reconcilePathPair(
19340
- pathFromLine,
19341
- linePaths[0],
19342
- entries,
19343
- oldKeys.length,
19344
- newKeys.length,
19345
- false
19346
- );
19477
+ /**
19478
+ * @returns {{ id: string, label: string, at: number }[]}
19479
+ */
19480
+ entries() {
19481
+ return this.stack.map((c) => ({ id: c.id, label: c.label, at: c.at }));
19347
19482
  }
19348
- if (type === "area" && Array.isArray(areaPaths) && areaPaths.length === 1 && pathFromArea) {
19349
- out.area = reconcilePathPair(
19350
- pathFromArea,
19351
- areaPaths[0],
19352
- entries,
19353
- oldKeys.length,
19354
- newKeys.length,
19355
- true
19356
- );
19483
+ /** Lightweight state for the historyChange event / a history-rail UI. */
19484
+ state() {
19485
+ return {
19486
+ canUndo: this.canUndo(),
19487
+ canRedo: this.canRedo(),
19488
+ index: this.pointer,
19489
+ length: this.stack.length
19490
+ };
19491
+ }
19492
+ _emitChange() {
19493
+ this.ctx.events.fireEvent("historyChange", [this.ctx, this.state()]);
19494
+ }
19495
+ /** Drop the stack + detach listeners (called on full destroy). */
19496
+ teardown() {
19497
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19498
+ clearTimeout(this._coalesceTimer);
19499
+ clearTimeout(this._settleTimer);
19500
+ this._coalesceTimer = null;
19501
+ this._settleTimer = null;
19502
+ this._unwireKeyboard();
19503
+ if (this._wired) {
19504
+ (_b = (_a = this.ctx).removeEventListener) == null ? void 0 : _b.call(_a, "mounted", this._onMounted);
19505
+ (_d = (_c = this.ctx).removeEventListener) == null ? void 0 : _d.call(_c, "updated", this._onUpdated);
19506
+ (_f = (_e = this.ctx).removeEventListener) == null ? void 0 : _f.call(_e, "scrolled", this._onUpdated);
19507
+ (_h = (_g = this.ctx).removeEventListener) == null ? void 0 : _h.call(_g, "dataPointSelection", this._onSelection);
19508
+ }
19509
+ this.stack = [];
19510
+ this.pointer = -1;
19511
+ this._wired = false;
19357
19512
  }
19358
- if (!out.line && !out.area) return null;
19359
- return out;
19360
19513
  }
19514
+ ApexCharts__default.registerFeatures({ history: History });
19515
+ const DataLabels = ApexCharts.__apex_DataLabels;
19361
19516
  function detectStreamScroll(w, realIndex, newXPixels, newYPixels) {
19362
19517
  var _a;
19363
19518
  const gl = w.globals;
@@ -21324,9 +21479,9 @@ function wrapParentTip(hf, tm) {
21324
21479
  }
21325
21480
  const v = (_c = tm._tip()) == null ? void 0 : _c.querySelector(".apexcharts-tooltip-text-y-value");
21326
21481
  if (!v) return;
21327
- let rest = `/ ${tm._formatValue(h.total)}`;
21482
+ let rest = `/ ${tm._formatValue(h.total, node._si)}`;
21328
21483
  if (tt.share && h.total) rest += ` · ${Math.round(h.share * 100)}%`;
21329
- v.innerHTML = `${tm._formatValue(h.value)} <span class="apexcharts-highlight-total" style="font-weight: 400; opacity: 0.6">${rest}</span>`;
21484
+ v.innerHTML = `${tm._formatValue(h.value, node._si)} <span class="apexcharts-highlight-total" style="font-weight: 400; opacity: 0.6">${rest}</span>`;
21330
21485
  };
21331
21486
  }
21332
21487
  function tone(rec, fore) {
@@ -21864,7 +22019,7 @@ class HighlightFilter {
21864
22019
  const active = hd.active;
21865
22020
  if (!active && !(prevMap == null ? void 0 : prevMap.size)) return void 0;
21866
22021
  const { j: j2, realIndex } = o2;
21867
- const key = BAR + `${realIndex}::${datumKey(w, realIndex, j2)}`;
22022
+ const key = BAR + `${realIndex}::${bar._keys(realIndex)[j2]}`;
21868
22023
  this._seen.add(key);
21869
22024
  const prev = prevMap == null ? void 0 : prevMap.get(key);
21870
22025
  const p = active ? (_a = hd.parts[realIndex]) == null ? void 0 : _a[j2] : null;
@@ -22174,14 +22329,14 @@ class HighlightFilter {
22174
22329
  }
22175
22330
  /**
22176
22331
  * The stack a stacked bar datum joins, as the renderer resolves it
22177
- * (BarStacked drawStackedColumnPaths): its series group, then its x value
22178
- * on a numeric or datetime column axis, else its index.
22332
+ * (BarStacked): its series group, then its x value on a numeric or
22333
+ * datetime axis, else its index.
22179
22334
  * @param {number} i @param {number} j
22180
22335
  */
22181
22336
  _barKey(i2, j2) {
22182
22337
  var _a;
22183
22338
  const w = this.w;
22184
- return w.labelData.seriesGroupIndex[i2] + "|" + (!w.config.plotOptions.bar.horizontal && w.axisFlags.isXNumeric ? (_a = w.seriesData.seriesX[i2]) == null ? void 0 : _a[j2] : j2);
22339
+ return w.labelData.seriesGroupIndex[i2] + "|" + (w.axisFlags.isXNumeric ? (_a = w.seriesData.seriesX[i2]) == null ? void 0 : _a[j2] : j2);
22185
22340
  }
22186
22341
  /**
22187
22342
  * Line.draw, once per series right after its whole has been rendered and
@@ -22245,7 +22400,7 @@ class HighlightFilter {
22245
22400
  const exit = !to && !!(prev == null ? void 0 : prev.d) && animate;
22246
22401
  const base = active && animate && (exit || to && !(prev == null ? void 0 : prev.d) && (prev || this.cfg.enter === "baseline")) ? this._lineBase(ln, type, series, i2, realIndex, ti, has ? raw : null) : null;
22247
22402
  if (pp && w.config.chart.stacked) {
22248
- this._swap(ln, () => ln._recordStackTops(realIndex, pp.yArrj));
22403
+ this._swap(ln, () => ln._recordStackTops(realIndex, pp.yArrj, pp.S[i2]));
22249
22404
  this._stkPrev.push(pp.yArrj);
22250
22405
  }
22251
22406
  if (exit) {