@cdx-ui/components 0.3.5 → 0.3.7

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 (416) hide show
  1. package/README.md +1 -1
  2. package/coverage.svg +1 -1
  3. package/docs/components/bar-chart/content.md +703 -0
  4. package/docs/components/content.md +10 -1
  5. package/docs/components/country-flag/content.md +232 -0
  6. package/docs/components/data-grid/content.md +3 -1
  7. package/docs/components/icon/content.md +1 -1
  8. package/docs/components/input/content.md +1 -0
  9. package/docs/components/line-chart/content.md +567 -0
  10. package/docs/components/pie-chart/content.md +493 -0
  11. package/docs/components/progress-bar/content.md +164 -3
  12. package/docs/components/progress-segmented/content.md +1 -1
  13. package/docs/components/select/content.md +1 -0
  14. package/docs/components/stepper/content.md +599 -0
  15. package/docs/components/system-alert/content.md +533 -0
  16. package/docs/foundations/expressions/icons/content.md +2 -1
  17. package/docs/foundations/styles/tokens/all-tokens/content.md +2 -1
  18. package/docs/getting-started/developers/content.md +1 -1
  19. package/docs/llms.txt +12 -0
  20. package/docs/patterns/content.md +1 -0
  21. package/docs/patterns/dashboard/content.md +423 -19
  22. package/docs/patterns/insights/content.md +659 -0
  23. package/docs/resources/content.md +1 -0
  24. package/docs/resources/release-notes/0.3.5/content.md +7 -0
  25. package/docs/resources/release-notes/0.3.6/content.md +29 -0
  26. package/docs/resources/release-notes/content.md +2 -0
  27. package/docs/resources/skills/content.md +66 -0
  28. package/lib/commonjs/components/BarChart/geometry.js +1130 -0
  29. package/lib/commonjs/components/BarChart/geometry.js.map +1 -0
  30. package/lib/commonjs/components/BarChart/index.js +935 -0
  31. package/lib/commonjs/components/BarChart/index.js.map +1 -0
  32. package/lib/commonjs/components/BarChart/styles.js +26 -0
  33. package/lib/commonjs/components/BarChart/styles.js.map +1 -0
  34. package/lib/commonjs/components/Button/index.js +2 -2
  35. package/lib/commonjs/components/Button/index.js.map +1 -1
  36. package/lib/commonjs/components/CountryFlag/index.js +56 -0
  37. package/lib/commonjs/components/CountryFlag/index.js.map +1 -0
  38. package/lib/commonjs/components/Input/index.js.map +1 -1
  39. package/lib/commonjs/components/LineChart/geometry.js +698 -0
  40. package/lib/commonjs/components/LineChart/geometry.js.map +1 -0
  41. package/lib/commonjs/components/LineChart/index.js +744 -0
  42. package/lib/commonjs/components/LineChart/index.js.map +1 -0
  43. package/lib/commonjs/components/LineChart/styles.js +25 -0
  44. package/lib/commonjs/components/LineChart/styles.js.map +1 -0
  45. package/lib/commonjs/components/PieChart/geometry.js +216 -0
  46. package/lib/commonjs/components/PieChart/geometry.js.map +1 -0
  47. package/lib/commonjs/components/PieChart/index.js +476 -0
  48. package/lib/commonjs/components/PieChart/index.js.map +1 -0
  49. package/lib/commonjs/components/PieChart/styles.js +88 -0
  50. package/lib/commonjs/components/PieChart/styles.js.map +1 -0
  51. package/lib/commonjs/components/ProgressBar/index.js +44 -12
  52. package/lib/commonjs/components/ProgressBar/index.js.map +1 -1
  53. package/lib/commonjs/components/ProgressBar/styles.js +31 -2
  54. package/lib/commonjs/components/ProgressBar/styles.js.map +1 -1
  55. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js +9 -31
  56. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
  57. package/lib/commonjs/components/ProgressCircle/ProgressRing.js +52 -0
  58. package/lib/commonjs/components/ProgressCircle/ProgressRing.js.map +1 -0
  59. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +8 -7
  60. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
  61. package/lib/commonjs/components/Select/index.js.map +1 -1
  62. package/lib/commonjs/components/Stepper/index.js +369 -0
  63. package/lib/commonjs/components/Stepper/index.js.map +1 -0
  64. package/lib/commonjs/components/Stepper/styles.js +211 -0
  65. package/lib/commonjs/components/Stepper/styles.js.map +1 -0
  66. package/lib/commonjs/components/Stepper/useStepperLabelFit.js +22 -0
  67. package/lib/commonjs/components/Stepper/useStepperLabelFit.js.map +1 -0
  68. package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js +52 -0
  69. package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js.map +1 -0
  70. package/lib/commonjs/components/Stepper/useStepperRingColors.js +31 -0
  71. package/lib/commonjs/components/Stepper/useStepperRingColors.js.map +1 -0
  72. package/lib/commonjs/components/SystemAlert/animation.js +218 -0
  73. package/lib/commonjs/components/SystemAlert/animation.js.map +1 -0
  74. package/lib/commonjs/components/SystemAlert/index.js +525 -0
  75. package/lib/commonjs/components/SystemAlert/index.js.map +1 -0
  76. package/lib/commonjs/components/SystemAlert/styles.js +178 -0
  77. package/lib/commonjs/components/SystemAlert/styles.js.map +1 -0
  78. package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js +53 -0
  79. package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
  80. package/lib/commonjs/components/Tabs/index.js +4 -0
  81. package/lib/commonjs/components/Tabs/index.js.map +1 -1
  82. package/lib/commonjs/components/Tabs/styles.js +1 -1
  83. package/lib/commonjs/components/Tabs/styles.js.map +1 -1
  84. package/lib/commonjs/components/chart/ChartInspector.js +610 -0
  85. package/lib/commonjs/components/chart/ChartInspector.js.map +1 -0
  86. package/lib/commonjs/components/chart/format.js +93 -0
  87. package/lib/commonjs/components/chart/format.js.map +1 -0
  88. package/lib/commonjs/components/chart/hiddenSvgProps.js +23 -0
  89. package/lib/commonjs/components/chart/hiddenSvgProps.js.map +1 -0
  90. package/lib/commonjs/components/chart/index.js +2 -0
  91. package/lib/commonjs/components/chart/index.js.map +1 -0
  92. package/lib/commonjs/components/chart/motion.js +97 -0
  93. package/lib/commonjs/components/chart/motion.js.map +1 -0
  94. package/lib/commonjs/components/chart/palette.js +178 -0
  95. package/lib/commonjs/components/chart/palette.js.map +1 -0
  96. package/lib/commonjs/components/chart/size.js +34 -0
  97. package/lib/commonjs/components/chart/size.js.map +1 -0
  98. package/lib/commonjs/components/chart/ticks.js +59 -0
  99. package/lib/commonjs/components/chart/ticks.js.map +1 -0
  100. package/lib/commonjs/components/chart/types.js +6 -0
  101. package/lib/commonjs/components/chart/types.js.map +1 -0
  102. package/lib/commonjs/components/chart/useChartHostWidth.js +27 -0
  103. package/lib/commonjs/components/chart/useChartHostWidth.js.map +1 -0
  104. package/lib/commonjs/components/index.js +72 -0
  105. package/lib/commonjs/components/index.js.map +1 -1
  106. package/lib/commonjs/figma/Accordion.figma.js +2 -2
  107. package/lib/commonjs/figma/Accordion.figma.js.map +1 -1
  108. package/lib/commonjs/figma/Alert.figma.js +7 -7
  109. package/lib/commonjs/figma/Alert.figma.js.map +1 -1
  110. package/lib/commonjs/figma/Badge.Indicator.figma.js +6 -5
  111. package/lib/commonjs/figma/Badge.Indicator.figma.js.map +1 -1
  112. package/lib/commonjs/figma/Badge.figma.js +22 -22
  113. package/lib/commonjs/figma/Badge.figma.js.map +1 -1
  114. package/lib/commonjs/figma/Button.figma.js +5 -2
  115. package/lib/commonjs/figma/Button.figma.js.map +1 -1
  116. package/lib/commonjs/figma/CLAUDE.md +1 -0
  117. package/lib/commonjs/figma/Card.Header.figma.js +8 -4
  118. package/lib/commonjs/figma/Card.Header.figma.js.map +1 -1
  119. package/lib/commonjs/figma/Card.figma.js +2 -1
  120. package/lib/commonjs/figma/Card.figma.js.map +1 -1
  121. package/lib/commonjs/figma/Chip.figma.js +2 -2
  122. package/lib/commonjs/figma/Chip.figma.js.map +1 -1
  123. package/lib/commonjs/figma/CurrencyInput.figma.js +1 -1
  124. package/lib/commonjs/figma/CurrencyInput.figma.js.map +1 -1
  125. package/lib/commonjs/figma/Dialog.figma.js +5 -2
  126. package/lib/commonjs/figma/Dialog.figma.js.map +1 -1
  127. package/lib/commonjs/figma/Divider.figma.js +1 -1
  128. package/lib/commonjs/figma/Divider.figma.js.map +1 -1
  129. package/lib/commonjs/figma/Drawer.figma.js +3 -3
  130. package/lib/commonjs/figma/Drawer.figma.js.map +1 -1
  131. package/lib/commonjs/figma/IconButton.figma.js +10 -4
  132. package/lib/commonjs/figma/IconButton.figma.js.map +1 -1
  133. package/lib/commonjs/figma/Input.figma.js +2 -1
  134. package/lib/commonjs/figma/Input.figma.js.map +1 -1
  135. package/lib/commonjs/figma/Insight.figma.js +3 -3
  136. package/lib/commonjs/figma/Insight.figma.js.map +1 -1
  137. package/lib/commonjs/figma/ProgressBar.figma.js +11 -2
  138. package/lib/commonjs/figma/ProgressBar.figma.js.map +1 -1
  139. package/lib/commonjs/figma/ProgressSegmented.figma.js +9 -1
  140. package/lib/commonjs/figma/ProgressSegmented.figma.js.map +1 -1
  141. package/lib/commonjs/figma/Select.figma.js +2 -1
  142. package/lib/commonjs/figma/Select.figma.js.map +1 -1
  143. package/lib/commonjs/figma/SystemAlert.Button.figma.js +28 -0
  144. package/lib/commonjs/figma/SystemAlert.Button.figma.js.map +1 -0
  145. package/lib/commonjs/figma/SystemAlert.Close.figma.js +22 -0
  146. package/lib/commonjs/figma/SystemAlert.Close.figma.js.map +1 -0
  147. package/lib/commonjs/figma/SystemAlert.Description.figma.js +20 -0
  148. package/lib/commonjs/figma/SystemAlert.Description.figma.js.map +1 -0
  149. package/lib/commonjs/figma/SystemAlert.IconButton.figma.js +31 -0
  150. package/lib/commonjs/figma/SystemAlert.IconButton.figma.js.map +1 -0
  151. package/lib/commonjs/figma/SystemAlert.figma.js +47 -0
  152. package/lib/commonjs/figma/SystemAlert.figma.js.map +1 -0
  153. package/lib/commonjs/styles.css +0 -1
  154. package/lib/module/components/BarChart/geometry.js +1110 -0
  155. package/lib/module/components/BarChart/geometry.js.map +1 -0
  156. package/lib/module/components/BarChart/index.js +929 -0
  157. package/lib/module/components/BarChart/index.js.map +1 -0
  158. package/lib/module/components/BarChart/styles.js +22 -0
  159. package/lib/module/components/BarChart/styles.js.map +1 -0
  160. package/lib/module/components/Button/index.js +2 -2
  161. package/lib/module/components/Button/index.js.map +1 -1
  162. package/lib/module/components/CountryFlag/index.js +52 -0
  163. package/lib/module/components/CountryFlag/index.js.map +1 -0
  164. package/lib/module/components/Input/index.js.map +1 -1
  165. package/lib/module/components/LineChart/geometry.js +685 -0
  166. package/lib/module/components/LineChart/geometry.js.map +1 -0
  167. package/lib/module/components/LineChart/index.js +738 -0
  168. package/lib/module/components/LineChart/index.js.map +1 -0
  169. package/lib/module/components/LineChart/styles.js +21 -0
  170. package/lib/module/components/LineChart/styles.js.map +1 -0
  171. package/lib/module/components/PieChart/geometry.js +209 -0
  172. package/lib/module/components/PieChart/geometry.js.map +1 -0
  173. package/lib/module/components/PieChart/index.js +471 -0
  174. package/lib/module/components/PieChart/index.js.map +1 -0
  175. package/lib/module/components/PieChart/styles.js +84 -0
  176. package/lib/module/components/PieChart/styles.js.map +1 -0
  177. package/lib/module/components/ProgressBar/index.js +47 -15
  178. package/lib/module/components/ProgressBar/index.js.map +1 -1
  179. package/lib/module/components/ProgressBar/styles.js +31 -2
  180. package/lib/module/components/ProgressBar/styles.js.map +1 -1
  181. package/lib/module/components/ProgressCircle/ProgressCircleRing.js +10 -32
  182. package/lib/module/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
  183. package/lib/module/components/ProgressCircle/ProgressRing.js +48 -0
  184. package/lib/module/components/ProgressCircle/ProgressRing.js.map +1 -0
  185. package/lib/module/components/ScrollShadow/useScrollShadow.js +8 -7
  186. package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
  187. package/lib/module/components/Select/index.js.map +1 -1
  188. package/lib/module/components/Stepper/index.js +360 -0
  189. package/lib/module/components/Stepper/index.js.map +1 -0
  190. package/lib/module/components/Stepper/styles.js +206 -0
  191. package/lib/module/components/Stepper/styles.js.map +1 -0
  192. package/lib/module/components/Stepper/useStepperLabelFit.js +18 -0
  193. package/lib/module/components/Stepper/useStepperLabelFit.js.map +1 -0
  194. package/lib/module/components/Stepper/useStepperLabelFit.web.js +48 -0
  195. package/lib/module/components/Stepper/useStepperLabelFit.web.js.map +1 -0
  196. package/lib/module/components/Stepper/useStepperRingColors.js +28 -0
  197. package/lib/module/components/Stepper/useStepperRingColors.js.map +1 -0
  198. package/lib/module/components/SystemAlert/animation.js +213 -0
  199. package/lib/module/components/SystemAlert/animation.js.map +1 -0
  200. package/lib/module/components/SystemAlert/index.js +515 -0
  201. package/lib/module/components/SystemAlert/index.js.map +1 -0
  202. package/lib/module/components/SystemAlert/styles.js +174 -0
  203. package/lib/module/components/SystemAlert/styles.js.map +1 -0
  204. package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js +49 -0
  205. package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
  206. package/lib/module/components/Tabs/index.js +4 -0
  207. package/lib/module/components/Tabs/index.js.map +1 -1
  208. package/lib/module/components/Tabs/styles.js +1 -1
  209. package/lib/module/components/Tabs/styles.js.map +1 -1
  210. package/lib/module/components/chart/ChartInspector.js +600 -0
  211. package/lib/module/components/chart/ChartInspector.js.map +1 -0
  212. package/lib/module/components/chart/format.js +84 -0
  213. package/lib/module/components/chart/format.js.map +1 -0
  214. package/lib/module/components/chart/hiddenSvgProps.js +20 -0
  215. package/lib/module/components/chart/hiddenSvgProps.js.map +1 -0
  216. package/lib/module/components/chart/index.js +2 -0
  217. package/lib/module/components/chart/index.js.map +1 -0
  218. package/lib/module/components/chart/motion.js +89 -0
  219. package/lib/module/components/chart/motion.js.map +1 -0
  220. package/lib/module/components/chart/palette.js +168 -0
  221. package/lib/module/components/chart/palette.js.map +1 -0
  222. package/lib/module/components/chart/size.js +29 -0
  223. package/lib/module/components/chart/size.js.map +1 -0
  224. package/lib/module/components/chart/ticks.js +55 -0
  225. package/lib/module/components/chart/ticks.js.map +1 -0
  226. package/lib/module/components/chart/types.js +4 -0
  227. package/lib/module/components/chart/types.js.map +1 -0
  228. package/lib/module/components/chart/useChartHostWidth.js +23 -0
  229. package/lib/module/components/chart/useChartHostWidth.js.map +1 -0
  230. package/lib/module/components/index.js +6 -0
  231. package/lib/module/components/index.js.map +1 -1
  232. package/lib/module/figma/Accordion.figma.js +2 -2
  233. package/lib/module/figma/Accordion.figma.js.map +1 -1
  234. package/lib/module/figma/Alert.figma.js +7 -7
  235. package/lib/module/figma/Alert.figma.js.map +1 -1
  236. package/lib/module/figma/Badge.Indicator.figma.js +6 -5
  237. package/lib/module/figma/Badge.Indicator.figma.js.map +1 -1
  238. package/lib/module/figma/Badge.figma.js +22 -22
  239. package/lib/module/figma/Badge.figma.js.map +1 -1
  240. package/lib/module/figma/Button.figma.js +5 -2
  241. package/lib/module/figma/Button.figma.js.map +1 -1
  242. package/lib/module/figma/CLAUDE.md +1 -0
  243. package/lib/module/figma/Card.Header.figma.js +8 -4
  244. package/lib/module/figma/Card.Header.figma.js.map +1 -1
  245. package/lib/module/figma/Card.figma.js +2 -1
  246. package/lib/module/figma/Card.figma.js.map +1 -1
  247. package/lib/module/figma/Chip.figma.js +2 -2
  248. package/lib/module/figma/Chip.figma.js.map +1 -1
  249. package/lib/module/figma/CurrencyInput.figma.js +1 -1
  250. package/lib/module/figma/CurrencyInput.figma.js.map +1 -1
  251. package/lib/module/figma/Dialog.figma.js +5 -2
  252. package/lib/module/figma/Dialog.figma.js.map +1 -1
  253. package/lib/module/figma/Divider.figma.js +1 -1
  254. package/lib/module/figma/Divider.figma.js.map +1 -1
  255. package/lib/module/figma/Drawer.figma.js +3 -3
  256. package/lib/module/figma/Drawer.figma.js.map +1 -1
  257. package/lib/module/figma/IconButton.figma.js +10 -4
  258. package/lib/module/figma/IconButton.figma.js.map +1 -1
  259. package/lib/module/figma/Input.figma.js +2 -1
  260. package/lib/module/figma/Input.figma.js.map +1 -1
  261. package/lib/module/figma/Insight.figma.js +3 -3
  262. package/lib/module/figma/Insight.figma.js.map +1 -1
  263. package/lib/module/figma/ProgressBar.figma.js +12 -2
  264. package/lib/module/figma/ProgressBar.figma.js.map +1 -1
  265. package/lib/module/figma/ProgressSegmented.figma.js +9 -1
  266. package/lib/module/figma/ProgressSegmented.figma.js.map +1 -1
  267. package/lib/module/figma/Select.figma.js +2 -1
  268. package/lib/module/figma/Select.figma.js.map +1 -1
  269. package/lib/module/figma/SystemAlert.Button.figma.js +22 -0
  270. package/lib/module/figma/SystemAlert.Button.figma.js.map +1 -0
  271. package/lib/module/figma/SystemAlert.Close.figma.js +18 -0
  272. package/lib/module/figma/SystemAlert.Close.figma.js.map +1 -0
  273. package/lib/module/figma/SystemAlert.Description.figma.js +14 -0
  274. package/lib/module/figma/SystemAlert.Description.figma.js.map +1 -0
  275. package/lib/module/figma/SystemAlert.IconButton.figma.js +25 -0
  276. package/lib/module/figma/SystemAlert.IconButton.figma.js.map +1 -0
  277. package/lib/module/figma/SystemAlert.figma.js +41 -0
  278. package/lib/module/figma/SystemAlert.figma.js.map +1 -0
  279. package/lib/module/styles.css +0 -1
  280. package/lib/typescript/components/BarChart/geometry.d.ts +236 -0
  281. package/lib/typescript/components/BarChart/geometry.d.ts.map +1 -0
  282. package/lib/typescript/components/BarChart/index.d.ts +193 -0
  283. package/lib/typescript/components/BarChart/index.d.ts.map +1 -0
  284. package/lib/typescript/components/BarChart/styles.d.ts +32 -0
  285. package/lib/typescript/components/BarChart/styles.d.ts.map +1 -0
  286. package/lib/typescript/components/CountryFlag/index.d.ts +35 -0
  287. package/lib/typescript/components/CountryFlag/index.d.ts.map +1 -0
  288. package/lib/typescript/components/Input/index.d.ts +2 -0
  289. package/lib/typescript/components/Input/index.d.ts.map +1 -1
  290. package/lib/typescript/components/LineChart/geometry.d.ts +248 -0
  291. package/lib/typescript/components/LineChart/geometry.d.ts.map +1 -0
  292. package/lib/typescript/components/LineChart/index.d.ts +105 -0
  293. package/lib/typescript/components/LineChart/index.d.ts.map +1 -0
  294. package/lib/typescript/components/LineChart/styles.d.ts +12 -0
  295. package/lib/typescript/components/LineChart/styles.d.ts.map +1 -0
  296. package/lib/typescript/components/PieChart/geometry.d.ts +43 -0
  297. package/lib/typescript/components/PieChart/geometry.d.ts.map +1 -0
  298. package/lib/typescript/components/PieChart/index.d.ts +147 -0
  299. package/lib/typescript/components/PieChart/index.d.ts.map +1 -0
  300. package/lib/typescript/components/PieChart/styles.d.ts +27 -0
  301. package/lib/typescript/components/PieChart/styles.d.ts.map +1 -0
  302. package/lib/typescript/components/ProgressBar/index.d.ts +16 -1
  303. package/lib/typescript/components/ProgressBar/index.d.ts.map +1 -1
  304. package/lib/typescript/components/ProgressBar/styles.d.ts +7 -2
  305. package/lib/typescript/components/ProgressBar/styles.d.ts.map +1 -1
  306. package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts.map +1 -1
  307. package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts +17 -0
  308. package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts.map +1 -0
  309. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
  310. package/lib/typescript/components/Select/index.d.ts +2 -0
  311. package/lib/typescript/components/Select/index.d.ts.map +1 -1
  312. package/lib/typescript/components/Stepper/index.d.ts +68 -0
  313. package/lib/typescript/components/Stepper/index.d.ts.map +1 -0
  314. package/lib/typescript/components/Stepper/styles.d.ts +46 -0
  315. package/lib/typescript/components/Stepper/styles.d.ts.map +1 -0
  316. package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts +15 -0
  317. package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts.map +1 -0
  318. package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts +10 -0
  319. package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts.map +1 -0
  320. package/lib/typescript/components/Stepper/useStepperRingColors.d.ts +6 -0
  321. package/lib/typescript/components/Stepper/useStepperRingColors.d.ts.map +1 -0
  322. package/lib/typescript/components/SystemAlert/animation.d.ts +56 -0
  323. package/lib/typescript/components/SystemAlert/animation.d.ts.map +1 -0
  324. package/lib/typescript/components/SystemAlert/index.d.ts +110 -0
  325. package/lib/typescript/components/SystemAlert/index.d.ts.map +1 -0
  326. package/lib/typescript/components/SystemAlert/styles.d.ts +38 -0
  327. package/lib/typescript/components/SystemAlert/styles.d.ts.map +1 -0
  328. package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts +14 -0
  329. package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts.map +1 -0
  330. package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
  331. package/lib/typescript/components/chart/ChartInspector.d.ts +101 -0
  332. package/lib/typescript/components/chart/ChartInspector.d.ts.map +1 -0
  333. package/lib/typescript/components/chart/format.d.ts +16 -0
  334. package/lib/typescript/components/chart/format.d.ts.map +1 -0
  335. package/lib/typescript/components/chart/hiddenSvgProps.d.ts +17 -0
  336. package/lib/typescript/components/chart/hiddenSvgProps.d.ts.map +1 -0
  337. package/lib/typescript/components/chart/index.d.ts +3 -0
  338. package/lib/typescript/components/chart/index.d.ts.map +1 -0
  339. package/lib/typescript/components/chart/motion.d.ts +27 -0
  340. package/lib/typescript/components/chart/motion.d.ts.map +1 -0
  341. package/lib/typescript/components/chart/palette.d.ts +44 -0
  342. package/lib/typescript/components/chart/palette.d.ts.map +1 -0
  343. package/lib/typescript/components/chart/size.d.ts +25 -0
  344. package/lib/typescript/components/chart/size.d.ts.map +1 -0
  345. package/lib/typescript/components/chart/ticks.d.ts +3 -0
  346. package/lib/typescript/components/chart/ticks.d.ts.map +1 -0
  347. package/lib/typescript/components/chart/types.d.ts +163 -0
  348. package/lib/typescript/components/chart/types.d.ts.map +1 -0
  349. package/lib/typescript/components/chart/useChartHostWidth.d.ts +9 -0
  350. package/lib/typescript/components/chart/useChartHostWidth.d.ts.map +1 -0
  351. package/lib/typescript/components/index.d.ts +6 -0
  352. package/lib/typescript/components/index.d.ts.map +1 -1
  353. package/lib/typescript/figma/Alert.figma.d.ts.map +1 -1
  354. package/lib/typescript/figma/Badge.Indicator.figma.d.ts.map +1 -1
  355. package/lib/typescript/figma/Badge.figma.d.ts.map +1 -1
  356. package/lib/typescript/figma/Button.figma.d.ts.map +1 -1
  357. package/lib/typescript/figma/Card.Header.figma.d.ts.map +1 -1
  358. package/lib/typescript/figma/Card.figma.d.ts.map +1 -1
  359. package/lib/typescript/figma/Dialog.figma.d.ts.map +1 -1
  360. package/lib/typescript/figma/IconButton.figma.d.ts.map +1 -1
  361. package/lib/typescript/figma/Input.figma.d.ts.map +1 -1
  362. package/lib/typescript/figma/ProgressBar.figma.d.ts.map +1 -1
  363. package/lib/typescript/figma/ProgressSegmented.figma.d.ts.map +1 -1
  364. package/lib/typescript/figma/Select.figma.d.ts.map +1 -1
  365. package/lib/typescript/figma/SystemAlert.Button.figma.d.ts +11 -0
  366. package/lib/typescript/figma/SystemAlert.Button.figma.d.ts.map +1 -0
  367. package/lib/typescript/figma/SystemAlert.Close.figma.d.ts +11 -0
  368. package/lib/typescript/figma/SystemAlert.Close.figma.d.ts.map +1 -0
  369. package/lib/typescript/figma/SystemAlert.Description.figma.d.ts +8 -0
  370. package/lib/typescript/figma/SystemAlert.Description.figma.d.ts.map +1 -0
  371. package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts +11 -0
  372. package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts.map +1 -0
  373. package/lib/typescript/figma/SystemAlert.figma.d.ts +8 -0
  374. package/lib/typescript/figma/SystemAlert.figma.d.ts.map +1 -0
  375. package/package.json +5 -5
  376. package/src/components/BarChart/geometry.ts +1469 -0
  377. package/src/components/BarChart/index.tsx +1417 -0
  378. package/src/components/BarChart/styles.ts +31 -0
  379. package/src/components/Button/index.tsx +1 -1
  380. package/src/components/CountryFlag/index.tsx +53 -0
  381. package/src/components/Input/index.tsx +2 -0
  382. package/src/components/LineChart/geometry.ts +957 -0
  383. package/src/components/LineChart/index.tsx +1106 -0
  384. package/src/components/LineChart/styles.ts +26 -0
  385. package/src/components/PieChart/geometry.ts +295 -0
  386. package/src/components/PieChart/index.tsx +715 -0
  387. package/src/components/PieChart/styles.ts +101 -0
  388. package/src/components/ProgressBar/index.tsx +66 -14
  389. package/src/components/ProgressBar/styles.ts +35 -4
  390. package/src/components/ProgressCircle/ProgressCircleRing.tsx +10 -39
  391. package/src/components/ProgressCircle/ProgressRing.tsx +60 -0
  392. package/src/components/ScrollShadow/useScrollShadow.ts +12 -11
  393. package/src/components/Select/index.tsx +2 -0
  394. package/src/components/Stepper/index.tsx +452 -0
  395. package/src/components/Stepper/styles.ts +234 -0
  396. package/src/components/Stepper/useStepperLabelFit.ts +21 -0
  397. package/src/components/Stepper/useStepperLabelFit.web.ts +53 -0
  398. package/src/components/Stepper/useStepperRingColors.ts +29 -0
  399. package/src/components/SystemAlert/animation.ts +252 -0
  400. package/src/components/SystemAlert/index.tsx +684 -0
  401. package/src/components/SystemAlert/styles.ts +212 -0
  402. package/src/components/SystemAlert/useSystemAlertStatusBarStyle.ts +57 -0
  403. package/src/components/Tabs/index.tsx +4 -0
  404. package/src/components/Tabs/styles.ts +1 -1
  405. package/src/components/chart/ChartInspector.tsx +726 -0
  406. package/src/components/chart/format.ts +95 -0
  407. package/src/components/chart/hiddenSvgProps.ts +15 -0
  408. package/src/components/chart/index.ts +24 -0
  409. package/src/components/chart/motion.ts +98 -0
  410. package/src/components/chart/palette.tsx +247 -0
  411. package/src/components/chart/size.ts +21 -0
  412. package/src/components/chart/ticks.ts +53 -0
  413. package/src/components/chart/types.ts +183 -0
  414. package/src/components/chart/useChartHostWidth.ts +26 -0
  415. package/src/components/index.ts +6 -0
  416. package/src/styles.css +0 -1
@@ -0,0 +1,1417 @@
1
+ /* SVG `x`/`y` are paint coordinates. RN View `x`/`y` layout props are deprecated and share the name. */
2
+ /* eslint-disable @typescript-eslint/no-deprecated */
3
+ import {
4
+ forwardRef,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ type ForwardedRef,
10
+ type ReactElement,
11
+ type ReactNode,
12
+ } from 'react';
13
+ import {
14
+ ScrollView,
15
+ Text,
16
+ View,
17
+ type NativeScrollEvent,
18
+ type NativeSyntheticEvent,
19
+ type ViewProps,
20
+ } from 'react-native';
21
+ import Animated, { useAnimatedProps } from 'react-native-reanimated';
22
+ import { G, Line, Path, Svg, Text as SvgText } from 'react-native-svg';
23
+ import { dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
24
+ import { cn, useControllableState } from '@cdx-ui/utils';
25
+ import type { AnimationRootDisableAll } from '../../animation';
26
+ import {
27
+ ChartInspector,
28
+ ChartTooltipLayer,
29
+ expandHitBox,
30
+ type ChartInspectorTarget,
31
+ } from '../chart/ChartInspector';
32
+ import {
33
+ defaultChartValueFormatter,
34
+ formatChartPercent,
35
+ resolveChartLocaleText,
36
+ resolveValueFormatter,
37
+ } from '../chart/format';
38
+ import { HIDDEN_SVG_PROPS } from '../chart/hiddenSvgProps';
39
+ import type {
40
+ ChartAxis,
41
+ ChartGrid,
42
+ ChartLocaleText,
43
+ ChartReferenceLine,
44
+ ChartSelectedItem,
45
+ ChartSeriesBase,
46
+ ChartSizeProps,
47
+ ChartSummaryCategory,
48
+ ChartTooltipContent,
49
+ ChartTooltipProps,
50
+ ChartValueFormatter,
51
+ ChartValueLocation,
52
+ } from '../chart/types';
53
+ import { useChartHostWidth } from '../chart/useChartHostWidth';
54
+ import {
55
+ CHART_MOTION_DURATION,
56
+ useChartMorph,
57
+ useChartMotionEnabled,
58
+ lerpVector,
59
+ } from '../chart/motion';
60
+ import {
61
+ ChartLinearFills,
62
+ chartFillUrl,
63
+ chartSeriesColor,
64
+ resolveChartColor,
65
+ useChartChrome,
66
+ useChartColorPaints,
67
+ useChartGradientId,
68
+ useChartPalette,
69
+ useChartSimpleColor,
70
+ type ChartChrome,
71
+ type ChartPalette,
72
+ } from '../chart/palette';
73
+ import {
74
+ BAR_GAP_RATIO,
75
+ CATEGORY_GAP_RATIO,
76
+ barKeyboardNeighbor,
77
+ barPath,
78
+ barValueAnchor,
79
+ DEFAULT_STACK_GAP,
80
+ formatWaterfallBarValue,
81
+ isExpandAxis,
82
+ layoutBarChart,
83
+ overlayDelta,
84
+ readBarValue,
85
+ resolveBarChartContentSize,
86
+ resolveBarChartGutters,
87
+ resolveBarGroups,
88
+ stackedTotalAnchors,
89
+ waterfallValueAnchor,
90
+ type BarChartKind,
91
+ type BarChartLayout,
92
+ type BarChartOrientation,
93
+ type BarGeometrySeries,
94
+ type BarRect,
95
+ type BarStackOffset,
96
+ } from './geometry';
97
+ import {
98
+ BAR_RADIUS,
99
+ barChartLegendItemVariants,
100
+ barChartLegendLabelVariants,
101
+ barChartLegendVariants,
102
+ barChartRootVariants,
103
+ resolveBarChartBox,
104
+ type BarChartSize,
105
+ } from './styles';
106
+
107
+ const INACTIVE_OPACITY = 0.4;
108
+ const DASH = '6 4';
109
+ const SVG_OVERFLOW_VISIBLE = { overflow: 'visible' } as const;
110
+ const LABEL_FADE_DURATION = 220;
111
+
112
+ const AnimatedPath = Animated.createAnimatedComponent(Path);
113
+
114
+ export type * from '../chart';
115
+ export type { BarChartSize, BarStackOffset };
116
+
117
+ /** Keys of `TRow` whose value is a boolean. */
118
+ export type BarChartBooleanKey<TRow> = {
119
+ [K in keyof TRow]-?: TRow[K] extends boolean | undefined ? K : never;
120
+ }[keyof TRow] &
121
+ string;
122
+
123
+ /** Running-total series. Each row is a change, except rows flagged as totals. */
124
+ export interface BarChartWaterfall<TRow> {
125
+ /**
126
+ * Row key whose `true` value marks a total bar, drawn from zero to the running total. A total
127
+ * row with a value on the first row sets the starting total; otherwise its value is ignored.
128
+ */
129
+ totalKey: BarChartBooleanKey<TRow>;
130
+ }
131
+
132
+ /** Draws this series as a thinner comparison bar beside another series' bar. */
133
+ export interface BarChartOverlay {
134
+ /** `id` of the series this one is compared against. */
135
+ seriesId: string;
136
+ }
137
+
138
+ export interface BarChartSeries<TRow> extends ChartSeriesBase<TRow> {
139
+ /** Series type. Reserved for mixed charts; bar is the only type today. */
140
+ type?: 'bar';
141
+ /**
142
+ * Stack group id. Series with the same `stack` draw as one bar per category; different ids
143
+ * draw side by side. Omit to draw the series on its own.
144
+ */
145
+ stack?: string;
146
+ /**
147
+ * How the stack group piles its values. Set it on any one series in the group.
148
+ * `expand` scales each category to 100%.
149
+ * @default 'diverging'
150
+ */
151
+ stackOffset?: BarStackOffset;
152
+ /**
153
+ * Draws the series as a waterfall (running total). A waterfall series draws alone: other
154
+ * series are not plotted.
155
+ */
156
+ waterfall?: BarChartWaterfall<TRow>;
157
+ /**
158
+ * Draws the series as a thin comparison bar beside the series it names (this month versus last
159
+ * month). The pair draws alone, and the named series is announced with the change.
160
+ */
161
+ overlay?: BarChartOverlay;
162
+ }
163
+
164
+ export interface BarChartProps<TRow extends object = object> extends ViewProps, ChartSizeProps {
165
+ /** One object per category. Series and axes read it through `dataKey`. */
166
+ dataset: readonly TRow[];
167
+ /** Bars to draw. Each reads one numeric column through `dataKey`. */
168
+ series: readonly BarChartSeries<TRow>[];
169
+ /**
170
+ * Horizontal axis. In `vertical` layout it is the category (band) axis: `dataKey` names the
171
+ * category column, `position` (`'bottom'`, `'top'`, `'none'`) places its labels, and
172
+ * `tickLabelInterval` picks which labels draw. In `horizontal` layout it is the value axis.
173
+ * Only the first axis is used.
174
+ */
175
+ xAxis?: readonly ChartAxis<TRow>[];
176
+ /**
177
+ * Vertical axis. In `vertical` layout it is the value axis (`min`, `max`, `tickNumber`,
178
+ * `valueFormatter`; `position: 'none'` hides its ticks). In `horizontal` layout it is the
179
+ * category axis (`position` `'left'`, `'right'`, or `'none'`). Only the first axis is used.
180
+ */
181
+ yAxis?: readonly ChartAxis<TRow>[];
182
+ /**
183
+ * `vertical` grows bars up from the baseline. `horizontal` grows them to the right.
184
+ * @default 'vertical'
185
+ */
186
+ layout?: BarChartOrientation;
187
+ /**
188
+ * Hides the series legend under the chart. Waterfall charts never show one.
189
+ * @default false
190
+ */
191
+ hideLegend?: boolean;
192
+ /**
193
+ * Grid lines along the value axis: `horizontal` in `vertical` layout, `vertical` in
194
+ * `horizontal` layout. The other direction is not drawn. Omit to draw the value-axis lines;
195
+ * pass an object without that key set to `true` to hide them and keep only the baseline.
196
+ * @default { horizontal: true } (vertical layout), { vertical: true } (horizontal layout)
197
+ */
198
+ grid?: ChartGrid;
199
+ /**
200
+ * Default series colors. `auto` uses the FI brand family (`simple-primary`, `-secondary`,
201
+ * `-tertiary`) for up to three series and switches the whole chart to `categorical-1`…`6`
202
+ * past that. `categorical` always uses the categorical slots. A series `color` wins.
203
+ * @default 'auto'
204
+ */
205
+ palette?: ChartPalette;
206
+ /**
207
+ * Dashed guides at a value: horizontal on a vertical chart, vertical on a horizontal chart.
208
+ */
209
+ referenceLines?: readonly ChartReferenceLine[];
210
+ /**
211
+ * Keeps this `dataset` row full color and mutes the rest. Use with `barLabel` to label only
212
+ * that bar, like "this week" in a spending insight.
213
+ */
214
+ emphasizedIndex?: number;
215
+ /**
216
+ * Draws formatted values at the end of each bar (each stack's total when stacked).
217
+ * @default false
218
+ */
219
+ barLabel?: boolean;
220
+ /**
221
+ * Corner radius in pixels. `0` draws square bars.
222
+ * @default 6
223
+ */
224
+ borderRadius?: number;
225
+ /**
226
+ * Pixel gap between stacked segments. With a gap every segment is rounded; `0` joins them and
227
+ * rounds only the outer cap.
228
+ * @default 3
229
+ */
230
+ stackGap?: number;
231
+ /**
232
+ * Share of each category slot left empty around its bars, from `0` to `0.95`. Higher is
233
+ * thinner bars with more space between categories.
234
+ * @default 0.36
235
+ */
236
+ categoryGapRatio?: number;
237
+ /**
238
+ * Gap between side-by-side bars in one category, as a share of one bar's thickness.
239
+ * @default 0.5
240
+ */
241
+ barGapRatio?: number;
242
+ /**
243
+ * When the categories need more room than the visible box, scroll instead of
244
+ * squeezing bars. Vertical charts scroll sideways; horizontal charts scroll vertically.
245
+ * The value axis stays pinned. Turn off to pack every category into `width` / `height`.
246
+ * @default true
247
+ */
248
+ scrollable?: boolean;
249
+ /**
250
+ * How many categories fit in the viewport when `scrollable` is on. Omit to use a
251
+ * readable minimum band (~48px).
252
+ */
253
+ visiblePoints?: number;
254
+ /**
255
+ * Controlled selected bar: `seriesId` and the `dataset` row. `null` clears the selection while
256
+ * staying controlled.
257
+ */
258
+ selectedItem?: ChartSelectedItem;
259
+ /**
260
+ * Initial selected bar when `selectedItem` is omitted.
261
+ */
262
+ defaultSelectedItem?: ChartSelectedItem;
263
+ /**
264
+ * Called when a bar is pressed, with that bar, or with `null` when the selected bar is pressed
265
+ * again. Providing this, `selectedItem`, or `defaultSelectedItem` makes the chart selectable.
266
+ */
267
+ onSelectedItemChange?: (item: ChartSelectedItem) => void;
268
+ /**
269
+ * Tooltip on hover, keyboard focus, or tap. `axis` lists every series in the category, `item`
270
+ * only the bar under the pointer, `none` hides it. On web the plot is one tab stop. Tab /
271
+ * Shift+Tab move between categories and leave the chart after the last (or before the first)
272
+ * group. Arrow keys move along the category axis (and between series). Home and End jump to
273
+ * the first or last category. Enter or Space selects. Escape hides the tooltip. Each bar is
274
+ * announced to screen readers.
275
+ * @default { trigger: 'axis' }
276
+ */
277
+ tooltip?: ChartTooltipProps;
278
+ /** BCP 47 locale for default number and percent formatting. */
279
+ locale?: string;
280
+ /**
281
+ * Overrides for generated strings: summary, trend, waterfall step names, legend fallback, and
282
+ * no-value text. Keys you omit keep the English defaults.
283
+ */
284
+ localeText?: Partial<ChartLocaleText>;
285
+ /**
286
+ * Bars grow from the baseline on mount and tween to new values when `dataset` changes. Pass
287
+ * `'disable-all'` to draw the final state. Reduce Motion and `GlobalAnimationSettingsProvider`
288
+ * `disable-all` also turn it off.
289
+ */
290
+ animation?: AnimationRootDisableAll;
291
+ /**
292
+ * Accessible summary. Defaults to `localeText.barChartSummary` over every category.
293
+ */
294
+ accessibilityLabel?: string;
295
+ /** Additional Uniwind/Tailwind classes, merged after the root's own styles. */
296
+ className?: string;
297
+ }
298
+
299
+ interface BarSelection {
300
+ readonly seriesId: string;
301
+ readonly dataIndex: number;
302
+ }
303
+
304
+ interface BarPlan<TRow> {
305
+ readonly kind: BarChartKind;
306
+ readonly series: readonly BarChartSeries<TRow>[];
307
+ readonly geometry: readonly BarGeometrySeries[];
308
+ readonly totalKey: string | undefined;
309
+ }
310
+
311
+ function resolveBarPlan<TRow>(series: readonly BarChartSeries<TRow>[]): BarPlan<TRow> {
312
+ const toGeometry = (item: BarChartSeries<TRow>): BarGeometrySeries => ({
313
+ id: item.id,
314
+ dataKey: item.dataKey,
315
+ label: item.label ?? '',
316
+ stack: item.stack,
317
+ stackOffset: item.stackOffset,
318
+ });
319
+ const waterfall = series.find((item) => item.waterfall !== undefined);
320
+ if (waterfall !== undefined) {
321
+ return {
322
+ kind: 'waterfall',
323
+ series: [waterfall],
324
+ geometry: [toGeometry(waterfall)],
325
+ totalKey: waterfall.waterfall?.totalKey,
326
+ };
327
+ }
328
+ for (const comparison of series) {
329
+ const primary =
330
+ comparison.overlay === undefined
331
+ ? undefined
332
+ : series.find((item) => item.id === comparison.overlay?.seriesId && item !== comparison);
333
+ if (primary !== undefined) {
334
+ const pair = [primary, comparison];
335
+ return {
336
+ kind: 'overlay',
337
+ series: pair,
338
+ geometry: pair.map((item) => ({ ...toGeometry(item), stack: undefined })),
339
+ totalKey: undefined,
340
+ };
341
+ }
342
+ }
343
+ return { kind: 'bars', series, geometry: series.map(toGeometry), totalKey: undefined };
344
+ }
345
+
346
+ function sameItem(a: ChartSelectedItem | undefined, b: BarSelection): boolean {
347
+ return a?.seriesId === b.seriesId && a.dataIndex === b.dataIndex;
348
+ }
349
+
350
+ function barSelection(bar: BarRect): BarSelection {
351
+ return { seriesId: bar.seriesId, dataIndex: bar.index };
352
+ }
353
+
354
+ function barAccessibilityName(
355
+ label: string,
356
+ seriesLabel: string,
357
+ formatted: string,
358
+ change: string | null = null,
359
+ ): string {
360
+ const base =
361
+ seriesLabel.length > 0 ? `${label}, ${seriesLabel}, ${formatted}` : `${label}, ${formatted}`;
362
+ return change === null ? base : `${base}, ${change}`;
363
+ }
364
+
365
+ type BarFormatter = (bar: BarRect, location: ChartValueLocation) => string;
366
+
367
+ function BarChartInner<TRow extends object>(
368
+ {
369
+ dataset,
370
+ series: seriesProp,
371
+ xAxis,
372
+ yAxis,
373
+ size = 'lg',
374
+ width: widthProp,
375
+ height: heightProp,
376
+ layout: orientation = 'vertical',
377
+ hideLegend = false,
378
+ grid,
379
+ palette: paletteProp = 'auto',
380
+ barLabel = false,
381
+ borderRadius = BAR_RADIUS,
382
+ stackGap = DEFAULT_STACK_GAP,
383
+ categoryGapRatio = CATEGORY_GAP_RATIO,
384
+ barGapRatio = BAR_GAP_RATIO,
385
+ referenceLines,
386
+ emphasizedIndex,
387
+ scrollable = true,
388
+ visiblePoints,
389
+ selectedItem,
390
+ defaultSelectedItem,
391
+ onSelectedItemChange,
392
+ tooltip: tooltipProp,
393
+ locale,
394
+ localeText: localeTextProp,
395
+ animation,
396
+ accessibilityLabel,
397
+ className,
398
+ style,
399
+ onLayout,
400
+ ...props
401
+ }: BarChartProps<TRow>,
402
+ ref: ForwardedRef<View>,
403
+ ) {
404
+ const animated = useChartMotionEnabled(animation);
405
+ const simpleColor = useChartSimpleColor();
406
+ const gradientId = useChartGradientId('bar-fill');
407
+ const chrome = useChartChrome();
408
+ const palette = useChartPalette();
409
+ const paints = useChartColorPaints();
410
+ const localeText = useMemo(() => resolveChartLocaleText(localeTextProp), [localeTextProp]);
411
+ const plan = useMemo(() => resolveBarPlan(seriesProp), [seriesProp]);
412
+ const { kind, geometry } = plan;
413
+ const overlay = kind === 'overlay';
414
+ const waterfall = kind === 'waterfall';
415
+ const expandAxis = useMemo(
416
+ () => isExpandAxis(kind, resolveBarGroups(geometry)),
417
+ [kind, geometry],
418
+ );
419
+ const categoryAxis = (orientation === 'vertical' ? xAxis : yAxis)?.[0];
420
+ const valueAxis = (orientation === 'vertical' ? yAxis : xAxis)?.[0];
421
+ const categoryKey = categoryAxis?.dataKey;
422
+ const categoryLabelPosition = categoryAxis?.position;
423
+ const showTicks = valueAxis?.position !== 'none';
424
+ const showGrid =
425
+ grid === undefined
426
+ ? true
427
+ : (orientation === 'vertical' ? grid.horizontal : grid.vertical) === true;
428
+ const tickLabelInterval = categoryAxis?.tickLabelInterval;
429
+ const categories = useMemo(
430
+ () =>
431
+ dataset.map((row, index) => {
432
+ if (categoryKey === undefined) {
433
+ return String(index);
434
+ }
435
+ const raw: unknown = row[categoryKey];
436
+ return raw === null || raw === undefined ? '' : String(raw as string | number | boolean);
437
+ }),
438
+ [dataset, categoryKey],
439
+ );
440
+ const hasNegative = useMemo(
441
+ () =>
442
+ dataset.some((row) => geometry.some((item) => (readBarValue(row, item.dataKey) ?? 0) < 0)),
443
+ [dataset, geometry],
444
+ );
445
+ const sourceById = useMemo(
446
+ () => new Map(plan.series.map((item) => [item.id, item] as const)),
447
+ [plan.series],
448
+ );
449
+ const valueAxisMin = valueAxis?.min;
450
+ const valueAxisMax = valueAxis?.max;
451
+ const valueAxisTicks = valueAxis?.tickNumber;
452
+ const valueAxisFormatter = valueAxis?.valueFormatter;
453
+ const valueAxisConfig = useMemo(
454
+ () => ({ min: valueAxisMin, max: valueAxisMax, tickNumber: valueAxisTicks }),
455
+ [valueAxisMin, valueAxisMax, valueAxisTicks],
456
+ );
457
+ const percentFormatter: ChartValueFormatter = useMemo(
458
+ () => (value) => (value === null ? '' : formatChartPercent(value, locale)),
459
+ [locale],
460
+ );
461
+ const formatTick = useMemo(() => {
462
+ const formatter =
463
+ valueAxisFormatter ?? (expandAxis ? percentFormatter : defaultChartValueFormatter(locale));
464
+ return (value: number) => formatter(value, { location: 'tick' });
465
+ }, [valueAxisFormatter, expandAxis, percentFormatter, locale]);
466
+ const formatBar: BarFormatter = (bar, location) => {
467
+ const source = sourceById.get(bar.seriesId);
468
+ const context = { location, seriesId: bar.seriesId, dataIndex: bar.index };
469
+ if (bar.expand === true && source?.valueFormatter === undefined) {
470
+ return (valueAxisFormatter ?? percentFormatter)(bar.plotValue / 100, context);
471
+ }
472
+ const formatter = resolveValueFormatter(source, valueAxis, locale);
473
+ const format = (value: number) => formatter(value, context);
474
+ return bar.waterfallRole === undefined
475
+ ? format(bar.value)
476
+ : formatWaterfallBarValue(bar, format);
477
+ };
478
+ const seriesLabelOf = (bar: BarRect): string => {
479
+ if (bar.waterfallRole === 'increase') {
480
+ return localeText.waterfallIncrease;
481
+ }
482
+ if (bar.waterfallRole === 'decrease') {
483
+ return localeText.waterfallDecrease;
484
+ }
485
+ return bar.seriesLabel;
486
+ };
487
+
488
+ const box = resolveBarChartBox(size, widthProp, heightProp);
489
+ const [hostWidth, handleHostLayout] = useChartHostWidth(onLayout);
490
+ const viewportWidth = hostWidth > 0 ? Math.min(hostWidth, box.width) : box.width;
491
+ const viewportHeight = box.height;
492
+ const gutters = useMemo(
493
+ () =>
494
+ resolveBarChartGutters({
495
+ categories,
496
+ hasNegative,
497
+ orientation,
498
+ barLabel,
499
+ categoryLabelPosition,
500
+ showTicks,
501
+ overlay,
502
+ }),
503
+ [categories, hasNegative, orientation, barLabel, categoryLabelPosition, showTicks, overlay],
504
+ );
505
+ const content = useMemo(
506
+ () =>
507
+ resolveBarChartContentSize({
508
+ viewportWidth,
509
+ viewportHeight,
510
+ categoryCount: dataset.length,
511
+ orientation,
512
+ gutters,
513
+ scrollable,
514
+ visiblePoints,
515
+ }),
516
+ [
517
+ viewportWidth,
518
+ viewportHeight,
519
+ dataset.length,
520
+ orientation,
521
+ gutters,
522
+ scrollable,
523
+ visiblePoints,
524
+ ],
525
+ );
526
+ const showCategoryLabel = useMemo(() => {
527
+ if (typeof tickLabelInterval !== 'function') {
528
+ return undefined;
529
+ }
530
+ return (index: number) => {
531
+ const row = dataset.at(index);
532
+ const value: unknown =
533
+ row === undefined || categoryKey === undefined ? index : row[categoryKey];
534
+ return tickLabelInterval(value, index);
535
+ };
536
+ }, [tickLabelInterval, dataset, categoryKey]);
537
+ const layout = useMemo(
538
+ () =>
539
+ layoutBarChart({
540
+ dataset,
541
+ categories,
542
+ series: geometry,
543
+ kind,
544
+ waterfallTotalKey: plan.totalKey,
545
+ width: content.width,
546
+ height: content.height,
547
+ orientation,
548
+ barLabel,
549
+ categoryLabelPosition,
550
+ showTicks,
551
+ referenceLines,
552
+ valueAxis: valueAxisConfig,
553
+ formatTick,
554
+ stackGap,
555
+ categoryGapRatio,
556
+ barGapRatio,
557
+ showCategoryLabel,
558
+ }),
559
+ [
560
+ dataset,
561
+ categories,
562
+ geometry,
563
+ kind,
564
+ plan.totalKey,
565
+ content.width,
566
+ content.height,
567
+ orientation,
568
+ barLabel,
569
+ categoryLabelPosition,
570
+ showTicks,
571
+ referenceLines,
572
+ valueAxisConfig,
573
+ formatTick,
574
+ stackGap,
575
+ categoryGapRatio,
576
+ barGapRatio,
577
+ showCategoryLabel,
578
+ ],
579
+ );
580
+ const overflowsX = content.width > viewportWidth + 0.5;
581
+ const overflowsY = content.height > viewportHeight + 0.5;
582
+ const fills = waterfall
583
+ ? dataset.map((_, index) => palette[index % palette.length] ?? simpleColor)
584
+ : plan.series.map((item, seriesIndex) => {
585
+ const custom = resolveChartColor(item.color, paints);
586
+ if (custom !== undefined) {
587
+ return custom;
588
+ }
589
+ if (overlay) {
590
+ return seriesIndex === 0 ? simpleColor : chrome.grid;
591
+ }
592
+ return paints[chartSeriesColor(seriesIndex, plan.series.length, paletteProp, 'bar')];
593
+ });
594
+ const summaryCategories: ChartSummaryCategory[] = dataset.map((row, dataIndex) => ({
595
+ label: categories[dataIndex] ?? '',
596
+ values: plan.series.flatMap((item) => {
597
+ const value = readBarValue(row, item.dataKey);
598
+ if (value === null) {
599
+ return [];
600
+ }
601
+ // An expand axis formats 0–1 shares; the summary reads raw values.
602
+ const formatter = resolveValueFormatter(item, expandAxis ? null : valueAxis, locale);
603
+ return [
604
+ {
605
+ label: item.label ?? '',
606
+ formattedValue: formatter(value, { location: 'a11y', seriesId: item.id, dataIndex }),
607
+ },
608
+ ];
609
+ }),
610
+ }));
611
+ const summary = accessibilityLabel ?? localeText.barChartSummary(summaryCategories);
612
+ const trigger = tooltipProp?.trigger ?? 'axis';
613
+ const tooltip = trigger !== 'none';
614
+ const interactive =
615
+ onSelectedItemChange !== undefined ||
616
+ selectedItem !== undefined ||
617
+ defaultSelectedItem !== undefined;
618
+ const [selectedState, setSelectedItem] = useControllableState<ChartSelectedItem>({
619
+ prop: selectedItem,
620
+ defaultProp: defaultSelectedItem ?? null,
621
+ onChange: onSelectedItemChange,
622
+ });
623
+ const selected = selectedState ?? null;
624
+ const visibleBars = layout.bars.filter((bar) =>
625
+ bar.stackId !== undefined
626
+ ? bar.width > 0 && bar.height > 0
627
+ : orientation === 'vertical'
628
+ ? bar.width > 0
629
+ : bar.height > 0,
630
+ );
631
+ const inspectable = (interactive || tooltip) && visibleBars.length > 0;
632
+ const [activeIndex, setActiveIndex] = useState<number | null>(null);
633
+ const [dismissed, setDismissed] = useState(false);
634
+ const [scrubbing, setScrubbing] = useState(false);
635
+ const scrollOffset = useRef({ x: 0, y: 0 });
636
+ const selectedIndex = selected
637
+ ? visibleBars.findIndex((bar) => sameItem(selected, barSelection(bar)))
638
+ : -1;
639
+ const activeBar = activeIndex === null ? undefined : visibleBars.at(activeIndex);
640
+ const highlightBar: BarSelection | null = activeBar
641
+ ? barSelection(activeBar)
642
+ : selectedIndex >= 0 && selected !== null
643
+ ? selected
644
+ : null;
645
+ const tooltipIndex = tooltip && !dismissed && activeIndex !== null ? activeIndex : null;
646
+ const activate = (index: number) => {
647
+ setDismissed(false);
648
+ setActiveIndex((prev) => (prev === index ? prev : index));
649
+ };
650
+ const deactivate = (index: number) => {
651
+ setActiveIndex((prev) => (prev === index ? null : prev));
652
+ };
653
+
654
+ const showLegend = !waterfall && plan.series.length > 0 && !hideLegend;
655
+
656
+ const plot = (
657
+ <BarPlot
658
+ layout={layout}
659
+ orientation={orientation}
660
+ overlay={overlay}
661
+ waterfall={waterfall}
662
+ showGrid={showGrid}
663
+ barLabel={barLabel}
664
+ formatBar={formatBar}
665
+ formatStackTotal={(anchor) => {
666
+ const context = { location: 'label' as const, dataIndex: anchor.index };
667
+ if (anchor.expand) {
668
+ return (valueAxisFormatter ?? percentFormatter)(anchor.total / 100, context);
669
+ }
670
+ return resolveValueFormatter(
671
+ sourceById.get(anchor.seriesId),
672
+ valueAxis,
673
+ locale,
674
+ )(anchor.total, context);
675
+ }}
676
+ fills={fills}
677
+ gradientId={gradientId}
678
+ chrome={chrome}
679
+ accent={simpleColor}
680
+ borderRadius={Math.max(borderRadius, 0)}
681
+ animated={animated}
682
+ highlightBar={highlightBar}
683
+ emphasizedIndex={emphasizedIndex}
684
+ />
685
+ );
686
+
687
+ const vertical = orientation === 'vertical';
688
+ const targets = inspectable
689
+ ? barInspectorTargets({
690
+ bars: visibleBars,
691
+ layout,
692
+ orientation,
693
+ overlay,
694
+ waterfall,
695
+ trigger,
696
+ offsetX: vertical ? layout.plot.x : 0,
697
+ fills,
698
+ fallbackFill: chrome.grid,
699
+ formatBar,
700
+ seriesLabelOf,
701
+ localeText,
702
+ selectedIndex: selectedIndex >= 0 ? selectedIndex : null,
703
+ })
704
+ : [];
705
+ const tip = tooltipIndex === null ? undefined : targets.at(tooltipIndex);
706
+ const dismissOnScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
707
+ scrollOffset.current = event.nativeEvent.contentOffset;
708
+ if (tooltipIndex !== null) {
709
+ setDismissed(true);
710
+ }
711
+ };
712
+
713
+ const inspector = inspectable ? (
714
+ <BarInspector
715
+ bars={visibleBars}
716
+ targets={targets}
717
+ orientation={orientation}
718
+ summary={summary}
719
+ selectable={interactive}
720
+ activeIndex={activeIndex}
721
+ selectedIndex={selectedIndex >= 0 ? selectedIndex : null}
722
+ holdToScrub={overflowsX || overflowsY}
723
+ onScrubLockChange={setScrubbing}
724
+ onActivate={activate}
725
+ onDeactivate={deactivate}
726
+ onDismiss={() => {
727
+ setDismissed(true);
728
+ }}
729
+ onSelectIndex={(index) => {
730
+ const bar = visibleBars.at(index);
731
+ if (bar) {
732
+ setDismissed(false);
733
+ const next = barSelection(bar);
734
+ setSelectedItem(sameItem(selected, next) ? null : next);
735
+ }
736
+ }}
737
+ />
738
+ ) : null;
739
+ const tooltipLayer =
740
+ tip !== undefined ? (
741
+ <ChartTooltipLayer
742
+ content={tip.tooltip}
743
+ noValueText={localeText.noValue}
744
+ slot="bar-chart"
745
+ anchorX={vertical ? layout.plot.x + tip.anchorX - scrollOffset.current.x : tip.anchorX}
746
+ anchorY={vertical ? tip.anchorY : tip.anchorY - scrollOffset.current.y}
747
+ anchorRadius={tip.anchorRadius}
748
+ width={viewportWidth}
749
+ height={viewportHeight}
750
+ />
751
+ ) : null;
752
+
753
+ const ticks = showTicks ? (
754
+ <>
755
+ {layout.valueTicks.map((tick) => (
756
+ <SvgText
757
+ key={`tick-${String(tick.value)}`}
758
+ x={tick.x}
759
+ y={tick.y}
760
+ fill={chrome.label}
761
+ fontFamily={chrome.fontFamily}
762
+ fontSize={chrome.labelSize}
763
+ textAnchor={orientation === 'vertical' ? 'end' : 'middle'}
764
+ alignmentBaseline="middle"
765
+ >
766
+ {tick.text}
767
+ </SvgText>
768
+ ))}
769
+ </>
770
+ ) : null;
771
+
772
+ const chart = (
773
+ <View
774
+ style={{ width: '100%', maxWidth: viewportWidth, height: viewportHeight }}
775
+ {...dataAttributes({ slot: 'bar-chart-plot' })}
776
+ >
777
+ {orientation === 'vertical' ? (
778
+ <View className="w-full flex-row" style={{ height: viewportHeight }}>
779
+ <Svg
780
+ width={layout.plot.x}
781
+ height={viewportHeight}
782
+ viewBox={`0 0 ${String(layout.plot.x)} ${String(content.height)}`}
783
+ {...HIDDEN_SVG_PROPS}
784
+ >
785
+ {ticks}
786
+ </Svg>
787
+ <ScrollView
788
+ horizontal
789
+ nestedScrollEnabled
790
+ scrollEnabled={overflowsX && !scrubbing}
791
+ showsHorizontalScrollIndicator={overflowsX}
792
+ scrollEventThrottle={16}
793
+ onScroll={dismissOnScroll}
794
+ className="flex-1"
795
+ style={{ height: viewportHeight }}
796
+ >
797
+ <View style={{ width: content.width - layout.plot.x, height: viewportHeight }}>
798
+ <Svg
799
+ width={content.width - layout.plot.x}
800
+ height={viewportHeight}
801
+ viewBox={`0 0 ${String(content.width - layout.plot.x)} ${String(content.height)}`}
802
+ style={SVG_OVERFLOW_VISIBLE}
803
+ pointerEvents="none"
804
+ {...HIDDEN_SVG_PROPS}
805
+ >
806
+ <G transform={`translate(${String(-layout.plot.x)}, 0)`}>{plot}</G>
807
+ </Svg>
808
+ {inspector}
809
+ </View>
810
+ </ScrollView>
811
+ </View>
812
+ ) : (
813
+ <View style={{ width: viewportWidth, height: viewportHeight }}>
814
+ <ScrollView
815
+ nestedScrollEnabled
816
+ scrollEnabled={overflowsY && !scrubbing}
817
+ showsVerticalScrollIndicator={overflowsY}
818
+ scrollEventThrottle={16}
819
+ onScroll={dismissOnScroll}
820
+ style={{ height: viewportHeight - gutters.paddingBottom }}
821
+ >
822
+ <View style={{ width: viewportWidth, height: content.height - gutters.paddingBottom }}>
823
+ <Svg
824
+ width={viewportWidth}
825
+ height={content.height - gutters.paddingBottom}
826
+ viewBox={`0 0 ${String(content.width)} ${String(content.height - gutters.paddingBottom)}`}
827
+ pointerEvents="none"
828
+ {...HIDDEN_SVG_PROPS}
829
+ >
830
+ {plot}
831
+ </Svg>
832
+ {inspector}
833
+ </View>
834
+ </ScrollView>
835
+ <Svg
836
+ width={viewportWidth}
837
+ height={gutters.paddingBottom}
838
+ viewBox={`0 0 ${String(content.width)} ${String(gutters.paddingBottom)}`}
839
+ {...HIDDEN_SVG_PROPS}
840
+ >
841
+ <G transform={`translate(0, ${String(-(content.height - gutters.paddingBottom))})`}>
842
+ {ticks}
843
+ </G>
844
+ </Svg>
845
+ </View>
846
+ )}
847
+ {tooltipLayer}
848
+ </View>
849
+ );
850
+
851
+ return (
852
+ <View
853
+ ref={ref}
854
+ className={cn(barChartRootVariants(), className)}
855
+ style={style}
856
+ accessible={!inspectable && !showLegend}
857
+ accessibilityRole={!inspectable && !showLegend ? 'image' : undefined}
858
+ accessibilityLabel={!inspectable && !showLegend ? summary : undefined}
859
+ {...props}
860
+ onLayout={handleHostLayout}
861
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'bar-chart' }))}
862
+ >
863
+ {chart}
864
+ {showLegend ? (
865
+ <View
866
+ className={barChartLegendVariants()}
867
+ {...dataAttributes({ slot: 'bar-chart-legend' })}
868
+ >
869
+ {plan.series.map((item, seriesIndex) => {
870
+ const isSelected = highlightBar?.seriesId === item.id;
871
+ return (
872
+ <View
873
+ key={item.id}
874
+ className={barChartLegendItemVariants()}
875
+ {...dataAttributes({ slot: 'bar-chart-legend-item' })}
876
+ >
877
+ <View
878
+ className="h-2 w-3 rounded-sm"
879
+ style={{ backgroundColor: fills[seriesIndex] }}
880
+ />
881
+ <Text className={barChartLegendLabelVariants({ selected: isSelected })}>
882
+ {item.label !== undefined && item.label.length > 0
883
+ ? item.label
884
+ : localeText.defaultSeriesLabel}
885
+ </Text>
886
+ </View>
887
+ );
888
+ })}
889
+ </View>
890
+ ) : null}
891
+ </View>
892
+ );
893
+ }
894
+
895
+ /**
896
+ * Category bars from `dataset` rows. MUI X Charts–shaped: `series[].dataKey`, axis arrays,
897
+ * `layout`, `stack` groups, and `selectedItem`.
898
+ */
899
+ export const BarChart = forwardRef(BarChartInner) as <TRow extends object>(
900
+ props: BarChartProps<TRow> & { ref?: ForwardedRef<View> },
901
+ ) => ReactElement | null;
902
+
903
+ (BarChart as { displayName?: string }).displayName = 'BarChart';
904
+
905
+ function BarPlot({
906
+ layout,
907
+ orientation,
908
+ overlay,
909
+ waterfall,
910
+ showGrid,
911
+ barLabel,
912
+ formatBar,
913
+ formatStackTotal,
914
+ fills,
915
+ gradientId,
916
+ chrome,
917
+ accent,
918
+ borderRadius,
919
+ animated,
920
+ highlightBar,
921
+ emphasizedIndex,
922
+ }: {
923
+ layout: BarChartLayout;
924
+ orientation: BarChartOrientation;
925
+ overlay: boolean;
926
+ waterfall: boolean;
927
+ showGrid: boolean;
928
+ barLabel: boolean;
929
+ formatBar: BarFormatter;
930
+ formatStackTotal: (anchor: ReturnType<typeof stackedTotalAnchors>[number]) => string;
931
+ fills: readonly string[];
932
+ gradientId: string;
933
+ chrome: ChartChrome;
934
+ accent: string;
935
+ borderRadius: number;
936
+ animated: boolean;
937
+ highlightBar: BarSelection | null;
938
+ emphasizedIndex: number | undefined;
939
+ }) {
940
+ const labeledBars = layout.bars.filter((bar) => {
941
+ if (bar.stackId !== undefined) {
942
+ return false;
943
+ }
944
+ if (emphasizedIndex !== undefined && bar.index !== emphasizedIndex) {
945
+ return false;
946
+ }
947
+ return !overlay || bar.seriesIndex === 0;
948
+ });
949
+ const stackLabels = stackedTotalAnchors(layout.bars, orientation).filter(
950
+ (anchor) => emphasizedIndex === undefined || anchor.index === emphasizedIndex,
951
+ );
952
+ const paintedBars = overlay
953
+ ? [...layout.bars].sort((a, b) => b.seriesIndex - a.seriesIndex)
954
+ : layout.bars;
955
+
956
+ return (
957
+ <>
958
+ <ChartLinearFills
959
+ idPrefix={gradientId}
960
+ colors={fills}
961
+ x2={orientation === 'vertical' ? '0%' : '100%'}
962
+ y2={orientation === 'vertical' ? '100%' : '0%'}
963
+ />
964
+ {showGrid ? (
965
+ layout.gridLines.map((line) => (
966
+ <Line
967
+ key={`grid-${String(line.x1)}-${String(line.y1)}`}
968
+ x1={line.x1}
969
+ y1={line.y1}
970
+ x2={line.x2}
971
+ y2={line.y2}
972
+ stroke={chrome.grid}
973
+ strokeWidth={1}
974
+ />
975
+ ))
976
+ ) : (
977
+ <Line
978
+ x1={orientation === 'vertical' ? layout.plot.x : layout.baseline}
979
+ y1={orientation === 'vertical' ? layout.baseline : layout.plot.y}
980
+ x2={orientation === 'vertical' ? layout.plot.x + layout.plot.width : layout.baseline}
981
+ y2={orientation === 'vertical' ? layout.baseline : layout.plot.y + layout.plot.height}
982
+ stroke={chrome.grid}
983
+ strokeWidth={1}
984
+ />
985
+ )}
986
+ {layout.referenceLines.map((line, index) => (
987
+ <G key={`ref-${String(line.x1)}-${String(line.y1)}-${String(index)}`}>
988
+ <Line
989
+ x1={line.x1}
990
+ y1={line.y1}
991
+ x2={line.x2}
992
+ y2={line.y2}
993
+ stroke={accent}
994
+ strokeWidth={1}
995
+ strokeDasharray={DASH}
996
+ />
997
+ {line.label !== undefined && line.label.length > 0 ? (
998
+ <SvgText
999
+ x={line.x1 + 4}
1000
+ y={orientation === 'vertical' ? line.y1 - 4 : line.y1 + 12}
1001
+ fill={accent}
1002
+ fontFamily={chrome.fontFamily}
1003
+ fontSize={chrome.captionSize}
1004
+ textAnchor="start"
1005
+ >
1006
+ {line.label}
1007
+ </SvgText>
1008
+ ) : null}
1009
+ </G>
1010
+ ))}
1011
+ {layout.connectors.map((line, index) => (
1012
+ <Line
1013
+ key={`wf-${String(line.x1)}-${String(line.y1)}-${String(index)}`}
1014
+ x1={line.x1}
1015
+ y1={line.y1}
1016
+ x2={line.x2}
1017
+ y2={line.y2}
1018
+ stroke={chrome.grid}
1019
+ strokeWidth={1}
1020
+ strokeDasharray={DASH}
1021
+ />
1022
+ ))}
1023
+ {paintedBars.map((bar) => {
1024
+ if (bar.width <= 0 || bar.height <= 0) {
1025
+ return null;
1026
+ }
1027
+ const dimmed =
1028
+ highlightBar !== null &&
1029
+ (bar.stackId !== undefined
1030
+ ? !sameItem(highlightBar, barSelection(bar))
1031
+ : highlightBar.dataIndex !== bar.index);
1032
+ const muted = emphasizedIndex !== undefined && bar.index !== emphasizedIndex;
1033
+ const comparison = overlay && bar.seriesIndex > 0;
1034
+ return (
1035
+ <AnimatedBar
1036
+ key={`${String(bar.index)}-${bar.seriesId}`}
1037
+ bar={bar}
1038
+ orientation={orientation}
1039
+ baseline={layout.baseline}
1040
+ animated={animated}
1041
+ fill={
1042
+ muted || (waterfall && dimmed)
1043
+ ? chrome.grid
1044
+ : comparison || waterfall
1045
+ ? (fills[bar.seriesIndex] ?? chrome.grid)
1046
+ : chartFillUrl(gradientId, bar.seriesIndex)
1047
+ }
1048
+ opacity={waterfall ? 1 : dimmed ? INACTIVE_OPACITY : 1}
1049
+ radius={borderRadius}
1050
+ />
1051
+ );
1052
+ })}
1053
+ {layout.categoryLabels.map((item, index) => (
1054
+ <SvgText
1055
+ key={`cat-${String(index)}-${item.label}`}
1056
+ x={item.x}
1057
+ y={item.y}
1058
+ fill={chrome.label}
1059
+ fontFamily={chrome.fontFamily}
1060
+ fontSize={chrome.labelSize}
1061
+ textAnchor={item.textAnchor}
1062
+ alignmentBaseline="middle"
1063
+ >
1064
+ {item.label}
1065
+ </SvgText>
1066
+ ))}
1067
+ {barLabel ? (
1068
+ <ValueLabels
1069
+ animated={animated}
1070
+ motionKey={`${orientation}-${overlay ? '1' : '0'}-${layout.bars
1071
+ .map((bar) => [bar.x, bar.y, bar.width, bar.height].join(','))
1072
+ .join(';')}`}
1073
+ >
1074
+ {(labelOpacity) => (
1075
+ <>
1076
+ {stackLabels.map((label) => (
1077
+ <SvgText
1078
+ key={`stack-val-${label.key}`}
1079
+ x={label.x}
1080
+ y={label.y}
1081
+ fill={chrome.label}
1082
+ fillOpacity={labelOpacity}
1083
+ fontFamily={chrome.fontFamily}
1084
+ fontSize={chrome.captionSize}
1085
+ textAnchor={label.textAnchor}
1086
+ alignmentBaseline="middle"
1087
+ >
1088
+ {formatStackTotal(label)}
1089
+ </SvgText>
1090
+ ))}
1091
+ {labeledBars.map((bar) => {
1092
+ const anchor = waterfall
1093
+ ? waterfallValueAnchor(bar, orientation)
1094
+ : barValueAnchor(bar, orientation);
1095
+ return (
1096
+ <G key={`val-${String(bar.index)}-${bar.seriesId}`}>
1097
+ <SvgText
1098
+ x={anchor.x}
1099
+ y={anchor.y}
1100
+ fill={chrome.label}
1101
+ fillOpacity={labelOpacity}
1102
+ fontFamily={chrome.fontFamily}
1103
+ fontSize={chrome.captionSize}
1104
+ textAnchor={anchor.textAnchor}
1105
+ alignmentBaseline="middle"
1106
+ >
1107
+ {formatBar(bar, 'label')}
1108
+ </SvgText>
1109
+ </G>
1110
+ );
1111
+ })}
1112
+ </>
1113
+ )}
1114
+ </ValueLabels>
1115
+ ) : null}
1116
+ </>
1117
+ );
1118
+ }
1119
+
1120
+ function barTooltipContent(
1121
+ group: readonly BarRect[],
1122
+ bar: BarRect,
1123
+ trigger: 'item' | 'axis',
1124
+ fills: readonly string[],
1125
+ fallbackFill: string,
1126
+ formatBar: BarFormatter,
1127
+ seriesLabelOf: (bar: BarRect) => string,
1128
+ ): ChartTooltipContent {
1129
+ const shown = trigger === 'axis' ? group : [bar];
1130
+ return {
1131
+ title: bar.label,
1132
+ items: shown.map((item) => ({
1133
+ seriesId: item.seriesId,
1134
+ label: seriesLabelOf(item),
1135
+ color: fills[item.seriesIndex] ?? fallbackFill,
1136
+ formattedValue: formatBar(item, 'tooltip'),
1137
+ })),
1138
+ };
1139
+ }
1140
+
1141
+ /**
1142
+ * One hit target per drawn bar. Grouped bars get the full value-axis span of their slot so short
1143
+ * bars stay easy to hover. Stacked segments keep their own rect, expanded to a 44px minimum
1144
+ * (WCAG 2.5.8 / HIG) so a 9px-tall bar is still a usable touch target.
1145
+ */
1146
+ /** Targets in scroll-content coordinates, shifted left by `offsetX`. */
1147
+ function barInspectorTargets({
1148
+ bars,
1149
+ layout,
1150
+ orientation,
1151
+ overlay,
1152
+ waterfall,
1153
+ trigger,
1154
+ offsetX,
1155
+ fills,
1156
+ fallbackFill,
1157
+ formatBar,
1158
+ seriesLabelOf,
1159
+ localeText,
1160
+ selectedIndex,
1161
+ }: {
1162
+ bars: readonly BarRect[];
1163
+ layout: BarChartLayout;
1164
+ orientation: BarChartOrientation;
1165
+ overlay: boolean;
1166
+ waterfall: boolean;
1167
+ trigger: 'item' | 'axis' | 'none';
1168
+ offsetX: number;
1169
+ fills: readonly string[];
1170
+ fallbackFill: string;
1171
+ formatBar: BarFormatter;
1172
+ seriesLabelOf: (bar: BarRect) => string;
1173
+ localeText: ChartLocaleText;
1174
+ selectedIndex: number | null;
1175
+ }): ChartInspectorTarget[] {
1176
+ const { plot } = layout;
1177
+ const vertical = orientation === 'vertical';
1178
+ const groups = new Map<number, BarRect[]>();
1179
+ bars.forEach((bar) => {
1180
+ const group = groups.get(bar.index);
1181
+ if (group === undefined) {
1182
+ groups.set(bar.index, [bar]);
1183
+ } else {
1184
+ group.push(bar);
1185
+ }
1186
+ });
1187
+ const outlines = new Map(
1188
+ [...groups].map(([categoryIndex, group]) => {
1189
+ const start = Math.min(...group.map((item) => (vertical ? item.x : item.y)));
1190
+ const end = Math.max(
1191
+ ...group.map((item) => (vertical ? item.x + item.width : item.y + item.height)),
1192
+ );
1193
+ const outline = vertical
1194
+ ? { left: start - offsetX - 4, top: plot.y, width: end - start + 8, height: plot.height }
1195
+ : { left: plot.x - offsetX, top: start - 4, width: plot.width, height: end - start + 8 };
1196
+ return [categoryIndex, outline] as const;
1197
+ }),
1198
+ );
1199
+ const trendText = (bar: BarRect): string | null => {
1200
+ if (!overlay || bar.seriesIndex !== 0) {
1201
+ return null;
1202
+ }
1203
+ const group = groups.get(bar.index) ?? [];
1204
+ const delta = overlayDelta(group, bar.index);
1205
+ if (delta === null) {
1206
+ return null;
1207
+ }
1208
+ const comparison = group.find((item) => item.seriesIndex === 1);
1209
+ return localeText.trend({
1210
+ direction: delta > 0 ? 'up' : delta < 0 ? 'down' : 'none',
1211
+ formattedDelta: formatBar({ ...bar, value: Math.abs(delta) }, 'a11y'),
1212
+ comparisonLabel: comparison?.seriesLabel ?? '',
1213
+ });
1214
+ };
1215
+ return bars.map((bar, index): ChartInspectorTarget => {
1216
+ const x = bar.x - offsetX;
1217
+ const raw =
1218
+ bar.stackId !== undefined || waterfall || !vertical
1219
+ ? { left: x, top: bar.y, width: bar.width, height: bar.height }
1220
+ : { left: x, top: plot.y, width: bar.width, height: plot.height };
1221
+ const box = expandHitBox(raw.left, raw.top, raw.width, raw.height);
1222
+ const group = groups.get(bar.index) ?? [bar];
1223
+ const outline = outlines.get(bar.index);
1224
+ return {
1225
+ key: `hit-${String(bar.index)}-${bar.seriesId}`,
1226
+ ...box,
1227
+ outline,
1228
+ anchorX: vertical ? x + bar.width / 2 : x + bar.width,
1229
+ anchorY: bar.y,
1230
+ anchorRadius: vertical ? bar.width / 2 : 0,
1231
+ tooltip: barTooltipContent(
1232
+ group,
1233
+ bar,
1234
+ trigger === 'item' ? 'item' : 'axis',
1235
+ fills,
1236
+ fallbackFill,
1237
+ formatBar,
1238
+ seriesLabelOf,
1239
+ ),
1240
+ accessibilityLabel: barAccessibilityName(
1241
+ bar.label,
1242
+ seriesLabelOf(bar),
1243
+ formatBar(bar, 'a11y'),
1244
+ trendText(bar),
1245
+ ),
1246
+ selected: selectedIndex === index,
1247
+ };
1248
+ });
1249
+ }
1250
+
1251
+ function BarInspector({
1252
+ bars,
1253
+ targets,
1254
+ orientation,
1255
+ summary,
1256
+ selectable,
1257
+ activeIndex,
1258
+ selectedIndex,
1259
+ onActivate,
1260
+ onDeactivate,
1261
+ onDismiss,
1262
+ onSelectIndex,
1263
+ holdToScrub,
1264
+ onScrubLockChange,
1265
+ }: {
1266
+ onScrubLockChange: (locked: boolean) => void;
1267
+ bars: readonly BarRect[];
1268
+ targets: readonly ChartInspectorTarget[];
1269
+ orientation: BarChartOrientation;
1270
+ summary: string;
1271
+ selectable: boolean;
1272
+ activeIndex: number | null;
1273
+ selectedIndex: number | null;
1274
+ holdToScrub: boolean;
1275
+ onActivate: (index: number) => void;
1276
+ onDeactivate: (index: number) => void;
1277
+ onDismiss: () => void;
1278
+ onSelectIndex: (index: number) => void;
1279
+ }) {
1280
+ return (
1281
+ <ChartInspector
1282
+ targets={targets}
1283
+ summary={summary}
1284
+ selectable={selectable}
1285
+ activeIndex={activeIndex}
1286
+ selectedIndex={selectedIndex}
1287
+ slot="bar-chart"
1288
+ holdToScrub={holdToScrub}
1289
+ onScrubLockChange={onScrubLockChange}
1290
+ hitTest={(x, y) => {
1291
+ let best: number | null = null;
1292
+ let bestDist = Number.POSITIVE_INFINITY;
1293
+ targets.forEach((target, index) => {
1294
+ if (
1295
+ x < target.left ||
1296
+ x > target.left + target.width ||
1297
+ y < target.top ||
1298
+ y > target.top + target.height
1299
+ ) {
1300
+ return;
1301
+ }
1302
+ const dist = Math.hypot(
1303
+ x - (target.left + target.width / 2),
1304
+ y - (target.top + target.height / 2),
1305
+ );
1306
+ if (dist < bestDist) {
1307
+ bestDist = dist;
1308
+ best = index;
1309
+ }
1310
+ });
1311
+ return best;
1312
+ }}
1313
+ onActivate={onActivate}
1314
+ onDeactivate={onDeactivate}
1315
+ onDismiss={onDismiss}
1316
+ onSelectIndex={onSelectIndex}
1317
+ neighborIndex={(index, key) => barKeyboardNeighbor(bars, index, key, orientation)}
1318
+ />
1319
+ );
1320
+ }
1321
+
1322
+ function collapsedBar(bar: BarRect, orientation: BarChartOrientation, baseline: number): number[] {
1323
+ if (orientation === 'vertical') {
1324
+ const onBaseline = Math.abs((bar.plotValue >= 0 ? bar.y + bar.height : bar.y) - baseline) < 1;
1325
+ return onBaseline ? [bar.x, baseline, bar.width, 0] : [bar.x, bar.y + bar.height, bar.width, 0];
1326
+ }
1327
+ const onBaseline = Math.abs((bar.plotValue >= 0 ? bar.x : bar.x + bar.width) - baseline) < 1;
1328
+ return onBaseline ? [baseline, bar.y, 0, bar.height] : [bar.x, bar.y, 0, bar.height];
1329
+ }
1330
+
1331
+ function AnimatedBar({
1332
+ bar,
1333
+ orientation,
1334
+ baseline,
1335
+ animated,
1336
+ fill,
1337
+ opacity,
1338
+ radius,
1339
+ }: {
1340
+ bar: BarRect;
1341
+ orientation: BarChartOrientation;
1342
+ baseline: number;
1343
+ animated: boolean;
1344
+ fill: string;
1345
+ opacity: number;
1346
+ radius: number;
1347
+ }) {
1348
+ const { rounded, roundAll, value } = bar;
1349
+ const morph = useChartMorph(
1350
+ [bar.x, bar.y, bar.width, bar.height],
1351
+ collapsedBar(bar, orientation, baseline),
1352
+ animated,
1353
+ );
1354
+ const { from, to, progress } = morph;
1355
+ const animatedProps = useAnimatedProps(() => {
1356
+ const [x = 0, y = 0, width = 0, height = 0] = lerpVector(from.get(), to.get(), progress.get());
1357
+ return {
1358
+ d: barPath({ x, y, width, height, rounded, roundAll, value }, orientation, radius),
1359
+ };
1360
+ }, [from, to, progress, rounded, roundAll, value, orientation, radius]);
1361
+
1362
+ return <AnimatedPath animatedProps={animatedProps} fill={fill} opacity={opacity} />;
1363
+ }
1364
+
1365
+ function ValueLabels({
1366
+ animated,
1367
+ motionKey,
1368
+ children,
1369
+ }: {
1370
+ animated: boolean;
1371
+ motionKey: string;
1372
+ children: (opacity: number) => ReactNode;
1373
+ }) {
1374
+ const [shownKey, setShownKey] = useState(animated ? null : motionKey);
1375
+
1376
+ useEffect(() => {
1377
+ if (!animated) {
1378
+ setShownKey(motionKey);
1379
+ return;
1380
+ }
1381
+ const id = setTimeout(() => setShownKey(motionKey), CHART_MOTION_DURATION);
1382
+ return () => {
1383
+ clearTimeout(id);
1384
+ };
1385
+ }, [animated, motionKey]);
1386
+
1387
+ if (!animated) {
1388
+ return <>{children(1)}</>;
1389
+ }
1390
+ if (shownKey !== motionKey) {
1391
+ return null;
1392
+ }
1393
+ return <FadeInLabels key={motionKey}>{children}</FadeInLabels>;
1394
+ }
1395
+
1396
+ /** Group opacity on native SVG flashes a full-opacity frame; fade each text's fill instead. */
1397
+ function FadeInLabels({ children }: { children: (opacity: number) => ReactNode }) {
1398
+ const [opacity, setOpacity] = useState(0);
1399
+
1400
+ useEffect(() => {
1401
+ let frame = 0;
1402
+ const started = Date.now();
1403
+ const tick = () => {
1404
+ const t = Math.min(1, (Date.now() - started) / LABEL_FADE_DURATION);
1405
+ setOpacity(1 - (1 - t) ** 3);
1406
+ if (t < 1) {
1407
+ frame = requestAnimationFrame(tick);
1408
+ }
1409
+ };
1410
+ frame = requestAnimationFrame(tick);
1411
+ return () => {
1412
+ cancelAnimationFrame(frame);
1413
+ };
1414
+ }, []);
1415
+
1416
+ return <>{children(opacity)}</>;
1417
+ }