@misoto22/design 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +50 -5
  4. package/bin/misoto22-design.mjs +59 -13
  5. package/dist/agent/Accordion.md +24 -1
  6. package/dist/agent/Alert.md +23 -1
  7. package/dist/agent/AppShell.md +30 -2
  8. package/dist/agent/ArchitectureFigure.md +25 -2
  9. package/dist/agent/AreaChart.md +27 -2
  10. package/dist/agent/Article.md +26 -3
  11. package/dist/agent/AspectRatio.md +57 -0
  12. package/dist/agent/Avatar.md +25 -5
  13. package/dist/agent/Badge.md +22 -1
  14. package/dist/agent/BarChart.md +29 -2
  15. package/dist/agent/BarList.md +27 -1
  16. package/dist/agent/BigNumber.md +26 -2
  17. package/dist/agent/BoxPlot.md +24 -1
  18. package/dist/agent/Breadcrumb.md +24 -2
  19. package/dist/agent/BulletChart.md +23 -1
  20. package/dist/agent/Button.md +25 -1
  21. package/dist/agent/Calendar.md +37 -3
  22. package/dist/agent/Card.md +24 -2
  23. package/dist/agent/Checkbox.md +23 -2
  24. package/dist/agent/Code.md +50 -0
  25. package/dist/agent/CodeBlock.md +74 -0
  26. package/dist/agent/Collapsible.md +22 -1
  27. package/dist/agent/Combobox.md +29 -3
  28. package/dist/agent/Command.md +25 -2
  29. package/dist/agent/ComposedChart.md +26 -2
  30. package/dist/agent/ContextMenu.md +37 -4
  31. package/dist/agent/DataflowFigure.md +22 -1
  32. package/dist/agent/DatePicker.md +34 -6
  33. package/dist/agent/DescriptionList.md +68 -0
  34. package/dist/agent/Diagram.md +27 -3
  35. package/dist/agent/DiagramCanvas.md +24 -1
  36. package/dist/agent/DiagramExportMenu.md +29 -3
  37. package/dist/agent/DiagramInspector.md +23 -1
  38. package/dist/agent/DiagramLegend.md +22 -1
  39. package/dist/agent/DiagramMinimap.md +24 -4
  40. package/dist/agent/DiagramToolbar.md +23 -1
  41. package/dist/agent/Dialog.md +26 -3
  42. package/dist/agent/DropdownMenu.md +45 -6
  43. package/dist/agent/EmptyState.md +26 -1
  44. package/dist/agent/ErrorState.md +27 -2
  45. package/dist/agent/Facet.md +25 -1
  46. package/dist/agent/Field.md +45 -4
  47. package/dist/agent/FigureBand.md +24 -1
  48. package/dist/agent/FloatingIconButton.md +23 -1
  49. package/dist/agent/FunnelChart.md +23 -1
  50. package/dist/agent/Heading.md +64 -0
  51. package/dist/agent/Heatmap.md +25 -1
  52. package/dist/agent/Histogram.md +26 -2
  53. package/dist/agent/Input.md +22 -1
  54. package/dist/agent/Kbd.md +19 -1
  55. package/dist/agent/LifecycleFigure.md +22 -1
  56. package/dist/agent/LineChart.md +26 -2
  57. package/dist/agent/LinkArrow.md +20 -1
  58. package/dist/agent/Markdown.md +68 -0
  59. package/dist/agent/NativeSelect.md +23 -2
  60. package/dist/agent/NavItem.md +21 -1
  61. package/dist/agent/Pagination.md +28 -2
  62. package/dist/agent/PieChart.md +26 -2
  63. package/dist/agent/Popover.md +23 -1
  64. package/dist/agent/Progress.md +27 -3
  65. package/dist/agent/RadarChart.md +26 -2
  66. package/dist/agent/RadialChart.md +26 -3
  67. package/dist/agent/RadioGroup.md +23 -2
  68. package/dist/agent/SankeyChart.md +25 -2
  69. package/dist/agent/ScatterChart.md +27 -2
  70. package/dist/agent/ScrollArea.md +24 -3
  71. package/dist/agent/SearchableMenu.md +23 -1
  72. package/dist/agent/Select.md +29 -3
  73. package/dist/agent/Separator.md +28 -4
  74. package/dist/agent/SequenceFigure.md +23 -1
  75. package/dist/agent/Sheet.md +25 -2
  76. package/dist/agent/Skeleton.md +24 -2
  77. package/dist/agent/Slider.md +27 -4
  78. package/dist/agent/Sparkline.md +23 -1
  79. package/dist/agent/Spinner.md +24 -2
  80. package/dist/agent/StatusDot.md +20 -1
  81. package/dist/agent/StatusPill.md +28 -2
  82. package/dist/agent/Steps.md +24 -1
  83. package/dist/agent/Switch.md +21 -1
  84. package/dist/agent/Table.md +26 -3
  85. package/dist/agent/Tabs.md +24 -2
  86. package/dist/agent/Tag.md +40 -9
  87. package/dist/agent/Text.md +65 -0
  88. package/dist/agent/Textarea.md +20 -1
  89. package/dist/agent/Timestamp.md +64 -0
  90. package/dist/agent/Toast.md +26 -2
  91. package/dist/agent/ToggleGroup.md +24 -2
  92. package/dist/agent/Toolbar.md +71 -0
  93. package/dist/agent/Tooltip.md +23 -1
  94. package/dist/agent/TreemapChart.md +24 -1
  95. package/dist/agent/WaterfallChart.md +24 -1
  96. package/dist/agent/WorkflowFigure.md +24 -2
  97. package/dist/agent/catalog.json +5602 -745
  98. package/dist/agent/index.md +13 -4
  99. package/dist/article.css +16 -4
  100. package/dist/charts/AreaChart/AreaChart.d.ts +9 -2
  101. package/dist/charts/AreaChart/AreaChart.js +12 -13
  102. package/dist/charts/AreaChart/AreaChart.js.map +1 -1
  103. package/dist/charts/BarChart/BarChart.d.ts +14 -3
  104. package/dist/charts/BarChart/BarChart.js +20 -12
  105. package/dist/charts/BarChart/BarChart.js.map +1 -1
  106. package/dist/charts/BarList/BarList.d.ts +9 -1
  107. package/dist/charts/BarList/BarList.js +9 -1
  108. package/dist/charts/BarList/BarList.js.map +1 -1
  109. package/dist/charts/BigNumber/BigNumber.d.ts +19 -3
  110. package/dist/charts/BigNumber/BigNumber.js +25 -5
  111. package/dist/charts/BigNumber/BigNumber.js.map +1 -1
  112. package/dist/charts/BoxPlot/BoxPlot.js +4 -2
  113. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -1
  114. package/dist/charts/BulletChart/BulletChart.js +28 -7
  115. package/dist/charts/BulletChart/BulletChart.js.map +1 -1
  116. package/dist/charts/ComposedChart/ComposedChart.d.ts +9 -2
  117. package/dist/charts/ComposedChart/ComposedChart.js +10 -10
  118. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -1
  119. package/dist/charts/FunnelChart/FunnelChart.d.ts +7 -2
  120. package/dist/charts/FunnelChart/FunnelChart.js +6 -16
  121. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -1
  122. package/dist/charts/Heatmap/Heatmap.d.ts +17 -3
  123. package/dist/charts/Heatmap/Heatmap.js +20 -11
  124. package/dist/charts/Heatmap/Heatmap.js.map +1 -1
  125. package/dist/charts/Histogram/Histogram.d.ts +24 -1
  126. package/dist/charts/Histogram/Histogram.js +43 -11
  127. package/dist/charts/Histogram/Histogram.js.map +1 -1
  128. package/dist/charts/LineChart/LineChart.d.ts +9 -2
  129. package/dist/charts/LineChart/LineChart.js +11 -12
  130. package/dist/charts/LineChart/LineChart.js.map +1 -1
  131. package/dist/charts/PieChart/PieChart.d.ts +15 -3
  132. package/dist/charts/PieChart/PieChart.js +14 -6
  133. package/dist/charts/PieChart/PieChart.js.map +1 -1
  134. package/dist/charts/RadarChart/RadarChart.d.ts +15 -3
  135. package/dist/charts/RadarChart/RadarChart.js +11 -9
  136. package/dist/charts/RadarChart/RadarChart.js.map +1 -1
  137. package/dist/charts/RadialChart/RadialChart.d.ts +22 -4
  138. package/dist/charts/RadialChart/RadialChart.js +28 -6
  139. package/dist/charts/RadialChart/RadialChart.js.map +1 -1
  140. package/dist/charts/SankeyChart/SankeyChart.d.ts +16 -4
  141. package/dist/charts/SankeyChart/SankeyChart.js +14 -6
  142. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -1
  143. package/dist/charts/ScatterChart/ScatterChart.d.ts +24 -3
  144. package/dist/charts/ScatterChart/ScatterChart.js +21 -15
  145. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -1
  146. package/dist/charts/Sparkline/Sparkline.js +5 -3
  147. package/dist/charts/Sparkline/Sparkline.js.map +1 -1
  148. package/dist/charts/TreemapChart/TreemapChart.d.ts +7 -2
  149. package/dist/charts/TreemapChart/TreemapChart.js +20 -11
  150. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -1
  151. package/dist/charts/WaterfallChart/WaterfallChart.js +4 -2
  152. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -1
  153. package/dist/charts/lib/export.d.ts +1 -0
  154. package/dist/charts/lib/figure.d.ts +21 -1
  155. package/dist/charts/lib/figure.js +5 -2
  156. package/dist/charts/lib/figure.js.map +1 -1
  157. package/dist/charts/lib/scale.d.ts +23 -0
  158. package/dist/charts/lib/scale.js +13 -0
  159. package/dist/charts/lib/scale.js.map +1 -0
  160. package/dist/charts/lib/selection.d.ts +20 -0
  161. package/dist/charts/lib/selection.js +18 -0
  162. package/dist/charts/lib/selection.js.map +1 -0
  163. package/dist/charts/lib/toolbar.d.ts +1 -0
  164. package/dist/components/Accordion/Accordion.js +8 -1
  165. package/dist/components/Accordion/Accordion.js.map +1 -1
  166. package/dist/components/AppShell/AppShell.d.ts +6 -0
  167. package/dist/components/AppShell/AppShell.js +34 -4
  168. package/dist/components/AppShell/AppShell.js.map +1 -1
  169. package/dist/components/Article/Article.d.ts +11 -4
  170. package/dist/components/Article/Article.js +13 -0
  171. package/dist/components/Article/Article.js.map +1 -1
  172. package/dist/components/AspectRatio/AspectRatio.d.ts +46 -0
  173. package/dist/components/AspectRatio/AspectRatio.js +33 -0
  174. package/dist/components/AspectRatio/AspectRatio.js.map +1 -0
  175. package/dist/components/Avatar/Avatar.d.ts +12 -3
  176. package/dist/components/Avatar/Avatar.js +6 -1
  177. package/dist/components/Avatar/Avatar.js.map +1 -1
  178. package/dist/components/Breadcrumb/Breadcrumb.d.ts +6 -0
  179. package/dist/components/Breadcrumb/Breadcrumb.js +15 -0
  180. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
  181. package/dist/components/Button/Button.js +14 -0
  182. package/dist/components/Button/Button.js.map +1 -1
  183. package/dist/components/Calendar/Calendar.d.ts +29 -3
  184. package/dist/components/Calendar/Calendar.js +60 -12
  185. package/dist/components/Calendar/Calendar.js.map +1 -1
  186. package/dist/components/Card/Card.d.ts +6 -0
  187. package/dist/components/Card/Card.js +1 -1
  188. package/dist/components/Card/Card.js.map +1 -1
  189. package/dist/components/Checkbox/Checkbox.d.ts +5 -2
  190. package/dist/components/Checkbox/Checkbox.js +12 -2
  191. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  192. package/dist/components/Code/Code.d.ts +30 -0
  193. package/dist/components/Code/Code.js +21 -0
  194. package/dist/components/Code/Code.js.map +1 -0
  195. package/dist/components/CodeBlock/CodeBlock.d.ts +141 -0
  196. package/dist/components/CodeBlock/CodeBlock.js +123 -0
  197. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  198. package/dist/components/Collapsible/Collapsible.js +10 -2
  199. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  200. package/dist/components/Combobox/Combobox.d.ts +15 -1
  201. package/dist/components/Combobox/Combobox.js +25 -5
  202. package/dist/components/Combobox/Combobox.js.map +1 -1
  203. package/dist/components/Command/Command.d.ts +4 -2
  204. package/dist/components/Command/Command.js +13 -1
  205. package/dist/components/Command/Command.js.map +1 -1
  206. package/dist/components/ContextMenu/ContextMenu.d.ts +30 -5
  207. package/dist/components/ContextMenu/ContextMenu.js +32 -2
  208. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  209. package/dist/components/DatePicker/DatePicker.d.ts +36 -4
  210. package/dist/components/DatePicker/DatePicker.js +55 -12
  211. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  212. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  213. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  214. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  215. package/dist/components/Diagram/Diagram.d.ts +8 -3
  216. package/dist/components/Diagram/Diagram.js +82 -10
  217. package/dist/components/Diagram/Diagram.js.map +1 -1
  218. package/dist/components/Dialog/Dialog.d.ts +18 -3
  219. package/dist/components/Dialog/Dialog.js +20 -2
  220. package/dist/components/Dialog/Dialog.js.map +1 -1
  221. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -11
  222. package/dist/components/DropdownMenu/DropdownMenu.js +32 -2
  223. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  224. package/dist/components/EmptyState/EmptyState.d.ts +25 -1
  225. package/dist/components/EmptyState/EmptyState.js +3 -1
  226. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  227. package/dist/components/ErrorState/ErrorState.d.ts +22 -1
  228. package/dist/components/ErrorState/ErrorState.js +11 -2
  229. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  230. package/dist/components/Field/Field.d.ts +70 -3
  231. package/dist/components/Field/Field.js +53 -12
  232. package/dist/components/Field/Field.js.map +1 -1
  233. package/dist/components/Field/field-control.d.ts +31 -0
  234. package/dist/components/Field/field-control.js +12 -0
  235. package/dist/components/Field/field-control.js.map +1 -0
  236. package/dist/components/FloatingIconButton/FloatingIconButton.js +2 -0
  237. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -1
  238. package/dist/components/Heading/Heading.d.ts +62 -0
  239. package/dist/components/Heading/Heading.js +44 -0
  240. package/dist/components/Heading/Heading.js.map +1 -0
  241. package/dist/components/Markdown/Markdown.d.ts +139 -0
  242. package/dist/components/Markdown/Markdown.js +135 -0
  243. package/dist/components/Markdown/Markdown.js.map +1 -0
  244. package/dist/components/Markdown/inline.d.ts +29 -0
  245. package/dist/components/Markdown/inline.js +74 -0
  246. package/dist/components/Markdown/inline.js.map +1 -0
  247. package/dist/components/Markdown/nodes.d.ts +65 -0
  248. package/dist/components/Markdown/nodes.js +1 -0
  249. package/dist/components/Markdown/nodes.js.map +1 -0
  250. package/dist/components/Markdown/parse.d.ts +24 -0
  251. package/dist/components/Markdown/parse.js +110 -0
  252. package/dist/components/Markdown/parse.js.map +1 -0
  253. package/dist/components/Markdown/slug.d.ts +15 -0
  254. package/dist/components/Markdown/slug.js +7 -0
  255. package/dist/components/Markdown/slug.js.map +1 -0
  256. package/dist/components/NativeSelect/NativeSelect.d.ts +6 -0
  257. package/dist/components/NativeSelect/NativeSelect.js +2 -3
  258. package/dist/components/NativeSelect/NativeSelect.js.map +1 -1
  259. package/dist/components/Pagination/Pagination.d.ts +24 -1
  260. package/dist/components/Pagination/Pagination.js +6 -3
  261. package/dist/components/Pagination/Pagination.js.map +1 -1
  262. package/dist/components/Progress/Progress.d.ts +17 -5
  263. package/dist/components/Progress/Progress.js +18 -6
  264. package/dist/components/Progress/Progress.js.map +1 -1
  265. package/dist/components/RadioGroup/RadioGroup.d.ts +6 -0
  266. package/dist/components/RadioGroup/RadioGroup.js +11 -1
  267. package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
  268. package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
  269. package/dist/components/ScrollArea/ScrollArea.js +2 -2
  270. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  271. package/dist/components/SearchableMenu/SearchableMenu.d.ts +11 -2
  272. package/dist/components/SearchableMenu/SearchableMenu.js +24 -2
  273. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -1
  274. package/dist/components/Select/Select.d.ts +18 -2
  275. package/dist/components/Select/Select.js +20 -3
  276. package/dist/components/Select/Select.js.map +1 -1
  277. package/dist/components/Separator/Separator.d.ts +21 -3
  278. package/dist/components/Separator/Separator.js +9 -1
  279. package/dist/components/Separator/Separator.js.map +1 -1
  280. package/dist/components/Sheet/Sheet.d.ts +3 -0
  281. package/dist/components/Sheet/Sheet.js +11 -2
  282. package/dist/components/Sheet/Sheet.js.map +1 -1
  283. package/dist/components/Skeleton/Skeleton.d.ts +8 -1
  284. package/dist/components/Skeleton/Skeleton.js +1 -1
  285. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  286. package/dist/components/Slider/Slider.d.ts +21 -3
  287. package/dist/components/Slider/Slider.js +54 -27
  288. package/dist/components/Slider/Slider.js.map +1 -1
  289. package/dist/components/Spinner/Spinner.d.ts +8 -0
  290. package/dist/components/Spinner/Spinner.js +3 -2
  291. package/dist/components/Spinner/Spinner.js.map +1 -1
  292. package/dist/components/StatusPill/StatusPill.d.ts +5 -0
  293. package/dist/components/StatusPill/StatusPill.js +6 -0
  294. package/dist/components/StatusPill/StatusPill.js.map +1 -1
  295. package/dist/components/Table/Table.d.ts +6 -0
  296. package/dist/components/Table/Table.js +3 -1
  297. package/dist/components/Table/Table.js.map +1 -1
  298. package/dist/components/Tag/Tag.d.ts +68 -9
  299. package/dist/components/Tag/Tag.js +50 -10
  300. package/dist/components/Tag/Tag.js.map +1 -1
  301. package/dist/components/Text/Text.d.ts +62 -0
  302. package/dist/components/Text/Text.js +30 -0
  303. package/dist/components/Text/Text.js.map +1 -0
  304. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  305. package/dist/components/Timestamp/Timestamp.js +56 -0
  306. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  307. package/dist/components/Toast/Toast.d.ts +9 -1
  308. package/dist/components/Toast/Toast.js +27 -4
  309. package/dist/components/Toast/Toast.js.map +1 -1
  310. package/dist/components/ToggleGroup/ToggleGroup.d.ts +6 -1
  311. package/dist/components/ToggleGroup/ToggleGroup.js +11 -1
  312. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  313. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  314. package/dist/components/Toolbar/Toolbar.js +43 -0
  315. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  316. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +6 -0
  317. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +48 -8
  318. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -1
  319. package/dist/diagrams/DataflowFigure/DataflowFigure.js +21 -2
  320. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -1
  321. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +14 -0
  322. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +14 -7
  323. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -1
  324. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +38 -4
  325. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +14 -16
  326. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -1
  327. package/dist/diagrams/DiagramInspector/DiagramInspector.js +2 -2
  328. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -1
  329. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +23 -7
  330. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +45 -18
  331. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -1
  332. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +44 -12
  333. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -1
  334. package/dist/diagrams/SequenceFigure/SequenceFigure.js +35 -5
  335. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -1
  336. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +3 -1
  337. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +76 -28
  338. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -1
  339. package/dist/diagrams/index.d.ts +1 -0
  340. package/dist/diagrams/index.js +10 -0
  341. package/dist/diagrams/index.js.map +1 -1
  342. package/dist/diagrams/lib/dev.d.ts +61 -0
  343. package/dist/diagrams/lib/dev.js +96 -0
  344. package/dist/diagrams/lib/dev.js.map +1 -0
  345. package/dist/diagrams/lib/frame.d.ts +48 -7
  346. package/dist/diagrams/lib/frame.js +17 -2
  347. package/dist/diagrams/lib/frame.js.map +1 -1
  348. package/dist/diagrams/lib/wires.js.map +1 -1
  349. package/dist/diagrams/spec.d.ts +64 -12
  350. package/dist/index.d.ts +16 -4
  351. package/dist/index.js +9 -0
  352. package/dist/index.js.map +1 -1
  353. package/dist/keyframes.css +57 -3
  354. package/dist/lib/overlay-container.d.ts +14 -4
  355. package/dist/lib/overlay-container.js.map +1 -1
  356. package/dist/lib/warn.d.ts +73 -0
  357. package/dist/lib/warn.js +38 -0
  358. package/dist/lib/warn.js.map +1 -0
  359. package/dist/semantic.css +17 -6
  360. package/dist/styles.css +1 -1
  361. package/dist/tokens.css +20 -2
  362. package/dist/tokens.d.ts +1 -1
  363. package/dist/tokens.js +39 -36
  364. package/dist/tokens.json +39 -36
  365. package/llms.txt +35 -0
  366. package/package.json +5 -2
  367. package/skills/misoto22-design/evals/evals.json +84 -0
  368. package/skills/misoto22-design/rules/a11y.md +55 -3
  369. package/skills/misoto22-design/rules/forms.md +5 -0
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
3
+ import {} from "react";
4
4
  import { Treemap as RechartsTreemap } from "recharts";
5
5
  import { ChartContainer, cssName } from "../lib/chart.js";
6
6
  import { ChartFigure } from "../lib/figure.js";
7
+ import {} from "../lib/empty.js";
7
8
  import { ChartTooltip, ChartTooltipContent } from "../lib/tooltip.js";
8
9
  function TreemapChart({
9
10
  config = {},
@@ -18,10 +19,10 @@ function TreemapChart({
18
19
  showLabels = true,
19
20
  children,
20
21
  chartProps,
21
- hideDataTable = false
22
+ hideDataTable = false,
23
+ empty
22
24
  }) {
23
- const chartId = useId().replace(/:/g, "");
24
- const leaves = flatten(data);
25
+ const leaves = flatten(data, dataKey);
25
26
  return /* @__PURE__ */ jsx(
26
27
  ChartFigure,
27
28
  {
@@ -34,6 +35,8 @@ function TreemapChart({
34
35
  rowKey: "path",
35
36
  columns: [{ key: dataKey, label: "Value" }]
36
37
  },
38
+ isEmpty: data.length === 0,
39
+ empty,
37
40
  children: /* @__PURE__ */ jsx(ChartContainer, { config, children: /* @__PURE__ */ jsx(
38
41
  RechartsTreemap,
39
42
  {
@@ -42,7 +45,7 @@ function TreemapChart({
42
45
  nameKey: "name",
43
46
  aspectRatio,
44
47
  isAnimationActive: false,
45
- content: /* @__PURE__ */ jsx(TreemapTile, { chartId, variant, config, showLabels }),
48
+ content: /* @__PURE__ */ jsx(TreemapTile, { variant, config, showLabels }),
46
49
  ...chartProps,
47
50
  children
48
51
  }
@@ -50,15 +53,22 @@ function TreemapChart({
50
53
  }
51
54
  );
52
55
  }
53
- function flatten(nodes, prefix = "") {
56
+ function flatten(nodes, dataKey, prefix = "") {
54
57
  return nodes.flatMap((node) => {
55
58
  const path = prefix ? `${prefix} \u203A ${node.name}` : node.name;
56
- if (node.children?.length) return flatten(node.children, path);
57
- return [{ ...node, path }];
59
+ if (node.children?.length) return flatten(node.children, dataKey, path);
60
+ const value = Number(node[dataKey]);
61
+ if (Number.isFinite(value) && value > 0) return [{ ...node, path }];
62
+ return [
63
+ {
64
+ ...node,
65
+ path,
66
+ [dataKey]: Number.isFinite(value) ? `${value.toLocaleString()} \u2014 not drawn` : "not drawn"
67
+ }
68
+ ];
58
69
  });
59
70
  }
60
71
  function TreemapTile({
61
- chartId,
62
72
  variant,
63
73
  config,
64
74
  showLabels,
@@ -96,8 +106,7 @@ function TreemapTile({
96
106
  children: name
97
107
  }
98
108
  ),
99
- /* @__PURE__ */ jsx("title", { children: name }),
100
- /* @__PURE__ */ jsx("desc", { children: chartId })
109
+ /* @__PURE__ */ jsx("title", { children: name })
101
110
  ] });
102
111
  }
103
112
  function Tooltip({ variant, roundness }) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/charts/TreemapChart/TreemapChart.tsx"],"sourcesContent":["'use client'\n\nimport { useId, type ComponentProps, type ReactNode } from 'react'\nimport { Treemap as RechartsTreemap } from 'recharts'\nimport { ChartContainer, cssName, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { ChartTooltip, ChartTooltipContent } from '../lib/tooltip'\nimport type { ChartTooltipSlotProps } from '../AreaChart/AreaChart'\n\n/**\n * How a tile is painted.\n *\n * `ramp` walks the series ramp so siblings separate by lightness; `nested`\n * darkens by DEPTH instead, which is the right encoding when the tree has more\n * than one level and the question is \"what is inside what\".\n */\nexport type TreemapVariant = 'ramp' | 'nested'\n\n/** A node in the tree. Children make it a branch. */\nexport interface TreemapNode {\n name: string\n /** Leaf value. Omit on a branch — a branch is the sum of its children. */\n size?: number\n children?: TreemapNode[]\n [key: string]: unknown\n}\n\n/** The raw Recharts Treemap props a consumer may still reach for. */\nexport type TreemapExtras = Omit<ComponentProps<typeof RechartsTreemap>, 'data' | 'dataKey'>\n\nexport interface TreemapChartProps {\n /** Tile names → their label and paint. Optional: the ramp covers unnamed tiles. */\n config?: ChartConfig\n /** The tree. One level is a flat set of tiles; two is a nested treemap. */\n data: TreemapNode[]\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n variant?: TreemapVariant\n /** The leaf field holding each tile's number. */\n dataKey?: string\n /** How square the tiles are pushed to be. Recharts' own squarify parameter. */\n aspectRatio?: number\n /** Prints each tile's name on it, where the tile is big enough to hold it. */\n showLabels?: boolean\n /** The hover panel. */\n children?: ReactNode\n chartProps?: TreemapExtras\n /** Drops the hidden table view. Only correct when the page prints the data itself. */\n hideDataTable?: boolean\n}\n\n/**\n * Part of a whole, when the whole has too many parts for a pie — and the parts\n * nest.\n *\n * A treemap encodes value as AREA, which the eye reads worse than length but\n * far better than angle, and it is the only form here that stays readable at\n * fifty items. Two rules make it honest: no negative values (an area cannot be\n * negative), and the tiles have to sum to something a reader recognises as the\n * whole.\n *\n * Reach for `<BarChart>` when there are under a dozen items and the ranking\n * matters — a bar's length is the more precise encoding, and a treemap's\n * layout deliberately does not order its tiles by value alone.\n *\n * @example\n * <TreemapChart title=\"Bundle size by package\" data={packages} showLabels>\n * <TreemapChart.Tooltip />\n * </TreemapChart>\n */\nexport function TreemapChart({\n config = {},\n data,\n title,\n showTitle,\n description,\n className,\n variant = 'ramp',\n dataKey = 'size',\n aspectRatio = 4 / 3,\n showLabels = true,\n children,\n chartProps,\n hideDataTable = false,\n}: TreemapChartProps) {\n const chartId = useId().replace(/:/g, '')\n\n // Flattened for the table view, because a nested tree read aloud row by row\n // is not something anyone can follow — the leaves are the facts.\n const leaves = flatten(data)\n\n return (\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : {\n rows: leaves,\n rowKey: 'path',\n columns: [{ key: dataKey, label: 'Value' }],\n }\n }\n >\n <ChartContainer config={config}>\n <RechartsTreemap\n data={data}\n dataKey={dataKey}\n nameKey=\"name\"\n aspectRatio={aspectRatio}\n isAnimationActive={false}\n content={\n <TreemapTile chartId={chartId} variant={variant} config={config} showLabels={showLabels} />\n }\n {...chartProps}\n >\n {children}\n </RechartsTreemap>\n </ChartContainer>\n </ChartFigure>\n )\n}\n\n/** Every leaf, with the path that names it. */\nfunction flatten(nodes: TreemapNode[], prefix = ''): Record<string, unknown>[] {\n return nodes.flatMap((node) => {\n const path = prefix ? `${prefix} › ${node.name}` : node.name\n if (node.children?.length) return flatten(node.children, path)\n return [{ ...node, path }]\n })\n}\n\ninterface TileProps {\n chartId: string\n variant: TreemapVariant\n config: ChartConfig\n showLabels: boolean\n // Recharts hands the geometry in as loose props on the cloned element.\n x?: number\n y?: number\n width?: number\n height?: number\n depth?: number\n index?: number\n name?: string\n}\n\n/**\n * One tile.\n *\n * The gap between tiles is a surface-coloured stroke rather than a smaller\n * rect, so the tiles still tile — a treemap whose parts do not touch stops\n * reading as a partition of one whole.\n */\nfunction TreemapTile({\n chartId,\n variant,\n config,\n showLabels,\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n depth = 1,\n index = 0,\n name = '',\n}: TileProps) {\n if (width <= 0 || height <= 0) return null\n\n const slot =\n variant === 'nested'\n ? `var(--series-${Math.min(8, depth * 2)})`\n : name in config\n ? `var(--color-${cssName(name)}-0)`\n : `var(--series-${(index % 8) + 1})`\n\n // Only label a tile with room for one. A clipped word is worse than none.\n const roomForLabel = showLabels && width > 56 && height > 26\n\n return (\n <g>\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n fill={slot}\n stroke=\"var(--chart-surface)\"\n strokeWidth={2}\n />\n {roomForLabel && (\n <text\n x={x + 8}\n y={y + 18}\n className=\"fill-(--chart-surface) text-[11px] font-medium\"\n style={{ pointerEvents: 'none' }}\n >\n {name}\n </text>\n )}\n {/* A hidden id so the tooltip can key off the same name the tile paints. */}\n <title>{name}</title>\n <desc>{chartId}</desc>\n </g>\n )\n}\n\n/** The hover panel. No heading: the tile's own name is the row label. */\nfunction Tooltip({ variant, roundness }: ChartTooltipSlotProps) {\n return (\n <ChartTooltip\n cursor={false}\n content={<ChartTooltipContent nameKey=\"name\" hideLabel roundness={roundness} variant={variant} />}\n />\n )\n}\n\nTreemapChart.Tooltip = Tooltip\n\nexport default TreemapChart\n"],"mappings":";AA4HY,cAoER,YApEQ;AA1HZ,SAAS,aAAkD;AAC3D,SAAS,WAAW,uBAAuB;AAC3C,SAAS,gBAAgB,eAAiC;AAC1D,SAAS,mBAAmB;AAC5B,SAAS,cAAc,2BAA2B;AAyE3C,SAAS,aAAa;AAAA,EAC3B,SAAS,CAAC;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc,IAAI;AAAA,EAClB,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAAsB;AACpB,QAAM,UAAU,MAAM,EAAE,QAAQ,MAAM,EAAE;AAIxC,QAAM,SAAS,QAAQ,IAAI;AAE3B,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OACE,gBACI,QACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,CAAC,EAAE,KAAK,SAAS,OAAO,QAAQ,CAAC;AAAA,MAC5C;AAAA,MAGN,8BAAC,kBAAe,QACd;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,SAAQ;AAAA,UACR;AAAA,UACA,mBAAmB;AAAA,UACnB,SACE,oBAAC,eAAY,SAAkB,SAAkB,QAAgB,YAAwB;AAAA,UAE1F,GAAG;AAAA,UAEH;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;AAGA,SAAS,QAAQ,OAAsB,SAAS,IAA+B;AAC7E,SAAO,MAAM,QAAQ,CAAC,SAAS;AAC7B,UAAM,OAAO,SAAS,GAAG,MAAM,WAAM,KAAK,IAAI,KAAK,KAAK;AACxD,QAAI,KAAK,UAAU,OAAQ,QAAO,QAAQ,KAAK,UAAU,IAAI;AAC7D,WAAO,CAAC,EAAE,GAAG,MAAM,KAAK,CAAC;AAAA,EAC3B,CAAC;AACH;AAwBA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AACT,GAAc;AACZ,MAAI,SAAS,KAAK,UAAU,EAAG,QAAO;AAEtC,QAAM,OACJ,YAAY,WACR,gBAAgB,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC,MACtC,QAAQ,SACN,eAAe,QAAQ,IAAI,CAAC,QAC5B,gBAAiB,QAAQ,IAAK,CAAC;AAGvC,QAAM,eAAe,cAAc,QAAQ,MAAM,SAAS;AAE1D,SACE,qBAAC,OACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,QAAO;AAAA,QACP,aAAa;AAAA;AAAA,IACf;AAAA,IACC,gBACC;AAAA,MAAC;AAAA;AAAA,QACC,GAAG,IAAI;AAAA,QACP,GAAG,IAAI;AAAA,QACP,WAAU;AAAA,QACV,OAAO,EAAE,eAAe,OAAO;AAAA,QAE9B;AAAA;AAAA,IACH;AAAA,IAGF,oBAAC,WAAO,gBAAK;AAAA,IACb,oBAAC,UAAM,mBAAQ;AAAA,KACjB;AAEJ;AAGA,SAAS,QAAQ,EAAE,SAAS,UAAU,GAA0B;AAC9D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ;AAAA,MACR,SAAS,oBAAC,uBAAoB,SAAQ,QAAO,WAAS,MAAC,WAAsB,SAAkB;AAAA;AAAA,EACjG;AAEJ;AAEA,aAAa,UAAU;AAEvB,IAAO,uBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/charts/TreemapChart/TreemapChart.tsx"],"sourcesContent":["'use client'\n\nimport { type ComponentProps, type ReactNode } from 'react'\nimport { Treemap as RechartsTreemap } from 'recharts'\nimport { ChartContainer, cssName, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { type ChartEmptyProps } from '../lib/empty'\nimport { ChartTooltip, ChartTooltipContent } from '../lib/tooltip'\nimport type { ChartTooltipSlotProps } from '../AreaChart/AreaChart'\n\n/**\n * How a tile is painted.\n *\n * `ramp` walks the series ramp so siblings separate by lightness; `nested`\n * darkens by DEPTH instead, which is the right encoding when the tree has more\n * than one level and the question is \"what is inside what\".\n */\nexport type TreemapVariant = 'ramp' | 'nested'\n\n/** A node in the tree. Children make it a branch. */\nexport interface TreemapNode {\n name: string\n /** Leaf value. Omit on a branch — a branch is the sum of its children. */\n size?: number\n children?: TreemapNode[]\n [key: string]: unknown\n}\n\n/** The raw Recharts Treemap props a consumer may still reach for. */\nexport type TreemapExtras = Omit<ComponentProps<typeof RechartsTreemap>, 'data' | 'dataKey'>\n\nexport interface TreemapChartProps {\n /** Tile names → their label and paint. Optional: the ramp covers unnamed tiles. */\n config?: ChartConfig\n /** The tree. One level is a flat set of tiles; two is a nested treemap. */\n data: TreemapNode[]\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n variant?: TreemapVariant\n /** The leaf field holding each tile's number. */\n dataKey?: string\n /** How square the tiles are pushed to be. Recharts' own squarify parameter. */\n aspectRatio?: number\n /** Prints each tile's name on it, where the tile is big enough to hold it. */\n showLabels?: boolean\n /** The hover panel. */\n children?: ReactNode\n chartProps?: TreemapExtras\n /** Drops the hidden table view. Only correct when the page prints the data itself. */\n hideDataTable?: boolean\n /**\n * What the chart shows when it has nothing to draw. `false` keeps the empty\n * plot, for a chart whose emptiness is itself the reading.\n */\n empty?: ChartEmptyProps | false\n}\n\n/**\n * Part of a whole, when the whole has too many parts for a pie — and the parts\n * nest.\n *\n * A treemap encodes value as AREA, which the eye reads worse than length but\n * far better than angle, and it is the only form here that stays readable at\n * fifty items. Two rules make it honest: no negative values (an area cannot be\n * negative), and the tiles have to sum to something a reader recognises as the\n * whole.\n *\n * Reach for `<BarChart>` when there are under a dozen items and the ranking\n * matters — a bar's length is the more precise encoding, and a treemap's\n * layout deliberately does not order its tiles by value alone.\n *\n * @example\n * <TreemapChart title=\"Bundle size by package\" data={packages} showLabels>\n * <TreemapChart.Tooltip />\n * </TreemapChart>\n */\nexport function TreemapChart({\n config = {},\n data,\n title,\n showTitle,\n description,\n className,\n variant = 'ramp',\n dataKey = 'size',\n aspectRatio = 4 / 3,\n showLabels = true,\n children,\n chartProps,\n hideDataTable = false,\n empty,\n}: TreemapChartProps) {\n // Flattened for the table view, because a nested tree read aloud row by row\n // is not something anyone can follow — the leaves are the facts.\n const leaves = flatten(data, dataKey)\n\n return (\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : {\n rows: leaves,\n rowKey: 'path',\n columns: [{ key: dataKey, label: 'Value' }],\n }\n }\n isEmpty={data.length === 0}\n empty={empty}\n >\n <ChartContainer config={config}>\n <RechartsTreemap\n data={data}\n dataKey={dataKey}\n nameKey=\"name\"\n aspectRatio={aspectRatio}\n isAnimationActive={false}\n content={\n <TreemapTile variant={variant} config={config} showLabels={showLabels} />\n }\n {...chartProps}\n >\n {children}\n </RechartsTreemap>\n </ChartContainer>\n </ChartFigure>\n )\n}\n\n/**\n * Every leaf, with the path that names it and whether the picture can hold it.\n *\n * A tile's AREA is its value, so a leaf at zero or below has no area to be\n * drawn at: the layout gives it no width, the tile is dropped, and the leaf\n * disappears from the picture while staying in this table. The table is the\n * only place left that can say so, and saying nothing would leave the two views\n * of one tree disagreeing about how many leaves there are.\n */\nfunction flatten(\n nodes: TreemapNode[],\n dataKey: string,\n prefix = '',\n): Record<string, unknown>[] {\n return nodes.flatMap((node) => {\n const path = prefix ? `${prefix} › ${node.name}` : node.name\n if (node.children?.length) return flatten(node.children, dataKey, path)\n\n const value = Number(node[dataKey])\n if (Number.isFinite(value) && value > 0) return [{ ...node, path }]\n return [\n {\n ...node,\n path,\n [dataKey]: Number.isFinite(value)\n ? `${value.toLocaleString()} — not drawn`\n : 'not drawn',\n },\n ]\n })\n}\n\ninterface TileProps {\n variant: TreemapVariant\n config: ChartConfig\n showLabels: boolean\n // Recharts hands the geometry in as loose props on the cloned element.\n x?: number\n y?: number\n width?: number\n height?: number\n depth?: number\n index?: number\n name?: string\n}\n\n/**\n * One tile.\n *\n * The gap between tiles is a surface-coloured stroke rather than a smaller\n * rect, so the tiles still tile — a treemap whose parts do not touch stops\n * reading as a partition of one whole.\n */\nfunction TreemapTile({\n variant,\n config,\n showLabels,\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n depth = 1,\n index = 0,\n name = '',\n}: TileProps) {\n if (width <= 0 || height <= 0) return null\n\n const slot =\n variant === 'nested'\n ? `var(--series-${Math.min(8, depth * 2)})`\n : name in config\n ? `var(--color-${cssName(name)}-0)`\n : `var(--series-${(index % 8) + 1})`\n\n // Only label a tile with room for one. A clipped word is worse than none.\n const roomForLabel = showLabels && width > 56 && height > 26\n\n return (\n <g>\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n fill={slot}\n stroke=\"var(--chart-surface)\"\n strokeWidth={2}\n />\n {roomForLabel && (\n <text\n x={x + 8}\n y={y + 18}\n className=\"fill-(--chart-surface) text-[11px] font-medium\"\n style={{ pointerEvents: 'none' }}\n >\n {name}\n </text>\n )}\n {/* The tile's whole accessible reading. `<desc>` used to carry the chart's\n generated id beside it — and `<desc>` IS the accessible description,\n so every tile announced its name followed by an opaque string nothing\n on the page could explain. */}\n <title>{name}</title>\n </g>\n )\n}\n\n/** The hover panel. No heading: the tile's own name is the row label. */\nfunction Tooltip({ variant, roundness }: ChartTooltipSlotProps) {\n return (\n <ChartTooltip\n cursor={false}\n content={<ChartTooltipContent nameKey=\"name\" hideLabel roundness={roundness} variant={variant} />}\n />\n )\n}\n\nTreemapChart.Tooltip = Tooltip\n\nexport default TreemapChart\n"],"mappings":";AAmIY,cAyFR,YAzFQ;AAjIZ,eAAoD;AACpD,SAAS,WAAW,uBAAuB;AAC3C,SAAS,gBAAgB,eAAiC;AAC1D,SAAS,mBAAmB;AAC5B,eAAqC;AACrC,SAAS,cAAc,2BAA2B;AA8E3C,SAAS,aAAa;AAAA,EAC3B,SAAS,CAAC;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc,IAAI;AAAA,EAClB,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACF,GAAsB;AAGpB,QAAM,SAAS,QAAQ,MAAM,OAAO;AAEpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OACE,gBACI,QACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,CAAC,EAAE,KAAK,SAAS,OAAO,QAAQ,CAAC;AAAA,MAC5C;AAAA,MAEN,SAAS,KAAK,WAAW;AAAA,MACzB;AAAA,MAEA,8BAAC,kBAAe,QACd;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,SAAQ;AAAA,UACR;AAAA,UACA,mBAAmB;AAAA,UACnB,SACE,oBAAC,eAAY,SAAkB,QAAgB,YAAwB;AAAA,UAExE,GAAG;AAAA,UAEH;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;AAWA,SAAS,QACP,OACA,SACA,SAAS,IACkB;AAC3B,SAAO,MAAM,QAAQ,CAAC,SAAS;AAC7B,UAAM,OAAO,SAAS,GAAG,MAAM,WAAM,KAAK,IAAI,KAAK,KAAK;AACxD,QAAI,KAAK,UAAU,OAAQ,QAAO,QAAQ,KAAK,UAAU,SAAS,IAAI;AAEtE,UAAM,QAAQ,OAAO,KAAK,OAAO,CAAC;AAClC,QAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,EAAG,QAAO,CAAC,EAAE,GAAG,MAAM,KAAK,CAAC;AAClE,WAAO;AAAA,MACL;AAAA,QACE,GAAG;AAAA,QACH;AAAA,QACA,CAAC,OAAO,GAAG,OAAO,SAAS,KAAK,IAC5B,GAAG,MAAM,eAAe,CAAC,sBACzB;AAAA,MACN;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAuBA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AACT,GAAc;AACZ,MAAI,SAAS,KAAK,UAAU,EAAG,QAAO;AAEtC,QAAM,OACJ,YAAY,WACR,gBAAgB,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC,MACtC,QAAQ,SACN,eAAe,QAAQ,IAAI,CAAC,QAC5B,gBAAiB,QAAQ,IAAK,CAAC;AAGvC,QAAM,eAAe,cAAc,QAAQ,MAAM,SAAS;AAE1D,SACE,qBAAC,OACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,QAAO;AAAA,QACP,aAAa;AAAA;AAAA,IACf;AAAA,IACC,gBACC;AAAA,MAAC;AAAA;AAAA,QACC,GAAG,IAAI;AAAA,QACP,GAAG,IAAI;AAAA,QACP,WAAU;AAAA,QACV,OAAO,EAAE,eAAe,OAAO;AAAA,QAE9B;AAAA;AAAA,IACH;AAAA,IAMF,oBAAC,WAAO,gBAAK;AAAA,KACf;AAEJ;AAGA,SAAS,QAAQ,EAAE,SAAS,UAAU,GAA0B;AAC9D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ;AAAA,MACR,SAAS,oBAAC,uBAAoB,SAAQ,QAAO,WAAS,MAAC,WAAsB,SAAkB;AAAA;AAAA,EACjG;AAEJ;AAEA,aAAa,UAAU;AAEvB,IAAO,uBAAQ;","names":[]}
@@ -17,7 +17,7 @@ import {
17
17
  } from "recharts";
18
18
  import { ChartContainer } from "../lib/chart.js";
19
19
  import { ChartFigure } from "../lib/figure.js";
20
- import { ChartEmpty } from "../lib/empty.js";
20
+ import {} from "../lib/empty.js";
21
21
  import { ChartBackground } from "../lib/background.js";
22
22
  import { ChartTooltip } from "../lib/tooltip.js";
23
23
  import { BarHatchedFill, SeriesGradient } from "../lib/paint.js";
@@ -103,7 +103,9 @@ function WaterfallChart({
103
103
  { key: "total", label: "Running total" }
104
104
  ]
105
105
  },
106
- children: isEmpty ? /* @__PURE__ */ jsx(ChartEmpty, { ...empty || {} }) : /* @__PURE__ */ jsx(ChartContainer, { config, children: /* @__PURE__ */ jsx(
106
+ isEmpty,
107
+ empty,
108
+ children: /* @__PURE__ */ jsx(ChartContainer, { config, children: /* @__PURE__ */ jsx(
107
109
  RechartsBarChart,
108
110
  {
109
111
  id: chartId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/charts/WaterfallChart/WaterfallChart.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n useContext,\n useId,\n useMemo,\n type ComponentProps,\n type ReactNode,\n} from 'react'\nimport {\n Bar as RechartsBar,\n BarChart as RechartsBarChart,\n CartesianGrid,\n ReferenceLine as RechartsReferenceLine,\n XAxis as RechartsXAxis,\n YAxis as RechartsYAxis,\n usePlotArea,\n} from 'recharts'\nimport { ChartContainer, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { ChartEmpty, type ChartEmptyProps } from '../lib/empty'\nimport { ChartBackground, type ChartBackgroundVariant } from '../lib/background'\nimport { ChartTooltip } from '../lib/tooltip'\nimport { BarHatchedFill, SeriesGradient } from '../lib/paint'\nimport { Annotation, ReferenceBand, ReferenceLine } from '../lib/annotations'\nimport { axisLabel } from '../lib/axis'\nimport { defaultTick } from '../lib/format'\nimport { cn } from '../../lib/cn'\nimport type { ChartXAxisProps, ChartYAxisProps } from '../BarChart/BarChart'\nimport type { ChartTooltipSlotProps } from '../AreaChart/AreaChart'\n\n/** The series a chart with only one of them falls back to. */\nconst DEFAULT_CONFIG: ChartConfig = { value: { label: 'Change' } }\n\nconst CHART_MARGIN = { top: 20, right: 12, bottom: 0, left: 12 }\n\n/** The corner every bar rounds to. */\nconst BAR_RADIUS = 2\n\n/** How a step moves the running total. */\nexport type WaterfallStepType = 'delta' | 'total'\n\n/** Which way a bar goes, which is what its texture encodes. */\nexport type WaterfallDirection = 'increase' | 'decrease' | 'total'\n\nexport interface WaterfallStep {\n /** The step's name on the category axis. Doubles as its key. */\n name: string\n /**\n * The signed change this step makes, or — on a `total` step — the absolute\n * figure the running total is set to.\n *\n * Omit it on a `total` and the running total is used, which is the right\n * default for the closing bar: an end total typed by hand can disagree with\n * the deltas above it, and the chart would draw the disagreement without\n * saying anything.\n */\n value?: number\n /**\n * `delta` (the default) adds `value` to the running total and floats the bar\n * where that lands. `total` plants a bar on the baseline — an opening\n * balance, a subtotal, a closing figure.\n */\n type?: WaterfallStepType\n}\n\n/** A step with its span, its direction and its running total worked out. */\ninterface ResolvedStep extends WaterfallStep {\n type: WaterfallStepType\n /**\n * What this step moved the total by. On a `total` step that is the gap it\n * closes — zero whenever the deltas above it already add up to it.\n */\n change: number\n /** The running total once this step has been applied. */\n total: number\n /**\n * The bar's two ends on the value axis, LOW first.\n *\n * Ascending because Recharts reads a floating bar's range that way and\n * collapses a descending one to nothing — which is every decrease in the\n * cascade. Which end the running total is at is recovered from `total`\n * rather than from the order here.\n */\n span: [number, number]\n direction: WaterfallDirection\n}\n\n/** A bar's two ends, low first — see `ResolvedStep.span`. */\nfunction ascending(a: number, b: number): [number, number] {\n return a <= b ? [a, b] : [b, a]\n}\n\n/** The steps, walked in order, each carrying the total it leaves behind. */\nfunction resolve(data: WaterfallStep[]): ResolvedStep[] {\n let running = 0\n\n return data.map((step) => {\n const type = step.type ?? 'delta'\n\n if (type === 'total') {\n const total = step.value ?? running\n const resolved: ResolvedStep = {\n ...step,\n type,\n change: total - running,\n total,\n span: ascending(0, total),\n direction: 'total',\n }\n running = total\n return resolved\n }\n\n const change = step.value ?? 0\n const from = running\n running += change\n\n return {\n ...step,\n type,\n change,\n total: running,\n span: ascending(from, running),\n direction: change < 0 ? 'decrease' : 'increase',\n }\n })\n}\n\ninterface WaterfallContextValue {\n config: ChartConfig\n seriesKey: string\n steps: ResolvedStep[]\n formatValue: (value: number) => string\n}\n\nconst WaterfallContext = createContext<WaterfallContextValue | null>(null)\n\nfunction useWaterfall(): WaterfallContextValue {\n const context = useContext(WaterfallContext)\n if (!context) throw new Error('A waterfall part must be rendered inside <WaterfallChart>')\n return context\n}\n\nexport interface WaterfallChartProps {\n /**\n * The single series — its label and its paint. Only the FIRST entry is read;\n * a waterfall has one quantity and as many bars as it has steps.\n */\n config?: ChartConfig\n /**\n * The steps, in the order they are applied. Order is the arithmetic: the\n * chart does not sort, because a different order is a different total at\n * every intermediate bar.\n */\n data: WaterfallStep[]\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** The composed parts — axes, grid, tooltip, and `<WaterfallChart.Bars>`. */\n children: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n /** Escape hatch onto the raw Recharts chart element. */\n chartProps?: ComponentProps<typeof RechartsBarChart>\n /** Formats every number the chart prints — ticks, labels, tooltip, table. */\n formatValue?: (value: number) => string\n /** Drops the hidden table view. Only correct when the page prints the data itself. */\n hideDataTable?: boolean\n /**\n * What the chart shows when it has nothing to draw. `false` keeps the axes,\n * for a chart whose emptiness is itself the reading.\n */\n empty?: ChartEmptyProps | false\n}\n\n/**\n * How a total got from one figure to another — an opening balance, the signed\n * contributions that moved it, and where it closed.\n *\n * The form for \"why did this change\", which a pair of bars cannot answer and a\n * pie chart answers wrongly, because contributions can be NEGATIVE and a slice\n * cannot. Reach for `<BarChart>` when the parts do not have to add up to the\n * gap between two totals, and for `<FunnelChart>` when the quantity only ever\n * shrinks.\n *\n * **The connectors are the claim to be careful about.** They draw the steps as\n * a sequence — this happened, then this — and most breakdowns are not\n * sequential at all: churn and expansion in the same month are simultaneous,\n * and the order they are listed in is an editorial choice. The arithmetic\n * survives any order; the STORY does not, and a reader will take the leftmost\n * bar as the first cause. Two related traps: the intermediate bars are floating\n * lengths read against no baseline, so a small step high up the cascade is hard\n * to compare with a large one near zero; and any step that is itself a net of\n * two larger opposing movements is invisible as such. Where the order is\n * arbitrary, say so in the `description`.\n *\n * Recharts earns its place for the axes, grid and tooltip. Each bar is a\n * floating range — from the running total to the new one — with a custom shape\n * over it, because Recharts has no waterfall mark and the connectors have to be\n * drawn from the same geometry as the bars they join.\n *\n * @example\n * <WaterfallChart title=\"Revenue bridge, FY25\" data={steps}>\n * <WaterfallChart.Grid />\n * <WaterfallChart.XAxis />\n * <WaterfallChart.YAxis label=\"AUD\" />\n * <WaterfallChart.Tooltip />\n * <WaterfallChart.Bars showValues />\n * </WaterfallChart>\n *\n * @example\n * const steps = [\n * { name: 'FY24', value: 1200, type: 'total' as const },\n * { name: 'New', value: 340 },\n * { name: 'Churn', value: -180 },\n * // No value: the closing bar is whatever the deltas add up to.\n * { name: 'FY25', type: 'total' as const },\n * ]\n */\nexport function WaterfallChart({\n config = DEFAULT_CONFIG,\n data,\n title,\n showTitle,\n description,\n children,\n className,\n chartProps,\n formatValue = defaultTick,\n hideDataTable = false,\n empty,\n}: WaterfallChartProps) {\n const chartId = useId().replace(/:/g, '')\n const seriesKey = Object.keys(config)[0] ?? 'value'\n const steps = useMemo(() => resolve(data), [data])\n\n const context = useMemo<WaterfallContextValue>(\n () => ({ config, seriesKey, steps, formatValue }),\n [config, seriesKey, steps, formatValue],\n )\n\n const isEmpty = empty !== false && steps.length === 0\n\n return (\n <WaterfallContext.Provider value={context}>\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : {\n rows: steps as unknown as Record<string, unknown>[],\n rowKey: 'name',\n columns: [\n { key: 'change', label: 'Change' },\n { key: 'total', label: 'Running total' },\n ],\n }\n }\n >\n {isEmpty ? (\n <ChartEmpty {...(empty || {})} />\n ) : (\n <ChartContainer config={config}>\n <RechartsBarChart\n id={chartId}\n accessibilityLayer\n margin={CHART_MARGIN}\n data={steps}\n barCategoryGap=\"25%\"\n {...chartProps}\n >\n {children}\n </RechartsBarChart>\n </ChartContainer>\n )}\n </ChartFigure>\n </WaterfallContext.Provider>\n )\n}\n\nexport interface WaterfallBarsProps {\n /** The bars' corner. */\n radius?: number\n /**\n * Joins each bar's closing edge to where the next one starts.\n *\n * On by default: without them a waterfall is a row of bars floating at\n * unrelated heights, and the cascade — which is the entire point of the form\n * — has to be reconstructed by the reader.\n */\n connectors?: boolean\n /**\n * Prints each step's signed change above or below its bar.\n *\n * Worth more here than on most charts: an intermediate bar has no baseline\n * under it, so its length is the one thing a reader cannot get off the axis.\n */\n showValues?: boolean\n /** Escape hatch onto the underlying Recharts bar. */\n barProps?: ComponentProps<typeof RechartsBar>\n}\n\n/**\n * The bars and the connectors between them.\n *\n * Increases and totals take the solid series fill; decreases take the 45°\n * hatch. Texture rather than a second colour, because the whole set has to stay\n * legible in greyscale and under forced colours — and a total is told from an\n * increase by geometry instead, since it is the only kind of bar that stands on\n * the baseline.\n */\nfunction Bars({\n radius = BAR_RADIUS,\n connectors = true,\n showValues = false,\n barProps,\n}: WaterfallBarsProps) {\n const { config, seriesKey, steps, formatValue } = useWaterfall()\n const id = useId().replace(/:/g, '')\n\n const shared = {\n id,\n seriesKey,\n radius,\n connectors,\n showValues,\n stepCount: steps.length,\n formatValue,\n }\n\n return (\n <>\n {/* The baseline the totals stand on. A waterfall without a visible zero\n asks the reader to take the floating bars on trust. */}\n <RechartsReferenceLine y={0} stroke=\"var(--chart-grid)\" strokeWidth={1} />\n <RechartsBar\n dataKey={(row: unknown) => (row as ResolvedStep).span}\n name={seriesKey}\n isAnimationActive={false}\n fill=\"transparent\"\n shape={(props: unknown) => <StepShape {...(props as StepGeometry)} {...shared} />}\n activeBar={(props: unknown) => <StepShape {...(props as StepGeometry)} {...shared} />}\n {...barProps}\n />\n <defs>\n <SeriesGradient id={id} dataKey={seriesKey} config={config} direction=\"vertical\" />\n <BarHatchedFill id={id} dataKey={seriesKey} />\n </defs>\n </>\n )\n}\n\n/** The geometry Recharts hands a custom bar shape, plus the step behind it. */\ninterface StepGeometry {\n x?: number\n y?: number\n width?: number\n height?: number\n index?: number\n payload?: ResolvedStep\n}\n\ninterface StepShapeProps extends StepGeometry {\n id: string\n seriesKey: string\n radius: number\n connectors: boolean\n showValues: boolean\n stepCount: number\n formatValue: (value: number) => string\n}\n\n/** One step: its bar, its connector to the next step, and its label. */\nfunction StepShape({\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n index = -1,\n payload,\n id,\n seriesKey,\n radius,\n connectors,\n showValues,\n stepCount,\n formatValue,\n}: StepShapeProps) {\n const plot = usePlotArea()\n\n if (!payload) return null\n\n const step = payload\n const fill =\n step.direction === 'decrease'\n ? `url(#${id}-hatched-${seriesKey})`\n : `url(#${id}-colors-${seriesKey})`\n\n // Which edge of the rect the running total lands on. Everything downstream —\n // the connector's height, where the label sits — hangs off this one fact,\n // and it is a comparison rather than a re-derived scale so it stays right\n // whatever domain the axis ends up with.\n const closesAtTop = step.total >= step.span[1]\n const closingY = closesAtTop ? y : y + height\n\n // A zero-change step has no rect to see. It still has to appear, or the\n // cascade silently loses a category and the axis stops matching the bars.\n const drawnHeight = Math.max(height, 1)\n\n // Recharts leaves a gap between category bands, and the connector has to\n // cross it. One band is the plot's width over the number of steps, so the\n // next bar's near edge is exactly one band along from this one's.\n const band = plot && stepCount > 0 ? plot.width / stepCount : width\n const hasNext = index >= 0 && index < stepCount - 1\n\n return (\n <g>\n {connectors && hasNext && (\n <line\n x1={x + width}\n y1={closingY}\n x2={x + band}\n y2={closingY}\n stroke=\"var(--chart-cursor)\"\n strokeWidth={1}\n strokeDasharray=\"3 3\"\n />\n )}\n <rect x={x} y={y} width={width} height={drawnHeight} rx={radius || undefined} fill={fill} />\n {showValues && (\n <text\n x={x + width / 2}\n // Above a rise, below a fall — so the label never sits inside the bar\n // it is describing and never collides with the connector leaving it.\n y={closesAtTop ? y - 6 : y + drawnHeight + 13}\n textAnchor=\"middle\"\n className=\"fill-(--ink) font-mono text-[11px] font-medium tabular-nums\"\n style={{ pointerEvents: 'none' }}\n >\n {step.direction === 'total'\n ? formatValue(step.total)\n : `${step.change > 0 ? '+' : ''}${formatValue(step.change)}`}\n </text>\n )}\n </g>\n )\n}\n\n/** The step axis. */\nfunction XAxis({\n dataKey = 'name',\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n minTickGap = 8,\n label,\n ...rest\n}: ChartXAxisProps) {\n return (\n <RechartsXAxis\n dataKey={dataKey}\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n minTickGap={minTickGap}\n {...rest}\n >\n {axisLabel(label, 'x')}\n </RechartsXAxis>\n )\n}\n\n/** The value axis. */\nfunction YAxis({\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n width = 'auto',\n label,\n tickFormatter,\n ...rest\n}: ChartYAxisProps) {\n const { formatValue } = useWaterfall()\n\n return (\n <RechartsYAxis\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n width={width}\n tickFormatter={tickFormatter ?? formatValue}\n {...rest}\n >\n {axisLabel(label, 'y')}\n </RechartsYAxis>\n )\n}\n\n/** Rules across the value axis. */\nfunction Grid({\n strokeDasharray = '3 3',\n vertical = false,\n ...rest\n}: ComponentProps<typeof CartesianGrid>) {\n return <CartesianGrid strokeDasharray={strokeDasharray} vertical={vertical} {...rest} />\n}\n\n/**\n * The hover panel, printing the change AND the total it leaves behind.\n *\n * The generic tooltip would print the bar's raw span, which is a pair of\n * running totals and reads as two unrelated numbers. Both facts a waterfall\n * carries — what this step did, and where the total stood afterwards — are\n * named here instead.\n */\nfunction Tooltip({ variant = 'solid', roundness = 'lg', defaultIndex }: ChartTooltipSlotProps) {\n const { formatValue } = useWaterfall()\n\n return (\n <ChartTooltip\n cursor={false}\n defaultIndex={defaultIndex}\n content={(props) => {\n const { active, payload } = props as unknown as {\n active?: boolean\n payload?: { payload?: ResolvedStep }[]\n }\n const step = payload?.[0]?.payload\n if (!active || !step) return <span className=\"p-4\" />\n\n return (\n <div\n className={cn(\n 'grid min-w-40 gap-1.5 border border-(--rule-2) px-2.5 py-1.5 text-xs',\n roundness === 'sm' && 'rounded-(--radius-sm)',\n roundness === 'md' && 'rounded-(--radius)',\n roundness === 'lg' && 'rounded-(--radius-lg)',\n variant === 'frosted'\n ? 'bg-(--chart-surface)/75 backdrop-blur-sm'\n : 'bg-(--chart-surface)',\n )}\n >\n <div className=\"font-medium text-(--ink)\">{step.name}</div>\n {step.direction !== 'total' && (\n <div className=\"flex justify-between gap-4 leading-none\">\n <span className=\"text-(--ink-3-aa)\">Change</span>\n <span className=\"font-mono font-medium tabular-nums text-(--ink)\">\n {step.change > 0 ? '+' : ''}\n {formatValue(step.change)}\n </span>\n </div>\n )}\n <div className=\"flex justify-between gap-4 leading-none\">\n <span className=\"text-(--ink-3-aa)\">\n {step.direction === 'total' ? 'Total' : 'Running total'}\n </span>\n <span className=\"font-mono font-medium tabular-nums text-(--ink)\">\n {formatValue(step.total)}\n </span>\n </div>\n </div>\n )\n }}\n />\n )\n}\n\n/** The decorative plate behind the plot. */\nfunction Background({ variant }: { variant: ChartBackgroundVariant }) {\n return <ChartBackground variant={variant} />\n}\n\nWaterfallChart.Bars = Bars\nWaterfallChart.XAxis = XAxis\nWaterfallChart.YAxis = YAxis\nWaterfallChart.Grid = Grid\nWaterfallChart.Tooltip = Tooltip\nWaterfallChart.Background = Background\nWaterfallChart.ReferenceLine = ReferenceLine\nWaterfallChart.ReferenceBand = ReferenceBand\nWaterfallChart.Annotation = Annotation\n\nexport default WaterfallChart\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/charts/WaterfallChart/WaterfallChart.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n useContext,\n useId,\n useMemo,\n type ComponentProps,\n type ReactNode,\n} from 'react'\nimport {\n Bar as RechartsBar,\n BarChart as RechartsBarChart,\n CartesianGrid,\n ReferenceLine as RechartsReferenceLine,\n XAxis as RechartsXAxis,\n YAxis as RechartsYAxis,\n usePlotArea,\n} from 'recharts'\nimport { ChartContainer, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { type ChartEmptyProps } from '../lib/empty'\nimport { ChartBackground, type ChartBackgroundVariant } from '../lib/background'\nimport { ChartTooltip } from '../lib/tooltip'\nimport { BarHatchedFill, SeriesGradient } from '../lib/paint'\nimport { Annotation, ReferenceBand, ReferenceLine } from '../lib/annotations'\nimport { axisLabel } from '../lib/axis'\nimport { defaultTick } from '../lib/format'\nimport { cn } from '../../lib/cn'\nimport type { ChartXAxisProps, ChartYAxisProps } from '../BarChart/BarChart'\nimport type { ChartTooltipSlotProps } from '../AreaChart/AreaChart'\n\n/** The series a chart with only one of them falls back to. */\nconst DEFAULT_CONFIG: ChartConfig = { value: { label: 'Change' } }\n\nconst CHART_MARGIN = { top: 20, right: 12, bottom: 0, left: 12 }\n\n/** The corner every bar rounds to. */\nconst BAR_RADIUS = 2\n\n/** How a step moves the running total. */\nexport type WaterfallStepType = 'delta' | 'total'\n\n/** Which way a bar goes, which is what its texture encodes. */\nexport type WaterfallDirection = 'increase' | 'decrease' | 'total'\n\nexport interface WaterfallStep {\n /** The step's name on the category axis. Doubles as its key. */\n name: string\n /**\n * The signed change this step makes, or — on a `total` step — the absolute\n * figure the running total is set to.\n *\n * Omit it on a `total` and the running total is used, which is the right\n * default for the closing bar: an end total typed by hand can disagree with\n * the deltas above it, and the chart would draw the disagreement without\n * saying anything.\n */\n value?: number\n /**\n * `delta` (the default) adds `value` to the running total and floats the bar\n * where that lands. `total` plants a bar on the baseline — an opening\n * balance, a subtotal, a closing figure.\n */\n type?: WaterfallStepType\n}\n\n/** A step with its span, its direction and its running total worked out. */\ninterface ResolvedStep extends WaterfallStep {\n type: WaterfallStepType\n /**\n * What this step moved the total by. On a `total` step that is the gap it\n * closes — zero whenever the deltas above it already add up to it.\n */\n change: number\n /** The running total once this step has been applied. */\n total: number\n /**\n * The bar's two ends on the value axis, LOW first.\n *\n * Ascending because Recharts reads a floating bar's range that way and\n * collapses a descending one to nothing — which is every decrease in the\n * cascade. Which end the running total is at is recovered from `total`\n * rather than from the order here.\n */\n span: [number, number]\n direction: WaterfallDirection\n}\n\n/** A bar's two ends, low first — see `ResolvedStep.span`. */\nfunction ascending(a: number, b: number): [number, number] {\n return a <= b ? [a, b] : [b, a]\n}\n\n/** The steps, walked in order, each carrying the total it leaves behind. */\nfunction resolve(data: WaterfallStep[]): ResolvedStep[] {\n let running = 0\n\n return data.map((step) => {\n const type = step.type ?? 'delta'\n\n if (type === 'total') {\n const total = step.value ?? running\n const resolved: ResolvedStep = {\n ...step,\n type,\n change: total - running,\n total,\n span: ascending(0, total),\n direction: 'total',\n }\n running = total\n return resolved\n }\n\n const change = step.value ?? 0\n const from = running\n running += change\n\n return {\n ...step,\n type,\n change,\n total: running,\n span: ascending(from, running),\n direction: change < 0 ? 'decrease' : 'increase',\n }\n })\n}\n\ninterface WaterfallContextValue {\n config: ChartConfig\n seriesKey: string\n steps: ResolvedStep[]\n formatValue: (value: number) => string\n}\n\nconst WaterfallContext = createContext<WaterfallContextValue | null>(null)\n\nfunction useWaterfall(): WaterfallContextValue {\n const context = useContext(WaterfallContext)\n if (!context) throw new Error('A waterfall part must be rendered inside <WaterfallChart>')\n return context\n}\n\nexport interface WaterfallChartProps {\n /**\n * The single series — its label and its paint. Only the FIRST entry is read;\n * a waterfall has one quantity and as many bars as it has steps.\n */\n config?: ChartConfig\n /**\n * The steps, in the order they are applied. Order is the arithmetic: the\n * chart does not sort, because a different order is a different total at\n * every intermediate bar.\n */\n data: WaterfallStep[]\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** The composed parts — axes, grid, tooltip, and `<WaterfallChart.Bars>`. */\n children: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n /** Escape hatch onto the raw Recharts chart element. */\n chartProps?: ComponentProps<typeof RechartsBarChart>\n /** Formats every number the chart prints — ticks, labels, tooltip, table. */\n formatValue?: (value: number) => string\n /** Drops the hidden table view. Only correct when the page prints the data itself. */\n hideDataTable?: boolean\n /**\n * What the chart shows when it has nothing to draw. `false` keeps the axes,\n * for a chart whose emptiness is itself the reading.\n */\n empty?: ChartEmptyProps | false\n}\n\n/**\n * How a total got from one figure to another — an opening balance, the signed\n * contributions that moved it, and where it closed.\n *\n * The form for \"why did this change\", which a pair of bars cannot answer and a\n * pie chart answers wrongly, because contributions can be NEGATIVE and a slice\n * cannot. Reach for `<BarChart>` when the parts do not have to add up to the\n * gap between two totals, and for `<FunnelChart>` when the quantity only ever\n * shrinks.\n *\n * **The connectors are the claim to be careful about.** They draw the steps as\n * a sequence — this happened, then this — and most breakdowns are not\n * sequential at all: churn and expansion in the same month are simultaneous,\n * and the order they are listed in is an editorial choice. The arithmetic\n * survives any order; the STORY does not, and a reader will take the leftmost\n * bar as the first cause. Two related traps: the intermediate bars are floating\n * lengths read against no baseline, so a small step high up the cascade is hard\n * to compare with a large one near zero; and any step that is itself a net of\n * two larger opposing movements is invisible as such. Where the order is\n * arbitrary, say so in the `description`.\n *\n * Recharts earns its place for the axes, grid and tooltip. Each bar is a\n * floating range — from the running total to the new one — with a custom shape\n * over it, because Recharts has no waterfall mark and the connectors have to be\n * drawn from the same geometry as the bars they join.\n *\n * @example\n * <WaterfallChart title=\"Revenue bridge, FY25\" data={steps}>\n * <WaterfallChart.Grid />\n * <WaterfallChart.XAxis />\n * <WaterfallChart.YAxis label=\"AUD\" />\n * <WaterfallChart.Tooltip />\n * <WaterfallChart.Bars showValues />\n * </WaterfallChart>\n *\n * @example\n * const steps = [\n * { name: 'FY24', value: 1200, type: 'total' as const },\n * { name: 'New', value: 340 },\n * { name: 'Churn', value: -180 },\n * // No value: the closing bar is whatever the deltas add up to.\n * { name: 'FY25', type: 'total' as const },\n * ]\n */\nexport function WaterfallChart({\n config = DEFAULT_CONFIG,\n data,\n title,\n showTitle,\n description,\n children,\n className,\n chartProps,\n formatValue = defaultTick,\n hideDataTable = false,\n empty,\n}: WaterfallChartProps) {\n const chartId = useId().replace(/:/g, '')\n const seriesKey = Object.keys(config)[0] ?? 'value'\n const steps = useMemo(() => resolve(data), [data])\n\n const context = useMemo<WaterfallContextValue>(\n () => ({ config, seriesKey, steps, formatValue }),\n [config, seriesKey, steps, formatValue],\n )\n\n const isEmpty = empty !== false && steps.length === 0\n\n return (\n <WaterfallContext.Provider value={context}>\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : {\n rows: steps as unknown as Record<string, unknown>[],\n rowKey: 'name',\n columns: [\n { key: 'change', label: 'Change' },\n { key: 'total', label: 'Running total' },\n ],\n }\n }\n isEmpty={isEmpty}\n empty={empty}\n >\n <ChartContainer config={config}>\n <RechartsBarChart\n id={chartId}\n accessibilityLayer\n margin={CHART_MARGIN}\n data={steps}\n barCategoryGap=\"25%\"\n {...chartProps}\n >\n {children}\n </RechartsBarChart>\n </ChartContainer>\n </ChartFigure>\n </WaterfallContext.Provider>\n )\n}\n\nexport interface WaterfallBarsProps {\n /** The bars' corner. */\n radius?: number\n /**\n * Joins each bar's closing edge to where the next one starts.\n *\n * On by default: without them a waterfall is a row of bars floating at\n * unrelated heights, and the cascade — which is the entire point of the form\n * — has to be reconstructed by the reader.\n */\n connectors?: boolean\n /**\n * Prints each step's signed change above or below its bar.\n *\n * Worth more here than on most charts: an intermediate bar has no baseline\n * under it, so its length is the one thing a reader cannot get off the axis.\n */\n showValues?: boolean\n /** Escape hatch onto the underlying Recharts bar. */\n barProps?: ComponentProps<typeof RechartsBar>\n}\n\n/**\n * The bars and the connectors between them.\n *\n * Increases and totals take the solid series fill; decreases take the 45°\n * hatch. Texture rather than a second colour, because the whole set has to stay\n * legible in greyscale and under forced colours — and a total is told from an\n * increase by geometry instead, since it is the only kind of bar that stands on\n * the baseline.\n */\nfunction Bars({\n radius = BAR_RADIUS,\n connectors = true,\n showValues = false,\n barProps,\n}: WaterfallBarsProps) {\n const { config, seriesKey, steps, formatValue } = useWaterfall()\n const id = useId().replace(/:/g, '')\n\n const shared = {\n id,\n seriesKey,\n radius,\n connectors,\n showValues,\n stepCount: steps.length,\n formatValue,\n }\n\n return (\n <>\n {/* The baseline the totals stand on. A waterfall without a visible zero\n asks the reader to take the floating bars on trust. */}\n <RechartsReferenceLine y={0} stroke=\"var(--chart-grid)\" strokeWidth={1} />\n <RechartsBar\n dataKey={(row: unknown) => (row as ResolvedStep).span}\n name={seriesKey}\n isAnimationActive={false}\n fill=\"transparent\"\n shape={(props: unknown) => <StepShape {...(props as StepGeometry)} {...shared} />}\n activeBar={(props: unknown) => <StepShape {...(props as StepGeometry)} {...shared} />}\n {...barProps}\n />\n <defs>\n <SeriesGradient id={id} dataKey={seriesKey} config={config} direction=\"vertical\" />\n <BarHatchedFill id={id} dataKey={seriesKey} />\n </defs>\n </>\n )\n}\n\n/** The geometry Recharts hands a custom bar shape, plus the step behind it. */\ninterface StepGeometry {\n x?: number\n y?: number\n width?: number\n height?: number\n index?: number\n payload?: ResolvedStep\n}\n\ninterface StepShapeProps extends StepGeometry {\n id: string\n seriesKey: string\n radius: number\n connectors: boolean\n showValues: boolean\n stepCount: number\n formatValue: (value: number) => string\n}\n\n/** One step: its bar, its connector to the next step, and its label. */\nfunction StepShape({\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n index = -1,\n payload,\n id,\n seriesKey,\n radius,\n connectors,\n showValues,\n stepCount,\n formatValue,\n}: StepShapeProps) {\n const plot = usePlotArea()\n\n if (!payload) return null\n\n const step = payload\n const fill =\n step.direction === 'decrease'\n ? `url(#${id}-hatched-${seriesKey})`\n : `url(#${id}-colors-${seriesKey})`\n\n // Which edge of the rect the running total lands on. Everything downstream —\n // the connector's height, where the label sits — hangs off this one fact,\n // and it is a comparison rather than a re-derived scale so it stays right\n // whatever domain the axis ends up with.\n const closesAtTop = step.total >= step.span[1]\n const closingY = closesAtTop ? y : y + height\n\n // A zero-change step has no rect to see. It still has to appear, or the\n // cascade silently loses a category and the axis stops matching the bars.\n const drawnHeight = Math.max(height, 1)\n\n // Recharts leaves a gap between category bands, and the connector has to\n // cross it. One band is the plot's width over the number of steps, so the\n // next bar's near edge is exactly one band along from this one's.\n const band = plot && stepCount > 0 ? plot.width / stepCount : width\n const hasNext = index >= 0 && index < stepCount - 1\n\n return (\n <g>\n {connectors && hasNext && (\n <line\n x1={x + width}\n y1={closingY}\n x2={x + band}\n y2={closingY}\n stroke=\"var(--chart-cursor)\"\n strokeWidth={1}\n strokeDasharray=\"3 3\"\n />\n )}\n <rect x={x} y={y} width={width} height={drawnHeight} rx={radius || undefined} fill={fill} />\n {showValues && (\n <text\n x={x + width / 2}\n // Above a rise, below a fall — so the label never sits inside the bar\n // it is describing and never collides with the connector leaving it.\n y={closesAtTop ? y - 6 : y + drawnHeight + 13}\n textAnchor=\"middle\"\n className=\"fill-(--ink) font-mono text-[11px] font-medium tabular-nums\"\n style={{ pointerEvents: 'none' }}\n >\n {step.direction === 'total'\n ? formatValue(step.total)\n : `${step.change > 0 ? '+' : ''}${formatValue(step.change)}`}\n </text>\n )}\n </g>\n )\n}\n\n/** The step axis. */\nfunction XAxis({\n dataKey = 'name',\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n minTickGap = 8,\n label,\n ...rest\n}: ChartXAxisProps) {\n return (\n <RechartsXAxis\n dataKey={dataKey}\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n minTickGap={minTickGap}\n {...rest}\n >\n {axisLabel(label, 'x')}\n </RechartsXAxis>\n )\n}\n\n/** The value axis. */\nfunction YAxis({\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n width = 'auto',\n label,\n tickFormatter,\n ...rest\n}: ChartYAxisProps) {\n const { formatValue } = useWaterfall()\n\n return (\n <RechartsYAxis\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n width={width}\n tickFormatter={tickFormatter ?? formatValue}\n {...rest}\n >\n {axisLabel(label, 'y')}\n </RechartsYAxis>\n )\n}\n\n/** Rules across the value axis. */\nfunction Grid({\n strokeDasharray = '3 3',\n vertical = false,\n ...rest\n}: ComponentProps<typeof CartesianGrid>) {\n return <CartesianGrid strokeDasharray={strokeDasharray} vertical={vertical} {...rest} />\n}\n\n/**\n * The hover panel, printing the change AND the total it leaves behind.\n *\n * The generic tooltip would print the bar's raw span, which is a pair of\n * running totals and reads as two unrelated numbers. Both facts a waterfall\n * carries — what this step did, and where the total stood afterwards — are\n * named here instead.\n */\nfunction Tooltip({ variant = 'solid', roundness = 'lg', defaultIndex }: ChartTooltipSlotProps) {\n const { formatValue } = useWaterfall()\n\n return (\n <ChartTooltip\n cursor={false}\n defaultIndex={defaultIndex}\n content={(props) => {\n const { active, payload } = props as unknown as {\n active?: boolean\n payload?: { payload?: ResolvedStep }[]\n }\n const step = payload?.[0]?.payload\n if (!active || !step) return <span className=\"p-4\" />\n\n return (\n <div\n className={cn(\n 'grid min-w-40 gap-1.5 border border-(--rule-2) px-2.5 py-1.5 text-xs',\n roundness === 'sm' && 'rounded-(--radius-sm)',\n roundness === 'md' && 'rounded-(--radius)',\n roundness === 'lg' && 'rounded-(--radius-lg)',\n variant === 'frosted'\n ? 'bg-(--chart-surface)/75 backdrop-blur-sm'\n : 'bg-(--chart-surface)',\n )}\n >\n <div className=\"font-medium text-(--ink)\">{step.name}</div>\n {step.direction !== 'total' && (\n <div className=\"flex justify-between gap-4 leading-none\">\n <span className=\"text-(--ink-3-aa)\">Change</span>\n <span className=\"font-mono font-medium tabular-nums text-(--ink)\">\n {step.change > 0 ? '+' : ''}\n {formatValue(step.change)}\n </span>\n </div>\n )}\n <div className=\"flex justify-between gap-4 leading-none\">\n <span className=\"text-(--ink-3-aa)\">\n {step.direction === 'total' ? 'Total' : 'Running total'}\n </span>\n <span className=\"font-mono font-medium tabular-nums text-(--ink)\">\n {formatValue(step.total)}\n </span>\n </div>\n </div>\n )\n }}\n />\n )\n}\n\n/** The decorative plate behind the plot. */\nfunction Background({ variant }: { variant: ChartBackgroundVariant }) {\n return <ChartBackground variant={variant} />\n}\n\nWaterfallChart.Bars = Bars\nWaterfallChart.XAxis = XAxis\nWaterfallChart.YAxis = YAxis\nWaterfallChart.Grid = Grid\nWaterfallChart.Tooltip = Tooltip\nWaterfallChart.Background = Background\nWaterfallChart.ReferenceLine = ReferenceLine\nWaterfallChart.ReferenceBand = ReferenceBand\nWaterfallChart.Annotation = Annotation\n\nexport default WaterfallChart\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
1
  import { ChartColumn } from './figure.js';
2
2
  export { downloadBlob } from '../../lib/svg-export.js';
3
3
  import 'react';
4
+ import './empty.js';
4
5
 
5
6
  /**
6
7
  * Taking a chart off the page: a PNG of the plot, or a CSV of its rows.
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
+ import { ChartEmptyProps } from './empty.js';
3
4
 
4
5
  interface ChartColumn {
5
6
  /** The row field this column reads. */
@@ -47,6 +48,19 @@ interface ChartFigureProps {
47
48
  children: ReactNode;
48
49
  /** The table view. Omit and it is generated; `false` opts out. */
49
50
  table?: Omit<ChartDataTableProps, 'caption'> | false;
51
+ /**
52
+ * Whether there is nothing to draw. The plot is replaced by `empty` when it
53
+ * is true.
54
+ *
55
+ * Named as a question rather than derived from `table.rows`, because a chart
56
+ * mid-load has no rows either and wants its skeleton, not a message.
57
+ */
58
+ isEmpty?: boolean;
59
+ /**
60
+ * What stands in for the plot when there is nothing to draw. `false` keeps
61
+ * the plot, for a chart whose emptiness is itself the reading.
62
+ */
63
+ empty?: ChartEmptyProps | false;
50
64
  }
51
65
  /**
52
66
  * The figure a chart is rendered inside: its name, its plot, and its data as a
@@ -55,7 +69,13 @@ interface ChartFigureProps {
55
69
  * Every chart root in the package wraps itself in this, so "the chart is
56
70
  * announced and its numbers are reachable" is a property of the system rather
57
71
  * than something each call site has to remember to add.
72
+ *
73
+ * The empty state is here for the same reason. Seven of the twenty charts had
74
+ * no `empty` at all — zero rows drew a named figure over a blank box, and
75
+ * `ChartDataTable` renders nothing below one row, so the picture and its text
76
+ * equivalent went silent together and the reader could not tell "no data" from
77
+ * "failed to load". A chart that forgets it now has to forget it here, once.
58
78
  */
59
- declare function ChartFigure({ title, showTitle, description, className, children, table, }: ChartFigureProps): react.JSX.Element;
79
+ declare function ChartFigure({ title, showTitle, description, className, children, table, isEmpty, empty, }: ChartFigureProps): react.JSX.Element;
60
80
 
61
81
  export { type ChartColumn, ChartDataTable, type ChartDataTableProps, ChartFigure, type ChartFigureProps };
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useId } from "react";
3
3
  import { cn } from "../../lib/cn.js";
4
+ import { ChartEmpty } from "./empty.js";
4
5
  function ChartDataTable({ caption, rows, columns, rowKey }) {
5
6
  if (rows.length === 0 || columns.length === 0) return null;
6
7
  return /* @__PURE__ */ jsxs("table", { className: "sr-only", children: [
@@ -27,7 +28,9 @@ function ChartFigure({
27
28
  description,
28
29
  className,
29
30
  children,
30
- table
31
+ table,
32
+ isEmpty = false,
33
+ empty
31
34
  }) {
32
35
  const captionId = `chart-title-${useId().replace(/:/g, "")}`;
33
36
  return (
@@ -47,7 +50,7 @@ function ChartFigure({
47
50
  ]
48
51
  }
49
52
  ),
50
- children,
53
+ isEmpty && empty !== false ? /* @__PURE__ */ jsx(ChartEmpty, { ...empty || {} }) : children,
51
54
  table && /* @__PURE__ */ jsx(ChartDataTable, { caption: title, ...table })
52
55
  ] })
53
56
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/charts/lib/figure.tsx"],"sourcesContent":["import { useId, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ChartColumn {\n /** The row field this column reads. */\n key: string\n /** What the column is called in the table header. */\n label: ReactNode\n}\n\nexport interface ChartDataTableProps {\n caption: string\n rows: Record<string, unknown>[]\n columns: ChartColumn[]\n /** The field naming each row — the category, the date, the bucket. */\n rowKey?: string\n}\n\n/**\n * The chart's data, as a table, for a reader who cannot see the chart.\n *\n * An SVG plot is a picture: `accessibilityLayer` gives Recharts' own keyboard\n * cursor and announces one point at a time, which is navigation rather than\n * access to the figures. This is the table view — visually hidden, in the\n * accessibility tree, and generated from the same rows the marks are drawn\n * from, so the two cannot disagree.\n *\n * `sr-only` rather than `hidden`: `display: none` removes it from the\n * accessibility tree as well as from the page, which would defeat the purpose.\n */\nexport function ChartDataTable({ caption, rows, columns, rowKey }: ChartDataTableProps) {\n if (rows.length === 0 || columns.length === 0) return null\n\n return (\n <table className=\"sr-only\">\n <caption>{caption}</caption>\n <thead>\n <tr>\n {rowKey && <th scope=\"col\">{rowKey}</th>}\n {columns.map((column) => (\n <th key={column.key} scope=\"col\">\n {column.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, index) => (\n <tr key={index}>\n {rowKey && <th scope=\"row\">{cellText(row[rowKey]) || String(index + 1)}</th>}\n {columns.map((column) => (\n <td key={column.key}>{cellText(row[column.key])}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n )\n}\n\n/** A cell value as text. Anything that is not a primitive reads as empty. */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'number') return value.toLocaleString()\n if (typeof value === 'string' || typeof value === 'boolean') return String(value)\n return ''\n}\n\nexport interface ChartFigureProps {\n /**\n * What the chart shows, in a sentence a reader could act on — \"Visitors per\n * month, 2026\" rather than \"Chart\".\n *\n * Required, and required for the same reason `Table.caption` and\n * `Progress.label` are: a plot with no name announces nothing, and it is not\n * a thing a call site can be trusted to remember. Hidden by default, because\n * most call sites already print a heading above the chart; `showTitle` puts\n * it back when they do not.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n className?: string\n children: ReactNode\n /** The table view. Omit and it is generated; `false` opts out. */\n table?: Omit<ChartDataTableProps, 'caption'> | false\n}\n\n/**\n * The figure a chart is rendered inside: its name, its plot, and its data as a\n * table.\n *\n * Every chart root in the package wraps itself in this, so \"the chart is\n * announced and its numbers are reachable\" is a property of the system rather\n * than something each call site has to remember to add.\n */\nexport function ChartFigure({\n title,\n showTitle = false,\n description,\n className,\n children,\n table,\n}: ChartFigureProps) {\n const captionId = `chart-title-${useId().replace(/:/g, '')}`\n\n return (\n // Named explicitly rather than relying on the caption. A `<figcaption>` IS\n // the figure's accessible name per the spec, but support for deriving it is\n // uneven across assistive technology — and a chart whose name resolves in\n // only some screen readers is a chart with no name.\n <figure aria-labelledby={captionId} className={cn('flex min-h-0 w-full flex-col', className)}>\n <figcaption\n id={captionId}\n className={cn(!showTitle && 'sr-only', showTitle && 'mb-3 flex flex-col gap-1')}\n >\n <span className={cn(showTitle && 'font-heading text-[length:var(--fs-item)] text-(--ink)')}>{title}</span>\n {description && (\n <span className={cn(showTitle && 'mono-meta text-(--ink-3-aa)')}>{description}</span>\n )}\n </figcaption>\n {children}\n {table && <ChartDataTable caption={title} {...table} />}\n </figure>\n )\n}\n"],"mappings":"AAmCM,cAEE,YAFF;AAnCN,SAAS,aAA6B;AACtC,SAAS,UAAU;AA6BZ,SAAS,eAAe,EAAE,SAAS,MAAM,SAAS,OAAO,GAAwB;AACtF,MAAI,KAAK,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEtD,SACE,qBAAC,WAAM,WAAU,WACf;AAAA,wBAAC,aAAS,mBAAQ;AAAA,IAClB,oBAAC,WACC,+BAAC,QACE;AAAA,gBAAU,oBAAC,QAAG,OAAM,OAAO,kBAAO;AAAA,MAClC,QAAQ,IAAI,CAAC,WACZ,oBAAC,QAAoB,OAAM,OACxB,iBAAO,SADD,OAAO,GAEhB,CACD;AAAA,OACH,GACF;AAAA,IACA,oBAAC,WACE,eAAK,IAAI,CAAC,KAAK,UACd,qBAAC,QACE;AAAA,gBAAU,oBAAC,QAAG,OAAM,OAAO,mBAAS,IAAI,MAAM,CAAC,KAAK,OAAO,QAAQ,CAAC,GAAE;AAAA,MACtE,QAAQ,IAAI,CAAC,WACZ,oBAAC,QAAqB,mBAAS,IAAI,OAAO,GAAG,CAAC,KAArC,OAAO,GAAgC,CACjD;AAAA,SAJM,KAKT,CACD,GACH;AAAA,KACF;AAEJ;AAGA,SAAS,SAAS,OAAwB;AACxC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,eAAe;AAC3D,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAW,QAAO,OAAO,KAAK;AAChF,SAAO;AACT;AAgCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,YAAY,eAAe,MAAM,EAAE,QAAQ,MAAM,EAAE,CAAC;AAE1D;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE,qBAAC,YAAO,mBAAiB,WAAW,WAAW,GAAG,gCAAgC,SAAS,GACzF;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAW,GAAG,CAAC,aAAa,WAAW,aAAa,0BAA0B;AAAA,UAE9E;AAAA,gCAAC,UAAK,WAAW,GAAG,aAAa,wDAAwD,GAAI,iBAAM;AAAA,YAClG,eACC,oBAAC,UAAK,WAAW,GAAG,aAAa,6BAA6B,GAAI,uBAAY;AAAA;AAAA;AAAA,MAElF;AAAA,MACC;AAAA,MACA,SAAS,oBAAC,kBAAe,SAAS,OAAQ,GAAG,OAAO;AAAA,OACvD;AAAA;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/charts/lib/figure.tsx"],"sourcesContent":["import { useId, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { ChartEmpty, type ChartEmptyProps } from './empty'\n\nexport interface ChartColumn {\n /** The row field this column reads. */\n key: string\n /** What the column is called in the table header. */\n label: ReactNode\n}\n\nexport interface ChartDataTableProps {\n caption: string\n rows: Record<string, unknown>[]\n columns: ChartColumn[]\n /** The field naming each row — the category, the date, the bucket. */\n rowKey?: string\n}\n\n/**\n * The chart's data, as a table, for a reader who cannot see the chart.\n *\n * An SVG plot is a picture: `accessibilityLayer` gives Recharts' own keyboard\n * cursor and announces one point at a time, which is navigation rather than\n * access to the figures. This is the table view — visually hidden, in the\n * accessibility tree, and generated from the same rows the marks are drawn\n * from, so the two cannot disagree.\n *\n * `sr-only` rather than `hidden`: `display: none` removes it from the\n * accessibility tree as well as from the page, which would defeat the purpose.\n */\nexport function ChartDataTable({ caption, rows, columns, rowKey }: ChartDataTableProps) {\n if (rows.length === 0 || columns.length === 0) return null\n\n return (\n <table className=\"sr-only\">\n <caption>{caption}</caption>\n <thead>\n <tr>\n {rowKey && <th scope=\"col\">{rowKey}</th>}\n {columns.map((column) => (\n <th key={column.key} scope=\"col\">\n {column.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, index) => (\n <tr key={index}>\n {rowKey && <th scope=\"row\">{cellText(row[rowKey]) || String(index + 1)}</th>}\n {columns.map((column) => (\n <td key={column.key}>{cellText(row[column.key])}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n )\n}\n\n/** A cell value as text. Anything that is not a primitive reads as empty. */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'number') return value.toLocaleString()\n if (typeof value === 'string' || typeof value === 'boolean') return String(value)\n return ''\n}\n\nexport interface ChartFigureProps {\n /**\n * What the chart shows, in a sentence a reader could act on — \"Visitors per\n * month, 2026\" rather than \"Chart\".\n *\n * Required, and required for the same reason `Table.caption` and\n * `Progress.label` are: a plot with no name announces nothing, and it is not\n * a thing a call site can be trusted to remember. Hidden by default, because\n * most call sites already print a heading above the chart; `showTitle` puts\n * it back when they do not.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n className?: string\n children: ReactNode\n /** The table view. Omit and it is generated; `false` opts out. */\n table?: Omit<ChartDataTableProps, 'caption'> | false\n /**\n * Whether there is nothing to draw. The plot is replaced by `empty` when it\n * is true.\n *\n * Named as a question rather than derived from `table.rows`, because a chart\n * mid-load has no rows either and wants its skeleton, not a message.\n */\n isEmpty?: boolean\n /**\n * What stands in for the plot when there is nothing to draw. `false` keeps\n * the plot, for a chart whose emptiness is itself the reading.\n */\n empty?: ChartEmptyProps | false\n}\n\n/**\n * The figure a chart is rendered inside: its name, its plot, and its data as a\n * table.\n *\n * Every chart root in the package wraps itself in this, so \"the chart is\n * announced and its numbers are reachable\" is a property of the system rather\n * than something each call site has to remember to add.\n *\n * The empty state is here for the same reason. Seven of the twenty charts had\n * no `empty` at all — zero rows drew a named figure over a blank box, and\n * `ChartDataTable` renders nothing below one row, so the picture and its text\n * equivalent went silent together and the reader could not tell \"no data\" from\n * \"failed to load\". A chart that forgets it now has to forget it here, once.\n */\nexport function ChartFigure({\n title,\n showTitle = false,\n description,\n className,\n children,\n table,\n isEmpty = false,\n empty,\n}: ChartFigureProps) {\n const captionId = `chart-title-${useId().replace(/:/g, '')}`\n\n return (\n // Named explicitly rather than relying on the caption. A `<figcaption>` IS\n // the figure's accessible name per the spec, but support for deriving it is\n // uneven across assistive technology — and a chart whose name resolves in\n // only some screen readers is a chart with no name.\n <figure aria-labelledby={captionId} className={cn('flex min-h-0 w-full flex-col', className)}>\n <figcaption\n id={captionId}\n className={cn(!showTitle && 'sr-only', showTitle && 'mb-3 flex flex-col gap-1')}\n >\n <span className={cn(showTitle && 'font-heading text-[length:var(--fs-item)] text-(--ink)')}>{title}</span>\n {description && (\n <span className={cn(showTitle && 'mono-meta text-(--ink-3-aa)')}>{description}</span>\n )}\n </figcaption>\n {isEmpty && empty !== false ? <ChartEmpty {...(empty || {})} /> : children}\n {table && <ChartDataTable caption={title} {...table} />}\n </figure>\n )\n}\n"],"mappings":"AAoCM,cAEE,YAFF;AApCN,SAAS,aAA6B;AACtC,SAAS,UAAU;AACnB,SAAS,kBAAwC;AA6B1C,SAAS,eAAe,EAAE,SAAS,MAAM,SAAS,OAAO,GAAwB;AACtF,MAAI,KAAK,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEtD,SACE,qBAAC,WAAM,WAAU,WACf;AAAA,wBAAC,aAAS,mBAAQ;AAAA,IAClB,oBAAC,WACC,+BAAC,QACE;AAAA,gBAAU,oBAAC,QAAG,OAAM,OAAO,kBAAO;AAAA,MAClC,QAAQ,IAAI,CAAC,WACZ,oBAAC,QAAoB,OAAM,OACxB,iBAAO,SADD,OAAO,GAEhB,CACD;AAAA,OACH,GACF;AAAA,IACA,oBAAC,WACE,eAAK,IAAI,CAAC,KAAK,UACd,qBAAC,QACE;AAAA,gBAAU,oBAAC,QAAG,OAAM,OAAO,mBAAS,IAAI,MAAM,CAAC,KAAK,OAAO,QAAQ,CAAC,GAAE;AAAA,MACtE,QAAQ,IAAI,CAAC,WACZ,oBAAC,QAAqB,mBAAS,IAAI,OAAO,GAAG,CAAC,KAArC,OAAO,GAAgC,CACjD;AAAA,SAJM,KAKT,CACD,GACH;AAAA,KACF;AAEJ;AAGA,SAAS,SAAS,OAAwB;AACxC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,eAAe;AAC3D,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAW,QAAO,OAAO,KAAK;AAChF,SAAO;AACT;AAmDO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAqB;AACnB,QAAM,YAAY,eAAe,MAAM,EAAE,QAAQ,MAAM,EAAE,CAAC;AAE1D;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE,qBAAC,YAAO,mBAAiB,WAAW,WAAW,GAAG,gCAAgC,SAAS,GACzF;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAW,GAAG,CAAC,aAAa,WAAW,aAAa,0BAA0B;AAAA,UAE9E;AAAA,gCAAC,UAAK,WAAW,GAAG,aAAa,wDAAwD,GAAI,iBAAM;AAAA,YAClG,eACC,oBAAC,UAAK,WAAW,GAAG,aAAa,6BAA6B,GAAI,uBAAY;AAAA;AAAA;AAAA,MAElF;AAAA,MACC,WAAW,UAAU,QAAQ,oBAAC,cAAY,GAAI,SAAS,CAAC,GAAI,IAAK;AAAA,MACjE,SAAS,oBAAC,kBAAe,SAAS,OAAQ,GAAG,OAAO;AAAA,OACvD;AAAA;AAEJ;","names":[]}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Where a value sits on a linear scale, and what to do when the scale has no
3
+ * width.
4
+ *
5
+ * The three engine-less forms — `Sparkline`, `Heatmap`, `BulletChart` — each
6
+ * normalise a value into a 0-to-1 position, and each had answered the
7
+ * degenerate case differently: the heatmap put a constant grid in the middle of
8
+ * its ramp, the bullet chart put a constant measure at the start of its track,
9
+ * and the sparkline divided by a substituted `1` and so drew every flat run
10
+ * along the floor. A flat run along the floor is the worst of the three,
11
+ * because in a column of sparklines "unchanged" and "pinned at its worst" is
12
+ * the one distinction the reader is scanning for.
13
+ *
14
+ * The middle is the honest answer: a scale with no width cannot rank anything,
15
+ * so no position on it is more true than another, and the centre is the only
16
+ * one that does not read as a verdict.
17
+ */
18
+ /** Where `value` sits between `min` and `max`, 0 to 1. Not held to the ends. */
19
+ declare function fraction(value: number, min: number, max: number): number;
20
+ /** `fraction`, held inside the track. */
21
+ declare function clampedFraction(value: number, min: number, max: number): number;
22
+
23
+ export { clampedFraction, fraction };
@@ -0,0 +1,13 @@
1
+ function fraction(value, min, max) {
2
+ const span = max - min;
3
+ if (span === 0) return 0.5;
4
+ return (value - min) / span;
5
+ }
6
+ function clampedFraction(value, min, max) {
7
+ return Math.min(1, Math.max(0, fraction(value, min, max)));
8
+ }
9
+ export {
10
+ clampedFraction,
11
+ fraction
12
+ };
13
+ //# sourceMappingURL=scale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/charts/lib/scale.ts"],"sourcesContent":["/**\n * Where a value sits on a linear scale, and what to do when the scale has no\n * width.\n *\n * The three engine-less forms — `Sparkline`, `Heatmap`, `BulletChart` — each\n * normalise a value into a 0-to-1 position, and each had answered the\n * degenerate case differently: the heatmap put a constant grid in the middle of\n * its ramp, the bullet chart put a constant measure at the start of its track,\n * and the sparkline divided by a substituted `1` and so drew every flat run\n * along the floor. A flat run along the floor is the worst of the three,\n * because in a column of sparklines \"unchanged\" and \"pinned at its worst\" is\n * the one distinction the reader is scanning for.\n *\n * The middle is the honest answer: a scale with no width cannot rank anything,\n * so no position on it is more true than another, and the centre is the only\n * one that does not read as a verdict.\n */\n\n/** Where `value` sits between `min` and `max`, 0 to 1. Not held to the ends. */\nexport function fraction(value: number, min: number, max: number): number {\n const span = max - min\n if (span === 0) return 0.5\n return (value - min) / span\n}\n\n/** `fraction`, held inside the track. */\nexport function clampedFraction(value: number, min: number, max: number): number {\n return Math.min(1, Math.max(0, fraction(value, min, max)))\n}\n"],"mappings":"AAmBO,SAAS,SAAS,OAAe,KAAa,KAAqB;AACxE,QAAM,OAAO,MAAM;AACnB,MAAI,SAAS,EAAG,QAAO;AACvB,UAAQ,QAAQ,OAAO;AACzB;AAGO,SAAS,gBAAgB,OAAe,KAAa,KAAqB;AAC/E,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC;AAC3D;","names":[]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A chart's selected series, sector, bar or node — controlled or not.
3
+ *
4
+ * Nine charts each grew a `defaultSelected*` prop and a `useState` seeded from
5
+ * it, and none of them grew the other half. A default seeds the state ONCE:
6
+ * pass a new one and nothing happens, because the state was already
7
+ * initialised. So a call site that wanted a chart's selection to follow a
8
+ * filter, a route or a sibling chart had no way to say so — the prop that looks
9
+ * like the way to do it silently is not.
10
+ *
11
+ * The pair is the React convention and it is the whole fix: pass `selected` to
12
+ * drive it from outside, or leave it undefined and the chart keeps its own.
13
+ *
14
+ * @param selected The controlled value. `undefined` leaves the chart in charge.
15
+ * @param fallback What an uncontrolled chart starts on.
16
+ * @param onChange Fires on every change, controlled or not.
17
+ */
18
+ declare function useChartSelection(selected: string | null | undefined, fallback: string | null, onChange: ((selected: string | null) => void) | undefined): [string | null, (next: string | null) => void];
19
+
20
+ export { useChartSelection };
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { useCallback, useState } from "react";
3
+ function useChartSelection(selected, fallback, onChange) {
4
+ const [own, setOwn] = useState(fallback);
5
+ const isControlled = selected !== void 0;
6
+ const select = useCallback(
7
+ (next) => {
8
+ setOwn(next);
9
+ onChange?.(next);
10
+ },
11
+ [onChange]
12
+ );
13
+ return [isControlled ? selected : own, select];
14
+ }
15
+ export {
16
+ useChartSelection
17
+ };
18
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/charts/lib/selection.ts"],"sourcesContent":["'use client'\n\nimport { useCallback, useState } from 'react'\n\n/**\n * A chart's selected series, sector, bar or node — controlled or not.\n *\n * Nine charts each grew a `defaultSelected*` prop and a `useState` seeded from\n * it, and none of them grew the other half. A default seeds the state ONCE:\n * pass a new one and nothing happens, because the state was already\n * initialised. So a call site that wanted a chart's selection to follow a\n * filter, a route or a sibling chart had no way to say so — the prop that looks\n * like the way to do it silently is not.\n *\n * The pair is the React convention and it is the whole fix: pass `selected` to\n * drive it from outside, or leave it undefined and the chart keeps its own.\n *\n * @param selected The controlled value. `undefined` leaves the chart in charge.\n * @param fallback What an uncontrolled chart starts on.\n * @param onChange Fires on every change, controlled or not.\n */\nexport function useChartSelection(\n selected: string | null | undefined,\n fallback: string | null,\n onChange: ((selected: string | null) => void) | undefined,\n): [string | null, (next: string | null) => void] {\n const [own, setOwn] = useState<string | null>(fallback)\n const isControlled = selected !== undefined\n\n const select = useCallback(\n (next: string | null) => {\n // The controlled chart still updates its own copy. It is never read while\n // `selected` is given, and it means an unmount of the controlling prop\n // does not throw the selection away.\n setOwn(next)\n onChange?.(next)\n },\n [onChange],\n )\n\n return [isControlled ? selected : own, select]\n}\n"],"mappings":";AAEA,SAAS,aAAa,gBAAgB;AAmB/B,SAAS,kBACd,UACA,UACA,UACgD;AAChD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAwB,QAAQ;AACtD,QAAM,eAAe,aAAa;AAElC,QAAM,SAAS;AAAA,IACb,CAAC,SAAwB;AAIvB,aAAO,IAAI;AACX,iBAAW,IAAI;AAAA,IACjB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO,CAAC,eAAe,WAAW,KAAK,MAAM;AAC/C;","names":[]}
@@ -5,6 +5,7 @@ import { ChartColumn } from './figure.js';
5
5
  import './brush.js';
6
6
  import 'recharts';
7
7
  import './chart.js';
8
+ import './empty.js';
8
9
 
9
10
  /** Which file a reader can take away. */
10
11
  type ChartExportFormat = 'png' | 'csv';
@@ -23,7 +23,14 @@ function AccordionItem({ title, children, className, ...rest }) {
23
23
  }
24
24
  )
25
25
  ] }) }),
26
- /* @__PURE__ */ jsx(AccordionPrimitive.Content, { className: "overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-accordion-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-accordion-down_var(--duration-base)_var(--ease)]", children: /* @__PURE__ */ jsx("div", { className: "pb-4 pe-8", children }) })
26
+ /* @__PURE__ */ jsx(
27
+ AccordionPrimitive.Content,
28
+ {
29
+ "data-m22-animated": true,
30
+ className: "overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-accordion-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-accordion-down_var(--duration-base)_var(--ease)]",
31
+ children: /* @__PURE__ */ jsx("div", { className: "pb-4 pe-8", children })
32
+ }
33
+ )
27
34
  ]
28
35
  }
29
36
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["'use client'\n\nimport * as AccordionPrimitive from '@radix-ui/react-accordion'\nimport { Plus } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/**\n * Radix root, re-exported. Pass `type=\"single\" collapsible` for an FAQ and\n * `type=\"multiple\"` for a settings stack; Radix's own discriminated union then\n * types `value` correctly for each.\n */\nexport const Accordion = AccordionPrimitive.Root\n\nexport interface AccordionItemProps\n extends Omit<ComponentProps<typeof AccordionPrimitive.Item>, 'children' | 'title'> {\n /** The row's summary — what the reader clicks. Named `title` rather than\n * inherited from the DOM attribute of the same name, which is a tooltip. */\n title: ReactNode\n children: ReactNode\n}\n\n/**\n * One disclosure row: a hairline-ruled trigger and its panel.\n *\n * The marker is a plus that rotates into a minus, not a chevron. A chevron says\n * \"there is more below\"; a plus says \"this opens\" — and in a stack of rows the\n * difference decides whether the reader expects navigation or expansion.\n *\n * The panel animates on Radix's own `--radix-accordion-content-height`, so it\n * opens to its real height without measuring anything at the call site.\n *\n * @example\n * <Accordion type=\"single\" collapsible>\n * <AccordionItem value=\"ship\" title=\"How do I ship it?\">Push to main.</AccordionItem>\n * </Accordion>\n */\nexport function AccordionItem({ title, children, className, ...rest }: AccordionItemProps) {\n return (\n <AccordionPrimitive.Item\n className={cn('border-b border-(--rule)', className)}\n {...rest}\n >\n <AccordionPrimitive.Header className=\"m-0\">\n <AccordionPrimitive.Trigger className=\"group flex w-full items-center justify-between gap-4 py-4 text-start font-sans text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:text-(--ink-2)\">\n {title}\n <Plus\n size={16}\n strokeWidth={1.5}\n aria-hidden\n className=\"shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-base) ease-(--ease-out-expo) group-data-[state=open]:rotate-45\"\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n <AccordionPrimitive.Content className=\"overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-accordion-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-accordion-down_var(--duration-base)_var(--ease)]\">\n <div className=\"pb-4 pe-8\">{children}</div>\n </AccordionPrimitive.Content>\n </AccordionPrimitive.Item>\n )\n}\n\nexport default Accordion\n"],"mappings":";AA4CQ,SAEE,KAFF;AA1CR,YAAY,wBAAwB;AACpC,SAAS,YAAY;AAErB,SAAS,UAAU;AAOZ,MAAM,YAAY,mBAAmB;AAyBrC,SAAS,cAAc,EAAE,OAAO,UAAU,WAAW,GAAG,KAAK,GAAuB;AACzF,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ;AAAA,4BAAC,mBAAmB,QAAnB,EAA0B,WAAU,OACnC,+BAAC,mBAAmB,SAAnB,EAA2B,WAAU,yKACnC;AAAA;AAAA,UACD;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,aAAa;AAAA,cACb,eAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA,WACF,GACF;AAAA,QACA,oBAAC,mBAAmB,SAAnB,EAA2B,WAAU,0NACpC,8BAAC,SAAI,WAAU,aAAa,UAAS,GACvC;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["'use client'\n\nimport * as AccordionPrimitive from '@radix-ui/react-accordion'\nimport { Plus } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/**\n * Radix root, re-exported. Pass `type=\"single\" collapsible` for an FAQ and\n * `type=\"multiple\"` for a settings stack; Radix's own discriminated union then\n * types `value` correctly for each.\n */\nexport const Accordion = AccordionPrimitive.Root\n\nexport interface AccordionItemProps\n extends Omit<ComponentProps<typeof AccordionPrimitive.Item>, 'children' | 'title'> {\n /** The row's summary — what the reader clicks. Named `title` rather than\n * inherited from the DOM attribute of the same name, which is a tooltip. */\n title: ReactNode\n children: ReactNode\n}\n\n/**\n * One disclosure row: a hairline-ruled trigger and its panel.\n *\n * The marker is a plus that rotates into a minus, not a chevron. A chevron says\n * \"there is more below\"; a plus says \"this opens\" — and in a stack of rows the\n * difference decides whether the reader expects navigation or expansion.\n *\n * The panel animates on Radix's own `--radix-accordion-content-height`, so it\n * opens to its real height without measuring anything at the call site.\n *\n * @example\n * <Accordion type=\"single\" collapsible>\n * <AccordionItem value=\"ship\" title=\"How do I ship it?\">Push to main.</AccordionItem>\n * </Accordion>\n */\nexport function AccordionItem({ title, children, className, ...rest }: AccordionItemProps) {\n return (\n <AccordionPrimitive.Item\n className={cn('border-b border-(--rule)', className)}\n {...rest}\n >\n <AccordionPrimitive.Header className=\"m-0\">\n <AccordionPrimitive.Trigger className=\"group flex w-full items-center justify-between gap-4 py-4 text-start font-sans text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:text-(--ink-2)\">\n {title}\n <Plus\n size={16}\n strokeWidth={1.5}\n aria-hidden\n className=\"shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-base) ease-(--ease-out-expo) group-data-[state=open]:rotate-45\"\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n <AccordionPrimitive.Content\n data-m22-animated\n className=\"overflow-hidden text-sm leading-relaxed text-(--ink-2) data-[state=closed]:animate-[m22-accordion-up_var(--duration-base)_var(--ease)] data-[state=open]:animate-[m22-accordion-down_var(--duration-base)_var(--ease)]\"\n >\n <div className=\"pb-4 pe-8\">{children}</div>\n </AccordionPrimitive.Content>\n </AccordionPrimitive.Item>\n )\n}\n\nexport default Accordion\n"],"mappings":";AA4CQ,SAEE,KAFF;AA1CR,YAAY,wBAAwB;AACpC,SAAS,YAAY;AAErB,SAAS,UAAU;AAOZ,MAAM,YAAY,mBAAmB;AAyBrC,SAAS,cAAc,EAAE,OAAO,UAAU,WAAW,GAAG,KAAK,GAAuB;AACzF,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ;AAAA,4BAAC,mBAAmB,QAAnB,EAA0B,WAAU,OACnC,+BAAC,mBAAmB,SAAnB,EAA2B,WAAU,yKACnC;AAAA;AAAA,UACD;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,aAAa;AAAA,cACb,eAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA,WACF,GACF;AAAA,QACA;AAAA,UAAC,mBAAmB;AAAA,UAAnB;AAAA,YACC,qBAAiB;AAAA,YACjB,WAAU;AAAA,YAEV,8BAAC,SAAI,WAAU,aAAa,UAAS;AAAA;AAAA,QACvC;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
@@ -42,6 +42,12 @@ interface AppShellProps extends HTMLAttributes<HTMLDivElement> {
42
42
  * cannot leave. The scrim is a `<button>` for the same reason — a `<div>` with
43
43
  * an `onClick` is not reachable by keyboard and not announced as anything.
44
44
  *
45
+ * Below `md` the closed drawer is `inert`. Sliding it off-screen is a visual
46
+ * state and nothing more: without that attribute every link in it stays
47
+ * focusable and stays in the accessibility tree, so Tab from the toggle walks
48
+ * into a menu nobody can see. Closing it also returns focus to the toggle,
49
+ * because the element focus was on is the element that just left.
50
+ *
45
51
  * @example
46
52
  * <AppShell brand={<Wordmark />} sidebar={<NavItem href="/">Home</NavItem>}>
47
53
  * <Card>…</Card>