@cdx-ui/components 0.3.5 → 0.3.6

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 (278) 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 +9 -1
  5. package/docs/components/line-chart/content.md +567 -0
  6. package/docs/components/pie-chart/content.md +493 -0
  7. package/docs/components/progress-segmented/content.md +1 -1
  8. package/docs/components/stepper/content.md +599 -0
  9. package/docs/components/system-alert/content.md +533 -0
  10. package/docs/foundations/styles/tokens/all-tokens/content.md +2 -1
  11. package/docs/llms.txt +9 -0
  12. package/docs/patterns/content.md +1 -0
  13. package/docs/patterns/dashboard/content.md +423 -19
  14. package/docs/patterns/insights/content.md +590 -0
  15. package/docs/resources/release-notes/0.3.5/content.md +7 -0
  16. package/docs/resources/release-notes/content.md +1 -0
  17. package/lib/commonjs/components/BarChart/geometry.js +1130 -0
  18. package/lib/commonjs/components/BarChart/geometry.js.map +1 -0
  19. package/lib/commonjs/components/BarChart/index.js +942 -0
  20. package/lib/commonjs/components/BarChart/index.js.map +1 -0
  21. package/lib/commonjs/components/BarChart/styles.js +26 -0
  22. package/lib/commonjs/components/BarChart/styles.js.map +1 -0
  23. package/lib/commonjs/components/Button/index.js +2 -2
  24. package/lib/commonjs/components/Button/index.js.map +1 -1
  25. package/lib/commonjs/components/LineChart/geometry.js +698 -0
  26. package/lib/commonjs/components/LineChart/geometry.js.map +1 -0
  27. package/lib/commonjs/components/LineChart/index.js +747 -0
  28. package/lib/commonjs/components/LineChart/index.js.map +1 -0
  29. package/lib/commonjs/components/LineChart/styles.js +25 -0
  30. package/lib/commonjs/components/LineChart/styles.js.map +1 -0
  31. package/lib/commonjs/components/PieChart/geometry.js +216 -0
  32. package/lib/commonjs/components/PieChart/geometry.js.map +1 -0
  33. package/lib/commonjs/components/PieChart/index.js +476 -0
  34. package/lib/commonjs/components/PieChart/index.js.map +1 -0
  35. package/lib/commonjs/components/PieChart/styles.js +88 -0
  36. package/lib/commonjs/components/PieChart/styles.js.map +1 -0
  37. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js +9 -31
  38. package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
  39. package/lib/commonjs/components/ProgressCircle/ProgressRing.js +52 -0
  40. package/lib/commonjs/components/ProgressCircle/ProgressRing.js.map +1 -0
  41. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +8 -7
  42. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
  43. package/lib/commonjs/components/Stepper/index.js +369 -0
  44. package/lib/commonjs/components/Stepper/index.js.map +1 -0
  45. package/lib/commonjs/components/Stepper/styles.js +211 -0
  46. package/lib/commonjs/components/Stepper/styles.js.map +1 -0
  47. package/lib/commonjs/components/Stepper/useStepperLabelFit.js +22 -0
  48. package/lib/commonjs/components/Stepper/useStepperLabelFit.js.map +1 -0
  49. package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js +52 -0
  50. package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js.map +1 -0
  51. package/lib/commonjs/components/Stepper/useStepperRingColors.js +31 -0
  52. package/lib/commonjs/components/Stepper/useStepperRingColors.js.map +1 -0
  53. package/lib/commonjs/components/SystemAlert/animation.js +218 -0
  54. package/lib/commonjs/components/SystemAlert/animation.js.map +1 -0
  55. package/lib/commonjs/components/SystemAlert/index.js +525 -0
  56. package/lib/commonjs/components/SystemAlert/index.js.map +1 -0
  57. package/lib/commonjs/components/SystemAlert/styles.js +178 -0
  58. package/lib/commonjs/components/SystemAlert/styles.js.map +1 -0
  59. package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js +53 -0
  60. package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
  61. package/lib/commonjs/components/Tabs/index.js +4 -0
  62. package/lib/commonjs/components/Tabs/index.js.map +1 -1
  63. package/lib/commonjs/components/Tabs/styles.js +1 -1
  64. package/lib/commonjs/components/Tabs/styles.js.map +1 -1
  65. package/lib/commonjs/components/chart/ChartInspector.js +610 -0
  66. package/lib/commonjs/components/chart/ChartInspector.js.map +1 -0
  67. package/lib/commonjs/components/chart/format.js +93 -0
  68. package/lib/commonjs/components/chart/format.js.map +1 -0
  69. package/lib/commonjs/components/chart/index.js +2 -0
  70. package/lib/commonjs/components/chart/index.js.map +1 -0
  71. package/lib/commonjs/components/chart/motion.js +97 -0
  72. package/lib/commonjs/components/chart/motion.js.map +1 -0
  73. package/lib/commonjs/components/chart/palette.js +178 -0
  74. package/lib/commonjs/components/chart/palette.js.map +1 -0
  75. package/lib/commonjs/components/chart/size.js +34 -0
  76. package/lib/commonjs/components/chart/size.js.map +1 -0
  77. package/lib/commonjs/components/chart/ticks.js +59 -0
  78. package/lib/commonjs/components/chart/ticks.js.map +1 -0
  79. package/lib/commonjs/components/chart/types.js +6 -0
  80. package/lib/commonjs/components/chart/types.js.map +1 -0
  81. package/lib/commonjs/components/chart/useChartHostWidth.js +27 -0
  82. package/lib/commonjs/components/chart/useChartHostWidth.js.map +1 -0
  83. package/lib/commonjs/components/index.js +60 -0
  84. package/lib/commonjs/components/index.js.map +1 -1
  85. package/lib/commonjs/figma/SystemAlert.Button.figma.js +28 -0
  86. package/lib/commonjs/figma/SystemAlert.Button.figma.js.map +1 -0
  87. package/lib/commonjs/figma/SystemAlert.Close.figma.js +22 -0
  88. package/lib/commonjs/figma/SystemAlert.Close.figma.js.map +1 -0
  89. package/lib/commonjs/figma/SystemAlert.Description.figma.js +20 -0
  90. package/lib/commonjs/figma/SystemAlert.Description.figma.js.map +1 -0
  91. package/lib/commonjs/figma/SystemAlert.IconButton.figma.js +31 -0
  92. package/lib/commonjs/figma/SystemAlert.IconButton.figma.js.map +1 -0
  93. package/lib/commonjs/figma/SystemAlert.figma.js +48 -0
  94. package/lib/commonjs/figma/SystemAlert.figma.js.map +1 -0
  95. package/lib/module/components/BarChart/geometry.js +1110 -0
  96. package/lib/module/components/BarChart/geometry.js.map +1 -0
  97. package/lib/module/components/BarChart/index.js +936 -0
  98. package/lib/module/components/BarChart/index.js.map +1 -0
  99. package/lib/module/components/BarChart/styles.js +22 -0
  100. package/lib/module/components/BarChart/styles.js.map +1 -0
  101. package/lib/module/components/Button/index.js +2 -2
  102. package/lib/module/components/Button/index.js.map +1 -1
  103. package/lib/module/components/LineChart/geometry.js +685 -0
  104. package/lib/module/components/LineChart/geometry.js.map +1 -0
  105. package/lib/module/components/LineChart/index.js +741 -0
  106. package/lib/module/components/LineChart/index.js.map +1 -0
  107. package/lib/module/components/LineChart/styles.js +21 -0
  108. package/lib/module/components/LineChart/styles.js.map +1 -0
  109. package/lib/module/components/PieChart/geometry.js +209 -0
  110. package/lib/module/components/PieChart/geometry.js.map +1 -0
  111. package/lib/module/components/PieChart/index.js +471 -0
  112. package/lib/module/components/PieChart/index.js.map +1 -0
  113. package/lib/module/components/PieChart/styles.js +84 -0
  114. package/lib/module/components/PieChart/styles.js.map +1 -0
  115. package/lib/module/components/ProgressCircle/ProgressCircleRing.js +10 -32
  116. package/lib/module/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
  117. package/lib/module/components/ProgressCircle/ProgressRing.js +48 -0
  118. package/lib/module/components/ProgressCircle/ProgressRing.js.map +1 -0
  119. package/lib/module/components/ScrollShadow/useScrollShadow.js +8 -7
  120. package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
  121. package/lib/module/components/Stepper/index.js +360 -0
  122. package/lib/module/components/Stepper/index.js.map +1 -0
  123. package/lib/module/components/Stepper/styles.js +206 -0
  124. package/lib/module/components/Stepper/styles.js.map +1 -0
  125. package/lib/module/components/Stepper/useStepperLabelFit.js +18 -0
  126. package/lib/module/components/Stepper/useStepperLabelFit.js.map +1 -0
  127. package/lib/module/components/Stepper/useStepperLabelFit.web.js +48 -0
  128. package/lib/module/components/Stepper/useStepperLabelFit.web.js.map +1 -0
  129. package/lib/module/components/Stepper/useStepperRingColors.js +28 -0
  130. package/lib/module/components/Stepper/useStepperRingColors.js.map +1 -0
  131. package/lib/module/components/SystemAlert/animation.js +213 -0
  132. package/lib/module/components/SystemAlert/animation.js.map +1 -0
  133. package/lib/module/components/SystemAlert/index.js +515 -0
  134. package/lib/module/components/SystemAlert/index.js.map +1 -0
  135. package/lib/module/components/SystemAlert/styles.js +174 -0
  136. package/lib/module/components/SystemAlert/styles.js.map +1 -0
  137. package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js +49 -0
  138. package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
  139. package/lib/module/components/Tabs/index.js +4 -0
  140. package/lib/module/components/Tabs/index.js.map +1 -1
  141. package/lib/module/components/Tabs/styles.js +1 -1
  142. package/lib/module/components/Tabs/styles.js.map +1 -1
  143. package/lib/module/components/chart/ChartInspector.js +600 -0
  144. package/lib/module/components/chart/ChartInspector.js.map +1 -0
  145. package/lib/module/components/chart/format.js +84 -0
  146. package/lib/module/components/chart/format.js.map +1 -0
  147. package/lib/module/components/chart/index.js +2 -0
  148. package/lib/module/components/chart/index.js.map +1 -0
  149. package/lib/module/components/chart/motion.js +89 -0
  150. package/lib/module/components/chart/motion.js.map +1 -0
  151. package/lib/module/components/chart/palette.js +168 -0
  152. package/lib/module/components/chart/palette.js.map +1 -0
  153. package/lib/module/components/chart/size.js +29 -0
  154. package/lib/module/components/chart/size.js.map +1 -0
  155. package/lib/module/components/chart/ticks.js +55 -0
  156. package/lib/module/components/chart/ticks.js.map +1 -0
  157. package/lib/module/components/chart/types.js +4 -0
  158. package/lib/module/components/chart/types.js.map +1 -0
  159. package/lib/module/components/chart/useChartHostWidth.js +23 -0
  160. package/lib/module/components/chart/useChartHostWidth.js.map +1 -0
  161. package/lib/module/components/index.js +5 -0
  162. package/lib/module/components/index.js.map +1 -1
  163. package/lib/module/figma/SystemAlert.Button.figma.js +22 -0
  164. package/lib/module/figma/SystemAlert.Button.figma.js.map +1 -0
  165. package/lib/module/figma/SystemAlert.Close.figma.js +18 -0
  166. package/lib/module/figma/SystemAlert.Close.figma.js.map +1 -0
  167. package/lib/module/figma/SystemAlert.Description.figma.js +14 -0
  168. package/lib/module/figma/SystemAlert.Description.figma.js.map +1 -0
  169. package/lib/module/figma/SystemAlert.IconButton.figma.js +25 -0
  170. package/lib/module/figma/SystemAlert.IconButton.figma.js.map +1 -0
  171. package/lib/module/figma/SystemAlert.figma.js +42 -0
  172. package/lib/module/figma/SystemAlert.figma.js.map +1 -0
  173. package/lib/typescript/components/BarChart/geometry.d.ts +236 -0
  174. package/lib/typescript/components/BarChart/geometry.d.ts.map +1 -0
  175. package/lib/typescript/components/BarChart/index.d.ts +193 -0
  176. package/lib/typescript/components/BarChart/index.d.ts.map +1 -0
  177. package/lib/typescript/components/BarChart/styles.d.ts +32 -0
  178. package/lib/typescript/components/BarChart/styles.d.ts.map +1 -0
  179. package/lib/typescript/components/LineChart/geometry.d.ts +248 -0
  180. package/lib/typescript/components/LineChart/geometry.d.ts.map +1 -0
  181. package/lib/typescript/components/LineChart/index.d.ts +105 -0
  182. package/lib/typescript/components/LineChart/index.d.ts.map +1 -0
  183. package/lib/typescript/components/LineChart/styles.d.ts +12 -0
  184. package/lib/typescript/components/LineChart/styles.d.ts.map +1 -0
  185. package/lib/typescript/components/PieChart/geometry.d.ts +43 -0
  186. package/lib/typescript/components/PieChart/geometry.d.ts.map +1 -0
  187. package/lib/typescript/components/PieChart/index.d.ts +147 -0
  188. package/lib/typescript/components/PieChart/index.d.ts.map +1 -0
  189. package/lib/typescript/components/PieChart/styles.d.ts +27 -0
  190. package/lib/typescript/components/PieChart/styles.d.ts.map +1 -0
  191. package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts.map +1 -1
  192. package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts +17 -0
  193. package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts.map +1 -0
  194. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
  195. package/lib/typescript/components/Stepper/index.d.ts +68 -0
  196. package/lib/typescript/components/Stepper/index.d.ts.map +1 -0
  197. package/lib/typescript/components/Stepper/styles.d.ts +46 -0
  198. package/lib/typescript/components/Stepper/styles.d.ts.map +1 -0
  199. package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts +15 -0
  200. package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts.map +1 -0
  201. package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts +10 -0
  202. package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts.map +1 -0
  203. package/lib/typescript/components/Stepper/useStepperRingColors.d.ts +6 -0
  204. package/lib/typescript/components/Stepper/useStepperRingColors.d.ts.map +1 -0
  205. package/lib/typescript/components/SystemAlert/animation.d.ts +56 -0
  206. package/lib/typescript/components/SystemAlert/animation.d.ts.map +1 -0
  207. package/lib/typescript/components/SystemAlert/index.d.ts +110 -0
  208. package/lib/typescript/components/SystemAlert/index.d.ts.map +1 -0
  209. package/lib/typescript/components/SystemAlert/styles.d.ts +38 -0
  210. package/lib/typescript/components/SystemAlert/styles.d.ts.map +1 -0
  211. package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts +14 -0
  212. package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts.map +1 -0
  213. package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
  214. package/lib/typescript/components/chart/ChartInspector.d.ts +101 -0
  215. package/lib/typescript/components/chart/ChartInspector.d.ts.map +1 -0
  216. package/lib/typescript/components/chart/format.d.ts +16 -0
  217. package/lib/typescript/components/chart/format.d.ts.map +1 -0
  218. package/lib/typescript/components/chart/index.d.ts +3 -0
  219. package/lib/typescript/components/chart/index.d.ts.map +1 -0
  220. package/lib/typescript/components/chart/motion.d.ts +27 -0
  221. package/lib/typescript/components/chart/motion.d.ts.map +1 -0
  222. package/lib/typescript/components/chart/palette.d.ts +44 -0
  223. package/lib/typescript/components/chart/palette.d.ts.map +1 -0
  224. package/lib/typescript/components/chart/size.d.ts +25 -0
  225. package/lib/typescript/components/chart/size.d.ts.map +1 -0
  226. package/lib/typescript/components/chart/ticks.d.ts +3 -0
  227. package/lib/typescript/components/chart/ticks.d.ts.map +1 -0
  228. package/lib/typescript/components/chart/types.d.ts +163 -0
  229. package/lib/typescript/components/chart/types.d.ts.map +1 -0
  230. package/lib/typescript/components/chart/useChartHostWidth.d.ts +9 -0
  231. package/lib/typescript/components/chart/useChartHostWidth.d.ts.map +1 -0
  232. package/lib/typescript/components/index.d.ts +5 -0
  233. package/lib/typescript/components/index.d.ts.map +1 -1
  234. package/lib/typescript/figma/SystemAlert.Button.figma.d.ts +11 -0
  235. package/lib/typescript/figma/SystemAlert.Button.figma.d.ts.map +1 -0
  236. package/lib/typescript/figma/SystemAlert.Close.figma.d.ts +11 -0
  237. package/lib/typescript/figma/SystemAlert.Close.figma.d.ts.map +1 -0
  238. package/lib/typescript/figma/SystemAlert.Description.figma.d.ts +8 -0
  239. package/lib/typescript/figma/SystemAlert.Description.figma.d.ts.map +1 -0
  240. package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts +11 -0
  241. package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts.map +1 -0
  242. package/lib/typescript/figma/SystemAlert.figma.d.ts +8 -0
  243. package/lib/typescript/figma/SystemAlert.figma.d.ts.map +1 -0
  244. package/package.json +5 -5
  245. package/src/components/BarChart/geometry.ts +1469 -0
  246. package/src/components/BarChart/index.tsx +1424 -0
  247. package/src/components/BarChart/styles.ts +31 -0
  248. package/src/components/Button/index.tsx +1 -1
  249. package/src/components/LineChart/geometry.ts +957 -0
  250. package/src/components/LineChart/index.tsx +1109 -0
  251. package/src/components/LineChart/styles.ts +26 -0
  252. package/src/components/PieChart/geometry.ts +295 -0
  253. package/src/components/PieChart/index.tsx +715 -0
  254. package/src/components/PieChart/styles.ts +101 -0
  255. package/src/components/ProgressCircle/ProgressCircleRing.tsx +10 -39
  256. package/src/components/ProgressCircle/ProgressRing.tsx +60 -0
  257. package/src/components/ScrollShadow/useScrollShadow.ts +12 -11
  258. package/src/components/Stepper/index.tsx +452 -0
  259. package/src/components/Stepper/styles.ts +234 -0
  260. package/src/components/Stepper/useStepperLabelFit.ts +21 -0
  261. package/src/components/Stepper/useStepperLabelFit.web.ts +53 -0
  262. package/src/components/Stepper/useStepperRingColors.ts +29 -0
  263. package/src/components/SystemAlert/animation.ts +252 -0
  264. package/src/components/SystemAlert/index.tsx +684 -0
  265. package/src/components/SystemAlert/styles.ts +212 -0
  266. package/src/components/SystemAlert/useSystemAlertStatusBarStyle.ts +57 -0
  267. package/src/components/Tabs/index.tsx +4 -0
  268. package/src/components/Tabs/styles.ts +1 -1
  269. package/src/components/chart/ChartInspector.tsx +726 -0
  270. package/src/components/chart/format.ts +95 -0
  271. package/src/components/chart/index.ts +24 -0
  272. package/src/components/chart/motion.ts +98 -0
  273. package/src/components/chart/palette.tsx +247 -0
  274. package/src/components/chart/size.ts +21 -0
  275. package/src/components/chart/ticks.ts +53 -0
  276. package/src/components/chart/types.ts +183 -0
  277. package/src/components/chart/useChartHostWidth.ts +26 -0
  278. package/src/components/index.ts +5 -0
@@ -0,0 +1,1110 @@
1
+ "use strict";
2
+
3
+ import { niceTicks } from '../chart/ticks';
4
+
5
+ /**
6
+ * Where category names sit around the plot. `top` / `bottom` apply to vertical
7
+ * bars; `left` / `right` apply to horizontal bars. Other combinations fall back
8
+ * to `bottom` or `left`. `none` draws no category names.
9
+ */
10
+
11
+ /**
12
+ * How a stack group piles its series. `diverging` stacks positive values up and negative values
13
+ * down from zero. `none` adds every value to one running total. `expand` scales each category to
14
+ * the same length, so segments show their share.
15
+ */
16
+
17
+ /** How the chart draws its series: side by side or stacked, a comparison overlay, or a waterfall. */
18
+
19
+ /** A series resolved for layout. `id` is the public series id. */
20
+
21
+ /** Share of each category slot left empty around the bars. MUI X `categoryGapRatio`. */
22
+ export const CATEGORY_GAP_RATIO = 0.36;
23
+ /** Gap between bars in one category, as a share of one bar's thickness. MUI X `barGapRatio`. */
24
+ export const BAR_GAP_RATIO = 0.5;
25
+ /** Primary bar share of the overlay slot. Comparison series are thinner and peek out. */
26
+ export const OVERLAY_PRIMARY_RATIO = 0.7;
27
+ export const OVERLAY_COMPARE_RATIO = 0.16;
28
+ export const OVERLAY_SPACING_RATIO = 0.08;
29
+ /** Pixel gap between stacked segments. `0` joins them with only the outer cap rounded. */
30
+ export const DEFAULT_STACK_GAP = 3;
31
+ export const DEFAULT_TICK_COUNT = 4;
32
+ /** Minimum pixels per category when scrolling. Keeps labels and bars readable. */
33
+ export const MIN_CATEGORY_BAND = 48;
34
+ /** `expand` ticks when the value axis sets no `min`, `max`, or `tickNumber`. */
35
+ const EXPAND_TICKS = [0, 25, 50, 75, 100];
36
+ function clamp(value, min, max) {
37
+ return Math.min(max, Math.max(min, value));
38
+ }
39
+ function round2(value) {
40
+ const rounded = Math.round(value * 100) / 100;
41
+ return Object.is(rounded, -0) ? 0 : rounded;
42
+ }
43
+
44
+ /** Reads a finite number from `row[key]`. Anything else is a missing value. */
45
+ export function readBarValue(row, key) {
46
+ const raw = row[key];
47
+ if (typeof raw !== 'number' || !Number.isFinite(raw)) {
48
+ return null;
49
+ }
50
+ return raw;
51
+ }
52
+ function readBarFlag(row, key) {
53
+ return key !== undefined && row[key] === true;
54
+ }
55
+ /**
56
+ * One slot per stack group, in first-appearance order. A series without `stack` gets its own
57
+ * slot. The first `stackOffset` set in a group applies to the whole group.
58
+ */
59
+ export function resolveBarGroups(series) {
60
+ const groups = [];
61
+ const byStack = new Map();
62
+ series.forEach((item, seriesIndex) => {
63
+ if (item.stack === undefined) {
64
+ groups.push({
65
+ stackId: undefined,
66
+ offset: null,
67
+ members: [{
68
+ item,
69
+ seriesIndex
70
+ }]
71
+ });
72
+ return;
73
+ }
74
+ let group = byStack.get(item.stack);
75
+ if (group === undefined) {
76
+ group = {
77
+ stackId: item.stack,
78
+ offset: null,
79
+ members: []
80
+ };
81
+ byStack.set(item.stack, group);
82
+ groups.push(group);
83
+ }
84
+ group.members.push({
85
+ item,
86
+ seriesIndex
87
+ });
88
+ if (group.offset === null && item.stackOffset !== undefined) {
89
+ group.offset = item.stackOffset;
90
+ }
91
+ });
92
+ return groups.map(group => group.stackId === undefined ? group : {
93
+ ...group,
94
+ offset: group.offset ?? 'diverging'
95
+ });
96
+ }
97
+ function groupDomainValues(dataset, group) {
98
+ if (group.offset === 'expand') {
99
+ return [0, 100];
100
+ }
101
+ const values = [];
102
+ for (const row of dataset) {
103
+ if (group.offset === null) {
104
+ for (const {
105
+ item
106
+ } of group.members) {
107
+ const value = readBarValue(row, item.dataKey);
108
+ if (value !== null) {
109
+ values.push(value);
110
+ }
111
+ }
112
+ continue;
113
+ }
114
+ let positive = 0;
115
+ let negative = 0;
116
+ let running = 0;
117
+ for (const {
118
+ item
119
+ } of group.members) {
120
+ const value = readBarValue(row, item.dataKey);
121
+ if (value === null) {
122
+ continue;
123
+ }
124
+ if (group.offset === 'none') {
125
+ running += value;
126
+ values.push(running);
127
+ } else if (value >= 0) {
128
+ positive += value;
129
+ } else {
130
+ negative += value;
131
+ }
132
+ }
133
+ if (group.offset === 'diverging') {
134
+ values.push(positive, negative);
135
+ }
136
+ }
137
+ return values;
138
+ }
139
+ function collectWaterfallTotals(dataset, dataKey, totalKey) {
140
+ let cursor = 0;
141
+ const totals = [0];
142
+ dataset.forEach((row, index) => {
143
+ const raw = readBarValue(row, dataKey);
144
+ if (readBarFlag(row, totalKey)) {
145
+ if (raw !== null && index === 0) {
146
+ cursor = raw;
147
+ }
148
+ totals.push(cursor);
149
+ return;
150
+ }
151
+ if (raw === null) {
152
+ return;
153
+ }
154
+ cursor += raw;
155
+ totals.push(cursor);
156
+ });
157
+ return totals;
158
+ }
159
+ function stackSegments(row, members, offset) {
160
+ const raw = [];
161
+ let positiveTotal = 0;
162
+ for (const {
163
+ item,
164
+ seriesIndex
165
+ } of members) {
166
+ const value = readBarValue(row, item.dataKey);
167
+ if (value === null) {
168
+ continue;
169
+ }
170
+ if (offset === 'expand' && value <= 0) {
171
+ continue;
172
+ }
173
+ if (value > 0) {
174
+ positiveTotal += value;
175
+ }
176
+ raw.push({
177
+ item,
178
+ seriesIndex,
179
+ value,
180
+ display: value
181
+ });
182
+ }
183
+ if (offset !== 'expand' || positiveTotal <= 0) {
184
+ return raw;
185
+ }
186
+ return raw.map(segment => ({
187
+ ...segment,
188
+ display: segment.value / positiveTotal * 100
189
+ }));
190
+ }
191
+ function computeDomain(values) {
192
+ if (values.length === 0) {
193
+ return {
194
+ min: 0,
195
+ max: 1
196
+ };
197
+ }
198
+ const min = Math.min(0, ...values);
199
+ let max = Math.max(0, ...values);
200
+ if (min === max) {
201
+ max = 1;
202
+ }
203
+ return {
204
+ min,
205
+ max
206
+ };
207
+ }
208
+ function finite(value) {
209
+ return value !== undefined && Number.isFinite(value) ? value : undefined;
210
+ }
211
+
212
+ /**
213
+ * Value-axis domain and ticks. `min` / `max` pin the domain exactly; otherwise it rounds out to
214
+ * readable ticks. On an `expand` axis, `min` / `max` are 0–1 shares.
215
+ */
216
+ export function resolveBarValueScale(values, axis, expand) {
217
+ const scale = expand ? 100 : 1;
218
+ const axisMin = finite(axis?.min);
219
+ const axisMax = finite(axis?.max);
220
+ const tickNumber = finite(axis?.tickNumber);
221
+ const raw = expand ? {
222
+ min: 0,
223
+ max: 100
224
+ } : computeDomain(values);
225
+ const lo = axisMin !== undefined ? axisMin * scale : raw.min;
226
+ let hi = axisMax !== undefined ? axisMax * scale : raw.max;
227
+ if (hi <= lo) {
228
+ hi = lo + 1;
229
+ }
230
+ const candidates = expand && axisMin === undefined && axisMax === undefined && tickNumber === undefined ? EXPAND_TICKS : niceTicks(lo, hi, Math.round(tickNumber ?? (expand ? EXPAND_TICKS.length : DEFAULT_TICK_COUNT)));
231
+ const min = axisMin !== undefined ? lo : Math.min(lo, candidates[0] ?? lo);
232
+ const max = axisMax !== undefined ? hi : Math.max(hi, candidates.at(-1) ?? hi);
233
+ const epsilon = (max - min) * 1e-9;
234
+ const ticks = candidates.filter(tick => tick >= min - epsilon && tick <= max + epsilon);
235
+ return {
236
+ min,
237
+ max,
238
+ ticks
239
+ };
240
+ }
241
+
242
+ /** Default tick formatter when the chart passes no `formatTick`. */
243
+ export function formatBarValue(value) {
244
+ if (Number.isInteger(value)) {
245
+ return String(value);
246
+ }
247
+ return String(round2(value));
248
+ }
249
+ function categoryLabelPadding(categories, orientation) {
250
+ if (orientation !== 'horizontal') {
251
+ return 40;
252
+ }
253
+ const longest = categories.reduce((max, label) => Math.max(max, label.length), 0);
254
+ return clamp(longest * 8 + 16, 64, 168);
255
+ }
256
+ const CATEGORY_EDGE_GUTTER = 28;
257
+ /** Average `text-xs` glyph width; SVG text cannot be measured before layout. */
258
+ const CATEGORY_LABEL_CHAR_WIDTH = 6.5;
259
+ const VALUE_TICK_LEFT = 40;
260
+ const VALUE_TICK_END = 28;
261
+ const TIGHT_GUTTER = 8;
262
+ const VALUES_END_GUTTER = 72;
263
+ const OVERLAY_VALUES_END_GUTTER = 156;
264
+ /** Room for `$1,234` above the tallest bar (`fontSize` 10 + iOS baseline). */
265
+ const VALUES_TOP_GUTTER = 36;
266
+ const PLOT_END_GUTTER = 20;
267
+ const NEGATIVE_VALUES_GUTTER = 16;
268
+ export function resolveCategoryLabelPosition(orientation, position) {
269
+ if (position === 'none') {
270
+ return 'none';
271
+ }
272
+ if (orientation === 'vertical') {
273
+ return position === 'top' ? 'top' : 'bottom';
274
+ }
275
+ return position === 'right' ? 'right' : 'left';
276
+ }
277
+ export function resolveBarChartGutters({
278
+ categories,
279
+ hasNegative,
280
+ orientation,
281
+ barLabel,
282
+ categoryLabelPosition,
283
+ showTicks = true,
284
+ overlay = false
285
+ }) {
286
+ const labels = resolveCategoryLabelPosition(orientation, categoryLabelPosition);
287
+ if (orientation === 'vertical') {
288
+ const labelsTop = labels === 'top';
289
+ return {
290
+ paddingLeft: showTicks ? VALUE_TICK_LEFT : TIGHT_GUTTER,
291
+ paddingRight: showTicks ? VALUE_TICK_END : PLOT_END_GUTTER,
292
+ paddingTop: labelsTop ? CATEGORY_EDGE_GUTTER : barLabel ? VALUES_TOP_GUTTER : TIGHT_GUTTER,
293
+ paddingBottom: (labelsTop || labels === 'none' ? TIGHT_GUTTER : CATEGORY_EDGE_GUTTER) + (hasNegative && barLabel ? NEGATIVE_VALUES_GUTTER : 0)
294
+ };
295
+ }
296
+ const labelsRight = labels === 'right';
297
+ const categoryPad = labels === 'none' ? TIGHT_GUTTER : categoryLabelPadding(categories, orientation);
298
+ const valuesEnd = barLabel && overlay ? OVERLAY_VALUES_END_GUTTER : barLabel ? VALUES_END_GUTTER : TIGHT_GUTTER;
299
+ return {
300
+ paddingLeft: labelsRight ? TIGHT_GUTTER : categoryPad,
301
+ paddingRight: labelsRight ? categoryPad : Math.max(valuesEnd, showTicks ? VALUE_TICK_END : 0),
302
+ paddingTop: TIGHT_GUTTER,
303
+ paddingBottom: showTicks ? CATEGORY_EDGE_GUTTER : TIGHT_GUTTER
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Gutters from rows with a string `label` field. `LineChart` lays out with the same gutters.
309
+ */
310
+ export function barChartGutters(data, orientation, barLabel, categoryLabelPosition, showTicks = true, overlay = false) {
311
+ return resolveBarChartGutters({
312
+ categories: data.map(row => {
313
+ const label = row.label;
314
+ return typeof label === 'string' ? label : '';
315
+ }),
316
+ hasNegative: data.some(row => Object.values(row).some(value => typeof value === 'number' && value < 0)),
317
+ orientation,
318
+ barLabel,
319
+ categoryLabelPosition,
320
+ showTicks,
321
+ overlay
322
+ });
323
+ }
324
+
325
+ /**
326
+ * Viewport is the visible box (screen or parent). Content grows when there are
327
+ * too many categories to keep a readable band (`scrollable` / `visiblePoints`).
328
+ */
329
+ export function resolveBarChartContentSize({
330
+ viewportWidth,
331
+ viewportHeight,
332
+ categoryCount,
333
+ orientation,
334
+ gutters,
335
+ scrollable,
336
+ visiblePoints
337
+ }) {
338
+ if (!scrollable || categoryCount <= 0) {
339
+ return {
340
+ width: viewportWidth,
341
+ height: viewportHeight
342
+ };
343
+ }
344
+ if (orientation === 'vertical') {
345
+ const plotWidth = Math.max(viewportWidth - gutters.paddingLeft - gutters.paddingRight, 0);
346
+ const band = visiblePoints !== undefined && visiblePoints > 0 ? plotWidth / visiblePoints : MIN_CATEGORY_BAND;
347
+ const contentPlot = Math.max(plotWidth, categoryCount * band);
348
+ return {
349
+ width: contentPlot + gutters.paddingLeft + gutters.paddingRight,
350
+ height: viewportHeight
351
+ };
352
+ }
353
+ const plotHeight = Math.max(viewportHeight - gutters.paddingTop - gutters.paddingBottom, 0);
354
+ const band = visiblePoints !== undefined && visiblePoints > 0 ? plotHeight / visiblePoints : MIN_CATEGORY_BAND;
355
+ const contentPlot = Math.max(plotHeight, categoryCount * band);
356
+ return {
357
+ width: viewportWidth,
358
+ height: contentPlot + gutters.paddingTop + gutters.paddingBottom
359
+ };
360
+ }
361
+ function groupedRect({
362
+ index,
363
+ seriesIndex,
364
+ slot,
365
+ item,
366
+ label,
367
+ value,
368
+ orientation,
369
+ band,
370
+ groupOffset,
371
+ thickness,
372
+ gap,
373
+ plotX,
374
+ plotY,
375
+ valueToX,
376
+ valueToY
377
+ }) {
378
+ const zeroY = valueToY(0);
379
+ const zeroX = valueToX(0);
380
+ const base = {
381
+ index,
382
+ seriesId: item.id,
383
+ seriesIndex,
384
+ label,
385
+ seriesLabel: item.label,
386
+ value,
387
+ plotValue: value,
388
+ rounded: true
389
+ };
390
+ if (orientation === 'vertical') {
391
+ const x = plotX + index * band + groupOffset + slot * (thickness + gap);
392
+ const y = value >= 0 ? valueToY(value) : zeroY;
393
+ const barHeight = Math.abs(valueToY(value) - zeroY);
394
+ return {
395
+ ...base,
396
+ x: round2(x),
397
+ y: round2(y),
398
+ width: round2(Math.max(thickness, 0)),
399
+ height: round2(Math.max(barHeight, 0))
400
+ };
401
+ }
402
+ const y = plotY + index * band + groupOffset + slot * (thickness + gap);
403
+ const x = value >= 0 ? zeroX : valueToX(value);
404
+ const barWidth = Math.abs(valueToX(value) - zeroX);
405
+ return {
406
+ ...base,
407
+ x: round2(x),
408
+ y: round2(y),
409
+ width: round2(Math.max(barWidth, 0)),
410
+ height: round2(Math.max(thickness, 0))
411
+ };
412
+ }
413
+ function overlayRect({
414
+ index,
415
+ seriesIndex,
416
+ item,
417
+ label,
418
+ value,
419
+ orientation,
420
+ band,
421
+ groupOffset,
422
+ inner,
423
+ plotX,
424
+ plotY,
425
+ valueToX,
426
+ valueToY
427
+ }) {
428
+ const isPrimary = seriesIndex === 0;
429
+ const primaryThickness = inner * OVERLAY_PRIMARY_RATIO;
430
+ const thickness = isPrimary ? primaryThickness : inner * OVERLAY_COMPARE_RATIO;
431
+ const slot = plotX + index * band + groupOffset;
432
+ const slotY = plotY + index * band + groupOffset;
433
+ const offset = isPrimary ? 0 : primaryThickness + inner * OVERLAY_SPACING_RATIO;
434
+ const zeroY = valueToY(0);
435
+ const zeroX = valueToX(0);
436
+ const base = {
437
+ index,
438
+ seriesId: item.id,
439
+ seriesIndex,
440
+ label,
441
+ seriesLabel: item.label,
442
+ value,
443
+ plotValue: value,
444
+ rounded: true
445
+ };
446
+ if (orientation === 'vertical') {
447
+ const x = slot + offset;
448
+ const y = value >= 0 ? valueToY(value) : zeroY;
449
+ const barHeight = Math.abs(valueToY(value) - zeroY);
450
+ return {
451
+ ...base,
452
+ x: round2(x),
453
+ y: round2(y),
454
+ width: round2(Math.max(thickness, 0)),
455
+ height: round2(Math.max(barHeight, 0))
456
+ };
457
+ }
458
+ const y = slotY + offset;
459
+ const x = value >= 0 ? zeroX : valueToX(value);
460
+ const barWidth = Math.abs(valueToX(value) - zeroX);
461
+ return {
462
+ ...base,
463
+ x: round2(x),
464
+ y: round2(y),
465
+ width: round2(Math.max(barWidth, 0)),
466
+ height: round2(Math.max(thickness, 0))
467
+ };
468
+ }
469
+ function layoutStackedRow({
470
+ row,
471
+ label,
472
+ index,
473
+ group,
474
+ orientation,
475
+ band,
476
+ groupOffset,
477
+ thickness,
478
+ plotX,
479
+ plotY,
480
+ valueToX,
481
+ valueToY,
482
+ bars,
483
+ stackGap
484
+ }) {
485
+ const offset = group.offset ?? 'diverging';
486
+ const shared = offset === 'none';
487
+ const segments = stackSegments(row, group.members, offset);
488
+ const lastPositiveId = shared ? segments.at(-1)?.item.id : [...segments].reverse().find(segment => segment.display >= 0)?.item.id;
489
+ const lastNegativeId = shared ? segments.at(-1)?.item.id : [...segments].reverse().find(segment => segment.display < 0)?.item.id;
490
+ const separated = stackGap > 0;
491
+ let positiveCursor = 0;
492
+ let negativeCursor = 0;
493
+ let positiveCount = 0;
494
+ let negativeCount = 0;
495
+ segments.forEach(segment => {
496
+ const positive = segment.display >= 0;
497
+ const countBefore = shared ? positiveCount + negativeCount : positive ? positiveCount : negativeCount;
498
+ const gap = countBefore > 0 ? stackGap : 0;
499
+ if (positive) {
500
+ positiveCount += 1;
501
+ } else {
502
+ negativeCount += 1;
503
+ }
504
+ const rounded = separated || positive && segment.item.id === lastPositiveId || !positive && segment.item.id === lastNegativeId;
505
+ const from = shared || positive ? positiveCursor : negativeCursor;
506
+ const to = from + segment.display;
507
+ if (shared) {
508
+ positiveCursor = to;
509
+ negativeCursor = to;
510
+ } else if (positive) {
511
+ positiveCursor = to;
512
+ } else {
513
+ negativeCursor = to;
514
+ }
515
+ const base = {
516
+ index,
517
+ seriesId: segment.item.id,
518
+ seriesIndex: segment.seriesIndex,
519
+ label,
520
+ seriesLabel: segment.item.label,
521
+ value: segment.value,
522
+ plotValue: segment.display,
523
+ rounded,
524
+ roundAll: separated && countBefore > 0,
525
+ stackId: group.stackId,
526
+ ...(offset === 'expand' ? {
527
+ expand: true
528
+ } : {})
529
+ };
530
+ if (orientation === 'vertical') {
531
+ const top = valueToY(Math.max(from, to));
532
+ const bottom = valueToY(Math.min(from, to));
533
+ bars.push({
534
+ ...base,
535
+ x: round2(plotX + index * band + groupOffset),
536
+ y: round2(positive ? top : top + gap),
537
+ width: round2(Math.max(thickness, 0)),
538
+ height: round2(Math.max(bottom - top - gap, 0))
539
+ });
540
+ return;
541
+ }
542
+ const left = valueToX(Math.min(from, to));
543
+ const right = valueToX(Math.max(from, to));
544
+ bars.push({
545
+ ...base,
546
+ x: round2(positive ? left + gap : left),
547
+ y: round2(plotY + index * band + groupOffset),
548
+ width: round2(Math.max(right - left - gap, 0)),
549
+ height: round2(Math.max(thickness, 0))
550
+ });
551
+ });
552
+ }
553
+ function waterfallRole(isTotal, delta) {
554
+ if (isTotal) {
555
+ return 'total';
556
+ }
557
+ return delta >= 0 ? 'increase' : 'decrease';
558
+ }
559
+ function layoutWaterfallBar({
560
+ item,
561
+ label,
562
+ index,
563
+ orientation,
564
+ band,
565
+ groupOffset,
566
+ thickness,
567
+ plotX,
568
+ plotY,
569
+ from,
570
+ to,
571
+ delta,
572
+ role,
573
+ valueToX,
574
+ valueToY
575
+ }) {
576
+ const low = Math.min(from, to);
577
+ const high = Math.max(from, to);
578
+ const base = {
579
+ index,
580
+ seriesId: item.id,
581
+ seriesIndex: index,
582
+ label,
583
+ seriesLabel: item.label,
584
+ value: role === 'total' ? to : delta,
585
+ plotValue: to - from,
586
+ rounded: true,
587
+ // Totals sit on the axis, so only the free end is rounded.
588
+ roundAll: role !== 'total',
589
+ waterfallRole: role
590
+ };
591
+ if (orientation === 'vertical') {
592
+ const top = valueToY(high);
593
+ const bottom = valueToY(low);
594
+ return {
595
+ ...base,
596
+ x: round2(plotX + index * band + groupOffset),
597
+ y: round2(top),
598
+ width: round2(Math.max(thickness, 0)),
599
+ height: round2(Math.max(bottom - top, 0))
600
+ };
601
+ }
602
+ const left = valueToX(low);
603
+ const right = valueToX(high);
604
+ return {
605
+ ...base,
606
+ x: round2(left),
607
+ y: round2(plotY + index * band + groupOffset),
608
+ width: round2(Math.max(right - left, 0)),
609
+ height: round2(Math.max(thickness, 0))
610
+ };
611
+ }
612
+ function waterfallConnector(fromBar, toBar, value, orientation, valueToX, valueToY) {
613
+ if (orientation === 'vertical') {
614
+ const y = round2(valueToY(value));
615
+ return {
616
+ x1: round2(fromBar.x + fromBar.width),
617
+ y1: y,
618
+ x2: round2(toBar.x),
619
+ y2: y
620
+ };
621
+ }
622
+ const x = round2(valueToX(value));
623
+ return {
624
+ x1: x,
625
+ y1: round2(fromBar.y + fromBar.height),
626
+ x2: x,
627
+ y2: round2(toBar.y)
628
+ };
629
+ }
630
+ export function barPath(bar, orientation, radius) {
631
+ 'worklet';
632
+
633
+ const {
634
+ x,
635
+ y,
636
+ width,
637
+ height
638
+ } = bar;
639
+ if (width <= 0 || height <= 0) {
640
+ return '';
641
+ }
642
+ if (!bar.rounded) {
643
+ return `M ${String(x)} ${String(y)} L ${String(x + width)} ${String(y)} L ${String(x + width)} ${String(y + height)} L ${String(x)} ${String(y + height)} Z`;
644
+ }
645
+ if (bar.roundAll) {
646
+ const r = Math.min(radius, width / 2, height / 2);
647
+ const x2 = x + width;
648
+ const y2 = y + height;
649
+ return `M ${String(x + r)} ${String(y)} L ${String(x2 - r)} ${String(y)} Q ${String(x2)} ${String(y)} ${String(x2)} ${String(y + r)} L ${String(x2)} ${String(y2 - r)} Q ${String(x2)} ${String(y2)} ${String(x2 - r)} ${String(y2)} L ${String(x + r)} ${String(y2)} Q ${String(x)} ${String(y2)} ${String(x)} ${String(y2 - r)} L ${String(x)} ${String(y + r)} Q ${String(x)} ${String(y)} ${String(x + r)} ${String(y)} Z`;
650
+ }
651
+ const r = orientation === 'vertical' ? Math.min(radius, width / 2, height) : Math.min(radius, height / 2, width);
652
+ const x2 = x + width;
653
+ const y2 = y + height;
654
+ const capPositive = bar.value >= 0;
655
+ if (orientation === 'vertical') {
656
+ if (capPositive) {
657
+ return `M ${String(x)} ${String(y2)} L ${String(x)} ${String(y + r)} Q ${String(x)} ${String(y)} ${String(x + r)} ${String(y)} L ${String(x2 - r)} ${String(y)} Q ${String(x2)} ${String(y)} ${String(x2)} ${String(y + r)} L ${String(x2)} ${String(y2)} Z`;
658
+ }
659
+ return `M ${String(x)} ${String(y)} L ${String(x2)} ${String(y)} L ${String(x2)} ${String(y2 - r)} Q ${String(x2)} ${String(y2)} ${String(x2 - r)} ${String(y2)} L ${String(x + r)} ${String(y2)} Q ${String(x)} ${String(y2)} ${String(x)} ${String(y2 - r)} Z`;
660
+ }
661
+ if (capPositive) {
662
+ return `M ${String(x)} ${String(y)} L ${String(x2 - r)} ${String(y)} Q ${String(x2)} ${String(y)} ${String(x2)} ${String(y + r)} L ${String(x2)} ${String(y2 - r)} Q ${String(x2)} ${String(y2)} ${String(x2 - r)} ${String(y2)} L ${String(x)} ${String(y2)} Z`;
663
+ }
664
+ return `M ${String(x2)} ${String(y)} L ${String(x2)} ${String(y2)} L ${String(x + r)} ${String(y2)} Q ${String(x)} ${String(y2)} ${String(x)} ${String(y2 - r)} L ${String(x)} ${String(y + r)} Q ${String(x)} ${String(y)} ${String(x + r)} ${String(y)} Z`;
665
+ }
666
+ const EMPTY_LAYOUT = {
667
+ bars: [],
668
+ categoryLabels: [],
669
+ valueTicks: [],
670
+ gridLines: [],
671
+ referenceLines: [],
672
+ connectors: [],
673
+ baseline: 0,
674
+ plot: {
675
+ x: 0,
676
+ y: 0,
677
+ width: 0,
678
+ height: 0
679
+ }
680
+ };
681
+
682
+ /** True when every stack group uses `expand`, so the value axis is a 0–1 share axis. */
683
+ export function isExpandAxis(kind, groups) {
684
+ return kind === 'bars' && groups.length > 0 && groups.every(group => group.offset === 'expand');
685
+ }
686
+ export function layoutBarChart({
687
+ dataset,
688
+ categories,
689
+ series,
690
+ kind = 'bars',
691
+ waterfallTotalKey,
692
+ width,
693
+ height,
694
+ orientation,
695
+ barLabel,
696
+ categoryLabelPosition,
697
+ showTicks = true,
698
+ referenceLines = [],
699
+ valueAxis,
700
+ formatTick = formatBarValue,
701
+ stackGap = DEFAULT_STACK_GAP,
702
+ categoryGapRatio = CATEGORY_GAP_RATIO,
703
+ barGapRatio = BAR_GAP_RATIO,
704
+ showCategoryLabel
705
+ }) {
706
+ if (width <= 0 || height <= 0 || dataset.length === 0 || series.length === 0) {
707
+ return EMPTY_LAYOUT;
708
+ }
709
+ const categoryOf = index => categories?.[index] ?? String(index);
710
+ const resolvedCategories = dataset.map((_, index) => categoryOf(index));
711
+ const overlay = kind === 'overlay';
712
+ const waterfall = kind === 'waterfall';
713
+ const groups = resolveBarGroups(series);
714
+ const expand = isExpandAxis(kind, groups);
715
+ const labels = resolveCategoryLabelPosition(orientation, categoryLabelPosition);
716
+ const {
717
+ paddingLeft,
718
+ paddingRight,
719
+ paddingTop,
720
+ paddingBottom
721
+ } = resolveBarChartGutters({
722
+ categories: resolvedCategories,
723
+ hasNegative: dataset.some(row => series.some(item => (readBarValue(row, item.dataKey) ?? 0) < 0)),
724
+ orientation,
725
+ barLabel,
726
+ categoryLabelPosition: labels,
727
+ showTicks,
728
+ overlay
729
+ });
730
+ const plot = {
731
+ x: paddingLeft,
732
+ y: paddingTop,
733
+ width: Math.max(width - paddingLeft - paddingRight, 0),
734
+ height: Math.max(height - paddingTop - paddingBottom, 0)
735
+ };
736
+ const waterfallSeries = series.at(0);
737
+ const dataValues = waterfall ? collectWaterfallTotals(dataset, waterfallSeries?.dataKey ?? '', waterfallTotalKey) : overlay ? groupDomainValues(dataset, {
738
+ stackId: undefined,
739
+ offset: null,
740
+ members: series.map((item, seriesIndex) => ({
741
+ item,
742
+ seriesIndex
743
+ }))
744
+ }) : groups.flatMap(group => groupDomainValues(dataset, group));
745
+ const values = [...dataValues, ...referenceLines.flatMap(line => Number.isFinite(line.value) ? [line.value] : [])];
746
+ const scale = resolveBarValueScale(values, valueAxis, expand);
747
+ const domainMin = scale.min;
748
+ const domainMax = scale.max;
749
+ const span = domainMax - domainMin || 1;
750
+ const categoryCount = dataset.length;
751
+ const slotCount = overlay || waterfall ? 1 : groups.length;
752
+ const band = orientation === 'vertical' ? plot.width / categoryCount : plot.height / categoryCount;
753
+ const gapShare = clamp(Number.isFinite(categoryGapRatio) ? categoryGapRatio : CATEGORY_GAP_RATIO, 0, 0.95);
754
+ const inner = band * (1 - gapShare);
755
+ const barGap = Math.max(Number.isFinite(barGapRatio) ? barGapRatio : BAR_GAP_RATIO, 0);
756
+ const gapSlots = Math.max(slotCount - 1, 0);
757
+ const thickness = inner / (slotCount + gapSlots * barGap);
758
+ const gap = gapSlots > 0 ? thickness * barGap : 0;
759
+ const groupOffset = (band - inner) / 2;
760
+ const toPlot = value => (clamp(value, domainMin, domainMax) - domainMin) / span;
761
+ const valueToY = value => plot.y + plot.height * (1 - toPlot(value));
762
+ const valueToX = value => plot.x + plot.width * toPlot(value);
763
+ const bars = [];
764
+ const categoryLabels = [];
765
+ const connectors = [];
766
+ let waterfallCursor = 0;
767
+ let waterfallPrev = null;
768
+ dataset.forEach((row, index) => {
769
+ const label = categoryOf(index);
770
+ const shownLabel = showCategoryLabel === undefined || showCategoryLabel(index) ? label : '';
771
+ // With `none`, category names still reach the tooltip and screen readers through the bars.
772
+ if (labels !== 'none' && orientation === 'vertical') {
773
+ const center = plot.x + index * band + band / 2;
774
+ const halfWidth = shownLabel.length * CATEGORY_LABEL_CHAR_WIDTH / 2;
775
+ const anchor = center - halfWidth < plot.x ? {
776
+ x: plot.x,
777
+ textAnchor: 'start'
778
+ } : center + halfWidth > plot.x + plot.width ? {
779
+ x: plot.x + plot.width,
780
+ textAnchor: 'end'
781
+ } : {
782
+ x: center,
783
+ textAnchor: 'middle'
784
+ };
785
+ categoryLabels.push({
786
+ label: shownLabel,
787
+ x: round2(anchor.x),
788
+ y: round2(labels === 'top' ? paddingTop / 2 : height - 8),
789
+ textAnchor: anchor.textAnchor
790
+ });
791
+ } else if (labels !== 'none') {
792
+ categoryLabels.push({
793
+ label: shownLabel,
794
+ x: round2(labels === 'right' ? plot.x + plot.width + 8 : plot.x - 8),
795
+ y: round2(plot.y + index * band + band / 2),
796
+ textAnchor: labels === 'right' ? 'start' : 'end'
797
+ });
798
+ }
799
+ if (waterfall) {
800
+ if (waterfallSeries === undefined) {
801
+ return;
802
+ }
803
+ const isTotal = readBarFlag(row, waterfallTotalKey);
804
+ const raw = readBarValue(row, waterfallSeries.dataKey);
805
+ if (!isTotal && raw === null) {
806
+ return;
807
+ }
808
+ const from = isTotal ? 0 : waterfallCursor;
809
+ if (isTotal) {
810
+ if (raw !== null && index === 0) {
811
+ waterfallCursor = raw;
812
+ }
813
+ } else if (raw !== null) {
814
+ waterfallCursor += raw;
815
+ }
816
+ const to = waterfallCursor;
817
+ const delta = isTotal ? to : raw ?? 0;
818
+ const bar = layoutWaterfallBar({
819
+ item: waterfallSeries,
820
+ label,
821
+ index,
822
+ orientation,
823
+ band,
824
+ groupOffset,
825
+ thickness,
826
+ plotX: plot.x,
827
+ plotY: plot.y,
828
+ from,
829
+ to,
830
+ delta,
831
+ role: waterfallRole(isTotal, delta),
832
+ valueToX,
833
+ valueToY
834
+ });
835
+ if (waterfallPrev !== null) {
836
+ connectors.push(waterfallConnector(waterfallPrev.bar, bar, waterfallPrev.total, orientation, valueToX, valueToY));
837
+ }
838
+ bars.push(bar);
839
+ waterfallPrev = {
840
+ bar,
841
+ total: to
842
+ };
843
+ return;
844
+ }
845
+ if (overlay) {
846
+ series.forEach((item, seriesIndex) => {
847
+ const value = readBarValue(row, item.dataKey);
848
+ if (value === null) {
849
+ return;
850
+ }
851
+ bars.push(overlayRect({
852
+ index,
853
+ seriesIndex,
854
+ item,
855
+ label,
856
+ value,
857
+ orientation,
858
+ band,
859
+ groupOffset,
860
+ inner,
861
+ plotX: plot.x,
862
+ plotY: plot.y,
863
+ valueToX,
864
+ valueToY
865
+ }));
866
+ });
867
+ return;
868
+ }
869
+ groups.forEach((group, slot) => {
870
+ const slotOffset = groupOffset + slot * (thickness + gap);
871
+ if (group.stackId !== undefined) {
872
+ layoutStackedRow({
873
+ row,
874
+ label,
875
+ index,
876
+ group,
877
+ orientation,
878
+ band,
879
+ groupOffset: slotOffset,
880
+ thickness,
881
+ plotX: plot.x,
882
+ plotY: plot.y,
883
+ valueToX,
884
+ valueToY,
885
+ bars,
886
+ stackGap: Math.max(stackGap, 0)
887
+ });
888
+ return;
889
+ }
890
+ const member = group.members.at(0);
891
+ if (member === undefined) {
892
+ return;
893
+ }
894
+ const value = readBarValue(row, member.item.dataKey);
895
+ if (value === null) {
896
+ return;
897
+ }
898
+ bars.push(groupedRect({
899
+ index,
900
+ seriesIndex: member.seriesIndex,
901
+ slot,
902
+ item: member.item,
903
+ label,
904
+ value,
905
+ orientation,
906
+ band,
907
+ groupOffset,
908
+ thickness,
909
+ gap,
910
+ plotX: plot.x,
911
+ plotY: plot.y,
912
+ valueToX,
913
+ valueToY
914
+ }));
915
+ });
916
+ });
917
+ const tickText = value => formatTick(expand ? value / 100 : value);
918
+ const valueTicks = scale.ticks.map(value => {
919
+ if (orientation === 'vertical') {
920
+ return {
921
+ value,
922
+ x: round2(plot.x - 8),
923
+ y: round2(valueToY(value)),
924
+ text: tickText(value)
925
+ };
926
+ }
927
+ return {
928
+ value,
929
+ x: round2(valueToX(value)),
930
+ y: round2(height - 8),
931
+ text: tickText(value)
932
+ };
933
+ });
934
+ const gridLines = scale.ticks.map(value => {
935
+ if (orientation === 'vertical') {
936
+ const y = round2(valueToY(value));
937
+ return {
938
+ x1: round2(plot.x),
939
+ y1: y,
940
+ x2: round2(plot.x + plot.width),
941
+ y2: y
942
+ };
943
+ }
944
+ const x = round2(valueToX(value));
945
+ return {
946
+ x1: x,
947
+ y1: round2(plot.y),
948
+ x2: x,
949
+ y2: round2(plot.y + plot.height)
950
+ };
951
+ });
952
+ const zero = clamp(0, domainMin, domainMax);
953
+ const baseline = round2(orientation === 'vertical' ? valueToY(zero) : valueToX(zero));
954
+ const laidReferenceLines = referenceLines.flatMap(line => {
955
+ if (!Number.isFinite(line.value) || line.value < domainMin || line.value > domainMax) {
956
+ return [];
957
+ }
958
+ if (orientation === 'vertical') {
959
+ const y = round2(valueToY(line.value));
960
+ return [{
961
+ x1: round2(plot.x),
962
+ y1: y,
963
+ x2: round2(plot.x + plot.width),
964
+ y2: y,
965
+ label: line.label
966
+ }];
967
+ }
968
+ const x = round2(valueToX(line.value));
969
+ return [{
970
+ x1: x,
971
+ y1: round2(plot.y),
972
+ x2: x,
973
+ y2: round2(plot.y + plot.height),
974
+ label: line.label
975
+ }];
976
+ });
977
+ return {
978
+ bars,
979
+ categoryLabels,
980
+ valueTicks,
981
+ gridLines,
982
+ referenceLines: laidReferenceLines,
983
+ connectors,
984
+ baseline,
985
+ plot
986
+ };
987
+ }
988
+ export function overlayDelta(bars, index) {
989
+ const group = bars.filter(bar => bar.index === index);
990
+ const primary = group.find(bar => bar.seriesIndex === 0);
991
+ const comparison = group.find(bar => bar.seriesIndex === 1);
992
+ if (primary === undefined || comparison === undefined) {
993
+ return null;
994
+ }
995
+ return round2(primary.value - comparison.value);
996
+ }
997
+
998
+ /** Waterfall steps read as signed changes; totals read as the running total. */
999
+ export function formatWaterfallBarValue(bar, formatValue) {
1000
+ if (bar.waterfallRole === 'increase') {
1001
+ return `+${formatValue(bar.value)}`;
1002
+ }
1003
+ if (bar.waterfallRole === 'decrease') {
1004
+ return `-${formatValue(Math.abs(bar.value))}`;
1005
+ }
1006
+ return formatValue(bar.value);
1007
+ }
1008
+ export function waterfallValueAnchor(bar, orientation) {
1009
+ if (orientation === 'vertical') {
1010
+ return {
1011
+ x: round2(bar.x + bar.width / 2),
1012
+ y: round2(bar.y - 4),
1013
+ textAnchor: 'middle'
1014
+ };
1015
+ }
1016
+ return {
1017
+ x: round2(bar.x + bar.width + 4),
1018
+ y: round2(bar.y + bar.height / 2),
1019
+ textAnchor: 'start'
1020
+ };
1021
+ }
1022
+ export function barValueAnchor(bar, orientation) {
1023
+ if (orientation === 'vertical') {
1024
+ return {
1025
+ x: round2(bar.x + bar.width / 2),
1026
+ y: round2(bar.plotValue >= 0 ? Math.max(bar.y - 12, 12) : bar.y + bar.height + 12),
1027
+ textAnchor: 'middle'
1028
+ };
1029
+ }
1030
+ return {
1031
+ x: round2(bar.plotValue >= 0 ? bar.x + bar.width + 4 : bar.x - 4),
1032
+ y: round2(bar.y + bar.height / 2),
1033
+ textAnchor: bar.plotValue >= 0 ? 'start' : 'end'
1034
+ };
1035
+ }
1036
+ /** One total label per stack in each category. */
1037
+ export function stackedTotalAnchors(bars, orientation) {
1038
+ const byStack = new Map();
1039
+ bars.forEach(bar => {
1040
+ if (bar.stackId === undefined) {
1041
+ return;
1042
+ }
1043
+ const key = `${String(bar.index)}:${bar.stackId}`;
1044
+ const group = byStack.get(key) ?? [];
1045
+ group.push(bar);
1046
+ byStack.set(key, group);
1047
+ });
1048
+ return [...byStack.entries()].flatMap(([key, group]) => {
1049
+ const outer = group.filter(bar => bar.plotValue >= 0).at(-1) ?? group.at(-1);
1050
+ if (outer === undefined) {
1051
+ return [];
1052
+ }
1053
+ const total = group.reduce((sum, bar) => sum + bar.plotValue, 0);
1054
+ const anchor = barValueAnchor({
1055
+ ...outer,
1056
+ plotValue: total,
1057
+ value: total
1058
+ }, orientation);
1059
+ return [{
1060
+ key,
1061
+ index: outer.index,
1062
+ ...anchor,
1063
+ total,
1064
+ expand: outer.expand === true,
1065
+ seriesId: outer.seriesId
1066
+ }];
1067
+ });
1068
+ }
1069
+
1070
+ /**
1071
+ * Next bar for keyboard movement. Along the category axis keeps the same series; along the
1072
+ * value axis steps series inside the category. Home / End jump the category axis.
1073
+ */
1074
+ export function barKeyboardNeighbor(bars, from, key, orientation) {
1075
+ const current = bars[from];
1076
+ if (current === undefined) {
1077
+ return null;
1078
+ }
1079
+ const categories = [...new Set(bars.map(bar => bar.index))];
1080
+ const categoryPos = categories.indexOf(current.index);
1081
+ const seriesInCategory = bars.map((bar, index) => ({
1082
+ bar,
1083
+ index
1084
+ })).filter(item => item.bar.index === current.index);
1085
+ const seriesPos = seriesInCategory.findIndex(item => item.index === from);
1086
+ const alongCategory = key === 'Tab' ? 1 : key === 'Shift+Tab' ? -1 : orientation === 'vertical' ? key === 'ArrowRight' || key === 'End' ? 1 : key === 'ArrowLeft' || key === 'Home' ? -1 : 0 : key === 'ArrowDown' || key === 'End' ? 1 : key === 'ArrowUp' || key === 'Home' ? -1 : 0;
1087
+ const alongSeries = orientation === 'vertical' ? key === 'ArrowDown' ? 1 : key === 'ArrowUp' ? -1 : 0 : key === 'ArrowRight' ? 1 : key === 'ArrowLeft' ? -1 : 0;
1088
+ if (key === 'Home' || key === 'End') {
1089
+ const nextCategory = alongCategory > 0 ? categories.at(-1) : categories[0];
1090
+ if (nextCategory === undefined) {
1091
+ return from;
1092
+ }
1093
+ const match = bars.findIndex(bar => bar.index === nextCategory && bar.seriesIndex === current.seriesIndex);
1094
+ return match >= 0 ? match : bars.findIndex(bar => bar.index === nextCategory);
1095
+ }
1096
+ if (alongCategory !== 0) {
1097
+ const nextCategory = categories[categoryPos + alongCategory];
1098
+ if (nextCategory === undefined) {
1099
+ return from;
1100
+ }
1101
+ const match = bars.findIndex(bar => bar.index === nextCategory && bar.seriesIndex === current.seriesIndex);
1102
+ return match >= 0 ? match : bars.findIndex(bar => bar.index === nextCategory);
1103
+ }
1104
+ if (alongSeries !== 0) {
1105
+ const next = seriesInCategory[seriesPos + alongSeries];
1106
+ return next === undefined ? from : next.index;
1107
+ }
1108
+ return null;
1109
+ }
1110
+ //# sourceMappingURL=geometry.js.map