@primeui/chart-core 0.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +1 -0
  3. package/dist/animations/index.d.mts +136 -0
  4. package/dist/animations/index.mjs +18 -0
  5. package/dist/annotation.utils-Bm0lOO1o.d.mts +290 -0
  6. package/dist/borderRadius.utils-Cz73LLR_.d.mts +54 -0
  7. package/dist/canvas-D4vigq47.d.mts +34 -0
  8. package/dist/canvas.utils-D2WHi2gL.d.mts +167 -0
  9. package/dist/cartesian/index.d.mts +94 -0
  10. package/dist/cartesian/index.mjs +93 -0
  11. package/dist/chunk-22ST6YPP.mjs +304 -0
  12. package/dist/chunk-2QK2KOBN.mjs +10 -0
  13. package/dist/chunk-2QRS4YQ5.mjs +18 -0
  14. package/dist/chunk-3FFJEX4A.mjs +261 -0
  15. package/dist/chunk-3IYSJ2U7.mjs +567 -0
  16. package/dist/chunk-3OZLP4I4.mjs +190 -0
  17. package/dist/chunk-3WEMHXZI.mjs +198 -0
  18. package/dist/chunk-3Z62EUJN.mjs +138 -0
  19. package/dist/chunk-4C6EVJ54.mjs +362 -0
  20. package/dist/chunk-53HW45JB.mjs +102 -0
  21. package/dist/chunk-55Y3WI6S.mjs +186 -0
  22. package/dist/chunk-5JCI2DEB.mjs +97 -0
  23. package/dist/chunk-66T4MRC5.mjs +113 -0
  24. package/dist/chunk-6HSEJLSR.mjs +376 -0
  25. package/dist/chunk-6STOLMCA.mjs +187 -0
  26. package/dist/chunk-7CMVDIOU.mjs +54 -0
  27. package/dist/chunk-7QQ6ETB4.mjs +228 -0
  28. package/dist/chunk-A6ZQZFL2.mjs +272 -0
  29. package/dist/chunk-ADKLH73T.mjs +1 -0
  30. package/dist/chunk-AGU3NG6D.mjs +22 -0
  31. package/dist/chunk-AHYIS6EB.mjs +230 -0
  32. package/dist/chunk-AP3UYWYT.mjs +4 -0
  33. package/dist/chunk-ARB5T6MP.mjs +326 -0
  34. package/dist/chunk-ARRGOEFX.mjs +585 -0
  35. package/dist/chunk-AUF4CHDP.mjs +422 -0
  36. package/dist/chunk-B4FTADAZ.mjs +561 -0
  37. package/dist/chunk-BABQKA6K.mjs +339 -0
  38. package/dist/chunk-BETFQBM2.mjs +197 -0
  39. package/dist/chunk-BKP26M4K.mjs +413 -0
  40. package/dist/chunk-BZN2QHGP.mjs +200 -0
  41. package/dist/chunk-C36VWQ7A.mjs +86 -0
  42. package/dist/chunk-CHW4RKY3.mjs +16 -0
  43. package/dist/chunk-CINXJIRR.mjs +120 -0
  44. package/dist/chunk-DN6AXQYZ.mjs +667 -0
  45. package/dist/chunk-DP2IZNN3.mjs +92 -0
  46. package/dist/chunk-DTWTCFRG.mjs +119 -0
  47. package/dist/chunk-EAMUNLRU.mjs +172 -0
  48. package/dist/chunk-EDAKJLNA.mjs +17 -0
  49. package/dist/chunk-ERVQB2VZ.mjs +59 -0
  50. package/dist/chunk-FFMT6OCO.mjs +92 -0
  51. package/dist/chunk-FHTC2YDB.mjs +102 -0
  52. package/dist/chunk-FRST55HY.mjs +16 -0
  53. package/dist/chunk-HDFGCN2F.mjs +132 -0
  54. package/dist/chunk-IEGLX7VL.mjs +42 -0
  55. package/dist/chunk-ILUWFYGY.mjs +220 -0
  56. package/dist/chunk-IXOWSEHO.mjs +114 -0
  57. package/dist/chunk-J4RI2C2G.mjs +172 -0
  58. package/dist/chunk-J65DBT4R.mjs +13 -0
  59. package/dist/chunk-JGOVWSKH.mjs +179 -0
  60. package/dist/chunk-JO7VACY2.mjs +25 -0
  61. package/dist/chunk-JWFBOPM6.mjs +122 -0
  62. package/dist/chunk-KNDZP446.mjs +895 -0
  63. package/dist/chunk-KP2TWD4Z.mjs +90 -0
  64. package/dist/chunk-KQIFO5I3.mjs +225 -0
  65. package/dist/chunk-KVDEROP6.mjs +59 -0
  66. package/dist/chunk-LKC7MZKK.mjs +87 -0
  67. package/dist/chunk-LVMDQ4OJ.mjs +305 -0
  68. package/dist/chunk-M7B3JF43.mjs +90 -0
  69. package/dist/chunk-MTGMXRNF.mjs +136 -0
  70. package/dist/chunk-N3TIT3OH.mjs +1040 -0
  71. package/dist/chunk-NHRK5KU2.mjs +890 -0
  72. package/dist/chunk-NKUYIWAP.mjs +243 -0
  73. package/dist/chunk-NPDZLYIF.mjs +238 -0
  74. package/dist/chunk-O2X6FF45.mjs +499 -0
  75. package/dist/chunk-OGJ6IIBW.mjs +176 -0
  76. package/dist/chunk-OHGCZZPZ.mjs +403 -0
  77. package/dist/chunk-OWW3K55O.mjs +351 -0
  78. package/dist/chunk-OXTFAWSK.mjs +60 -0
  79. package/dist/chunk-PLSDU3C2.mjs +890 -0
  80. package/dist/chunk-PRDVPOZX.mjs +223 -0
  81. package/dist/chunk-Q6PPVIHU.mjs +21 -0
  82. package/dist/chunk-QQBXUDM4.mjs +885 -0
  83. package/dist/chunk-QS76E3TD.mjs +111 -0
  84. package/dist/chunk-QWQ6HY4I.mjs +209 -0
  85. package/dist/chunk-R6Y3R7EW.mjs +135 -0
  86. package/dist/chunk-RBLZRT5K.mjs +190 -0
  87. package/dist/chunk-RO4N6YFS.mjs +167 -0
  88. package/dist/chunk-RQ3CKQOX.mjs +984 -0
  89. package/dist/chunk-SALTGZFR.mjs +208 -0
  90. package/dist/chunk-SANZPAJ4.mjs +14 -0
  91. package/dist/chunk-SDBPQ5CF.mjs +624 -0
  92. package/dist/chunk-SSLTFJ3U.mjs +364 -0
  93. package/dist/chunk-SXHVDJGF.mjs +77 -0
  94. package/dist/chunk-TA4MVAEX.mjs +243 -0
  95. package/dist/chunk-TAHCOZHF.mjs +1772 -0
  96. package/dist/chunk-TQ6S34QZ.mjs +152 -0
  97. package/dist/chunk-UPRXABX5.mjs +90 -0
  98. package/dist/chunk-VGLSBZDN.mjs +71 -0
  99. package/dist/chunk-VN7CKCSE.mjs +364 -0
  100. package/dist/chunk-VVI3OBPJ.mjs +524 -0
  101. package/dist/chunk-VWF57TS3.mjs +62 -0
  102. package/dist/chunk-WA3OVISZ.mjs +179 -0
  103. package/dist/chunk-WCG35U6M.mjs +964 -0
  104. package/dist/chunk-WFTX4AQJ.mjs +194 -0
  105. package/dist/chunk-WFVOQ2QZ.mjs +18 -0
  106. package/dist/chunk-WH3C3Y7P.mjs +149 -0
  107. package/dist/chunk-WPFUV7K3.mjs +488 -0
  108. package/dist/chunk-WRULPWHD.mjs +492 -0
  109. package/dist/chunk-WS64BZXT.mjs +1 -0
  110. package/dist/chunk-WY4AURRE.mjs +2419 -0
  111. package/dist/chunk-WYLILAOO.mjs +167 -0
  112. package/dist/chunk-X4D7FKUS.mjs +62 -0
  113. package/dist/chunk-X7T34OLW.mjs +139 -0
  114. package/dist/chunk-XIHBK5D3.mjs +68 -0
  115. package/dist/chunk-XQQCGFYB.mjs +50 -0
  116. package/dist/chunk-XTVE4P3L.mjs +214 -0
  117. package/dist/chunk-XUAASRXW.mjs +579 -0
  118. package/dist/chunk-Y3L3D4GQ.mjs +685 -0
  119. package/dist/chunk-YBJ56XJS.mjs +132 -0
  120. package/dist/chunk-ZQFK6CAE.mjs +1 -0
  121. package/dist/chunk-ZT2Z7ERM.mjs +874 -0
  122. package/dist/chunk-ZTL2FQEW.mjs +714 -0
  123. package/dist/circular/arc/index.d.mts +8 -0
  124. package/dist/circular/arc/index.mjs +3 -0
  125. package/dist/circular/index.d.mts +44 -0
  126. package/dist/circular/index.mjs +13 -0
  127. package/dist/collect.utils-DiKB4ciO.d.mts +12 -0
  128. package/dist/computeChartState-BTVIqwyO.d.mts +304 -0
  129. package/dist/controller-BJE1AZ3q.d.mts +82 -0
  130. package/dist/controller-BoNigQJr.d.mts +63 -0
  131. package/dist/controllers/index.d.mts +16 -0
  132. package/dist/controllers/index.mjs +110 -0
  133. package/dist/datalabel.utils-CkjGeB8S.d.mts +122 -0
  134. package/dist/decimation.utils-CcvJVhI4.d.mts +244 -0
  135. package/dist/geometry-DUUQJXVM.d.mts +60 -0
  136. package/dist/index-DseIZa1j.d.mts +167 -0
  137. package/dist/index.d.mts +88 -0
  138. package/dist/index.mjs +110 -0
  139. package/dist/orchestrator/index.d.mts +264 -0
  140. package/dist/orchestrator/index.mjs +33 -0
  141. package/dist/plugins/index.d.mts +18 -0
  142. package/dist/plugins/index.mjs +1 -0
  143. package/dist/property-animations-D433wXzz.d.mts +580 -0
  144. package/dist/property-store-NORUWFND.d.mts +17 -0
  145. package/dist/radial/index.d.mts +14 -0
  146. package/dist/radial/index.mjs +37 -0
  147. package/dist/renderers/axis/index.d.mts +39 -0
  148. package/dist/renderers/axis/index.mjs +8 -0
  149. package/dist/renderers/circular/index.d.mts +13 -0
  150. package/dist/renderers/circular/index.mjs +13 -0
  151. package/dist/renderers/index.d.mts +83 -0
  152. package/dist/renderers/index.mjs +75 -0
  153. package/dist/renderers/navigator/index.d.mts +103 -0
  154. package/dist/renderers/navigator/index.mjs +8 -0
  155. package/dist/resize.utils-D_2qm6rv.d.mts +142 -0
  156. package/dist/ring.utils-DXvrxMkU.d.mts +138 -0
  157. package/dist/scale-KFv30jqZ.d.mts +307 -0
  158. package/dist/scales-Drf8AIhL.d.mts +75 -0
  159. package/dist/series/bar/canvas/index.d.mts +8 -0
  160. package/dist/series/bar/canvas/index.mjs +10 -0
  161. package/dist/series/bar/controller/index.d.mts +105 -0
  162. package/dist/series/bar/controller/index.mjs +44 -0
  163. package/dist/series/bar/controller-canvas/index.d.mts +7 -0
  164. package/dist/series/bar/controller-canvas/index.mjs +49 -0
  165. package/dist/series/bar/controller-svg/index.d.mts +7 -0
  166. package/dist/series/bar/controller-svg/index.mjs +49 -0
  167. package/dist/series/bar/index.d.mts +60 -0
  168. package/dist/series/bar/index.mjs +13 -0
  169. package/dist/series/bar/svg/index.d.mts +8 -0
  170. package/dist/series/bar/svg/index.mjs +11 -0
  171. package/dist/series/candlestick/canvas/index.d.mts +8 -0
  172. package/dist/series/candlestick/canvas/index.mjs +8 -0
  173. package/dist/series/candlestick/controller/index.d.mts +123 -0
  174. package/dist/series/candlestick/controller/index.mjs +40 -0
  175. package/dist/series/candlestick/controller-canvas/index.d.mts +7 -0
  176. package/dist/series/candlestick/controller-canvas/index.mjs +45 -0
  177. package/dist/series/candlestick/controller-svg/index.d.mts +7 -0
  178. package/dist/series/candlestick/controller-svg/index.mjs +45 -0
  179. package/dist/series/candlestick/index.d.mts +11 -0
  180. package/dist/series/candlestick/index.mjs +10 -0
  181. package/dist/series/candlestick/svg/index.d.mts +8 -0
  182. package/dist/series/candlestick/svg/index.mjs +8 -0
  183. package/dist/series/heatmap/canvas/index.d.mts +16 -0
  184. package/dist/series/heatmap/canvas/index.mjs +9 -0
  185. package/dist/series/heatmap/controller/index.d.mts +110 -0
  186. package/dist/series/heatmap/controller/index.mjs +23 -0
  187. package/dist/series/heatmap/controller-canvas/index.d.mts +7 -0
  188. package/dist/series/heatmap/controller-canvas/index.mjs +28 -0
  189. package/dist/series/heatmap/controller-svg/index.d.mts +7 -0
  190. package/dist/series/heatmap/controller-svg/index.mjs +28 -0
  191. package/dist/series/heatmap/index.d.mts +34 -0
  192. package/dist/series/heatmap/index.mjs +13 -0
  193. package/dist/series/heatmap/svg/index.d.mts +15 -0
  194. package/dist/series/heatmap/svg/index.mjs +10 -0
  195. package/dist/series/line/canvas/index.d.mts +6 -0
  196. package/dist/series/line/canvas/index.mjs +9 -0
  197. package/dist/series/line/controller/index.d.mts +111 -0
  198. package/dist/series/line/controller/index.mjs +47 -0
  199. package/dist/series/line/controller-canvas/index.d.mts +7 -0
  200. package/dist/series/line/controller-canvas/index.mjs +54 -0
  201. package/dist/series/line/controller-svg/index.d.mts +7 -0
  202. package/dist/series/line/controller-svg/index.mjs +54 -0
  203. package/dist/series/line/index.d.mts +49 -0
  204. package/dist/series/line/index.mjs +13 -0
  205. package/dist/series/line/svg/index.d.mts +6 -0
  206. package/dist/series/line/svg/index.mjs +9 -0
  207. package/dist/series/pie/canvas/index.d.mts +8 -0
  208. package/dist/series/pie/canvas/index.mjs +10 -0
  209. package/dist/series/pie/controller/index.d.mts +174 -0
  210. package/dist/series/pie/controller/index.mjs +110 -0
  211. package/dist/series/pie/controller-canvas/index.d.mts +8 -0
  212. package/dist/series/pie/controller-canvas/index.mjs +119 -0
  213. package/dist/series/pie/controller-svg/index.d.mts +8 -0
  214. package/dist/series/pie/controller-svg/index.mjs +118 -0
  215. package/dist/series/pie/index.d.mts +59 -0
  216. package/dist/series/pie/index.mjs +15 -0
  217. package/dist/series/pie/svg/index.d.mts +6 -0
  218. package/dist/series/pie/svg/index.mjs +11 -0
  219. package/dist/series/polar/canvas/index.d.mts +6 -0
  220. package/dist/series/polar/canvas/index.mjs +7 -0
  221. package/dist/series/polar/controller/index.d.mts +102 -0
  222. package/dist/series/polar/controller/index.mjs +46 -0
  223. package/dist/series/polar/controller-canvas/index.d.mts +8 -0
  224. package/dist/series/polar/controller-canvas/index.mjs +52 -0
  225. package/dist/series/polar/controller-svg/index.d.mts +8 -0
  226. package/dist/series/polar/controller-svg/index.mjs +52 -0
  227. package/dist/series/polar/index.d.mts +25 -0
  228. package/dist/series/polar/index.mjs +16 -0
  229. package/dist/series/polar/svg/index.d.mts +10 -0
  230. package/dist/series/polar/svg/index.mjs +8 -0
  231. package/dist/series/radar/canvas/index.d.mts +6 -0
  232. package/dist/series/radar/canvas/index.mjs +8 -0
  233. package/dist/series/radar/controller/index.d.mts +121 -0
  234. package/dist/series/radar/controller/index.mjs +43 -0
  235. package/dist/series/radar/controller-canvas/index.d.mts +8 -0
  236. package/dist/series/radar/controller-canvas/index.mjs +51 -0
  237. package/dist/series/radar/controller-svg/index.d.mts +8 -0
  238. package/dist/series/radar/controller-svg/index.mjs +51 -0
  239. package/dist/series/radar/index.d.mts +40 -0
  240. package/dist/series/radar/index.mjs +13 -0
  241. package/dist/series/radar/svg/index.d.mts +12 -0
  242. package/dist/series/radar/svg/index.mjs +9 -0
  243. package/dist/series/scatter/canvas/index.d.mts +8 -0
  244. package/dist/series/scatter/canvas/index.mjs +11 -0
  245. package/dist/series/scatter/controller/index.d.mts +124 -0
  246. package/dist/series/scatter/controller/index.mjs +44 -0
  247. package/dist/series/scatter/controller-canvas/index.d.mts +7 -0
  248. package/dist/series/scatter/controller-canvas/index.mjs +51 -0
  249. package/dist/series/scatter/controller-svg/index.d.mts +7 -0
  250. package/dist/series/scatter/controller-svg/index.mjs +51 -0
  251. package/dist/series/scatter/index.d.mts +25 -0
  252. package/dist/series/scatter/index.mjs +14 -0
  253. package/dist/series/scatter/svg/index.d.mts +8 -0
  254. package/dist/series/scatter/svg/index.mjs +12 -0
  255. package/dist/series/treemap/canvas/index.d.mts +50 -0
  256. package/dist/series/treemap/canvas/index.mjs +10 -0
  257. package/dist/series/treemap/controller/index.d.mts +130 -0
  258. package/dist/series/treemap/controller/index.mjs +20 -0
  259. package/dist/series/treemap/controller-canvas/index.d.mts +7 -0
  260. package/dist/series/treemap/controller-canvas/index.mjs +25 -0
  261. package/dist/series/treemap/controller-svg/index.d.mts +7 -0
  262. package/dist/series/treemap/controller-svg/index.mjs +25 -0
  263. package/dist/series/treemap/index.d.mts +15 -0
  264. package/dist/series/treemap/index.mjs +12 -0
  265. package/dist/series/treemap/svg/index.d.mts +15 -0
  266. package/dist/series/treemap/svg/index.mjs +9 -0
  267. package/dist/slices-DtewiwJx.d.mts +72 -0
  268. package/dist/spatialIndex.utils-B_GJkotZ.d.mts +5 -0
  269. package/dist/squarify.utils-B9CQBpa1.d.mts +50 -0
  270. package/dist/stacking-CChuAcLN.d.mts +319 -0
  271. package/dist/streaming.utils-DH-g1gNP.d.mts +49 -0
  272. package/dist/sync/index.d.mts +130 -0
  273. package/dist/sync/index.mjs +5 -0
  274. package/dist/tooltip.renderer-D5wpSlBa.d.mts +210 -0
  275. package/dist/utils/color/index.d.mts +58 -0
  276. package/dist/utils/color/index.mjs +4 -0
  277. package/dist/utils/data/index.d.mts +180 -0
  278. package/dist/utils/data/index.mjs +7 -0
  279. package/dist/utils/export/index.d.mts +14 -0
  280. package/dist/utils/export/index.mjs +6 -0
  281. package/dist/utils/index.d.mts +49 -0
  282. package/dist/utils/index.mjs +29 -0
  283. package/dist/utils/interaction/index.d.mts +255 -0
  284. package/dist/utils/interaction/index.mjs +9 -0
  285. package/dist/utils/layout/index.d.mts +3 -0
  286. package/dist/utils/layout/index.mjs +10 -0
  287. package/dist/utils/math/index.d.mts +162 -0
  288. package/dist/utils/math/index.mjs +1 -0
  289. package/dist/utils/render/index.d.mts +19 -0
  290. package/dist/utils/render/index.mjs +3 -0
  291. package/dist/utils/specialized/index.d.mts +37 -0
  292. package/dist/utils/specialized/index.mjs +66 -0
  293. package/dist/utils/text/index.d.mts +39 -0
  294. package/dist/utils/text/index.mjs +8 -0
  295. package/dist/utils/theme/index.d.mts +295 -0
  296. package/dist/utils/theme/index.mjs +5 -0
  297. package/dist/utils/zoom/index.d.mts +90 -0
  298. package/dist/utils/zoom/index.mjs +3 -0
  299. package/package.json +56 -0
@@ -0,0 +1,1772 @@
1
+ import { detectDatasetTypes, processClickCore, processMouseLeaveCore, processMouseMoveCore, computeThemeDependentFields, computeChartState } from './chunk-3IYSJ2U7.mjs';
2
+ import { calculateRingLayouts, buildPieVisibilityMap, applyRingLayout, computeSliceRadiusRatios, buildSliceStyleData, buildRingTarget, buildHitTestStates } from './chunk-ARB5T6MP.mjs';
3
+ import { computeCartesianLayout } from './chunk-WY4AURRE.mjs';
4
+ import { boxAreaToBounds, computeLabelAwareRadius, buildPieDataLabelLayout } from './chunk-4C6EVJ54.mjs';
5
+ import { calculatePieLayout } from './chunk-RBLZRT5K.mjs';
6
+ import { findSliceAtPoint, isFullCircle } from './chunk-LVMDQ4OJ.mjs';
7
+ import { AnimatedScene, resolveAnimatedProps, PIE_ANIMATABLE_PROPS } from './chunk-VN7CKCSE.mjs';
8
+ import { resolveLegendHoverTarget, resolveLegendClickAction } from './chunk-ZT2Z7ERM.mjs';
9
+ import { reconcileSvgChildren } from './chunk-KP2TWD4Z.mjs';
10
+ import { parseAnimationConfig } from './chunk-3WEMHXZI.mjs';
11
+ import { ResizeDebouncer } from './chunk-KVDEROP6.mjs';
12
+ import { processDimensions } from './chunk-YBJ56XJS.mjs';
13
+ import { cancelRaf, raf } from './chunk-EDAKJLNA.mjs';
14
+ import { INACTIVE_STATE, getNavigableDatasets, handleKeyNavigation, generateAnnouncement } from './chunk-JWFBOPM6.mjs';
15
+ import { hoverConfigToFullEffect } from './chunk-BZN2QHGP.mjs';
16
+ import { buildAccessibilityRenderContext, generateSeriesDescription } from './chunk-OGJ6IIBW.mjs';
17
+ import { collectItems } from './chunk-FFMT6OCO.mjs';
18
+ import { normalizeDataLabelConfig, formatDataLabelText } from './chunk-OXTFAWSK.mjs';
19
+ import { formatCompactValue } from './chunk-XTVE4P3L.mjs';
20
+ import { DEFAULT_HOVER_BRIGHTNESS } from './chunk-NKUYIWAP.mjs';
21
+ import { isDrilldownEnabled, getDrilldownRootLabel } from './chunk-WA3OVISZ.mjs';
22
+ import { FIELD_DEFAULTS, resolveAccessor, makeItemContext, getColorValue, resolveSwatchColor, asGradient, getDefaultColor, defaultLightTheme } from './chunk-O2X6FF45.mjs';
23
+ import { createSvgElement } from './chunk-SSLTFJ3U.mjs';
24
+ import { shallowEqual, warnChartDevOnce } from './chunk-RQ3CKQOX.mjs';
25
+ import { defineComponent } from '@primeui/core';
26
+
27
+ // src/controllers/AxisRegistrationController.ts
28
+ var AxisRegistrationController = class {
29
+ constructor(options) {
30
+ this.stableProps = null;
31
+ this.options = options;
32
+ }
33
+ mount(props) {
34
+ this.stableProps = props;
35
+ if (this.options.axis === "x") {
36
+ this.options.registerX(this.options.id, props);
37
+ } else {
38
+ this.options.registerY(this.options.id, props);
39
+ }
40
+ }
41
+ update(props) {
42
+ if (this.stableProps === null) return;
43
+ const propsChanged = !shallowEqual(this.stableProps, props);
44
+ if (!propsChanged) return;
45
+ this.stableProps = props;
46
+ if (this.options.axis === "x") {
47
+ this.options.updateX(this.options.id, props);
48
+ } else {
49
+ this.options.updateY(this.options.id, props);
50
+ }
51
+ }
52
+ unmount() {
53
+ if (this.options.axis === "x") {
54
+ this.options.unregisterX(this.options.id);
55
+ } else {
56
+ this.options.unregisterY(this.options.id);
57
+ }
58
+ }
59
+ // Bypasses the shallowEqual gate — used by chart.redraw() to flush fresh
60
+ // function references (callbacks, formatters) that shallowEqual would suppress.
61
+ forceRefresh(props) {
62
+ if (this.options.axis === "x") {
63
+ this.options.updateX(this.options.id, props);
64
+ } else {
65
+ this.options.updateY(this.options.id, props);
66
+ }
67
+ }
68
+ };
69
+
70
+ // src/controllers/DatasetRegistrationController.ts
71
+ var DatasetRegistrationController = class {
72
+ constructor(options) {
73
+ this.stableProps = null;
74
+ this.prevType = void 0;
75
+ this.options = options;
76
+ }
77
+ mount(props, extras) {
78
+ this.stableProps = props;
79
+ const resolvedType = this.resolveType(props);
80
+ this.prevType = resolvedType;
81
+ this.options.register(this.options.id, resolvedType, this.mergeProps(props, extras));
82
+ }
83
+ update(props, extras) {
84
+ if (this.stableProps === null) return;
85
+ const propsChanged = !shallowEqual(this.stableProps, props);
86
+ if (propsChanged) {
87
+ this.stableProps = props;
88
+ }
89
+ const stable = this.stableProps;
90
+ if (this.options.typeResolver) {
91
+ const resolvedType = this.resolveType(stable);
92
+ if (resolvedType !== this.prevType) {
93
+ this.options.unregister(this.options.id);
94
+ this.prevType = resolvedType;
95
+ this.options.register(this.options.id, resolvedType, this.mergeProps(stable, extras));
96
+ return;
97
+ }
98
+ }
99
+ if (!propsChanged) return;
100
+ this.options.update(this.options.id, this.mergeProps(stable, extras));
101
+ }
102
+ unmount() {
103
+ this.options.unregister(this.options.id);
104
+ }
105
+ // Bypasses the shallowEqual gate — used by chart.redraw() to flush fresh
106
+ // function references (callbacks, formatters) that shallowEqual would suppress.
107
+ forceRefresh(props, extras) {
108
+ this.options.update(this.options.id, this.mergeProps(props, extras));
109
+ }
110
+ resolveType(props) {
111
+ return this.options.typeResolver ? this.options.typeResolver(props) : this.options.type;
112
+ }
113
+ mergeProps(props, extras) {
114
+ return {
115
+ ...props,
116
+ order: extras?.order,
117
+ stackId: extras?.stackId,
118
+ rangeId: extras?.rangeId
119
+ };
120
+ }
121
+ };
122
+
123
+ // src/controllers/CartesianLayoutController.ts
124
+ var CartesianLayoutController = class {
125
+ constructor() {
126
+ this._lastVisibleLayouts = /* @__PURE__ */ new Map();
127
+ this._prevInputs = null;
128
+ this._prevResult = null;
129
+ }
130
+ /**
131
+ * Compute cartesian layout with built-in memoization.
132
+ * Skips recomputation when layout-affecting inputs are reference-equal to the
133
+ * previous call — `hover` is intentionally excluded so pointer movement
134
+ * does not trigger an expensive full layout pass.
135
+ */
136
+ compute(input) {
137
+ if (this._prevInputs !== null && this._prevResult !== null) {
138
+ if (this._inputsEqual(this._prevInputs, input)) {
139
+ return this._prevResult;
140
+ }
141
+ if (this._inputsEqualExceptTheme(this._prevInputs, input)) {
142
+ const refreshed = { ...this._prevResult, context: { ...this._prevResult.context, theme: input.theme } };
143
+ this._prevInputs = input;
144
+ this._prevResult = refreshed;
145
+ return refreshed;
146
+ }
147
+ }
148
+ const { hoverConfig, ...rest } = input;
149
+ const result = computeCartesianLayout({ ...rest, hoverConfig: hoverConfig ? hoverConfigToFullEffect(hoverConfig) : void 0 }, this._lastVisibleLayouts);
150
+ this._prevInputs = input;
151
+ this._prevResult = result;
152
+ return result;
153
+ }
154
+ destroy() {
155
+ this._lastVisibleLayouts.clear();
156
+ this._prevInputs = null;
157
+ this._prevResult = null;
158
+ }
159
+ _inputsEqual(a, b) {
160
+ return a.theme === b.theme && this._inputsEqualExceptTheme(a, b);
161
+ }
162
+ // Same as `_inputsEqual` but ignores `theme` — used to detect a color-only theme
163
+ // change so the controller can refresh `context.theme` without recomputing geometry.
164
+ _inputsEqualExceptTheme(a, b) {
165
+ return a.cartesianArea === b.cartesianArea && a.datasets === b.datasets && a.axes === b.axes && a.features === b.features && a.datasetVisibility === b.datasetVisibility && a.hoverConfig === b.hoverConfig && a.chartId === b.chartId && a.renderer === b.renderer && a.dimensions.width === b.dimensions.width && a.dimensions.height === b.dimensions.height && a.zoomState === b.zoomState && a.dir === b.dir && a.font === b.font && a.locale === b.locale;
166
+ }
167
+ };
168
+
169
+ // src/controllers/ChartStateController.ts
170
+ var ChartStateController = class {
171
+ constructor() {
172
+ this._prevInputs = null;
173
+ this._prevResult = null;
174
+ }
175
+ /**
176
+ * Compute chart state with built-in memoization.
177
+ * - Inputs reference-equal to the previous call → return the cached result.
178
+ * - Only `theme` changed → reuse the cached (theme-independent) geometry and
179
+ * recompute ONLY the theme-dependent style/legend fields (SPREAD-CLONE,
180
+ * never mutating the cached result).
181
+ * - Otherwise → full `computeChartState`.
182
+ */
183
+ compute(inputs) {
184
+ if (this._prevInputs !== null && this._prevResult !== null) {
185
+ if (this._inputsEqual(this._prevInputs, inputs)) {
186
+ return this._prevResult;
187
+ }
188
+ if (this._inputsEqualExceptTheme(this._prevInputs, inputs)) {
189
+ const themeTail = computeThemeDependentFields(inputs, {
190
+ isCartesian: this._prevResult.isCartesian,
191
+ datasetTypes: this._prevResult.datasetTypes,
192
+ titleLayout: this._prevResult.titleLayout,
193
+ titleConfig: this._prevResult.titleConfig,
194
+ captionConfig: this._prevResult.captionConfig,
195
+ titleRO: inputs.ruleOverrides.title,
196
+ captionRO: inputs.ruleOverrides.caption
197
+ });
198
+ const refreshed = { ...this._prevResult, ...themeTail };
199
+ this._prevInputs = inputs;
200
+ this._prevResult = refreshed;
201
+ return refreshed;
202
+ }
203
+ }
204
+ const result = computeChartState(inputs);
205
+ this._prevInputs = inputs;
206
+ this._prevResult = result;
207
+ return result;
208
+ }
209
+ destroy() {
210
+ this._prevInputs = null;
211
+ this._prevResult = null;
212
+ }
213
+ _inputsEqual(a, b) {
214
+ return a.theme === b.theme && this._inputsEqualExceptTheme(a, b);
215
+ }
216
+ // Same as `_inputsEqual` but ignores `theme` — used to detect a color-only theme
217
+ // change so the controller can refresh the style/legend fields without recomputing
218
+ // the area geometry. Reference-equality on every other input.
219
+ _inputsEqualExceptTheme(a, b) {
220
+ return a.width === b.width && a.height === b.height && a.features === b.features && a.datasets === b.datasets && a.datasetVisibility === b.datasetVisibility && a.hiddenItems === b.hiddenItems && a.axes === b.axes && a.rendererHint === b.rendererHint && a.drilldownIsActive === b.drilldownIsActive && a.measuredCaptionHeight === b.measuredCaptionHeight && a.adaptive === b.adaptive && a.ruleOverrides === b.ruleOverrides && a.globalFontFamily === b.globalFontFamily;
221
+ }
222
+ };
223
+
224
+ // src/controllers/KeyboardNavigationController.ts
225
+ var KeyboardNavigationController = class {
226
+ constructor() {
227
+ this._state = { ...INACTIVE_STATE };
228
+ this._lastFocusedIndexMap = /* @__PURE__ */ new Map();
229
+ /** Called when navigation moves to a point. */
230
+ this.onNavigate = null;
231
+ /** Called when navigation resets (mouse move, Escape, or disabled). */
232
+ this.onReset = null;
233
+ }
234
+ get state() {
235
+ return this._state;
236
+ }
237
+ /**
238
+ * Process a keydown event. Returns true if the key was handled (caller should preventDefault).
239
+ */
240
+ handleKeyDown(key, datasets, visibility, config) {
241
+ const navDatasets = getNavigableDatasets(datasets, visibility);
242
+ const action = handleKeyNavigation(
243
+ key,
244
+ this._state.focusedDatasetId,
245
+ this._state.focusedIndex,
246
+ navDatasets,
247
+ {
248
+ wrapAround: config?.wrapAround ?? true,
249
+ seriesMode: config?.seriesNavigation?.mode ?? "normal",
250
+ rememberPointFocus: config?.seriesNavigation?.rememberPointFocus ?? false
251
+ },
252
+ this._lastFocusedIndexMap
253
+ );
254
+ if (action.type === "none") return false;
255
+ this._applyAction(action, datasets);
256
+ return true;
257
+ }
258
+ /**
259
+ * Activate navigation from a focus event (keyboard Tab into the chart) rather than
260
+ * a key press, so the focus indicator + screen-reader announcement appear immediately
261
+ * on focus instead of waiting for the first arrow key. No-op when already active or
262
+ * there is nothing navigable. Navigates to the remembered point of the first dataset
263
+ * when `rememberPointFocus` is set, otherwise its first point. Fires `onNavigate`
264
+ * exactly like an arrow key.
265
+ *
266
+ * @returns true if navigation was activated.
267
+ */
268
+ activate(datasets, visibility, config) {
269
+ if (this._state.active) return false;
270
+ const navDatasets = getNavigableDatasets(datasets, visibility);
271
+ if (navDatasets.length === 0) return false;
272
+ const first = navDatasets[0];
273
+ const remembered = config?.seriesNavigation?.rememberPointFocus ? this._lastFocusedIndexMap.get(first.id) : void 0;
274
+ const index = remembered != null ? Math.min(remembered, first.dataLength - 1) : 0;
275
+ this._applyAction({ type: "navigate", datasetId: first.id, index }, datasets);
276
+ return true;
277
+ }
278
+ /** Call on mousemove — resets active keyboard nav. */
279
+ handleMouseMove() {
280
+ if (this._state.active) {
281
+ this._state = { ...INACTIVE_STATE };
282
+ this.onReset?.();
283
+ }
284
+ }
285
+ reset() {
286
+ this._state = { ...INACTIVE_STATE };
287
+ }
288
+ destroy() {
289
+ this.reset();
290
+ this.onNavigate = null;
291
+ this.onReset = null;
292
+ }
293
+ _applyAction(action, datasets) {
294
+ if (action.type === "navigate") {
295
+ this._state = {
296
+ active: true,
297
+ focusedDatasetId: action.datasetId,
298
+ focusedIndex: action.index,
299
+ focusedSection: "series"
300
+ };
301
+ this._lastFocusedIndexMap.set(action.datasetId, action.index);
302
+ const ann = generateAnnouncement(datasets, action.datasetId, action.index);
303
+ this.onNavigate?.(action.datasetId, action.index, ann);
304
+ } else if (action.type === "reset") {
305
+ this._state = { ...INACTIVE_STATE };
306
+ this.onReset?.();
307
+ }
308
+ }
309
+ };
310
+
311
+ // src/controllers/ResponsiveDimensionsController.ts
312
+ var ResponsiveDimensionsController = class {
313
+ constructor() {
314
+ this._observer = null;
315
+ this._debouncer = null;
316
+ this._options = {};
317
+ /** Called with processed dimensions whenever the container resizes. */
318
+ this.onDimensionsChange = null;
319
+ }
320
+ /**
321
+ * Attach to a container element. Performs an initial synchronous measurement
322
+ * and begins observing resizes. Call detach() before re-attaching.
323
+ */
324
+ attach(containerEl, options) {
325
+ this.detach();
326
+ this._options = options;
327
+ this._debouncer = new ResizeDebouncer({
328
+ useRAF: options.debounceWithRAF ?? true,
329
+ delayMs: options.debounceDelay ?? 0
330
+ });
331
+ const rect = containerEl.getBoundingClientRect();
332
+ if (rect.width > 0 && rect.height > 0) {
333
+ this._emit(rect.width, rect.height);
334
+ }
335
+ this._observer = new ResizeObserver((entries) => {
336
+ const entry = entries[0];
337
+ if (!entry) return;
338
+ const { width, height } = entry.contentRect;
339
+ if (width <= 0 || height <= 0) return;
340
+ const processed = this._process(width, height);
341
+ this._debouncer.schedule(processed, (dims) => this._emit(dims.width, dims.height));
342
+ });
343
+ this._observer.observe(containerEl);
344
+ }
345
+ detach() {
346
+ this._observer?.disconnect();
347
+ this._observer = null;
348
+ this._debouncer?.cancel();
349
+ this._debouncer = null;
350
+ }
351
+ destroy() {
352
+ this.detach();
353
+ this.onDimensionsChange = null;
354
+ }
355
+ _process(rawWidth, rawHeight) {
356
+ const { aspectRatio, maxDimensions, maxWidth, maxHeight } = this._options;
357
+ return processDimensions(rawWidth, rawHeight, {
358
+ aspectRatio,
359
+ maxWidth: maxDimensions ? maxWidth : void 0,
360
+ maxHeight: maxDimensions ? maxHeight : void 0
361
+ });
362
+ }
363
+ _emit(rawWidth, rawHeight) {
364
+ const { width, height } = this._process(rawWidth, rawHeight);
365
+ this.onDimensionsChange?.(width, height);
366
+ }
367
+ };
368
+
369
+ // src/controllers/DrilldownStateController.ts
370
+ var EMPTY_SNAPSHOT = Object.freeze({
371
+ currentParentId: null,
372
+ path: [],
373
+ enabled: false,
374
+ isActive: false
375
+ });
376
+ var DrilldownStateController = class {
377
+ constructor() {
378
+ this.snapshot = EMPTY_SNAPSHOT;
379
+ this.subscribers = /* @__PURE__ */ new Set();
380
+ /** Stable getter — same reference until state actually changes (required for useSyncExternalStore). */
381
+ this.getSnapshot = () => this.snapshot;
382
+ this.subscribe = (fn) => {
383
+ this.subscribers.add(fn);
384
+ return () => {
385
+ this.subscribers.delete(fn);
386
+ };
387
+ };
388
+ this.drillInto = (nodeId, label, ancestors) => {
389
+ const prev = this.snapshot;
390
+ let nextPath;
391
+ if (ancestors && ancestors.length > 0) {
392
+ const existingIds = new Set(prev.path.map((b) => b.id));
393
+ const missing = ancestors.filter((a) => !existingIds.has(a.id));
394
+ nextPath = [...prev.path, ...missing, { id: nodeId, label }];
395
+ } else {
396
+ nextPath = [...prev.path, { id: nodeId, label }];
397
+ }
398
+ this.commit({
399
+ currentParentId: nodeId,
400
+ path: nextPath,
401
+ enabled: prev.enabled,
402
+ isActive: true
403
+ });
404
+ };
405
+ this.drillToLevel = (index) => {
406
+ const prev = this.snapshot;
407
+ if (index < 0 || index >= prev.path.length) return;
408
+ const newPath = prev.path.slice(0, index + 1);
409
+ const target = newPath[newPath.length - 1];
410
+ this.commit({
411
+ currentParentId: target.id,
412
+ path: newPath,
413
+ enabled: prev.enabled,
414
+ isActive: target.id !== null
415
+ });
416
+ };
417
+ }
418
+ /**
419
+ * Sync controller to the current dataset map. Called whenever datasets change.
420
+ * Recomputes `enabled` and the root breadcrumb label without touching the rest
421
+ * of the path.
422
+ */
423
+ setDatasets(datasets) {
424
+ const enabled = isDrilldownEnabled(datasets);
425
+ const prev = this.snapshot;
426
+ if (!enabled) {
427
+ if (!prev.enabled && prev.path.length === 0 && prev.currentParentId === null) return;
428
+ this.commit({ currentParentId: null, path: [], enabled: false, isActive: false });
429
+ return;
430
+ }
431
+ const rootLabel = getDrilldownRootLabel(datasets);
432
+ let nextPath = prev.path;
433
+ if (prev.path.length === 0) {
434
+ nextPath = [{ id: null, label: rootLabel }];
435
+ } else if (prev.path[0].label !== rootLabel) {
436
+ nextPath = [{ id: null, label: rootLabel }, ...prev.path.slice(1)];
437
+ }
438
+ if (prev.enabled === enabled && nextPath === prev.path) return;
439
+ this.commit({
440
+ currentParentId: prev.currentParentId,
441
+ path: nextPath,
442
+ enabled,
443
+ isActive: prev.currentParentId !== null
444
+ });
445
+ }
446
+ commit(next) {
447
+ this.snapshot = next;
448
+ for (const fn of this.subscribers) fn();
449
+ }
450
+ };
451
+
452
+ // src/chart.logic.ts
453
+ var annotationIdCounter = 0;
454
+ function generateAnnotationId() {
455
+ return `annotation-${++annotationIdCounter}`;
456
+ }
457
+ function getTooltipConfig(instance) {
458
+ return instance.$state.features.get("tooltip")?.props;
459
+ }
460
+ function getIsCartesian(instance) {
461
+ return instance.$state.rendererHint === "cartesian" || instance.$state.rendererHint === "heatmap";
462
+ }
463
+ function getIsRect(instance) {
464
+ return instance.$state.rendererHint === "rect";
465
+ }
466
+ function getIsSharedMode(tooltipConfig) {
467
+ return tooltipConfig?.mode === "shared";
468
+ }
469
+ function getHasCrosshair(tooltipConfig) {
470
+ const crosshair = tooltipConfig?.crosshair;
471
+ if (typeof crosshair === "boolean") return crosshair;
472
+ return crosshair?.enabled !== false && crosshair !== void 0;
473
+ }
474
+ function getUseNearestSnap(tooltipConfig, isSharedMode, hasCrosshair) {
475
+ const snap = tooltipConfig?.snap;
476
+ return snap !== "none" && (isSharedMode || hasCrosshair || snap != null);
477
+ }
478
+ function useChartLogic(instance) {
479
+ const { $props, $state, $emit } = instance;
480
+ const onMouseMove = (x, y) => {
481
+ const tooltipConfig = getTooltipConfig(instance);
482
+ const isCartesian = getIsCartesian(instance);
483
+ const isSharedMode = getIsSharedMode(tooltipConfig);
484
+ const hasCrosshair = getHasCrosshair(tooltipConfig);
485
+ const ctx = {
486
+ isCartesian,
487
+ isRect: getIsRect(instance),
488
+ isSharedMode,
489
+ hasCrosshair,
490
+ useNearestSnap: getUseNearestSnap(tooltipConfig, isSharedMode, hasCrosshair),
491
+ dir: $props.dir,
492
+ delegate: $props.rendererDelegate,
493
+ syncInfo: $props.syncInfo,
494
+ datasets: $state.datasets,
495
+ width: $props.width,
496
+ height: $props.height
497
+ };
498
+ const result = processMouseMoveCore(x, y, ctx, $state.lastHover, tooltipConfig);
499
+ $emit("tooltip-change", result.tooltip);
500
+ if (result.hoverUpdate) {
501
+ $emit("hover-change", result.hoverUpdate);
502
+ }
503
+ if (result.cursor !== $state.cursor) {
504
+ $emit("cursor-change", result.cursor);
505
+ }
506
+ if (result.syncBroadcast) {
507
+ $emit("sync-broadcast", result.syncBroadcast);
508
+ }
509
+ };
510
+ const onMouseLeave = () => {
511
+ const result = processMouseLeaveCore($state.lastHover, $props.syncInfo, getIsRect(instance));
512
+ $emit("tooltip-change", null);
513
+ if (result.clearHover) {
514
+ $emit("hover-change", { datasetId: null, index: null });
515
+ }
516
+ if (result.cursor !== $state.cursor) {
517
+ $emit("cursor-change", result.cursor);
518
+ }
519
+ if (result.syncBroadcast) {
520
+ $emit("sync-broadcast", result.syncBroadcast);
521
+ }
522
+ if (result.clearRectOverlay) {
523
+ $emit("rect-hover-update", null);
524
+ }
525
+ };
526
+ const onClick = (x, y) => {
527
+ const result = processClickCore(x, y, {
528
+ isCartesian: getIsCartesian(instance),
529
+ isRect: getIsRect(instance),
530
+ delegate: $props.rendererDelegate,
531
+ onClickCb: $props.onClickCb,
532
+ drilldownEnabled: $props.drilldownEnabled
533
+ });
534
+ switch (result.kind) {
535
+ case "callback":
536
+ $emit("click-callback", { datasetId: result.datasetId, index: result.index, label: result.label, value: result.value, color: result.color });
537
+ break;
538
+ case "toggleDataset":
539
+ $emit("dataset-toggle", result.datasetId);
540
+ break;
541
+ case "toggleItem":
542
+ $emit("item-toggle", { datasetId: result.datasetId, index: result.index });
543
+ break;
544
+ case "drilldown":
545
+ $emit("tooltip-change", null);
546
+ $emit("hover-change", { datasetId: null, index: null });
547
+ $emit("rect-hover-update", null);
548
+ $emit("drilldown");
549
+ break;
550
+ }
551
+ };
552
+ const onLegendClick = (item) => {
553
+ if ($props.onLegendClickCb) {
554
+ $emit("legend-callback", { datasetId: item.datasetId, index: item.index, label: item.label, type: item.type });
555
+ return;
556
+ }
557
+ const isCartesian = getIsCartesian(instance);
558
+ const isRect = getIsRect(instance);
559
+ const { hasRadarDatasets, hasPolarDatasets } = detectDatasetTypes($state.datasets);
560
+ const result = resolveLegendClickAction(item, {
561
+ isCartesian,
562
+ isRect,
563
+ hasRadarDatasets,
564
+ hasPolarDatasets,
565
+ datasets: $state.datasets,
566
+ hiddenItems: $state.hiddenItems
567
+ });
568
+ switch (result.kind) {
569
+ case "toggleDataset":
570
+ $emit("dataset-toggle", result.datasetId);
571
+ break;
572
+ case "toggleItems":
573
+ $emit("items-toggle", { datasetId: result.datasetId, indices: result.indices });
574
+ break;
575
+ }
576
+ };
577
+ const onLegendHover = (item) => {
578
+ const isCartesian = getIsCartesian(instance);
579
+ const { hasRadarDatasets, hasPolarDatasets } = detectDatasetTypes($state.datasets);
580
+ const target = resolveLegendHoverTarget(item, { isCartesian, hasRadarDatasets, hasPolarDatasets });
581
+ $emit("legend-hover", target ?? { datasetId: null, index: null });
582
+ };
583
+ const onSyncedResult = (result) => {
584
+ $emit("tooltip-change", result.tooltip);
585
+ const hoverChanged = $state.lastHover.datasetId !== result.hoverDatasetId || $state.lastHover.index !== result.hoverIndex;
586
+ if (hoverChanged) {
587
+ $emit("hover-change", { datasetId: result.hoverDatasetId, index: result.hoverIndex, axisIdx: result.axisIdx, axisX: result.axisX });
588
+ }
589
+ const newCursor = result.hoverDatasetId !== null ? "pointer" : "default";
590
+ if (newCursor !== $state.cursor) {
591
+ $emit("cursor-change", newCursor);
592
+ }
593
+ };
594
+ const registerDataset = (id, type, props) => {
595
+ const order = $state.orderCounter;
596
+ const propsRec = props;
597
+ const storedProps = propsRec.order === void 0 ? { ...propsRec, order } : props;
598
+ if ($state.datasets.has(id)) {
599
+ warnChartDevOnce(`duplicate-dataset-id:${id}`, `Duplicate dataset id "${id}" \u2014 the later series overwrites the earlier one in the registry, so they cannot be shown, toggled, or hit-tested independently. Give each series a unique id.`);
600
+ }
601
+ $state.datasets.set(id, { id, type, props: storedProps, order });
602
+ $state.orderCounter++;
603
+ $state.datasetVisibility.set(id, true);
604
+ $emit("state-changed");
605
+ };
606
+ const unregisterDataset = (id) => {
607
+ $state.datasets.delete(id);
608
+ $state.datasetVisibility.delete(id);
609
+ $state.hiddenItems.delete(id);
610
+ $emit("state-changed");
611
+ };
612
+ const updateDataset = (id, props) => {
613
+ const existing = $state.datasets.get(id);
614
+ if (!existing) return;
615
+ const propsRec = props;
616
+ const storedProps = propsRec.order === void 0 ? { ...propsRec, order: existing.order } : props;
617
+ if (shallowEqual(existing.props, storedProps)) {
618
+ return;
619
+ }
620
+ $state.datasets.set(id, { ...existing, props: storedProps });
621
+ $emit("state-changed");
622
+ };
623
+ const registerXAxis = (id, props) => {
624
+ $state.axes.x.set(id, { id, axis: "x", props });
625
+ $emit("state-changed");
626
+ };
627
+ const updateXAxis = (id, props) => {
628
+ const existing = $state.axes.x.get(id);
629
+ if (!existing) return;
630
+ if (shallowEqual(existing.props, props)) {
631
+ return;
632
+ }
633
+ $state.axes.x.set(id, { ...existing, props });
634
+ $emit("state-changed");
635
+ };
636
+ const unregisterXAxis = (id) => {
637
+ $state.axes.x.delete(id);
638
+ $emit("state-changed");
639
+ };
640
+ const registerYAxis = (id, props) => {
641
+ $state.axes.y.set(id, { id, axis: "y", props });
642
+ $emit("state-changed");
643
+ };
644
+ const updateYAxis = (id, props) => {
645
+ const existing = $state.axes.y.get(id);
646
+ if (!existing) return;
647
+ if (shallowEqual(existing.props, props)) {
648
+ return;
649
+ }
650
+ $state.axes.y.set(id, { ...existing, props });
651
+ $emit("state-changed");
652
+ };
653
+ const unregisterYAxis = (id) => {
654
+ $state.axes.y.delete(id);
655
+ $emit("state-changed");
656
+ };
657
+ const registerCustomization = (type, props) => {
658
+ const existing = $state.features.get(type);
659
+ if (existing && shallowEqual(existing.props, props)) {
660
+ return;
661
+ }
662
+ $state.features.set(type, { type, props });
663
+ $emit("state-changed");
664
+ };
665
+ const unregisterCustomization = (type) => {
666
+ $state.features.delete(type);
667
+ $emit("state-changed");
668
+ };
669
+ const registerAnnotation = (render) => {
670
+ const id = generateAnnotationId();
671
+ $state.annotations.set(id, render);
672
+ $emit("state-changed");
673
+ return id;
674
+ };
675
+ const unregisterAnnotation = (id) => {
676
+ $state.annotations.delete(id);
677
+ $emit("state-changed");
678
+ };
679
+ const toggleDatasetVisibility = (datasetId) => {
680
+ const current = $state.datasetVisibility.get(datasetId) ?? true;
681
+ $state.datasetVisibility.set(datasetId, !current);
682
+ $emit("state-changed");
683
+ };
684
+ const setDatasetVisibility = (datasetId, visible) => {
685
+ if (($state.datasetVisibility.get(datasetId) ?? true) === visible) return;
686
+ $state.datasetVisibility.set(datasetId, visible);
687
+ $emit("state-changed");
688
+ };
689
+ const toggleItemVisibility = (datasetId, index) => {
690
+ const existing = $state.hiddenItems.get(datasetId);
691
+ const hiddenSet = new Set(existing || []);
692
+ if (hiddenSet.has(index)) {
693
+ hiddenSet.delete(index);
694
+ } else {
695
+ hiddenSet.add(index);
696
+ }
697
+ $state.hiddenItems.set(datasetId, hiddenSet);
698
+ $emit("state-changed");
699
+ };
700
+ const setRendererHint = (hint) => {
701
+ if ($state.rendererHint === hint) return;
702
+ $state.rendererHint = hint;
703
+ $emit("state-changed");
704
+ };
705
+ return {
706
+ onMouseMove,
707
+ onMouseLeave,
708
+ onClick,
709
+ onLegendClick,
710
+ onLegendHover,
711
+ onSyncedResult,
712
+ registerDataset,
713
+ unregisterDataset,
714
+ updateDataset,
715
+ registerXAxis,
716
+ updateXAxis,
717
+ unregisterXAxis,
718
+ registerYAxis,
719
+ updateYAxis,
720
+ unregisterYAxis,
721
+ registerCustomization,
722
+ unregisterCustomization,
723
+ registerAnnotation,
724
+ unregisterAnnotation,
725
+ toggleDatasetVisibility,
726
+ setDatasetVisibility,
727
+ toggleItemVisibility,
728
+ setRendererHint
729
+ };
730
+ }
731
+
732
+ // src/chart.props.ts
733
+ var defaultChartCoreProps = {
734
+ width: 0,
735
+ height: 0,
736
+ dir: "ltr",
737
+ theme: {},
738
+ rendererDelegate: null,
739
+ syncInfo: null,
740
+ onClickCb: void 0,
741
+ onLegendClickCb: void 0,
742
+ drilldownEnabled: false
743
+ };
744
+
745
+ // src/chart.refs.ts
746
+ var defaultChartCoreRefs = {
747
+ container: null
748
+ };
749
+
750
+ // src/chart.state.ts
751
+ function createDefaultChartCoreState() {
752
+ return {
753
+ tooltip: null,
754
+ lastHover: { datasetId: null, index: null },
755
+ cursor: "default",
756
+ datasets: /* @__PURE__ */ new Map(),
757
+ datasetVisibility: /* @__PURE__ */ new Map(),
758
+ hiddenItems: /* @__PURE__ */ new Map(),
759
+ axes: { x: /* @__PURE__ */ new Map(), y: /* @__PURE__ */ new Map() },
760
+ features: /* @__PURE__ */ new Map(),
761
+ annotations: /* @__PURE__ */ new Map(),
762
+ orderCounter: 0,
763
+ rendererHint: null
764
+ };
765
+ }
766
+
767
+ // src/createChart.ts
768
+ var createChart = defineComponent("Chart", ({ getCurrentInstance, defineRefs, defineProps, defineState, defineEmits, defineExpose }) => {
769
+ defineRefs(defaultChartCoreRefs);
770
+ defineProps(defaultChartCoreProps);
771
+ defineState(createDefaultChartCoreState());
772
+ defineEmits(["tooltip-change", "hover-change", "cursor-change", "sync-broadcast", "rect-hover-update", "click-callback", "dataset-toggle", "item-toggle", "items-toggle", "drilldown", "legend-callback", "legend-hover", "state-changed"]);
773
+ const currentInstance = getCurrentInstance();
774
+ const expose = useChartLogic(currentInstance);
775
+ defineExpose(expose);
776
+ });
777
+
778
+ // src/series/pie/controller/data-labels.ts
779
+ function buildDataLabelDatasetProps(pieEntries) {
780
+ const map = /* @__PURE__ */ new Map();
781
+ for (const entry of pieEntries) {
782
+ map.set(entry.order, {
783
+ label: entry.props.categoryField,
784
+ data: entry.props.data
785
+ });
786
+ }
787
+ return map;
788
+ }
789
+ function buildPieLabelConfigs(layout, labelConfig, theme) {
790
+ const configs = [];
791
+ const total = layout.visibleSlices.reduce((sum, s) => sum + Math.abs(s.value), 0);
792
+ for (const slice of layout.visibleSlices) {
793
+ const percentage = total > 0 ? Math.abs(slice.value) / total * 100 : 0;
794
+ const midAngle = (slice.rotation + slice.endAngle) / 2;
795
+ const text = formatDataLabelText(slice.value, percentage, labelConfig.display) ?? String(slice.value);
796
+ const colorIdx = slice.dataIndex;
797
+ const sliceColor = typeof layout.colors[colorIdx] === "string" ? layout.colors[colorIdx] : getDefaultColor(colorIdx, theme ?? defaultLightTheme);
798
+ configs.push({ text, angle: midAngle, index: slice.dataIndex, value: slice.value, percentage, color: sliceColor });
799
+ }
800
+ return configs;
801
+ }
802
+ function renderSvgDataLabels(lg, frame, labelConfig, input, datasetPropsMap) {
803
+ const built = buildPieDataLabelLayout(frame, labelConfig, { width: input.width, height: input.height }, datasetPropsMap);
804
+ if (!built) return;
805
+ const { labelLayout, configsByIndex } = built;
806
+ const textColor = labelConfig.color;
807
+ const center = frame.center;
808
+ const renderFn = input.features.get("dataLabels")?.props?.render;
809
+ const fontSize = labelConfig.fontSize ?? 11;
810
+ const fontFamily = labelConfig.fontFamily ?? "sans-serif";
811
+ const fontWeight = labelConfig.fontWeight ?? "normal";
812
+ const textOffset = labelConfig.textOffset ?? 0;
813
+ const lineHeight = labelConfig.lineHeight ?? 1.2;
814
+ const lineStyle = labelConfig.lineStyle ?? "elbow";
815
+ for (const labelItem of labelLayout.labels) {
816
+ const { leaderLine, lines, side, color: lineColor } = labelItem;
817
+ const g = createSvgElement("g");
818
+ g.setAttribute("style", "pointer-events: none");
819
+ if (renderFn) {
820
+ const originalConfig = configsByIndex.get(labelItem.index);
821
+ const absLeaderLine = {
822
+ x1: center.x + leaderLine.x1,
823
+ y1: center.y + leaderLine.y1,
824
+ x2: center.x + leaderLine.x2,
825
+ y2: center.y + leaderLine.y2,
826
+ x3: center.x + leaderLine.x3,
827
+ y3: center.y + leaderLine.y3
828
+ };
829
+ const textX = center.x + labelItem.x + (side === "right" ? textOffset : -textOffset);
830
+ const textY = center.y + labelItem.y;
831
+ void renderFn({
832
+ index: labelItem.index,
833
+ value: labelItem.value,
834
+ percentage: labelItem.percentage,
835
+ formattedText: labelItem.text,
836
+ label: originalConfig?.label ?? "",
837
+ // Per-label color override wins over the leader-line/slice color in the custom render context.
838
+ color: labelItem.textColor ?? textColor ?? lineColor,
839
+ x: textX,
840
+ y: textY,
841
+ side,
842
+ leaderLine: absLeaderLine,
843
+ center: { x: center.x, y: center.y }
844
+ });
845
+ } else {
846
+ if (lineStyle !== "none") {
847
+ const lineEl = createSvgElement("polyline");
848
+ if (lineStyle === "straight") {
849
+ lineEl.setAttribute("points", `${center.x + leaderLine.x1},${center.y + leaderLine.y1} ${center.x + leaderLine.x3},${center.y + leaderLine.y3}`);
850
+ } else {
851
+ lineEl.setAttribute("points", `${center.x + leaderLine.x1},${center.y + leaderLine.y1} ${center.x + leaderLine.x2},${center.y + leaderLine.y2} ${center.x + leaderLine.x3},${center.y + leaderLine.y3}`);
852
+ }
853
+ lineEl.setAttribute("fill", "none");
854
+ lineEl.setAttribute("stroke", labelItem.connectorColor ?? lineColor);
855
+ lineEl.setAttribute("stroke-width", String(labelItem.connectorWidth ?? 1));
856
+ g.appendChild(lineEl);
857
+ }
858
+ const isNone = lineStyle === "none";
859
+ const textX = isNone ? center.x + labelItem.x : center.x + labelItem.x + (side === "right" ? textOffset : -textOffset);
860
+ const textY = center.y + labelItem.y;
861
+ const itemFontSize = labelItem.fontSize ?? fontSize;
862
+ const itemTextColor = labelItem.textColor ?? textColor;
863
+ for (let li = 0; li < lines.length; li++) {
864
+ const lineY = textY - (lines.length - 1) * itemFontSize * lineHeight / 2 + li * itemFontSize * lineHeight;
865
+ const textEl = createSvgElement("text");
866
+ textEl.setAttribute("x", String(textX));
867
+ textEl.setAttribute("y", String(lineY));
868
+ textEl.setAttribute("text-anchor", isNone ? "middle" : side === "right" ? "start" : "end");
869
+ textEl.setAttribute("dominant-baseline", "central");
870
+ textEl.setAttribute("font-size", String(itemFontSize));
871
+ textEl.setAttribute("font-family", fontFamily);
872
+ textEl.setAttribute("font-weight", fontWeight);
873
+ textEl.setAttribute("class", "p-chart-data-label");
874
+ if (itemTextColor) textEl.setAttribute("style", `fill: ${itemTextColor}`);
875
+ textEl.textContent = lines[li];
876
+ g.appendChild(textEl);
877
+ }
878
+ }
879
+ lg.appendChild(g);
880
+ }
881
+ }
882
+
883
+ // src/series/pie/controller/sort-animation.ts
884
+ function detectSortOrderChange(prevVisibleOrder, newVisibleOrder) {
885
+ const old = prevVisibleOrder;
886
+ if (old.length !== newVisibleOrder.length) return false;
887
+ if (old.length === 0) return false;
888
+ const toRingMap = (ids) => {
889
+ const m = /* @__PURE__ */ new Map();
890
+ for (const id of ids) {
891
+ const dsIdx = parseInt(id.split("-")[0]);
892
+ if (!m.has(dsIdx)) m.set(dsIdx, []);
893
+ m.get(dsIdx).push(id);
894
+ }
895
+ return m;
896
+ };
897
+ const oldByRing = toRingMap(old);
898
+ const newByRing = toRingMap(newVisibleOrder);
899
+ for (const [dsIdx, newOrder] of newByRing) {
900
+ const oldOrder = oldByRing.get(dsIdx) ?? [];
901
+ if (oldOrder.length !== newOrder.length) return false;
902
+ const oldSet = new Set(oldOrder);
903
+ for (const id of newOrder) {
904
+ if (!oldSet.has(id)) return false;
905
+ }
906
+ }
907
+ for (const [dsIdx, newOrder] of newByRing) {
908
+ const oldOrder = oldByRing.get(dsIdx);
909
+ for (let i = 0; i < newOrder.length; i++) {
910
+ if (oldOrder[i] !== newOrder[i]) return true;
911
+ }
912
+ }
913
+ return false;
914
+ }
915
+
916
+ // src/series/pie/controller/types.ts
917
+ var PIE_SLICE_SCENE_REGISTRY = {
918
+ number: ["rotation", "endAngle", "radiusRatio", "value", "percentage", "innerRadius", "outerRadius"],
919
+ color: []
920
+ };
921
+
922
+ // src/series/pie/controller/render.ts
923
+ function renderFrame(ctx, slices, renderer, input) {
924
+ if (!ctx._animationLayout) return;
925
+ ctx._lastRenderedSlices = slices;
926
+ ctx._hitTestStates = buildHitTestStates(slices);
927
+ const { center, outerRadius, innerRadius, sweepAngle, borderAlign, borderJoinStyle } = ctx._animationLayout;
928
+ const visibleSliceCount = slices.filter((s) => s.targetAngle > 1).length;
929
+ const frame = {
930
+ slices,
931
+ center,
932
+ outerRadius,
933
+ innerRadius,
934
+ baseRotation: ctx._baseRotation,
935
+ fullCircle: isFullCircle(sweepAngle),
936
+ willBeSingleSlice: visibleSliceCount <= 1,
937
+ currentRingRadii: ctx._currentRingRadii,
938
+ // Hover comes from the controller-owned `hoverState` field (written by
939
+ // applyHover), NOT `input.hover` — so the anim tick paints live hover and
940
+ // hover-during-animation deferral stays correct. The datasetId→datasetIndex
941
+ // mapping is preserved (same as the previous `input.hover` derivation).
942
+ hoverState: { datasetId: ctx.hoverState.datasetId, index: ctx.hoverState.index, datasetIndex: ctx.hoverState.datasetId != null ? ctx._pieEntries.find((e) => e.id === ctx.hoverState.datasetId)?.order ?? null : null },
943
+ hoverOffset: ctx._hoverOffset,
944
+ hoverScale: ctx._hoverScale,
945
+ borderAlign,
946
+ borderJoinStyle
947
+ };
948
+ const hoverConfig = input.hoverConfig;
949
+ if (renderer === "canvas" && ctx._canvasEl) {
950
+ const canvasCtx = ctx._canvasEl.getContext("2d");
951
+ if (canvasCtx) {
952
+ canvasCtx.clearRect(0, 0, ctx._canvasEl.width, ctx._canvasEl.height);
953
+ ctx._paint.renderCanvasSlices(canvasCtx, frame, hoverConfig, input.theme);
954
+ const dlProps = input.features.get("dataLabels")?.props;
955
+ const labelConfig = normalizeDataLabelConfig(dlProps, input.adaptiveDataLabelFontSize, void 0, input.globalFont);
956
+ if (labelConfig) {
957
+ const datasetPropsMap = buildDataLabelDatasetProps(ctx._pieEntries);
958
+ const onImageLoad = () => {
959
+ if (ctx._imageRepaintRaf !== null) return;
960
+ ctx._imageRepaintRaf = raf(() => {
961
+ ctx._imageRepaintRaf = null;
962
+ if (ctx._lastRenderedSlices.length > 0 && ctx._lastInput) {
963
+ renderFrame(ctx, ctx._lastRenderedSlices, renderer, ctx._lastInput);
964
+ ctx.onFrame?.();
965
+ }
966
+ });
967
+ };
968
+ ctx._paint.renderCanvasDataLabels(canvasCtx, frame, { x: 0, y: 0, width: input.width, height: input.height }, labelConfig, labelConfig.color ?? input.theme.dataLabel, datasetPropsMap, hoverConfig, input.theme, onImageLoad);
969
+ }
970
+ if (ctx._sliceRenderFns.size > 0) {
971
+ const onImageLoad = () => {
972
+ if (ctx._imageRepaintRaf !== null) return;
973
+ ctx._imageRepaintRaf = raf(() => {
974
+ ctx._imageRepaintRaf = null;
975
+ if (ctx._lastRenderedSlices.length > 0 && ctx._lastInput) {
976
+ renderFrame(ctx, ctx._lastRenderedSlices, renderer, ctx._lastInput);
977
+ ctx.onFrame?.();
978
+ }
979
+ });
980
+ };
981
+ ctx._paint.renderCanvasContent(canvasCtx, frame, ctx._sliceRenderFns, onImageLoad, input.theme, input.globalFont?.family);
982
+ }
983
+ }
984
+ }
985
+ if (renderer === "svg" && ctx._svgGroup) {
986
+ const chartGroup = ctx._svgGroup;
987
+ ctx._pathElements.clear();
988
+ ctx._sliceGroups.clear();
989
+ ctx._contentSlots.clear();
990
+ for (const pieEntry of ctx._pieEntries) {
991
+ const datasetGroup = chartGroup.querySelector(`[data-dataset="${pieEntry.id}"]`);
992
+ if (!datasetGroup) continue;
993
+ reconcileSvgChildren(datasetGroup, ctx._paint.buildSvgChildren(frame, hoverConfig, input.theme, input.chartId, pieEntry.order));
994
+ registerSvgSliceElements(ctx, datasetGroup, pieEntry.order);
995
+ }
996
+ ctx._paint.updateCustomContent(frame, { customContent: ctx._customContent, renderFns: ctx._sliceRenderFns }, input.theme, input.globalFont?.family);
997
+ const lg = ctx._labelGroup;
998
+ if (lg) {
999
+ while (lg.firstChild) lg.removeChild(lg.firstChild);
1000
+ const dlProps = input.features.get("dataLabels")?.props;
1001
+ const labelConfig = normalizeDataLabelConfig(dlProps, input.adaptiveDataLabelFontSize, void 0, input.globalFont);
1002
+ if (labelConfig) {
1003
+ renderSvgDataLabels(lg, frame, labelConfig, input, buildDataLabelDatasetProps(ctx._pieEntries));
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+ function registerSvgSliceElements(ctx, renderedGroup, datasetIndex) {
1009
+ renderedGroup.querySelectorAll("[data-slice-index]").forEach((el) => {
1010
+ const sliceIndex = el.getAttribute("data-slice-index");
1011
+ if (sliceIndex !== null) {
1012
+ const key = `${datasetIndex}-${sliceIndex}`;
1013
+ ctx._sliceGroups.set(key, el);
1014
+ const pathEl = el.querySelector("path");
1015
+ if (pathEl) ctx._pathElements.set(key, pathEl);
1016
+ const contentSlot = el.querySelector(".slice-content");
1017
+ if (contentSlot) ctx._contentSlots.set(key, contentSlot);
1018
+ }
1019
+ });
1020
+ }
1021
+
1022
+ // src/series/pie/controller/scene.ts
1023
+ function startAnimLoop(ctx) {
1024
+ if (ctx._animRafId !== null) {
1025
+ cancelRaf(ctx._animRafId);
1026
+ }
1027
+ const tick = (now) => {
1028
+ ctx._animRafId = null;
1029
+ const { allIdle } = ctx._scene.tick(now);
1030
+ if (ctx._lastInput) {
1031
+ const slices = buildSlicesFromScene(ctx);
1032
+ renderFrame(ctx, slices, ctx._lastInput.renderer, ctx._lastInput);
1033
+ ctx.onFrame?.();
1034
+ }
1035
+ if (!allIdle) {
1036
+ ctx._animRafId = raf(tick);
1037
+ } else {
1038
+ ctx._orderChanged = false;
1039
+ }
1040
+ };
1041
+ ctx._animRafId = raf(tick);
1042
+ }
1043
+ function snapshotAccumulatedPositions(ctx) {
1044
+ const ringOrder = /* @__PURE__ */ new Map();
1045
+ for (const id of ctx._sceneTargetOrder) {
1046
+ const dsIdx = parseInt(id.split("-")[0]);
1047
+ if (!ringOrder.has(dsIdx)) ringOrder.set(dsIdx, []);
1048
+ ringOrder.get(dsIdx).push(id);
1049
+ }
1050
+ for (const el of ctx._scene.getElements()) {
1051
+ if (el.phase !== "exit") continue;
1052
+ const dsIdx = parseInt(el.id.split("-")[0]);
1053
+ if (!ringOrder.has(dsIdx)) ringOrder.set(dsIdx, []);
1054
+ ringOrder.get(dsIdx).push(el.id);
1055
+ }
1056
+ for (const [, ids] of ringOrder) {
1057
+ let currentRotation = ctx._baseRotation;
1058
+ for (const id of ids) {
1059
+ const el = ctx._scene.getElement(id);
1060
+ if (!el) continue;
1061
+ const span = el.current.endAngle - el.current.rotation;
1062
+ if (span < 1e-3) continue;
1063
+ el.current.rotation = currentRotation;
1064
+ el.current.endAngle = currentRotation + span;
1065
+ currentRotation = el.current.endAngle;
1066
+ }
1067
+ }
1068
+ }
1069
+ function buildSlicesFromScene(ctx) {
1070
+ const result = [];
1071
+ const ringOrder = /* @__PURE__ */ new Map();
1072
+ for (const id of ctx._sceneTargetOrder) {
1073
+ const dsIdx = parseInt(id.split("-")[0]);
1074
+ if (!ringOrder.has(dsIdx)) ringOrder.set(dsIdx, []);
1075
+ ringOrder.get(dsIdx).push(id);
1076
+ }
1077
+ for (const el of ctx._scene.getElements()) {
1078
+ if (el.phase !== "exit") continue;
1079
+ const dsIdx = parseInt(el.id.split("-")[0]);
1080
+ if (!ringOrder.has(dsIdx)) ringOrder.set(dsIdx, []);
1081
+ ringOrder.get(dsIdx).push(el.id);
1082
+ }
1083
+ for (const [, ids] of ringOrder) {
1084
+ let currentRotation = ctx._baseRotation;
1085
+ for (const id of ids) {
1086
+ const el = ctx._scene.getElement(id);
1087
+ if (!el) continue;
1088
+ const { value, percentage, radiusRatio, innerRadius, outerRadius } = el.current;
1089
+ let rotation;
1090
+ let endAngle;
1091
+ if (ctx._orderChanged) {
1092
+ rotation = el.current.rotation;
1093
+ endAngle = el.current.endAngle;
1094
+ if (endAngle - rotation < 1e-3) continue;
1095
+ } else {
1096
+ const span = el.current.endAngle - el.current.rotation;
1097
+ if (span < 1e-3) continue;
1098
+ rotation = currentRotation;
1099
+ endAngle = currentRotation + span;
1100
+ currentRotation = endAngle;
1101
+ }
1102
+ const parts = id.split("-");
1103
+ const datasetIndex = parseInt(parts[0]);
1104
+ const style = ctx._styleData.get(id) ?? ctx._prevStyleData.get(id);
1105
+ const targetState = ctx._sceneTargetMap.get(id);
1106
+ const isExiting = el.phase === "exit";
1107
+ const pieEntry = ctx._pieEntries.find((e) => e.order === datasetIndex);
1108
+ const overrides = pieEntry ? ctx._propertyOverrides.get(pieEntry.id) : void 0;
1109
+ const effectiveInner = overrides && typeof overrides.innerRadius === "number" ? overrides.innerRadius : innerRadius;
1110
+ const effectiveOuter = overrides && typeof overrides.outerRadius === "number" ? overrides.outerRadius : outerRadius;
1111
+ ctx._currentRingRadii.set(parts[0], { inner: effectiveInner, outer: effectiveOuter });
1112
+ const originalIndex = parseInt(parts[1]);
1113
+ const entryData = pieEntry?.props?.data ?? [];
1114
+ const rawOffsetProp = pieEntry?.props?.offset;
1115
+ const baseOffsetResolved = rawOffsetProp != null ? resolveAccessor(rawOffsetProp, makeItemContext(entryData[originalIndex], originalIndex), { arrayMode: "clip", fallback: 0 }) ?? 0 : 0;
1116
+ const rawOpacityProp = pieEntry?.props?.opacity;
1117
+ const userOpacityResolved = rawOpacityProp != null ? resolveAccessor(rawOpacityProp, makeItemContext(entryData[originalIndex], originalIndex), { arrayMode: "clip", fallback: 1 }) ?? 1 : 1;
1118
+ const effectiveBaseOffset = overrides && typeof overrides.offset === "number" ? overrides.offset : baseOffsetResolved;
1119
+ const effectiveUserOpacity = overrides && typeof overrides.opacity === "number" ? overrides.opacity : userOpacityResolved;
1120
+ const effectiveBorderStrokeWidth = overrides && typeof overrides.borderStrokeWidth === "number" ? overrides.borderStrokeWidth : style?.borderStrokeWidth;
1121
+ const effectiveSpacing = overrides && typeof overrides.spacing === "number" ? overrides.spacing : style?.spacing;
1122
+ const effectiveBorderRadius = overrides && typeof overrides.cornerRadius === "number" ? overrides.cornerRadius : style?.borderRadius;
1123
+ result.push({
1124
+ originalIndex,
1125
+ datasetIndex,
1126
+ value,
1127
+ percentage,
1128
+ targetPercentage: isExiting ? 0 : targetState?.percentage ?? percentage,
1129
+ rotation,
1130
+ endAngle,
1131
+ radiusRatio,
1132
+ color: style?.color,
1133
+ colorValue: style?.colorValue,
1134
+ borderStrokeWidth: effectiveBorderStrokeWidth,
1135
+ borderColor: style?.borderColor,
1136
+ borderRadius: effectiveBorderRadius,
1137
+ borderDash: style?.borderDash,
1138
+ borderDashOffset: style?.borderDashOffset,
1139
+ spacing: effectiveSpacing,
1140
+ targetAngle: isExiting ? 0 : style?.targetAngle ?? (targetState ? targetState.endAngle - targetState.rotation : endAngle - rotation),
1141
+ baseOffset: effectiveBaseOffset !== 0 ? effectiveBaseOffset : void 0,
1142
+ userOpacity: effectiveUserOpacity !== 1 ? effectiveUserOpacity : void 0,
1143
+ hoverColor: style?.hoverColor,
1144
+ hoverBorderColor: style?.hoverBorderColor
1145
+ });
1146
+ }
1147
+ }
1148
+ if (ctx._propertyOverrides.size > 0) {
1149
+ for (const entry of ctx._pieEntries) {
1150
+ const overrides = ctx._propertyOverrides.get(entry.id);
1151
+ if (!overrides) continue;
1152
+ const hasStart = typeof overrides.startAngle === "number";
1153
+ const hasEnd = typeof overrides.endAngle === "number";
1154
+ if (!hasStart && !hasEnd) continue;
1155
+ const datasetIndex = entry.order;
1156
+ const datasetSlices = result.filter((s) => s.datasetIndex === datasetIndex);
1157
+ if (datasetSlices.length === 0) continue;
1158
+ let minRotation = datasetSlices[0].rotation;
1159
+ let maxEndAngle = datasetSlices[0].endAngle;
1160
+ for (const s of datasetSlices) {
1161
+ if (s.rotation < minRotation) minRotation = s.rotation;
1162
+ if (s.endAngle > maxEndAngle) maxEndAngle = s.endAngle;
1163
+ }
1164
+ const baseStart = minRotation;
1165
+ const baseSweep = Math.max(1e-3, maxEndAngle - minRotation);
1166
+ const targetStart = hasStart ? overrides.startAngle : baseStart;
1167
+ const targetSweep = hasEnd ? Math.max(0, overrides.endAngle - targetStart) : baseSweep;
1168
+ const sweepScale = targetSweep / baseSweep;
1169
+ for (const s of datasetSlices) {
1170
+ const sliceStart = targetStart + (s.rotation - baseStart) * sweepScale;
1171
+ const sliceSweep = (s.endAngle - s.rotation) * sweepScale;
1172
+ s.rotation = sliceStart;
1173
+ s.endAngle = sliceStart + sliceSweep;
1174
+ }
1175
+ }
1176
+ }
1177
+ return result;
1178
+ }
1179
+ function renderSceneFrame(ctx) {
1180
+ if (!ctx._lastInput || !ctx._animationLayout || ctx._ringTargets.length === 0) return;
1181
+ const slices = buildSlicesFromScene(ctx);
1182
+ renderFrame(ctx, slices, ctx._lastInput.renderer, ctx._lastInput);
1183
+ ctx.onFrame?.();
1184
+ }
1185
+
1186
+ // src/series/pie/controller/index.ts
1187
+ var PieRendererController = class {
1188
+ constructor(paint) {
1189
+ this._sceneTargetMap = /* @__PURE__ */ new Map();
1190
+ this._sceneTargetOrder = [];
1191
+ this._animRafId = null;
1192
+ this._lastFingerprint = "";
1193
+ /**
1194
+ * Per-dataset property-animation overrides resolved from AnimatedPropertyStore.
1195
+ * Set by `applyPropertyAnimations()` each property-animation tick and merged
1196
+ * into the rendered output at two places:
1197
+ * - `_buildSlicesFromScene` — slice-level fields (opacity, borderStrokeWidth,
1198
+ * spacing, offset, cornerRadius) get merged onto each slice belonging
1199
+ * to the dataset.
1200
+ * - `_renderFrame` — frame-level geometry (innerRadius, outerRadius,
1201
+ * startAngle, endAngle) overrides the per-dataset ring radii in
1202
+ * `_currentRingRadii` and the rotation/sweep applied to slices.
1203
+ *
1204
+ * Per-slice granularity (e.g. hover one slice and animate just its
1205
+ * `outerRadius`) is deferred to Step 14 — Step 10 keeps per-dataset only.
1206
+ */
1207
+ this._propertyOverrides = /* @__PURE__ */ new Map();
1208
+ // Layout caches
1209
+ this._layoutData = /* @__PURE__ */ new Map();
1210
+ this._renderPropsMap = /* @__PURE__ */ new Map();
1211
+ this._pieEntries = [];
1212
+ this._hasPieData = false;
1213
+ this._center = null;
1214
+ // Per-dataset computed data
1215
+ this._visibilityMaps = /* @__PURE__ */ new Map();
1216
+ this._ringLayouts = [];
1217
+ this._ringTargets = [];
1218
+ this._styleData = /* @__PURE__ */ new Map();
1219
+ this._prevStyleData = /* @__PURE__ */ new Map();
1220
+ this._baseRotation = -90;
1221
+ // Sort animation state
1222
+ this._prevVisibleOrder = [];
1223
+ this._orderChanged = false;
1224
+ // Hover state
1225
+ this._hoverOffset = 0;
1226
+ this._hoverScale = 1;
1227
+ /**
1228
+ * Current hovered slice, written ONLY by `applyHover` (mirrors polar). The 2D
1229
+ * SVG/canvas frame build derives its per-dataset `hoverState` from this field
1230
+ * so the animation tick reads live hover (not stale `_lastInput.hover`), which
1231
+ * makes hover-during-animation deferral safe.
1232
+ */
1233
+ this.hoverState = { datasetId: null, index: null };
1234
+ // SVG element maps (owned by controller, same as Bar)
1235
+ this._pathElements = /* @__PURE__ */ new Map();
1236
+ this._sliceGroups = /* @__PURE__ */ new Map();
1237
+ this._contentSlots = /* @__PURE__ */ new Map();
1238
+ this._customContent = /* @__PURE__ */ new Map();
1239
+ this._sliceRenderFns = /* @__PURE__ */ new Map();
1240
+ this._labelGroup = null;
1241
+ // Hit test state (updated on every frame)
1242
+ this._hitTestStates = /* @__PURE__ */ new Map();
1243
+ this._currentRingRadii = /* @__PURE__ */ new Map();
1244
+ this._lastRenderedSlices = [];
1245
+ this._animationLayout = null;
1246
+ // DOM targets
1247
+ this._svgGroup = null;
1248
+ this._canvasEl = null;
1249
+ // Input cache
1250
+ this._lastInput = null;
1251
+ // Image-load repaint
1252
+ this._imageRepaintRaf = null;
1253
+ /**
1254
+ * Called after each animation frame — wrappers use this to trigger
1255
+ * framework-specific repaint (e.g. setLayoutVersion / triggerRepaint).
1256
+ */
1257
+ this.onFrame = null;
1258
+ this._paint = paint;
1259
+ this._scene = new AnimatedScene({
1260
+ // ENTER: all slices sweep in from the chart start angle (classic clock sweep).
1261
+ // _baseRotation is set in Phase 1 before diff() is called.
1262
+ emptyState: (target) => ({
1263
+ rotation: this._baseRotation,
1264
+ endAngle: this._baseRotation,
1265
+ radiusRatio: target.radiusRatio,
1266
+ value: 0,
1267
+ percentage: 0,
1268
+ innerRadius: target.innerRadius,
1269
+ outerRadius: target.outerRadius
1270
+ }),
1271
+ // EXIT: slice shrinks to zero span at its own position.
1272
+ // rotation is fixed so the slice collapses inward, not toward the chart start angle.
1273
+ exitEmptyState: (target) => ({
1274
+ rotation: target.rotation,
1275
+ endAngle: target.rotation,
1276
+ radiusRatio: target.radiusRatio,
1277
+ value: target.value,
1278
+ percentage: target.percentage,
1279
+ innerRadius: target.innerRadius,
1280
+ outerRadius: target.outerRadius
1281
+ }),
1282
+ registry: PIE_SLICE_SCENE_REGISTRY
1283
+ });
1284
+ }
1285
+ // ================================================================
1286
+ // PUBLIC API
1287
+ // ================================================================
1288
+ /**
1289
+ * Full update — computes layouts, visibility, ring targets, drives SVG DOM
1290
+ * mutations and starts the animation. Mirrors BarRendererController.update().
1291
+ */
1292
+ update(input) {
1293
+ this._lastInput = input;
1294
+ this._svgGroup = input.svgGroup ?? null;
1295
+ this._canvasEl = input.canvasEl ?? null;
1296
+ this.hoverState = { datasetId: input.hover.datasetId, index: input.hover.index };
1297
+ const { datasets, datasetVisibility, hiddenItems, chartArea, renderer, features, hoverConfig, globalFont, adaptiveDataLabelFontSize, width, height } = input;
1298
+ const sortedDatasets = Array.from(datasets.entries()).sort(([, a], [, b]) => a.order - b.order);
1299
+ const collected = collectItems(sortedDatasets, datasetVisibility);
1300
+ this._pieEntries = collected.pie;
1301
+ this._hasPieData = this._pieEntries.length > 0;
1302
+ if (!this._hasPieData) {
1303
+ this._layoutData.clear();
1304
+ this._renderPropsMap.clear();
1305
+ this._center = null;
1306
+ this._ringTargets = [];
1307
+ if (renderer === "svg" && this._svgGroup) {
1308
+ while (this._svgGroup.firstChild) this._svgGroup.removeChild(this._svgGroup.firstChild);
1309
+ }
1310
+ return;
1311
+ }
1312
+ this._hoverOffset = hoverConfig?.offset ?? 0;
1313
+ this._hoverScale = hoverConfig?.scale ?? 1;
1314
+ const dlProps = features.get("dataLabels")?.props;
1315
+ const dlConfig = normalizeDataLabelConfig(dlProps, adaptiveDataLabelFontSize, void 0, globalFont);
1316
+ const isDatasetVisible = (id) => datasetVisibility.get(id) ?? true;
1317
+ const isItemVisible = (dsId, idx) => !(hiddenItems.get(dsId)?.has(idx) ?? false);
1318
+ const stackingProps = features.get("stacking")?.props;
1319
+ const ringGap = (stackingProps?.gap ?? 0) / 100;
1320
+ const hasStackedPies = this._pieEntries.length > 1 || this._pieEntries.some((e) => e.props.stackId !== void 0);
1321
+ this._ringLayouts = hasStackedPies ? calculateRingLayouts(datasets, isDatasetVisible, isItemVisible, 0, ringGap) : [];
1322
+ this._layoutData.clear();
1323
+ this._renderPropsMap.clear();
1324
+ this._sliceRenderFns.clear();
1325
+ this._prevStyleData = this._styleData;
1326
+ this._styleData = /* @__PURE__ */ new Map();
1327
+ const ringTargets = [];
1328
+ const visibilityMaps = /* @__PURE__ */ new Map();
1329
+ for (const pieEntry of this._pieEntries) {
1330
+ const datasetId = pieEntry.id;
1331
+ const datasetIndex = pieEntry.order;
1332
+ const props = pieEntry.props;
1333
+ const data = props.data ?? [];
1334
+ const vis = buildPieVisibilityMap(data, datasetId, isDatasetVisible, isItemVisible);
1335
+ visibilityMaps.set(datasetId, vis);
1336
+ const ringResult = applyRingLayout(
1337
+ chartArea,
1338
+ this._ringLayouts.find((r) => r.datasetId === datasetId),
1339
+ props
1340
+ );
1341
+ const renderProps = {
1342
+ ...props,
1343
+ innerRadius: ringResult.effectiveInnerRadius,
1344
+ visibility: vis,
1345
+ interaction: {
1346
+ hoveredIndex: input.hover.datasetId === datasetId ? input.hover.index : null
1347
+ },
1348
+ effects: {
1349
+ hoverBrightness: hoverConfig?.brightness ?? DEFAULT_HOVER_BRIGHTNESS,
1350
+ hoverOffset: this._hoverOffset,
1351
+ hoverScale: this._hoverScale,
1352
+ dimOpacity: hoverConfig?.dimOpacity ?? 1,
1353
+ hoverBackgroundColor: hoverConfig?.backgroundColor,
1354
+ hoverBorderColor: hoverConfig?.borderColor,
1355
+ hoverBorderStrokeWidth: hoverConfig?.borderStrokeWidth,
1356
+ hoverBorderDash: hoverConfig?.borderDash,
1357
+ hoverBorderDashOffset: hoverConfig?.borderDashOffset
1358
+ }
1359
+ };
1360
+ this._renderPropsMap.set(datasetId, renderProps);
1361
+ let layout = calculatePieLayout(renderProps, ringResult.effectiveChartArea);
1362
+ if (dlConfig) {
1363
+ const labelConfigs = buildPieLabelConfigs(layout, dlConfig, this._lastInput?.theme);
1364
+ if (labelConfigs.length > 0) {
1365
+ const bounds = { ...boxAreaToBounds({ x: 0, y: 0, width, height }), centerX: layout.center.x, centerY: layout.center.y };
1366
+ const shrunkRadius = computeLabelAwareRadius(labelConfigs, layout.outerRadius, bounds, {
1367
+ fontSize: dlConfig.fontSize,
1368
+ fontWeight: dlConfig.fontWeight,
1369
+ fontFamily: dlConfig.fontFamily,
1370
+ leaderOffset: dlConfig.leaderOffset,
1371
+ horizontalOffset: dlConfig.horizontalOffset,
1372
+ textOffset: dlConfig.textOffset,
1373
+ lineStyle: dlConfig.lineStyle,
1374
+ alignTo: dlConfig.alignTo
1375
+ });
1376
+ if (shrunkRadius < layout.outerRadius) {
1377
+ const appliedRatio = (() => {
1378
+ const r = renderProps.outerRadius;
1379
+ return r != null && r > 0 && r <= 1 ? r : 1;
1380
+ })();
1381
+ const naturalRadius = layout.outerRadius / appliedRatio;
1382
+ const shrunkRatio = shrunkRadius / naturalRadius;
1383
+ layout = calculatePieLayout({ ...renderProps, outerRadius: shrunkRatio }, ringResult.effectiveChartArea);
1384
+ }
1385
+ }
1386
+ }
1387
+ const pieLayout = { ...layout, datasetIndex };
1388
+ this._layoutData.set(datasetId, pieLayout);
1389
+ this._animationLayout = {
1390
+ center: pieLayout.center,
1391
+ outerRadius: pieLayout.outerRadius,
1392
+ innerRadius: pieLayout.innerRadius,
1393
+ startAngle: pieLayout.startAngle,
1394
+ sweepAngle: pieLayout.sweepAngle,
1395
+ borderAlign: props.borderAlign,
1396
+ borderJoinStyle: props.borderJoinStyle
1397
+ };
1398
+ const ratioField = props.sliceRadiusValue;
1399
+ const radiusRatios = ratioField ? computeSliceRadiusRatios(data, ratioField, vis) : null;
1400
+ const categoryAccessor = props.categoryField ?? props.labelField ?? FIELD_DEFAULTS.categoryField;
1401
+ const sliceSeriesIndex = props.order ?? datasetIndex;
1402
+ const sliceSeriesId = props.id ?? datasetId;
1403
+ const slices = pieLayout.slices.map((s) => {
1404
+ const i = s.originalIndex;
1405
+ const item = data[i];
1406
+ const angle = s.endAngle - s.rotation;
1407
+ const styleKey = `${datasetIndex}-${i}`;
1408
+ const category = categoryAccessor ? resolveAccessor(categoryAccessor, makeItemContext(item, i)) ?? void 0 : void 0;
1409
+ const categoryStr = category != null ? String(category) : void 0;
1410
+ this._styleData.set(styleKey, buildSliceStyleData(props, item, i, angle, radiusRatios ? radiusRatios[i] : 1, input.theme, s.value, categoryStr, sliceSeriesIndex, sliceSeriesId));
1411
+ return {
1412
+ originalIndex: i,
1413
+ value: s.value,
1414
+ percentage: s.percentage,
1415
+ rotation: s.rotation,
1416
+ endAngle: s.endAngle,
1417
+ radiusRatio: radiusRatios ? radiusRatios[i] : 1
1418
+ };
1419
+ });
1420
+ ringTargets.push(buildRingTarget(chartArea, datasetIndex, slices, this._ringLayouts, datasetId, props, pieLayout.innerRadius, pieLayout.outerRadius));
1421
+ const pieProps = props;
1422
+ if (pieProps.renderContent && pieProps.data) {
1423
+ this._sliceRenderFns.set(datasetId, {
1424
+ fn: pieProps.renderContent,
1425
+ data: pieProps.data,
1426
+ props,
1427
+ datasetIndex
1428
+ });
1429
+ }
1430
+ }
1431
+ this._visibilityMaps = visibilityMaps;
1432
+ this._ringTargets = ringTargets;
1433
+ this._baseRotation = this._pieEntries[0]?.props ? this._pieEntries[0].props.startAngle ?? -90 : -90;
1434
+ const firstLayout = this._layoutData.values().next().value;
1435
+ this._center = firstLayout?.center ?? null;
1436
+ if (ringTargets.length > 0) {
1437
+ const animConfig = features.get("animation")?.props;
1438
+ const { enabled, duration, easing } = parseAnimationConfig(animConfig);
1439
+ const sortState = this._pieEntries[0]?.props?.sort ?? "";
1440
+ const hiddenStr = [...hiddenItems.entries()].map(([k, v]) => `${k}:[${[...v].sort()}]`).join(";");
1441
+ const fingerprint = ringTargets.map((r) => `${r.datasetIndex}:${r.slices.map((s) => `${s.originalIndex}:${s.value}:${s.percentage}`).join(",")}`).join("|") + `|sort:${sortState}|hidden:${hiddenStr}`;
1442
+ const dataChanged = fingerprint !== this._lastFingerprint;
1443
+ const shouldAnimate = enabled && duration > 0 && dataChanged;
1444
+ const isFirstRender = this._sceneTargetOrder.length === 0;
1445
+ const targets = /* @__PURE__ */ new Map();
1446
+ const sortedRings = [...ringTargets].sort((a, b) => a.datasetIndex - b.datasetIndex);
1447
+ for (let ri = 0; ri < sortedRings.length; ri++) {
1448
+ const ring = sortedRings[ri];
1449
+ const isHiddenRing = ring.outerRadius < 1e-3;
1450
+ let targetInner = ring.innerRadius;
1451
+ let targetOuter = ring.outerRadius;
1452
+ if (isHiddenRing) {
1453
+ let innerNeighbor;
1454
+ for (let j = ri + 1; j < sortedRings.length; j++) {
1455
+ if (sortedRings[j].outerRadius > 1e-3) {
1456
+ innerNeighbor = sortedRings[j];
1457
+ break;
1458
+ }
1459
+ }
1460
+ if (innerNeighbor) {
1461
+ targetInner = innerNeighbor.outerRadius;
1462
+ targetOuter = innerNeighbor.outerRadius;
1463
+ } else {
1464
+ let holeRadius = 0;
1465
+ for (const r of sortedRings) {
1466
+ if (r.outerRadius > 1e-3) holeRadius = r.innerRadius;
1467
+ }
1468
+ targetInner = holeRadius;
1469
+ targetOuter = holeRadius;
1470
+ }
1471
+ }
1472
+ for (const slice of ring.slices) {
1473
+ const id = `${ring.datasetIndex}-${slice.originalIndex}`;
1474
+ targets.set(id, {
1475
+ rotation: slice.rotation,
1476
+ endAngle: slice.endAngle,
1477
+ radiusRatio: slice.radiusRatio ?? 1,
1478
+ value: slice.value,
1479
+ percentage: slice.percentage,
1480
+ innerRadius: targetInner,
1481
+ outerRadius: targetOuter
1482
+ });
1483
+ }
1484
+ }
1485
+ if (dataChanged) {
1486
+ if (this._animRafId !== null) {
1487
+ cancelRaf(this._animRafId);
1488
+ this._animRafId = null;
1489
+ }
1490
+ }
1491
+ const currentVisibleOrder = [...targets.entries()].filter(([, state]) => state.endAngle - state.rotation > 1e-3).map(([id]) => id);
1492
+ this._orderChanged = shouldAnimate && !isFirstRender && detectSortOrderChange(this._prevVisibleOrder, currentVisibleOrder);
1493
+ this._prevVisibleOrder = currentVisibleOrder;
1494
+ if (this._orderChanged) {
1495
+ this._snapshotAccumulatedPositions();
1496
+ }
1497
+ const opts = { numbers: { duration: shouldAnimate ? duration : 0, easing } };
1498
+ this._scene.diff(targets, opts);
1499
+ this._sceneTargetOrder = [...targets.keys()];
1500
+ this._sceneTargetMap = new Map(targets);
1501
+ if (dataChanged) {
1502
+ this._lastFingerprint = fingerprint;
1503
+ if (!shouldAnimate) {
1504
+ this._scene.tick(performance.now());
1505
+ }
1506
+ }
1507
+ if (dataChanged && renderer === "svg" && this._svgGroup) {
1508
+ const chartGroup = this._svgGroup;
1509
+ const centerTransform = this._animationLayout ? `translate(${this._animationLayout.center.x}, ${this._animationLayout.center.y})` : void 0;
1510
+ const a11y = buildAccessibilityRenderContext(input.features);
1511
+ for (const pieEntry of this._pieEntries) {
1512
+ let datasetGroup = chartGroup.querySelector(`[data-dataset="${pieEntry.id}"]`);
1513
+ if (!datasetGroup) {
1514
+ datasetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
1515
+ datasetGroup.setAttribute("data-dataset", pieEntry.id);
1516
+ if (centerTransform) datasetGroup.setAttribute("transform", centerTransform);
1517
+ if (a11y?.enabled && a11y.landmarkVerbosity === "all") {
1518
+ datasetGroup.setAttribute("role", "region");
1519
+ datasetGroup.setAttribute("aria-label", generateSeriesDescription(pieEntry.props?.name ?? pieEntry.id, "pie", this._layoutData.get(pieEntry.id)?.visibleSlices.length ?? 0));
1520
+ }
1521
+ chartGroup.appendChild(datasetGroup);
1522
+ }
1523
+ }
1524
+ }
1525
+ if (!isFirstRender && renderer === "svg" && this._svgGroup && this._animationLayout) {
1526
+ for (const pieEntry of this._pieEntries) {
1527
+ const group = this._svgGroup.querySelector(`[data-dataset="${pieEntry.id}"]`);
1528
+ if (group) {
1529
+ group.setAttribute("transform", `translate(${this._animationLayout.center.x}, ${this._animationLayout.center.y})`);
1530
+ }
1531
+ }
1532
+ }
1533
+ if (shouldAnimate) {
1534
+ this._startAnimLoop();
1535
+ }
1536
+ }
1537
+ if (renderer === "svg" && this._svgGroup) {
1538
+ const liveDatasetIds = new Set(this._pieEntries.map((e) => e.id));
1539
+ this._svgGroup.querySelectorAll("[data-dataset]").forEach((group) => {
1540
+ const id = group.getAttribute("data-dataset");
1541
+ if (id !== null && !liveDatasetIds.has(id)) group.remove();
1542
+ });
1543
+ }
1544
+ if (this._animRafId === null && this._ringTargets.length > 0) {
1545
+ const staticSlices = this._buildSlicesFromScene();
1546
+ this._renderFrame(staticSlices, renderer, input);
1547
+ this.onFrame?.();
1548
+ }
1549
+ }
1550
+ /**
1551
+ * Lightweight hover repaint — does not recompute layouts.
1552
+ */
1553
+ applyHover(hover) {
1554
+ if (!this._lastInput || !this._animationLayout) return;
1555
+ const renderer = this._lastInput.renderer;
1556
+ this.hoverState = hover;
1557
+ if (this._animRafId === null && this._lastRenderedSlices.length > 0) {
1558
+ this._renderFrame(this._lastRenderedSlices, renderer, this._lastInput);
1559
+ }
1560
+ this.onFrame?.();
1561
+ }
1562
+ /**
1563
+ * Called by framework wrappers on canvas repaint (e.g. resize).
1564
+ * Redraws the current frame to the provided context.
1565
+ */
1566
+ renderCanvasContent() {
1567
+ if (!this._lastInput || this._lastRenderedSlices.length === 0 || !this._animationLayout) return;
1568
+ this._renderFrame(this._lastRenderedSlices, "canvas", this._lastInput);
1569
+ }
1570
+ /**
1571
+ * Register a custom content SVG element (from framework ref callbacks).
1572
+ * key format: `${datasetIndex}-${sliceIndex}`
1573
+ */
1574
+ registerCustomContent(key, el) {
1575
+ if (el) {
1576
+ this._customContent.set(key, el);
1577
+ } else {
1578
+ this._customContent.delete(key);
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Register the label group element (for imperative SVG data label rendering).
1583
+ */
1584
+ registerLabelGroup(el) {
1585
+ this._labelGroup = el;
1586
+ }
1587
+ /**
1588
+ * Hit test at pixel coordinates.
1589
+ */
1590
+ hitTest(x, y) {
1591
+ for (const [dsIdx, sliceStates] of this._hitTestStates) {
1592
+ const pieEntry = this._pieEntries.find((e) => String(e.order) === dsIdx);
1593
+ if (!pieEntry) continue;
1594
+ const layout = this._layoutData.get(pieEntry.id);
1595
+ if (!layout) continue;
1596
+ const ringRadii = this._currentRingRadii.get(dsIdx);
1597
+ for (const ss of sliceStates) {
1598
+ if (!ss.visible) continue;
1599
+ const inner = ringRadii?.inner ?? layout.innerRadius;
1600
+ const outer = ringRadii?.outer ?? layout.outerRadius;
1601
+ const sliceAngles = [{ rotation: ss.rotation, endAngle: ss.endAngle }];
1602
+ const hitIdx = findSliceAtPoint(x - layout.center.x, y - layout.center.y, sliceAngles, outer, inner);
1603
+ if (hitIdx === 0) {
1604
+ const data = pieEntry.props.data ?? [];
1605
+ const item = data[ss.originalIndex];
1606
+ const propsWithLabel = pieEntry.props;
1607
+ const labelAccessor = propsWithLabel.categoryField ?? propsWithLabel.labelField ?? FIELD_DEFAULTS.categoryField;
1608
+ const label = item ? String(resolveAccessor(labelAccessor, makeItemContext(item, ss.originalIndex))) : "";
1609
+ const colorExtras = {
1610
+ value: ss.value,
1611
+ category: label,
1612
+ seriesIndex: pieEntry.props.order ?? Number(dsIdx),
1613
+ seriesId: pieEntry.props.id ?? pieEntry.id
1614
+ };
1615
+ const colorValue = getColorValue(pieEntry.props, ss.originalIndex, item, this._lastInput?.theme, colorExtras);
1616
+ return {
1617
+ datasetId: pieEntry.id,
1618
+ index: ss.originalIndex,
1619
+ label,
1620
+ value: ss.value,
1621
+ // Renormalized share (0-100) over the visible slices, so the
1622
+ // tooltip (auto-percentage + ctx.percentage) tracks legend toggles.
1623
+ percentage: ss.percentage,
1624
+ // Default tooltip readout: route the raw value through the shared
1625
+ // compact formatter so a mid-animation tween float (e.g.
1626
+ // 61.31423044588703) renders cleanly ("61.3"), matching axes/labels.
1627
+ formattedValue: formatCompactValue(ss.value),
1628
+ color: ss.color ?? "#000",
1629
+ colorGradient: asGradient(colorValue),
1630
+ swatchColor: resolveSwatchColor(pieEntry.props, ss.originalIndex, item, this._lastInput?.theme, colorExtras)
1631
+ };
1632
+ }
1633
+ }
1634
+ }
1635
+ return null;
1636
+ }
1637
+ /**
1638
+ * Get the pixel position of a slice's midpoint (for tooltip positioning).
1639
+ */
1640
+ getSlicePosition(datasetId, index) {
1641
+ const layout = this._layoutData.get(datasetId);
1642
+ if (!layout?.visibleSlices) return null;
1643
+ const slice = layout.visibleSlices.find((s) => s.dataIndex === index);
1644
+ if (!slice) return null;
1645
+ const midAngle = (slice.rotation + slice.endAngle) / 2;
1646
+ const midRadius = (layout.innerRadius + layout.outerRadius) / 2;
1647
+ const rad = midAngle * Math.PI / 180;
1648
+ return {
1649
+ x: layout.center.x + Math.cos(rad) * midRadius,
1650
+ y: layout.center.y + Math.sin(rad) * midRadius
1651
+ };
1652
+ }
1653
+ // ================================================================
1654
+ // GETTERS
1655
+ // ================================================================
1656
+ get hasPieData() {
1657
+ return this._hasPieData;
1658
+ }
1659
+ get center() {
1660
+ return this._center;
1661
+ }
1662
+ get layouts() {
1663
+ return this._layoutData;
1664
+ }
1665
+ get entries() {
1666
+ return this._pieEntries;
1667
+ }
1668
+ get ringLayouts() {
1669
+ return this._ringLayouts;
1670
+ }
1671
+ get ringTargets() {
1672
+ return this._ringTargets;
1673
+ }
1674
+ get visibilityMaps() {
1675
+ return this._visibilityMaps;
1676
+ }
1677
+ get ringRadii() {
1678
+ return this._currentRingRadii;
1679
+ }
1680
+ get hoverOffset() {
1681
+ return this._hoverOffset;
1682
+ }
1683
+ get hoverScale() {
1684
+ return this._hoverScale;
1685
+ }
1686
+ get baseRotation() {
1687
+ return this._baseRotation;
1688
+ }
1689
+ get animationLayout() {
1690
+ return this._animationLayout;
1691
+ }
1692
+ get lastRenderedSlices() {
1693
+ return this._lastRenderedSlices;
1694
+ }
1695
+ get sliceRenderFns() {
1696
+ return this._sliceRenderFns;
1697
+ }
1698
+ destroy() {
1699
+ if (this._animRafId !== null) {
1700
+ cancelRaf(this._animRafId);
1701
+ this._animRafId = null;
1702
+ }
1703
+ this._scene.reset();
1704
+ if (this._imageRepaintRaf !== null) {
1705
+ cancelRaf(this._imageRepaintRaf);
1706
+ this._imageRepaintRaf = null;
1707
+ }
1708
+ this._propertyOverrides.clear();
1709
+ }
1710
+ /**
1711
+ * Apply animated property values without triggering a full layout cycle.
1712
+ * Called from the property-animation framework wrapper (RadialRenderer)
1713
+ * each animation tick.
1714
+ *
1715
+ * Per-dataset: writes overrides into `_propertyOverrides` (or deletes the
1716
+ * entry when the store has no live values for it), then triggers a frame
1717
+ * rebuild so `_buildSlicesFromScene` + `_renderFrame` re-merge into the
1718
+ * rendered slices and frame geometry.
1719
+ *
1720
+ * SVG: re-renders imperatively via `_renderSceneFrame`. Canvas: the same
1721
+ * call clears + repaints the canvas in place.
1722
+ */
1723
+ applyPropertyAnimations(store) {
1724
+ if (!this._lastInput || !this._hasPieData) return;
1725
+ let appliedAny = false;
1726
+ for (const entry of this._pieEntries) {
1727
+ const overrides = resolveAnimatedProps(store, "pie", entry.id, PIE_ANIMATABLE_PROPS);
1728
+ if (Object.keys(overrides).length === 0) {
1729
+ if (this._propertyOverrides.delete(entry.id)) appliedAny = true;
1730
+ continue;
1731
+ }
1732
+ appliedAny = true;
1733
+ this._propertyOverrides.set(entry.id, overrides);
1734
+ }
1735
+ if (!appliedAny) return;
1736
+ this._renderSceneFrame();
1737
+ }
1738
+ /**
1739
+ * Full re-render of the current scene state.
1740
+ *
1741
+ * Used by the property-animation framework wrapper: when a property
1742
+ * animation completes and its override is released from the store,
1743
+ * `renderFrameCycle()` rebuilds slices from the `_propertyOverrides`-
1744
+ * cleared base values so SVG attributes snap back to the layout-derived
1745
+ * defaults (which can drift during in-place patches).
1746
+ */
1747
+ renderFrameCycle() {
1748
+ this._renderSceneFrame();
1749
+ }
1750
+ /** Rebuild + render the current scene state (delegates to `scene.renderSceneFrame`). */
1751
+ _renderSceneFrame() {
1752
+ renderSceneFrame(this);
1753
+ }
1754
+ /** Animation raf loop (delegates to `scene.startAnimLoop`). */
1755
+ _startAnimLoop() {
1756
+ startAnimLoop(this);
1757
+ }
1758
+ /** Snapshot accumulated visual positions before a sort tween (delegates to `scene`). */
1759
+ _snapshotAccumulatedPositions() {
1760
+ snapshotAccumulatedPositions(this);
1761
+ }
1762
+ /** Project the scene to RadialSliceState[] (delegates to `scene.buildSlicesFromScene`). */
1763
+ _buildSlicesFromScene() {
1764
+ return buildSlicesFromScene(this);
1765
+ }
1766
+ /** Render one frame to the active renderer (delegates to `render.renderFrame`). */
1767
+ _renderFrame(slices, renderer, input) {
1768
+ renderFrame(this, slices, renderer, input);
1769
+ }
1770
+ };
1771
+
1772
+ export { AxisRegistrationController, CartesianLayoutController, ChartStateController, DatasetRegistrationController, DrilldownStateController, KeyboardNavigationController, PieRendererController, ResponsiveDimensionsController, createChart };