@misoto22/design 0.7.0 → 0.9.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 (398) 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/ColorPicker.md +75 -0
  28. package/dist/agent/Combobox.md +29 -3
  29. package/dist/agent/Command.md +25 -2
  30. package/dist/agent/ComposedChart.md +26 -2
  31. package/dist/agent/ContextMenu.md +37 -4
  32. package/dist/agent/DataflowFigure.md +22 -1
  33. package/dist/agent/DatePicker.md +34 -6
  34. package/dist/agent/DescriptionList.md +68 -0
  35. package/dist/agent/Diagram.md +27 -3
  36. package/dist/agent/DiagramCanvas.md +24 -1
  37. package/dist/agent/DiagramExportMenu.md +29 -3
  38. package/dist/agent/DiagramInspector.md +23 -1
  39. package/dist/agent/DiagramLegend.md +22 -1
  40. package/dist/agent/DiagramMinimap.md +24 -4
  41. package/dist/agent/DiagramToolbar.md +23 -1
  42. package/dist/agent/Dialog.md +26 -3
  43. package/dist/agent/DropdownMenu.md +45 -6
  44. package/dist/agent/EmptyState.md +26 -1
  45. package/dist/agent/ErrorState.md +27 -2
  46. package/dist/agent/Facet.md +25 -1
  47. package/dist/agent/Field.md +45 -4
  48. package/dist/agent/FigureBand.md +24 -1
  49. package/dist/agent/FloatingIconButton.md +23 -1
  50. package/dist/agent/FunnelChart.md +23 -1
  51. package/dist/agent/Heading.md +64 -0
  52. package/dist/agent/Heatmap.md +25 -1
  53. package/dist/agent/Histogram.md +26 -2
  54. package/dist/agent/Input.md +22 -1
  55. package/dist/agent/Kbd.md +19 -1
  56. package/dist/agent/LifecycleFigure.md +22 -1
  57. package/dist/agent/LineChart.md +26 -2
  58. package/dist/agent/LinkArrow.md +20 -1
  59. package/dist/agent/Markdown.md +68 -0
  60. package/dist/agent/NativeSelect.md +23 -2
  61. package/dist/agent/NavItem.md +21 -1
  62. package/dist/agent/NumberField.md +72 -0
  63. package/dist/agent/Pagination.md +28 -2
  64. package/dist/agent/PieChart.md +26 -2
  65. package/dist/agent/Popover.md +23 -1
  66. package/dist/agent/Progress.md +27 -3
  67. package/dist/agent/RadarChart.md +26 -2
  68. package/dist/agent/RadialChart.md +26 -3
  69. package/dist/agent/RadioGroup.md +23 -2
  70. package/dist/agent/SankeyChart.md +25 -2
  71. package/dist/agent/ScatterChart.md +27 -2
  72. package/dist/agent/ScrollArea.md +24 -3
  73. package/dist/agent/SearchableMenu.md +23 -1
  74. package/dist/agent/Select.md +29 -3
  75. package/dist/agent/Separator.md +28 -4
  76. package/dist/agent/SequenceFigure.md +23 -1
  77. package/dist/agent/Sheet.md +25 -2
  78. package/dist/agent/Sidebar.md +193 -0
  79. package/dist/agent/Skeleton.md +24 -2
  80. package/dist/agent/Slider.md +31 -4
  81. package/dist/agent/Sparkline.md +23 -1
  82. package/dist/agent/Spinner.md +24 -2
  83. package/dist/agent/StatusDot.md +20 -1
  84. package/dist/agent/StatusPill.md +28 -2
  85. package/dist/agent/Steps.md +24 -1
  86. package/dist/agent/Switch.md +21 -1
  87. package/dist/agent/Table.md +28 -3
  88. package/dist/agent/Tabs.md +24 -2
  89. package/dist/agent/Tag.md +40 -9
  90. package/dist/agent/Text.md +65 -0
  91. package/dist/agent/Textarea.md +20 -1
  92. package/dist/agent/Timestamp.md +64 -0
  93. package/dist/agent/Toast.md +26 -2
  94. package/dist/agent/ToggleGroup.md +24 -2
  95. package/dist/agent/Toolbar.md +71 -0
  96. package/dist/agent/Tooltip.md +23 -1
  97. package/dist/agent/TreemapChart.md +24 -1
  98. package/dist/agent/WaterfallChart.md +24 -1
  99. package/dist/agent/WorkflowFigure.md +24 -2
  100. package/dist/agent/catalog.json +5915 -699
  101. package/dist/agent/index.md +20 -8
  102. package/dist/article.css +16 -4
  103. package/dist/charts/AreaChart/AreaChart.d.ts +9 -2
  104. package/dist/charts/AreaChart/AreaChart.js +12 -13
  105. package/dist/charts/AreaChart/AreaChart.js.map +1 -1
  106. package/dist/charts/BarChart/BarChart.d.ts +14 -3
  107. package/dist/charts/BarChart/BarChart.js +20 -12
  108. package/dist/charts/BarChart/BarChart.js.map +1 -1
  109. package/dist/charts/BarList/BarList.d.ts +9 -1
  110. package/dist/charts/BarList/BarList.js +9 -1
  111. package/dist/charts/BarList/BarList.js.map +1 -1
  112. package/dist/charts/BigNumber/BigNumber.d.ts +19 -3
  113. package/dist/charts/BigNumber/BigNumber.js +25 -5
  114. package/dist/charts/BigNumber/BigNumber.js.map +1 -1
  115. package/dist/charts/BoxPlot/BoxPlot.js +4 -2
  116. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -1
  117. package/dist/charts/BulletChart/BulletChart.js +44 -9
  118. package/dist/charts/BulletChart/BulletChart.js.map +1 -1
  119. package/dist/charts/ComposedChart/ComposedChart.d.ts +9 -2
  120. package/dist/charts/ComposedChart/ComposedChart.js +10 -10
  121. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -1
  122. package/dist/charts/FunnelChart/FunnelChart.d.ts +7 -2
  123. package/dist/charts/FunnelChart/FunnelChart.js +6 -16
  124. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -1
  125. package/dist/charts/Heatmap/Heatmap.d.ts +17 -3
  126. package/dist/charts/Heatmap/Heatmap.js +20 -11
  127. package/dist/charts/Heatmap/Heatmap.js.map +1 -1
  128. package/dist/charts/Histogram/Histogram.d.ts +24 -1
  129. package/dist/charts/Histogram/Histogram.js +43 -11
  130. package/dist/charts/Histogram/Histogram.js.map +1 -1
  131. package/dist/charts/LineChart/LineChart.d.ts +9 -2
  132. package/dist/charts/LineChart/LineChart.js +11 -12
  133. package/dist/charts/LineChart/LineChart.js.map +1 -1
  134. package/dist/charts/PieChart/PieChart.d.ts +15 -3
  135. package/dist/charts/PieChart/PieChart.js +14 -6
  136. package/dist/charts/PieChart/PieChart.js.map +1 -1
  137. package/dist/charts/RadarChart/RadarChart.d.ts +15 -3
  138. package/dist/charts/RadarChart/RadarChart.js +15 -11
  139. package/dist/charts/RadarChart/RadarChart.js.map +1 -1
  140. package/dist/charts/RadialChart/RadialChart.d.ts +22 -4
  141. package/dist/charts/RadialChart/RadialChart.js +28 -6
  142. package/dist/charts/RadialChart/RadialChart.js.map +1 -1
  143. package/dist/charts/SankeyChart/SankeyChart.d.ts +16 -4
  144. package/dist/charts/SankeyChart/SankeyChart.js +14 -6
  145. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -1
  146. package/dist/charts/ScatterChart/ScatterChart.d.ts +24 -3
  147. package/dist/charts/ScatterChart/ScatterChart.js +21 -15
  148. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -1
  149. package/dist/charts/Sparkline/Sparkline.js +5 -3
  150. package/dist/charts/Sparkline/Sparkline.js.map +1 -1
  151. package/dist/charts/TreemapChart/TreemapChart.d.ts +7 -2
  152. package/dist/charts/TreemapChart/TreemapChart.js +20 -11
  153. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -1
  154. package/dist/charts/WaterfallChart/WaterfallChart.js +4 -2
  155. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -1
  156. package/dist/charts/lib/export.d.ts +1 -0
  157. package/dist/charts/lib/figure.d.ts +21 -1
  158. package/dist/charts/lib/figure.js +5 -2
  159. package/dist/charts/lib/figure.js.map +1 -1
  160. package/dist/charts/lib/scale.d.ts +23 -0
  161. package/dist/charts/lib/scale.js +13 -0
  162. package/dist/charts/lib/scale.js.map +1 -0
  163. package/dist/charts/lib/selection.d.ts +20 -0
  164. package/dist/charts/lib/selection.js +18 -0
  165. package/dist/charts/lib/selection.js.map +1 -0
  166. package/dist/charts/lib/toolbar.d.ts +1 -0
  167. package/dist/components/Accordion/Accordion.js +8 -1
  168. package/dist/components/Accordion/Accordion.js.map +1 -1
  169. package/dist/components/AppShell/AppShell.d.ts +6 -0
  170. package/dist/components/AppShell/AppShell.js +34 -4
  171. package/dist/components/AppShell/AppShell.js.map +1 -1
  172. package/dist/components/Article/Article.d.ts +11 -4
  173. package/dist/components/Article/Article.js +13 -0
  174. package/dist/components/Article/Article.js.map +1 -1
  175. package/dist/components/AspectRatio/AspectRatio.d.ts +46 -0
  176. package/dist/components/AspectRatio/AspectRatio.js +33 -0
  177. package/dist/components/AspectRatio/AspectRatio.js.map +1 -0
  178. package/dist/components/Avatar/Avatar.d.ts +12 -3
  179. package/dist/components/Avatar/Avatar.js +6 -1
  180. package/dist/components/Avatar/Avatar.js.map +1 -1
  181. package/dist/components/Breadcrumb/Breadcrumb.d.ts +6 -0
  182. package/dist/components/Breadcrumb/Breadcrumb.js +15 -0
  183. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
  184. package/dist/components/Button/Button.js +14 -0
  185. package/dist/components/Button/Button.js.map +1 -1
  186. package/dist/components/Calendar/Calendar.d.ts +29 -3
  187. package/dist/components/Calendar/Calendar.js +60 -12
  188. package/dist/components/Calendar/Calendar.js.map +1 -1
  189. package/dist/components/Card/Card.d.ts +6 -0
  190. package/dist/components/Card/Card.js +1 -1
  191. package/dist/components/Card/Card.js.map +1 -1
  192. package/dist/components/Checkbox/Checkbox.d.ts +5 -2
  193. package/dist/components/Checkbox/Checkbox.js +12 -2
  194. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  195. package/dist/components/Code/Code.d.ts +30 -0
  196. package/dist/components/Code/Code.js +21 -0
  197. package/dist/components/Code/Code.js.map +1 -0
  198. package/dist/components/CodeBlock/CodeBlock.d.ts +141 -0
  199. package/dist/components/CodeBlock/CodeBlock.js +123 -0
  200. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  201. package/dist/components/Collapsible/Collapsible.js +10 -2
  202. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  203. package/dist/components/ColorPicker/ColorPicker.d.ts +67 -0
  204. package/dist/components/ColorPicker/ColorPicker.js +334 -0
  205. package/dist/components/ColorPicker/ColorPicker.js.map +1 -0
  206. package/dist/components/Combobox/Combobox.d.ts +15 -1
  207. package/dist/components/Combobox/Combobox.js +25 -5
  208. package/dist/components/Combobox/Combobox.js.map +1 -1
  209. package/dist/components/Command/Command.d.ts +4 -2
  210. package/dist/components/Command/Command.js +14 -2
  211. package/dist/components/Command/Command.js.map +1 -1
  212. package/dist/components/ContextMenu/ContextMenu.d.ts +30 -5
  213. package/dist/components/ContextMenu/ContextMenu.js +33 -3
  214. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  215. package/dist/components/DatePicker/DatePicker.d.ts +36 -4
  216. package/dist/components/DatePicker/DatePicker.js +55 -12
  217. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  218. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  219. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  220. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  221. package/dist/components/Diagram/Diagram.d.ts +8 -3
  222. package/dist/components/Diagram/Diagram.js +82 -10
  223. package/dist/components/Diagram/Diagram.js.map +1 -1
  224. package/dist/components/Dialog/Dialog.d.ts +18 -3
  225. package/dist/components/Dialog/Dialog.js +21 -3
  226. package/dist/components/Dialog/Dialog.js.map +1 -1
  227. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -11
  228. package/dist/components/DropdownMenu/DropdownMenu.js +33 -3
  229. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  230. package/dist/components/EmptyState/EmptyState.d.ts +25 -1
  231. package/dist/components/EmptyState/EmptyState.js +3 -1
  232. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  233. package/dist/components/ErrorState/ErrorState.d.ts +22 -1
  234. package/dist/components/ErrorState/ErrorState.js +12 -3
  235. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  236. package/dist/components/Field/Field.d.ts +70 -3
  237. package/dist/components/Field/Field.js +53 -12
  238. package/dist/components/Field/Field.js.map +1 -1
  239. package/dist/components/Field/field-control.d.ts +31 -0
  240. package/dist/components/Field/field-control.js +12 -0
  241. package/dist/components/Field/field-control.js.map +1 -0
  242. package/dist/components/FloatingIconButton/FloatingIconButton.js +2 -0
  243. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -1
  244. package/dist/components/Heading/Heading.d.ts +62 -0
  245. package/dist/components/Heading/Heading.js +44 -0
  246. package/dist/components/Heading/Heading.js.map +1 -0
  247. package/dist/components/Markdown/Markdown.d.ts +139 -0
  248. package/dist/components/Markdown/Markdown.js +135 -0
  249. package/dist/components/Markdown/Markdown.js.map +1 -0
  250. package/dist/components/Markdown/inline.d.ts +29 -0
  251. package/dist/components/Markdown/inline.js +74 -0
  252. package/dist/components/Markdown/inline.js.map +1 -0
  253. package/dist/components/Markdown/nodes.d.ts +65 -0
  254. package/dist/components/Markdown/nodes.js +1 -0
  255. package/dist/components/Markdown/nodes.js.map +1 -0
  256. package/dist/components/Markdown/parse.d.ts +24 -0
  257. package/dist/components/Markdown/parse.js +110 -0
  258. package/dist/components/Markdown/parse.js.map +1 -0
  259. package/dist/components/Markdown/slug.d.ts +15 -0
  260. package/dist/components/Markdown/slug.js +7 -0
  261. package/dist/components/Markdown/slug.js.map +1 -0
  262. package/dist/components/NativeSelect/NativeSelect.d.ts +6 -0
  263. package/dist/components/NativeSelect/NativeSelect.js +2 -3
  264. package/dist/components/NativeSelect/NativeSelect.js.map +1 -1
  265. package/dist/components/NavItem/NavItem.js +1 -1
  266. package/dist/components/NavItem/NavItem.js.map +1 -1
  267. package/dist/components/NumberField/NumberField.d.ts +69 -0
  268. package/dist/components/NumberField/NumberField.js +162 -0
  269. package/dist/components/NumberField/NumberField.js.map +1 -0
  270. package/dist/components/Pagination/Pagination.d.ts +24 -1
  271. package/dist/components/Pagination/Pagination.js +6 -3
  272. package/dist/components/Pagination/Pagination.js.map +1 -1
  273. package/dist/components/Popover/Popover.js +1 -1
  274. package/dist/components/Popover/Popover.js.map +1 -1
  275. package/dist/components/Progress/Progress.d.ts +17 -5
  276. package/dist/components/Progress/Progress.js +18 -6
  277. package/dist/components/Progress/Progress.js.map +1 -1
  278. package/dist/components/RadioGroup/RadioGroup.d.ts +6 -0
  279. package/dist/components/RadioGroup/RadioGroup.js +11 -1
  280. package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
  281. package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
  282. package/dist/components/ScrollArea/ScrollArea.js +2 -2
  283. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  284. package/dist/components/SearchableMenu/SearchableMenu.d.ts +11 -2
  285. package/dist/components/SearchableMenu/SearchableMenu.js +24 -2
  286. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -1
  287. package/dist/components/Select/Select.d.ts +18 -2
  288. package/dist/components/Select/Select.js +21 -4
  289. package/dist/components/Select/Select.js.map +1 -1
  290. package/dist/components/Separator/Separator.d.ts +21 -3
  291. package/dist/components/Separator/Separator.js +9 -1
  292. package/dist/components/Separator/Separator.js.map +1 -1
  293. package/dist/components/Sheet/Sheet.d.ts +3 -0
  294. package/dist/components/Sheet/Sheet.js +12 -3
  295. package/dist/components/Sheet/Sheet.js.map +1 -1
  296. package/dist/components/Sidebar/Sidebar.d.ts +349 -0
  297. package/dist/components/Sidebar/Sidebar.js +455 -0
  298. package/dist/components/Sidebar/Sidebar.js.map +1 -0
  299. package/dist/components/Skeleton/Skeleton.d.ts +8 -1
  300. package/dist/components/Skeleton/Skeleton.js +1 -1
  301. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  302. package/dist/components/Slider/Slider.d.ts +42 -3
  303. package/dist/components/Slider/Slider.js +121 -30
  304. package/dist/components/Slider/Slider.js.map +1 -1
  305. package/dist/components/Spinner/Spinner.d.ts +8 -0
  306. package/dist/components/Spinner/Spinner.js +3 -2
  307. package/dist/components/Spinner/Spinner.js.map +1 -1
  308. package/dist/components/StatusPill/StatusPill.d.ts +5 -0
  309. package/dist/components/StatusPill/StatusPill.js +6 -0
  310. package/dist/components/StatusPill/StatusPill.js.map +1 -1
  311. package/dist/components/Table/Table.d.ts +20 -0
  312. package/dist/components/Table/Table.js +3 -1
  313. package/dist/components/Table/Table.js.map +1 -1
  314. package/dist/components/Tabs/Tabs.js +7 -1
  315. package/dist/components/Tabs/Tabs.js.map +1 -1
  316. package/dist/components/Tag/Tag.d.ts +68 -9
  317. package/dist/components/Tag/Tag.js +50 -10
  318. package/dist/components/Tag/Tag.js.map +1 -1
  319. package/dist/components/Text/Text.d.ts +62 -0
  320. package/dist/components/Text/Text.js +30 -0
  321. package/dist/components/Text/Text.js.map +1 -0
  322. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  323. package/dist/components/Timestamp/Timestamp.js +56 -0
  324. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  325. package/dist/components/Toast/Toast.d.ts +9 -1
  326. package/dist/components/Toast/Toast.js +27 -4
  327. package/dist/components/Toast/Toast.js.map +1 -1
  328. package/dist/components/ToggleGroup/ToggleGroup.d.ts +6 -1
  329. package/dist/components/ToggleGroup/ToggleGroup.js +11 -1
  330. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  331. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  332. package/dist/components/Toolbar/Toolbar.js +43 -0
  333. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  334. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +6 -0
  335. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +48 -8
  336. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -1
  337. package/dist/diagrams/DataflowFigure/DataflowFigure.js +21 -2
  338. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -1
  339. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +14 -0
  340. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +14 -7
  341. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -1
  342. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +38 -4
  343. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +14 -16
  344. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -1
  345. package/dist/diagrams/DiagramInspector/DiagramInspector.js +2 -2
  346. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -1
  347. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +23 -7
  348. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +45 -18
  349. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -1
  350. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +44 -12
  351. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -1
  352. package/dist/diagrams/SequenceFigure/SequenceFigure.js +35 -5
  353. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -1
  354. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +3 -1
  355. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +76 -28
  356. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -1
  357. package/dist/diagrams/index.d.ts +1 -0
  358. package/dist/diagrams/index.js +10 -0
  359. package/dist/diagrams/index.js.map +1 -1
  360. package/dist/diagrams/lib/dev.d.ts +61 -0
  361. package/dist/diagrams/lib/dev.js +96 -0
  362. package/dist/diagrams/lib/dev.js.map +1 -0
  363. package/dist/diagrams/lib/frame.d.ts +48 -7
  364. package/dist/diagrams/lib/frame.js +17 -2
  365. package/dist/diagrams/lib/frame.js.map +1 -1
  366. package/dist/diagrams/lib/route.d.ts +22 -1
  367. package/dist/diagrams/lib/route.js +24 -1
  368. package/dist/diagrams/lib/route.js.map +1 -1
  369. package/dist/diagrams/lib/wires.js +9 -5
  370. package/dist/diagrams/lib/wires.js.map +1 -1
  371. package/dist/diagrams/spec.d.ts +64 -12
  372. package/dist/index.d.ts +19 -4
  373. package/dist/index.js +12 -0
  374. package/dist/index.js.map +1 -1
  375. package/dist/keyframes.css +57 -3
  376. package/dist/lib/color.d.ts +80 -0
  377. package/dist/lib/color.js +189 -0
  378. package/dist/lib/color.js.map +1 -0
  379. package/dist/lib/numeric.d.ts +35 -0
  380. package/dist/lib/numeric.js +29 -0
  381. package/dist/lib/numeric.js.map +1 -0
  382. package/dist/lib/overlay-container.d.ts +14 -4
  383. package/dist/lib/overlay-container.js.map +1 -1
  384. package/dist/lib/warn.d.ts +73 -0
  385. package/dist/lib/warn.js +38 -0
  386. package/dist/lib/warn.js.map +1 -0
  387. package/dist/semantic.css +17 -6
  388. package/dist/styles.css +1 -1
  389. package/dist/themes.css +8 -0
  390. package/dist/tokens.css +86 -5
  391. package/dist/tokens.d.ts +4 -1
  392. package/dist/tokens.js +56 -36
  393. package/dist/tokens.json +56 -36
  394. package/llms.txt +35 -0
  395. package/package.json +5 -2
  396. package/skills/misoto22-design/evals/evals.json +84 -0
  397. package/skills/misoto22-design/rules/a11y.md +55 -3
  398. package/skills/misoto22-design/rules/forms.md +8 -1
@@ -0,0 +1,162 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { MoveHorizontal } from "lucide-react";
4
+ import { useId, useRef, useState } from "react";
5
+ import { cn } from "../../lib/cn.js";
6
+ import { CONTROL_BASE, CONTROL_BORDER, isInvalid } from "../../lib/control.js";
7
+ import { clampToRange, parseNumber, snapToStep } from "../../lib/numeric.js";
8
+ const PX_PER_STEP = 4;
9
+ const COARSE = 10;
10
+ function NumberField({
11
+ value,
12
+ defaultValue,
13
+ onValueChange,
14
+ min = Number.NEGATIVE_INFINITY,
15
+ max = Number.POSITIVE_INFINITY,
16
+ step = 1,
17
+ unit,
18
+ scrub = true,
19
+ invalid,
20
+ disabled,
21
+ className,
22
+ ref,
23
+ onChange,
24
+ onFocus,
25
+ onBlur,
26
+ onKeyDown,
27
+ "aria-describedby": describedBy,
28
+ "aria-invalid": ariaInvalid,
29
+ ...rest
30
+ }) {
31
+ const bad = isInvalid(invalid, ariaInvalid);
32
+ const unitId = useId();
33
+ const [uncontrolled, setUncontrolled] = useState(
34
+ defaultValue ?? (Number.isFinite(min) ? min : 0)
35
+ );
36
+ const current = value ?? uncontrolled;
37
+ const [draft, setDraft] = useState(null);
38
+ const draftRef = useRef(null);
39
+ const setDraftText = (text) => {
40
+ draftRef.current = text;
41
+ setDraft(text);
42
+ };
43
+ const emit = (next) => {
44
+ if (value === void 0) setUncontrolled(next);
45
+ onValueChange?.(next);
46
+ };
47
+ const settle = (next) => emit(clampToRange(snapToStep(next, step, Number.isFinite(min) ? min : 0), min, max));
48
+ const entry = useRef(current);
49
+ const drag = useRef(null);
50
+ const startScrub = (event) => {
51
+ if (event.button !== 0 || disabled) return;
52
+ event.preventDefault();
53
+ event.currentTarget.setPointerCapture(event.pointerId);
54
+ drag.current = {
55
+ pointerId: event.pointerId,
56
+ startX: event.clientX,
57
+ startValue: current,
58
+ // A scrub is spatial, so it follows the reading direction the way the
59
+ // slider's own track does: in an RTL page, more is to the left.
60
+ sign: getComputedStyle(event.currentTarget).direction === "rtl" ? -1 : 1
61
+ };
62
+ };
63
+ const moveScrub = (event) => {
64
+ const state = drag.current;
65
+ if (!state || state.pointerId !== event.pointerId) return;
66
+ const notches = Math.round((event.clientX - state.startX) / PX_PER_STEP) * state.sign;
67
+ settle(state.startValue + notches * step * (event.shiftKey ? COARSE : 1));
68
+ };
69
+ const endScrub = (event) => {
70
+ if (drag.current?.pointerId !== event.pointerId) return;
71
+ event.currentTarget.releasePointerCapture(event.pointerId);
72
+ drag.current = null;
73
+ };
74
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative w-full", disabled && "opacity-(--disabled-opacity)", className), children: [
75
+ /* @__PURE__ */ jsx(
76
+ "input",
77
+ {
78
+ ref,
79
+ type: "number",
80
+ inputMode: "decimal",
81
+ disabled,
82
+ value: draft ?? String(current),
83
+ min: Number.isFinite(min) ? min : void 0,
84
+ max: Number.isFinite(max) ? max : void 0,
85
+ step,
86
+ "aria-describedby": [describedBy, unit ? unitId : null].filter(Boolean).join(" ") || void 0,
87
+ "aria-invalid": ariaInvalid ?? (invalid ? true : void 0),
88
+ onFocus: (event) => {
89
+ entry.current = current;
90
+ onFocus?.(event);
91
+ },
92
+ onChange: (event) => {
93
+ setDraftText(event.target.value);
94
+ const parsed = parseNumber(event.target.value);
95
+ if (parsed !== null) emit(parsed);
96
+ onChange?.(event);
97
+ },
98
+ onBlur: (event) => {
99
+ const text = draftRef.current;
100
+ const parsed = text === null ? null : parseNumber(text);
101
+ if (parsed !== null) settle(parsed);
102
+ setDraftText(null);
103
+ onBlur?.(event);
104
+ },
105
+ onKeyDown: (event) => {
106
+ if (event.key === "Enter") {
107
+ const text = draftRef.current;
108
+ const parsed = text === null ? null : parseNumber(text);
109
+ if (parsed !== null) settle(parsed);
110
+ setDraftText(null);
111
+ }
112
+ if (event.key === "Escape") {
113
+ emit(entry.current);
114
+ setDraftText(null);
115
+ event.currentTarget.blur();
116
+ }
117
+ onKeyDown?.(event);
118
+ },
119
+ className: cn(
120
+ CONTROL_BASE,
121
+ bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,
122
+ "tabular-nums",
123
+ // Full literal strings: Tailwind only emits what it can read here.
124
+ scrub && "ps-9",
125
+ unit && "pe-12",
126
+ // Three browsers, three spinners, none of them this system's.
127
+ "[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
128
+ ),
129
+ ...rest
130
+ }
131
+ ),
132
+ scrub && /* @__PURE__ */ jsx(
133
+ "span",
134
+ {
135
+ "aria-hidden": "true",
136
+ onPointerDown: startScrub,
137
+ onPointerMove: moveScrub,
138
+ onPointerUp: endScrub,
139
+ onPointerCancel: endScrub,
140
+ className: cn(
141
+ "absolute inset-y-0 start-0 flex touch-none select-none items-center ps-2.5 text-(--ink-3-aa) transition-colors duration-(--duration-fast)",
142
+ disabled ? "pointer-events-none" : "cursor-ew-resize hover:text-(--ink)"
143
+ ),
144
+ children: /* @__PURE__ */ jsx(MoveHorizontal, { className: "size-4" })
145
+ }
146
+ ),
147
+ unit && /* @__PURE__ */ jsx(
148
+ "span",
149
+ {
150
+ id: unitId,
151
+ className: "pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-sm text-(--ink-3-aa)",
152
+ children: unit
153
+ }
154
+ )
155
+ ] });
156
+ }
157
+ var NumberField_default = NumberField;
158
+ export {
159
+ NumberField,
160
+ NumberField_default as default
161
+ };
162
+ //# sourceMappingURL=NumberField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/NumberField/NumberField.tsx"],"sourcesContent":["'use client'\n\nimport { MoveHorizontal } from 'lucide-react'\nimport { useId, useRef, useState, type InputHTMLAttributes, type PointerEvent, type Ref } from 'react'\nimport { cn } from '../../lib/cn'\nimport { CONTROL_BASE, CONTROL_BORDER, isInvalid } from '../../lib/control'\nimport { clampToRange, parseNumber, snapToStep } from '../../lib/numeric'\n\n/**\n * Pixels of travel per step while scrubbing.\n *\n * Four is the number a pointer reads as \"one notch\". At one or two the value\n * runs away from the hand; past about eight the gesture stops feeling like it\n * is driving anything and a reader goes back to typing.\n */\nconst PX_PER_STEP = 4\n\n/** How much Shift multiplies a scrub by. Ten, as in every drawing tool. */\nconst COARSE = 10\n\nexport interface NumberFieldProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'min' | 'max' | 'step' | 'type'> {\n value?: number\n defaultValue?: number\n /** Fires with the parsed number. Raw `onChange` still fires with the event. */\n onValueChange?: (value: number) => void\n min?: number\n max?: number\n /** The increment for the arrows and for one notch of a scrub. */\n step?: number\n /**\n * A unit drawn inside the end of the field — `%`, `px`, `ms`.\n *\n * The field reserves a fixed slot for it, so keep it to about four\n * characters; a longer one runs under a long number. It is announced through\n * `aria-describedby` as well as drawn, because a unit nobody hears turns\n * \"300\" into a number with no dimension.\n */\n unit?: string\n /**\n * A grip at the start of the field that changes the value as it is dragged.\n *\n * On by default, and the reason to reach for this over `<Input type=\"number\">`:\n * a value that is being TUNED — a duration, a weight, an offset — is found by\n * sweeping through neighbouring values, not by typing candidates one at a\n * time. Shift multiplies the travel by ten.\n *\n * Pointer only, and `aria-hidden` for that reason: the same journey on a\n * keyboard is the arrow keys on the field itself, which the platform already\n * provides. Turn it off where the number is a quantity rather than a setting\n * — a line item's quantity is chosen, not swept to.\n */\n scrub?: boolean\n /** Paints the resting border with `--danger` and reflects `aria-invalid`. */\n invalid?: boolean\n ref?: Ref<HTMLInputElement>\n}\n\n/**\n * A number, typed or swept to.\n *\n * `Input` with `type=\"number\"` is a box that happens to reject letters. This is\n * the control for a number that has a RANGE and a sensible increment: the\n * arrows step it, the grip sweeps it, and `min`, `max` and `step` are honoured\n * on the way out rather than merely announced.\n *\n * Reach for `Slider` instead when the position along the range is the\n * information — a volume, a confidence, anything a reader judges by where the\n * thumb sits. Reach for this when the digits are. A slider that also has to be\n * exact is a `Slider` with `editable`, not one of these beside it.\n *\n * **Clamping happens when the field is left, not while it is being typed.** A\n * minimum of 10 would otherwise make 50 unreachable: the `5` is clamped up to\n * 10 before the `0` arrives. So `onValueChange` can report a number outside the\n * range mid-keystroke, and the value that settles is always inside it.\n *\n * The native spinner buttons are hidden — they are three different controls in\n * three browsers, and none of them is this system's. The grip replaces them for\n * a pointer; the arrow keys were always the keyboard's answer.\n *\n * @example\n * <Field label=\"Line height\"><NumberField defaultValue={1.5} min={1} max={3} step={0.1} /></Field>\n * @example\n * <Field label=\"Timeout\"><NumberField defaultValue={30} min={0} max={600} unit=\"s\" /></Field>\n */\nexport function NumberField({\n value,\n defaultValue,\n onValueChange,\n min = Number.NEGATIVE_INFINITY,\n max = Number.POSITIVE_INFINITY,\n step = 1,\n unit,\n scrub = true,\n invalid,\n disabled,\n className,\n ref,\n onChange,\n onFocus,\n onBlur,\n onKeyDown,\n 'aria-describedby': describedBy,\n 'aria-invalid': ariaInvalid,\n ...rest\n}: NumberFieldProps) {\n const bad = isInvalid(invalid, ariaInvalid)\n const unitId = useId()\n\n const [uncontrolled, setUncontrolled] = useState<number>(\n defaultValue ?? (Number.isFinite(min) ? min : 0),\n )\n const current = value ?? uncontrolled\n\n // Non-null only while the box is being typed into. The field shows the draft\n // verbatim then, so a half-typed \"1.\" or \"-\" survives to the next keystroke\n // instead of being rewritten into a number the moment it parses.\n //\n // Mirrored into a ref because Escape has to clear it and then blur the field\n // in the SAME event: `setDraft(null)` has not been applied by the time the\n // blur handler runs, so a handler reading the state variable would still see\n // the abandoned text and commit it.\n const [draft, setDraft] = useState<string | null>(null)\n const draftRef = useRef<string | null>(null)\n const setDraftText = (text: string | null) => {\n draftRef.current = text\n setDraft(text)\n }\n\n const emit = (next: number) => {\n if (value === undefined) setUncontrolled(next)\n onValueChange?.(next)\n }\n\n /** Reconciles a number with the range and the step, then commits it. */\n const settle = (next: number) => emit(clampToRange(snapToStep(next, step, Number.isFinite(min) ? min : 0), min, max))\n\n // What the value was when the box gained focus, so Escape has something to\n // go back to: the raw emits above have already moved it by then.\n const entry = useRef(current)\n\n const drag = useRef<{ pointerId: number; startX: number; startValue: number; sign: 1 | -1 } | null>(null)\n\n const startScrub = (event: PointerEvent<HTMLSpanElement>) => {\n if (event.button !== 0 || disabled) return\n // Otherwise the gesture selects the text of the field it is dragging over.\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startValue: current,\n // A scrub is spatial, so it follows the reading direction the way the\n // slider's own track does: in an RTL page, more is to the left.\n sign: getComputedStyle(event.currentTarget).direction === 'rtl' ? -1 : 1,\n }\n }\n\n const moveScrub = (event: PointerEvent<HTMLSpanElement>) => {\n const state = drag.current\n if (!state || state.pointerId !== event.pointerId) return\n const notches = Math.round((event.clientX - state.startX) / PX_PER_STEP) * state.sign\n settle(state.startValue + notches * step * (event.shiftKey ? COARSE : 1))\n }\n\n const endScrub = (event: PointerEvent<HTMLSpanElement>) => {\n if (drag.current?.pointerId !== event.pointerId) return\n event.currentTarget.releasePointerCapture(event.pointerId)\n drag.current = null\n }\n\n return (\n <div className={cn('relative w-full', disabled && 'opacity-(--disabled-opacity)', className)}>\n <input\n ref={ref}\n type=\"number\"\n inputMode=\"decimal\"\n disabled={disabled}\n value={draft ?? String(current)}\n min={Number.isFinite(min) ? min : undefined}\n max={Number.isFinite(max) ? max : undefined}\n step={step}\n aria-describedby={[describedBy, unit ? unitId : null].filter(Boolean).join(' ') || undefined}\n aria-invalid={ariaInvalid ?? (invalid ? true : undefined)}\n onFocus={(event) => {\n entry.current = current\n onFocus?.(event)\n }}\n onChange={(event) => {\n setDraftText(event.target.value)\n // The raw number, unclamped — see the note on the component.\n const parsed = parseNumber(event.target.value)\n if (parsed !== null) emit(parsed)\n onChange?.(event)\n }}\n onBlur={(event) => {\n const text = draftRef.current\n const parsed = text === null ? null : parseNumber(text)\n if (parsed !== null) settle(parsed)\n setDraftText(null)\n onBlur?.(event)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n const text = draftRef.current\n const parsed = text === null ? null : parseNumber(text)\n if (parsed !== null) settle(parsed)\n setDraftText(null)\n }\n if (event.key === 'Escape') {\n emit(entry.current)\n setDraftText(null)\n event.currentTarget.blur()\n }\n onKeyDown?.(event)\n }}\n className={cn(\n CONTROL_BASE,\n bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,\n 'tabular-nums',\n // Full literal strings: Tailwind only emits what it can read here.\n scrub && 'ps-9',\n unit && 'pe-12',\n // Three browsers, three spinners, none of them this system's.\n '[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n )}\n {...rest}\n />\n {scrub && (\n <span\n // Not a button: it commits nothing, has no keyboard journey of its\n // own, and announcing it would give a reader a control that does\n // nothing when they press it. The arrow keys on the field are the\n // keyboard's path to the same values.\n aria-hidden=\"true\"\n onPointerDown={startScrub}\n onPointerMove={moveScrub}\n onPointerUp={endScrub}\n onPointerCancel={endScrub}\n className={cn(\n 'absolute inset-y-0 start-0 flex touch-none select-none items-center ps-2.5 text-(--ink-3-aa) transition-colors duration-(--duration-fast)',\n disabled ? 'pointer-events-none' : 'cursor-ew-resize hover:text-(--ink)',\n )}\n >\n <MoveHorizontal className=\"size-4\" />\n </span>\n )}\n {unit && (\n <span\n id={unitId}\n className=\"pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-sm text-(--ink-3-aa)\"\n >\n {unit}\n </span>\n )}\n </div>\n )\n}\n\nexport default NumberField\n"],"mappings":"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","names":[]}
@@ -10,6 +10,24 @@ interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'>
10
10
  /** How many numbered pages sit either side of the current one. */
11
11
  siblings?: number;
12
12
  label?: string;
13
+ /**
14
+ * Names the two step controls.
15
+ *
16
+ * Each is a chevron and nothing else, so these strings are its entire
17
+ * accessible name — the same reason `AppShell` exposes `openLabel` and
18
+ * `closeLabel` rather than baking them in.
19
+ */
20
+ previousLabel?: string;
21
+ nextLabel?: string;
22
+ /**
23
+ * Names one numbered page.
24
+ *
25
+ * A function rather than a template, because the number does not sit in the
26
+ * same place in every language and neither does the noun around it —
27
+ * `"Page 3"`, `"第 3 页"`, `"Seite 3"`. The visible digit is the same either
28
+ * way; this is what a screen reader hears instead of a bare number.
29
+ */
30
+ pageLabel?: (page: number) => string;
13
31
  }
14
32
  declare const GAP: "\u2026";
15
33
  /**
@@ -36,9 +54,14 @@ declare function paginationRange(page: number, pageCount: number, siblings?: num
36
54
  *
37
55
  * Renders nothing at one page or fewer. A pager for a single page is furniture.
38
56
  *
57
+ * Every string a reader hears is a prop. The chevrons carry no text, so
58
+ * `previousLabel` and `nextLabel` are the whole of those two controls' names,
59
+ * and `pageLabel` is a function because "Page 3" is a phrase whose parts move
60
+ * around between languages.
61
+ *
39
62
  * @example
40
63
  * <Pagination page={page} pageCount={12} onPageChange={setPage} />
41
64
  */
42
- declare function Pagination({ page, pageCount, onPageChange, siblings, label, className, ...rest }: PaginationProps): react.JSX.Element | null;
65
+ declare function Pagination({ page, pageCount, onPageChange, siblings, label, previousLabel, nextLabel, pageLabel, className, ...rest }: PaginationProps): react.JSX.Element | null;
43
66
 
44
67
  export { Pagination, type PaginationProps, Pagination as default, paginationRange };
@@ -34,6 +34,9 @@ function Pagination({
34
34
  onPageChange,
35
35
  siblings = 1,
36
36
  label = "Pagination",
37
+ previousLabel = "Previous page",
38
+ nextLabel = "Next page",
39
+ pageLabel = (page2) => `Page ${page2}`,
37
40
  className,
38
41
  ...rest
39
42
  }) {
@@ -45,7 +48,7 @@ function Pagination({
45
48
  "button",
46
49
  {
47
50
  type: "button",
48
- "aria-label": "Previous page",
51
+ "aria-label": previousLabel,
49
52
  disabled: page <= 1,
50
53
  onClick: () => onPageChange(page - 1),
51
54
  className: cn(STEP, "border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)"),
@@ -74,7 +77,7 @@ function Pagination({
74
77
  {
75
78
  type: "button",
76
79
  "aria-current": entry === page ? "page" : void 0,
77
- "aria-label": `Page ${entry}`,
80
+ "aria-label": pageLabel(entry),
78
81
  "data-indicator-active": entry === page ? "true" : void 0,
79
82
  onClick: () => onPageChange(entry),
80
83
  className: cn(
@@ -91,7 +94,7 @@ function Pagination({
91
94
  "button",
92
95
  {
93
96
  type: "button",
94
- "aria-label": "Next page",
97
+ "aria-label": nextLabel,
95
98
  disabled: page >= pageCount,
96
99
  onClick: () => onPageChange(page + 1),
97
100
  className: cn(STEP, "border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)"),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["'use client'\n\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useSelectionIndicator } from '../../lib/useSelectionIndicator'\n\nexport interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** 1-based. */\n page: number\n /** Total pages. A value of 1 or less renders nothing. */\n pageCount: number\n onPageChange: (page: number) => void\n /** How many numbered pages sit either side of the current one. */\n siblings?: number\n label?: string\n}\n\nconst GAP = '…' as const\n\n/**\n * Builds the visible page list: always the first and last page, a window around\n * the current one, and an ellipsis wherever the sequence skips.\n *\n * Returned as numbers and a literal ellipsis rather than as pre-rendered nodes,\n * so the shape is testable without a DOM — the off-by-one at the window edges\n * is the whole difficulty of this component.\n */\nexport function paginationRange(page: number, pageCount: number, siblings = 1): (number | typeof GAP)[] {\n const first = 1\n const last = pageCount\n\n // Below the width the elided form would occupy — first, last, the window and\n // two ellipses — printing every page is both shorter and more useful. Without\n // this, a five-page list renders \"1 2 … 5\", which hides a page to save\n // nothing.\n const widest = 2 * siblings + 5\n if (pageCount <= widest) {\n return Array.from({ length: pageCount }, (_, index) => index + 1)\n }\n\n const start = Math.max(first, page - siblings)\n const end = Math.min(last, page + siblings)\n\n const pages: (number | typeof GAP)[] = []\n if (start > first) {\n pages.push(first)\n // A single skipped page is printed, not elided: \"1 … 3\" is longer than\n // \"1 2 3\" and tells the reader less.\n if (start > first + 2) pages.push(GAP)\n else if (start === first + 2) pages.push(first + 1)\n }\n for (let n = start; n <= end; n += 1) pages.push(n)\n if (end < last) {\n if (end < last - 2) pages.push(GAP)\n else if (end === last - 2) pages.push(last - 1)\n pages.push(last)\n }\n return pages\n}\n\nconst STEP =\n 'inline-flex size-(--control-h-sm) items-center justify-center rounded-(--radius-pill) border transition-colors duration-(--duration-fast) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none'\n\n/**\n * Numbered pagination.\n *\n * The current page is marked by one filled pill that TRAVELS between the\n * numbers rather than by a background switching off on one and on on another.\n * Two backgrounds cross-fading reads as two things changing; a shape moving\n * reads as the one thing that actually did.\n *\n * A `<nav>` wrapping a list, and the current page is a `<button aria-current>`\n * rather than a styled `<span>` — a reader jumping by landmark needs to find\n * the control, and a reader on the current page needs to be told they are\n * already there.\n *\n * Renders nothing at one page or fewer. A pager for a single page is furniture.\n *\n * @example\n * <Pagination page={page} pageCount={12} onPageChange={setPage} />\n */\nexport function Pagination({\n page,\n pageCount,\n onPageChange,\n siblings = 1,\n label = 'Pagination',\n className,\n ...rest\n}: PaginationProps) {\n // Called before the early return, because a hook cannot be conditional — and\n // keyed on the page so the pill re-measures when the elided window shifts and\n // the numbers under it change.\n const [listRef, indicator] = useSelectionIndicator<HTMLOListElement>(String(page))\n\n if (pageCount <= 1) return null\n const pages = paginationRange(page, pageCount, siblings)\n\n return (\n <nav aria-label={label} className={cn('flex items-center gap-1.5', className)} {...rest}>\n <button\n type=\"button\"\n aria-label=\"Previous page\"\n disabled={page <= 1}\n onClick={() => onPageChange(page - 1)}\n className={cn(STEP, 'border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)')}\n >\n <ChevronLeft size={16} strokeWidth={1.5} aria-hidden />\n </button>\n\n <ol ref={listRef} className=\"relative m-0 flex list-none items-center gap-1 p-0\">\n {indicator.ready && (\n <span\n aria-hidden\n data-m22-animated\n className=\"absolute rounded-(--radius-pill) bg-(--accent) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) motion-reduce:transition-none\"\n style={{\n transform: `translate(${indicator.offset}px, ${indicator.top}px)`,\n width: indicator.width,\n height: indicator.height,\n insetInlineStart: 0,\n top: 0,\n }}\n />\n )}\n {pages.map((entry, index) =>\n entry === GAP ? (\n <li key={`gap-${index}`} aria-hidden=\"true\" className=\"px-1 mono-meta text-(--ink-3-aa)\">\n {GAP}\n </li>\n ) : (\n <li key={entry} className=\"relative z-1\">\n <button\n type=\"button\"\n aria-current={entry === page ? 'page' : undefined}\n aria-label={`Page ${entry}`}\n data-indicator-active={entry === page ? 'true' : undefined}\n onClick={() => onPageChange(entry)}\n className={cn(\n STEP,\n 'mono-meta tabular-nums',\n entry === page\n ? 'border-transparent text-(--accent-foreground)'\n : 'border-transparent text-(--ink-2) hover:border-(--rule-2) hover:text-(--ink)',\n )}\n >\n {entry}\n </button>\n </li>\n ),\n )}\n </ol>\n\n <button\n type=\"button\"\n aria-label=\"Next page\"\n disabled={page >= pageCount}\n onClick={() => onPageChange(page + 1)}\n className={cn(STEP, 'border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)')}\n >\n <ChevronRight size={16} strokeWidth={1.5} aria-hidden />\n </button>\n </nav>\n )\n}\n\nexport default Pagination\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["'use client'\n\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useSelectionIndicator } from '../../lib/useSelectionIndicator'\n\nexport interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** 1-based. */\n page: number\n /** Total pages. A value of 1 or less renders nothing. */\n pageCount: number\n onPageChange: (page: number) => void\n /** How many numbered pages sit either side of the current one. */\n siblings?: number\n label?: string\n /**\n * Names the two step controls.\n *\n * Each is a chevron and nothing else, so these strings are its entire\n * accessible name — the same reason `AppShell` exposes `openLabel` and\n * `closeLabel` rather than baking them in.\n */\n previousLabel?: string\n nextLabel?: string\n /**\n * Names one numbered page.\n *\n * A function rather than a template, because the number does not sit in the\n * same place in every language and neither does the noun around it —\n * `\"Page 3\"`, `\"第 3 页\"`, `\"Seite 3\"`. The visible digit is the same either\n * way; this is what a screen reader hears instead of a bare number.\n */\n pageLabel?: (page: number) => string\n}\n\nconst GAP = '…' as const\n\n/**\n * Builds the visible page list: always the first and last page, a window around\n * the current one, and an ellipsis wherever the sequence skips.\n *\n * Returned as numbers and a literal ellipsis rather than as pre-rendered nodes,\n * so the shape is testable without a DOM — the off-by-one at the window edges\n * is the whole difficulty of this component.\n */\nexport function paginationRange(page: number, pageCount: number, siblings = 1): (number | typeof GAP)[] {\n const first = 1\n const last = pageCount\n\n // Below the width the elided form would occupy — first, last, the window and\n // two ellipses — printing every page is both shorter and more useful. Without\n // this, a five-page list renders \"1 2 … 5\", which hides a page to save\n // nothing.\n const widest = 2 * siblings + 5\n if (pageCount <= widest) {\n return Array.from({ length: pageCount }, (_, index) => index + 1)\n }\n\n const start = Math.max(first, page - siblings)\n const end = Math.min(last, page + siblings)\n\n const pages: (number | typeof GAP)[] = []\n if (start > first) {\n pages.push(first)\n // A single skipped page is printed, not elided: \"1 … 3\" is longer than\n // \"1 2 3\" and tells the reader less.\n if (start > first + 2) pages.push(GAP)\n else if (start === first + 2) pages.push(first + 1)\n }\n for (let n = start; n <= end; n += 1) pages.push(n)\n if (end < last) {\n if (end < last - 2) pages.push(GAP)\n else if (end === last - 2) pages.push(last - 1)\n pages.push(last)\n }\n return pages\n}\n\nconst STEP =\n 'inline-flex size-(--control-h-sm) items-center justify-center rounded-(--radius-pill) border transition-colors duration-(--duration-fast) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none'\n\n/**\n * Numbered pagination.\n *\n * The current page is marked by one filled pill that TRAVELS between the\n * numbers rather than by a background switching off on one and on on another.\n * Two backgrounds cross-fading reads as two things changing; a shape moving\n * reads as the one thing that actually did.\n *\n * A `<nav>` wrapping a list, and the current page is a `<button aria-current>`\n * rather than a styled `<span>` — a reader jumping by landmark needs to find\n * the control, and a reader on the current page needs to be told they are\n * already there.\n *\n * Renders nothing at one page or fewer. A pager for a single page is furniture.\n *\n * Every string a reader hears is a prop. The chevrons carry no text, so\n * `previousLabel` and `nextLabel` are the whole of those two controls' names,\n * and `pageLabel` is a function because \"Page 3\" is a phrase whose parts move\n * around between languages.\n *\n * @example\n * <Pagination page={page} pageCount={12} onPageChange={setPage} />\n */\nexport function Pagination({\n page,\n pageCount,\n onPageChange,\n siblings = 1,\n label = 'Pagination',\n previousLabel = 'Previous page',\n nextLabel = 'Next page',\n pageLabel = (page) => `Page ${page}`,\n className,\n ...rest\n}: PaginationProps) {\n // Called before the early return, because a hook cannot be conditional — and\n // keyed on the page so the pill re-measures when the elided window shifts and\n // the numbers under it change.\n const [listRef, indicator] = useSelectionIndicator<HTMLOListElement>(String(page))\n\n if (pageCount <= 1) return null\n const pages = paginationRange(page, pageCount, siblings)\n\n return (\n <nav aria-label={label} className={cn('flex items-center gap-1.5', className)} {...rest}>\n <button\n type=\"button\"\n aria-label={previousLabel}\n disabled={page <= 1}\n onClick={() => onPageChange(page - 1)}\n className={cn(STEP, 'border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)')}\n >\n <ChevronLeft size={16} strokeWidth={1.5} aria-hidden />\n </button>\n\n <ol ref={listRef} className=\"relative m-0 flex list-none items-center gap-1 p-0\">\n {indicator.ready && (\n <span\n aria-hidden\n data-m22-animated\n className=\"absolute rounded-(--radius-pill) bg-(--accent) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) motion-reduce:transition-none\"\n style={{\n transform: `translate(${indicator.offset}px, ${indicator.top}px)`,\n width: indicator.width,\n height: indicator.height,\n insetInlineStart: 0,\n top: 0,\n }}\n />\n )}\n {pages.map((entry, index) =>\n entry === GAP ? (\n <li key={`gap-${index}`} aria-hidden=\"true\" className=\"px-1 mono-meta text-(--ink-3-aa)\">\n {GAP}\n </li>\n ) : (\n <li key={entry} className=\"relative z-1\">\n <button\n type=\"button\"\n aria-current={entry === page ? 'page' : undefined}\n aria-label={pageLabel(entry)}\n data-indicator-active={entry === page ? 'true' : undefined}\n onClick={() => onPageChange(entry)}\n className={cn(\n STEP,\n 'mono-meta tabular-nums',\n entry === page\n ? 'border-transparent text-(--accent-foreground)'\n : 'border-transparent text-(--ink-2) hover:border-(--rule-2) hover:text-(--ink)',\n )}\n >\n {entry}\n </button>\n </li>\n ),\n )}\n </ol>\n\n <button\n type=\"button\"\n aria-label={nextLabel}\n disabled={page >= pageCount}\n onClick={() => onPageChange(page + 1)}\n className={cn(STEP, 'border-(--rule-2) text-(--ink-2) hover:border-(--ink) hover:text-(--ink)')}\n >\n <ChevronRight size={16} strokeWidth={1.5} aria-hidden />\n </button>\n </nav>\n )\n}\n\nexport default Pagination\n"],"mappings":"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","names":["page"]}
@@ -26,7 +26,7 @@ function PopoverContent({
26
26
  sideOffset,
27
27
  "data-m22-animated": true,
28
28
  className: cn(
29
- "z-(--z-dropdown) w-72 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-4 panel-blur",
29
+ "z-(--z-dropdown) w-72 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-4 shadow-(--panel-lift) panel-blur",
30
30
  "data-[state=open]:animate-[m22-pop-in_var(--duration-fast)_var(--ease-out-expo)] data-[state=closed]:animate-[m22-pop-out_var(--duration-fast)_var(--ease)] origin-(--radix-popper-transform-origin)",
31
31
  className
32
32
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Popover/Popover.tsx"],"sourcesContent":["'use client'\n\nimport * as PopoverPrimitive from '@radix-ui/react-popover'\nimport { X } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\n\n/** Radix Popover root, trigger, anchor and close, as typed passthroughs. */\nexport const Popover = PopoverPrimitive.Root\nexport const PopoverTrigger = PopoverPrimitive.Trigger\nexport const PopoverAnchor = PopoverPrimitive.Anchor\nexport const PopoverClose = PopoverPrimitive.Close\n\nexport interface PopoverContentProps\n extends ComponentProps<typeof PopoverPrimitive.Content> {\n /** Names the panel for assistive tech. Required — a popover is a dialog. */\n label: string\n /** Show the top-end close control. */\n showClose?: boolean\n}\n\n/**\n * A panel anchored to a control, holding content the reader can interact with.\n *\n * The line against `Tooltip` is not visual, it is behavioural: a tooltip\n * describes and cannot be entered; a popover holds things you tab to. Anything\n * with a link, a field or a button in it is a popover, and putting that inside\n * a tooltip makes it unreachable — the tooltip closes as soon as focus tries to\n * move into it.\n *\n * Against `DropdownMenu`: a menu is a list of actions with menu semantics and\n * arrow-key navigation. A popover is free-form, and its contents Tab like the\n * rest of the page.\n *\n * @example\n * <Popover>\n * <PopoverTrigger asChild><Button variant=\"secondary\">Filters</Button></PopoverTrigger>\n * <PopoverContent label=\"Filters\">\n * <Field label=\"Status\"><Select>…</Select></Field>\n * </PopoverContent>\n * </Popover>\n */\nexport function PopoverContent({\n label,\n showClose = false,\n className,\n sideOffset = 8,\n children,\n ...rest\n}: PopoverContentProps) {\n const container = useOverlayContainer()\n\n return (\n <PopoverPrimitive.Portal container={container ?? undefined}>\n <PopoverPrimitive.Content\n collisionBoundary={container ?? undefined}\n collisionPadding={8}\n aria-label={label}\n sideOffset={sideOffset}\n data-m22-animated\n className={cn(\n 'z-(--z-dropdown) w-72 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-4 panel-blur',\n 'data-[state=open]:animate-[m22-pop-in_var(--duration-fast)_var(--ease-out-expo)] data-[state=closed]:animate-[m22-pop-out_var(--duration-fast)_var(--ease)] origin-(--radix-popper-transform-origin)',\n className,\n )}\n {...rest}\n >\n {children}\n {showClose && (\n <PopoverPrimitive.Close\n aria-label=\"Close\"\n className=\"absolute end-2 top-2 grid size-8 place-items-center rounded-(--radius-pill) text-(--ink-3-aa) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)\"\n >\n <X size={14} strokeWidth={1.5} aria-hidden />\n </PopoverPrimitive.Close>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport default Popover\n"],"mappings":";AAuDM,SAmBM,KAnBN;AArDN,YAAY,sBAAsB;AAClC,SAAS,SAAS;AAElB,SAAS,UAAU;AACnB,SAAS,2BAA2B;AAG7B,MAAM,UAAU,iBAAiB;AACjC,MAAM,iBAAiB,iBAAiB;AACxC,MAAM,gBAAgB,iBAAiB;AACvC,MAAM,eAAe,iBAAiB;AA+BtC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,QAAM,YAAY,oBAAoB;AAEtC,SACE,oBAAC,iBAAiB,QAAjB,EAAwB,WAAW,aAAa,QAC/C;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,mBAAmB,aAAa;AAAA,MAChC,kBAAkB;AAAA,MAClB,cAAY;AAAA,MACZ;AAAA,MACA,qBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,aACC;AAAA,UAAC,iBAAiB;AAAA,UAAjB;AAAA,YACC,cAAW;AAAA,YACX,WAAU;AAAA,YAEV,8BAAC,KAAE,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC;AAAA;AAAA,QAC7C;AAAA;AAAA;AAAA,EAEJ,GACF;AAEJ;AAEA,IAAO,kBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Popover/Popover.tsx"],"sourcesContent":["'use client'\n\nimport * as PopoverPrimitive from '@radix-ui/react-popover'\nimport { X } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\n\n/** Radix Popover root, trigger, anchor and close, as typed passthroughs. */\nexport const Popover = PopoverPrimitive.Root\nexport const PopoverTrigger = PopoverPrimitive.Trigger\nexport const PopoverAnchor = PopoverPrimitive.Anchor\nexport const PopoverClose = PopoverPrimitive.Close\n\nexport interface PopoverContentProps\n extends ComponentProps<typeof PopoverPrimitive.Content> {\n /** Names the panel for assistive tech. Required — a popover is a dialog. */\n label: string\n /** Show the top-end close control. */\n showClose?: boolean\n}\n\n/**\n * A panel anchored to a control, holding content the reader can interact with.\n *\n * The line against `Tooltip` is not visual, it is behavioural: a tooltip\n * describes and cannot be entered; a popover holds things you tab to. Anything\n * with a link, a field or a button in it is a popover, and putting that inside\n * a tooltip makes it unreachable — the tooltip closes as soon as focus tries to\n * move into it.\n *\n * Against `DropdownMenu`: a menu is a list of actions with menu semantics and\n * arrow-key navigation. A popover is free-form, and its contents Tab like the\n * rest of the page.\n *\n * @example\n * <Popover>\n * <PopoverTrigger asChild><Button variant=\"secondary\">Filters</Button></PopoverTrigger>\n * <PopoverContent label=\"Filters\">\n * <Field label=\"Status\"><Select>…</Select></Field>\n * </PopoverContent>\n * </Popover>\n */\nexport function PopoverContent({\n label,\n showClose = false,\n className,\n sideOffset = 8,\n children,\n ...rest\n}: PopoverContentProps) {\n const container = useOverlayContainer()\n\n return (\n <PopoverPrimitive.Portal container={container ?? undefined}>\n <PopoverPrimitive.Content\n collisionBoundary={container ?? undefined}\n collisionPadding={8}\n aria-label={label}\n sideOffset={sideOffset}\n data-m22-animated\n className={cn(\n 'z-(--z-dropdown) w-72 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-4 shadow-(--panel-lift) panel-blur',\n 'data-[state=open]:animate-[m22-pop-in_var(--duration-fast)_var(--ease-out-expo)] data-[state=closed]:animate-[m22-pop-out_var(--duration-fast)_var(--ease)] origin-(--radix-popper-transform-origin)',\n className,\n )}\n {...rest}\n >\n {children}\n {showClose && (\n <PopoverPrimitive.Close\n aria-label=\"Close\"\n className=\"absolute end-2 top-2 grid size-8 place-items-center rounded-(--radius-pill) text-(--ink-3-aa) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)\"\n >\n <X size={14} strokeWidth={1.5} aria-hidden />\n </PopoverPrimitive.Close>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport default Popover\n"],"mappings":";AAuDM,SAmBM,KAnBN;AArDN,YAAY,sBAAsB;AAClC,SAAS,SAAS;AAElB,SAAS,UAAU;AACnB,SAAS,2BAA2B;AAG7B,MAAM,UAAU,iBAAiB;AACjC,MAAM,iBAAiB,iBAAiB;AACxC,MAAM,gBAAgB,iBAAiB;AACvC,MAAM,eAAe,iBAAiB;AA+BtC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,QAAM,YAAY,oBAAoB;AAEtC,SACE,oBAAC,iBAAiB,QAAjB,EAAwB,WAAW,aAAa,QAC/C;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,mBAAmB,aAAa;AAAA,MAChC,kBAAkB;AAAA,MAClB,cAAY;AAAA,MACZ;AAAA,MACA,qBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,aACC;AAAA,UAAC,iBAAiB;AAAA,UAAjB;AAAA,YACC,cAAW;AAAA,YACX,WAAU;AAAA,YAEV,8BAAC,KAAE,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC;AAAA;AAAA,QAC7C;AAAA;AAAA;AAAA,EAEJ,GACF;AAEJ;AAEA,IAAO,kBAAQ;","names":[]}
@@ -2,13 +2,25 @@ import * as react from 'react';
2
2
  import { ComponentProps } from 'react';
3
3
  import * as ProgressPrimitive from '@radix-ui/react-progress';
4
4
 
5
- interface ProgressProps extends Omit<ComponentProps<typeof ProgressPrimitive.Root>, 'value'> {
5
+ interface ProgressProps extends Omit<ComponentProps<typeof ProgressPrimitive.Root>, 'value' | 'max'> {
6
6
  /**
7
- * 0100. Omit (or pass `null`) when the duration is genuinely unknown — the
8
- * bar then sweeps instead of filling, and Radix drops `aria-valuenow` so a
9
- * screen reader is told "indeterminate" rather than a number that is a guess.
7
+ * 0 to `max`, which is 100 unless you say otherwise. Omit (or pass `null`)
8
+ * when the duration is genuinely unknown the bar then sweeps instead of
9
+ * filling, and Radix drops `aria-valuenow` so a screen reader is told
10
+ * "indeterminate" rather than a number that is a guess.
10
11
  */
11
12
  value?: number | null;
13
+ /**
14
+ * What `value` is measured against. 100 by default.
15
+ *
16
+ * The width is computed from it as well as announced from it. It used to be
17
+ * only announced: `max` reached Radix through `...rest` while the width was
18
+ * `value` clamped to 100, so `max={500}` with `value={100}` painted a full
19
+ * bar and told a screen reader "100 of 500". A max that is not a positive
20
+ * number is refused the way Radix refuses it — Radix warns, and both fall
21
+ * back to 100 rather than drawing one picture and announcing another.
22
+ */
23
+ max?: number;
12
24
  /** Names what is progressing. Required: a bare bar announces nothing. */
13
25
  label: string;
14
26
  /** Prints the percentage above the bar. Only meaningful when `value` is set. */
@@ -26,6 +38,6 @@ interface ProgressProps extends Omit<ComponentProps<typeof ProgressPrimitive.Roo
26
38
  * @example
27
39
  * <Progress label="Indexing" />
28
40
  */
29
- declare function Progress({ value, label, showValue, className, ...rest }: ProgressProps): react.JSX.Element;
41
+ declare function Progress({ value, max, label, showValue, className, ...rest }: ProgressProps): react.JSX.Element;
30
42
 
31
43
  export { Progress, type ProgressProps, Progress as default };
@@ -2,21 +2,33 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as ProgressPrimitive from "@radix-ui/react-progress";
4
4
  import { cn } from "../../lib/cn.js";
5
- function Progress({ value = null, label, showValue = false, className, ...rest }) {
5
+ import { warnBlankName } from "../../lib/warn.js";
6
+ function Progress({
7
+ value = null,
8
+ max,
9
+ label,
10
+ showValue = false,
11
+ className,
12
+ ...rest
13
+ }) {
14
+ warnBlankName("Progress", "label", label, "the progressbar is announced with no name and its percentage describes nothing");
6
15
  const indeterminate = value === null || value === void 0;
7
- const clamped = indeterminate ? 0 : Math.min(100, Math.max(0, value));
16
+ const ceiling = typeof max === "number" && !Number.isNaN(max) && max > 0 ? max : 100;
17
+ const bounded = indeterminate ? 0 : Math.min(ceiling, Math.max(0, value));
18
+ const percent = bounded / ceiling * 100;
8
19
  return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-2", className), children: [
9
20
  showValue && !indeterminate && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between mono-meta text-(--ink-3-aa)", children: [
10
21
  /* @__PURE__ */ jsx("span", { children: label }),
11
22
  /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
12
- Math.round(clamped),
23
+ Math.round(percent),
13
24
  "%"
14
25
  ] })
15
26
  ] }),
16
27
  /* @__PURE__ */ jsx(
17
28
  ProgressPrimitive.Root,
18
29
  {
19
- value: indeterminate ? null : clamped,
30
+ value: indeterminate ? null : bounded,
31
+ max,
20
32
  "aria-label": label,
21
33
  className: "relative h-1 w-full overflow-hidden rounded-(--radius-pill) bg-(--stone)",
22
34
  ...rest,
@@ -24,13 +36,13 @@ function Progress({ value = null, label, showValue = false, className, ...rest }
24
36
  "span",
25
37
  {
26
38
  "data-m22-animated": true,
27
- className: "absolute inset-y-0 start-0 w-1/4 rounded-(--radius-pill) bg-(--accent) rtl:-scale-x-100 motion-safe:animate-[m22-sweep_1.4s_var(--ease)_infinite] motion-reduce:w-full motion-reduce:opacity-40"
39
+ className: "absolute inset-y-0 start-0 w-1/4 rounded-(--radius-pill) bg-(--accent) rtl:-scale-x-100 motion-safe:animate-[m22-sweep_1.4s_var(--ease)_infinite]"
28
40
  }
29
41
  ) : /* @__PURE__ */ jsx(
30
42
  ProgressPrimitive.Indicator,
31
43
  {
32
44
  className: "h-full rounded-(--radius-pill) bg-(--accent) transition-[width] duration-(--duration-slow) ease-(--ease-out-expo)",
33
- style: { width: `${clamped}%` }
45
+ style: { width: `${percent}%` }
34
46
  }
35
47
  )
36
48
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Progress/Progress.tsx"],"sourcesContent":["'use client'\n\nimport * as ProgressPrimitive from '@radix-ui/react-progress'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ProgressProps\n extends Omit<ComponentProps<typeof ProgressPrimitive.Root>, 'value'> {\n /**\n * 0100. Omit (or pass `null`) when the duration is genuinely unknown — the\n * bar then sweeps instead of filling, and Radix drops `aria-valuenow` so a\n * screen reader is told \"indeterminate\" rather than a number that is a guess.\n */\n value?: number | null\n /** Names what is progressing. Required: a bare bar announces nothing. */\n label: string\n /** Prints the percentage above the bar. Only meaningful when `value` is set. */\n showValue?: boolean\n}\n\n/**\n * A bar that fills, or sweeps when the end is unknown.\n *\n * Flat: a track in `--stone`, a fill in `--ink`. The White Reset has no\n * gradient and no glow, so the only thing carrying the reading is the boundary\n * between the two.\n *\n * @example\n * <Progress value={62} label=\"Uploading photos\" showValue />\n * @example\n * <Progress label=\"Indexing\" />\n */\nexport function Progress({ value = null, label, showValue = false, className, ...rest }: ProgressProps) {\n const indeterminate = value === null || value === undefined\n const clamped = indeterminate ? 0 : Math.min(100, Math.max(0, value))\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {showValue && !indeterminate && (\n <div className=\"flex items-baseline justify-between mono-meta text-(--ink-3-aa)\">\n <span>{label}</span>\n <span className=\"tabular-nums\">{Math.round(clamped)}%</span>\n </div>\n )}\n <ProgressPrimitive.Root\n value={indeterminate ? null : clamped}\n aria-label={label}\n className=\"relative h-1 w-full overflow-hidden rounded-(--radius-pill) bg-(--stone)\"\n {...rest}\n >\n {indeterminate ? (\n <span\n data-m22-animated\n // A quarter-width bar travelling the track. `transform` only, so it\n // runs on the compositor and never triggers layout. `rtl:-scale-x-100`\n // mirrors the whole motion rather than needing a second keyframe:\n // in a right-to-left document the sweep has to run the other way, or\n // it reads as progress running backwards.\n className=\"absolute inset-y-0 start-0 w-1/4 rounded-(--radius-pill) bg-(--accent) rtl:-scale-x-100 motion-safe:animate-[m22-sweep_1.4s_var(--ease)_infinite] motion-reduce:w-full motion-reduce:opacity-40\"\n />\n ) : (\n <ProgressPrimitive.Indicator\n // Width, not a translate. A `translateX(-N%)` fill has to be negated\n // in a right-to-left document, and an inline style cannot carry a\n // direction variant — so the bar grew from the wrong edge. Width\n // grows from the inline start on its own, in either direction.\n className=\"h-full rounded-(--radius-pill) bg-(--accent) transition-[width] duration-(--duration-slow) ease-(--ease-out-expo)\"\n style={{ width: `${clamped}%` }}\n />\n )}\n </ProgressPrimitive.Root>\n </div>\n )\n}\n\nexport default Progress\n"],"mappings":";AAwCU,cACA,YADA;AAtCV,YAAY,uBAAuB;AAEnC,SAAS,UAAU;AA4BZ,SAAS,SAAS,EAAE,QAAQ,MAAM,OAAO,YAAY,OAAO,WAAW,GAAG,KAAK,GAAkB;AACtG,QAAM,gBAAgB,UAAU,QAAQ,UAAU;AAClD,QAAM,UAAU,gBAAgB,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC;AAEpE,SACE,qBAAC,SAAI,WAAW,GAAG,8BAA8B,SAAS,GACvD;AAAA,iBAAa,CAAC,iBACb,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAM,iBAAM;AAAA,MACb,qBAAC,UAAK,WAAU,gBAAgB;AAAA,aAAK,MAAM,OAAO;AAAA,QAAE;AAAA,SAAC;AAAA,OACvD;AAAA,IAEF;AAAA,MAAC,kBAAkB;AAAA,MAAlB;AAAA,QACC,OAAO,gBAAgB,OAAO;AAAA,QAC9B,cAAY;AAAA,QACZ,WAAU;AAAA,QACT,GAAG;AAAA,QAEH,0BACC;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YAMjB,WAAU;AAAA;AAAA,QACZ,IAEA;AAAA,UAAC,kBAAkB;AAAA,UAAlB;AAAA,YAKC,WAAU;AAAA,YACV,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI;AAAA;AAAA,QAChC;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Progress/Progress.tsx"],"sourcesContent":["'use client'\n\nimport * as ProgressPrimitive from '@radix-ui/react-progress'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\nimport { warnBlankName } from '../../lib/warn'\n\nexport interface ProgressProps\n extends Omit<ComponentProps<typeof ProgressPrimitive.Root>, 'value' | 'max'> {\n /**\n * 0 to `max`, which is 100 unless you say otherwise. Omit (or pass `null`)\n * when the duration is genuinely unknown — the bar then sweeps instead of\n * filling, and Radix drops `aria-valuenow` so a screen reader is told\n * \"indeterminate\" rather than a number that is a guess.\n */\n value?: number | null\n /**\n * What `value` is measured against. 100 by default.\n *\n * The width is computed from it as well as announced from it. It used to be\n * only announced: `max` reached Radix through `...rest` while the width was\n * `value` clamped to 100, so `max={500}` with `value={100}` painted a full\n * bar and told a screen reader \"100 of 500\". A max that is not a positive\n * number is refused the way Radix refuses it — Radix warns, and both fall\n * back to 100 rather than drawing one picture and announcing another.\n */\n max?: number\n /** Names what is progressing. Required: a bare bar announces nothing. */\n label: string\n /** Prints the percentage above the bar. Only meaningful when `value` is set. */\n showValue?: boolean\n}\n\n/**\n * A bar that fills, or sweeps when the end is unknown.\n *\n * Flat: a track in `--stone`, a fill in `--ink`. The White Reset has no\n * gradient and no glow, so the only thing carrying the reading is the boundary\n * between the two.\n *\n * @example\n * <Progress value={62} label=\"Uploading photos\" showValue />\n * @example\n * <Progress label=\"Indexing\" />\n */\nexport function Progress({\n value = null,\n max,\n label,\n showValue = false,\n className,\n ...rest\n}: ProgressProps) {\n warnBlankName('Progress', 'label', label, 'the progressbar is announced with no name and its percentage describes nothing')\n const indeterminate = value === null || value === undefined\n // Radix's own test for a usable max, so the fallback below is the same\n // fallback it applies — and `max` is forwarded raw, so Radix still prints the\n // warning rather than this component swallowing a bad prop quietly.\n const ceiling = typeof max === 'number' && !Number.isNaN(max) && max > 0 ? max : 100\n const bounded = indeterminate ? 0 : Math.min(ceiling, Math.max(0, value))\n const percent = (bounded / ceiling) * 100\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {showValue && !indeterminate && (\n <div className=\"flex items-baseline justify-between mono-meta text-(--ink-3-aa)\">\n <span>{label}</span>\n <span className=\"tabular-nums\">{Math.round(percent)}%</span>\n </div>\n )}\n <ProgressPrimitive.Root\n value={indeterminate ? null : bounded}\n max={max}\n aria-label={label}\n className=\"relative h-1 w-full overflow-hidden rounded-(--radius-pill) bg-(--stone)\"\n {...rest}\n >\n {indeterminate ? (\n <span\n data-m22-animated\n // A quarter-width bar travelling the track. `transform` only, so it\n // runs on the compositor and never triggers layout. `rtl:-scale-x-100`\n // mirrors the whole motion rather than needing a second keyframe:\n // in a right-to-left document the sweep has to run the other way, or\n // it reads as progress running backwards.\n //\n // Nothing overrides it under `motion-reduce`, and that is the fix\n // rather than an omission: it used to become `w-full` at 40%\n // opacity, which is the shape of a FINISHED bar at rest, so a\n // reader who asked for less motion was told the operation was done.\n // Still, it is a quarter of the track at the inline start — the\n // same answer Spinner gives, where a ring that has stopped turning\n // still reads as unfinished because it is not a whole circle.\n className=\"absolute inset-y-0 start-0 w-1/4 rounded-(--radius-pill) bg-(--accent) rtl:-scale-x-100 motion-safe:animate-[m22-sweep_1.4s_var(--ease)_infinite]\"\n />\n ) : (\n <ProgressPrimitive.Indicator\n // Width, not a translate. A `translateX(-N%)` fill has to be negated\n // in a right-to-left document, and an inline style cannot carry a\n // direction variant — so the bar grew from the wrong edge. Width\n // grows from the inline start on its own, in either direction.\n className=\"h-full rounded-(--radius-pill) bg-(--accent) transition-[width] duration-(--duration-slow) ease-(--ease-out-expo)\"\n style={{ width: `${percent}%` }}\n />\n )}\n </ProgressPrimitive.Root>\n </div>\n )\n}\n\nexport default Progress\n"],"mappings":";AAkEU,cACA,YADA;AAhEV,YAAY,uBAAuB;AAEnC,SAAS,UAAU;AACnB,SAAS,qBAAqB;AAwCvB,SAAS,SAAS;AAAA,EACvB,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,gBAAc,YAAY,SAAS,OAAO,gFAAgF;AAC1H,QAAM,gBAAgB,UAAU,QAAQ,UAAU;AAIlD,QAAM,UAAU,OAAO,QAAQ,YAAY,CAAC,OAAO,MAAM,GAAG,KAAK,MAAM,IAAI,MAAM;AACjF,QAAM,UAAU,gBAAgB,IAAI,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG,KAAK,CAAC;AACxE,QAAM,UAAW,UAAU,UAAW;AAEtC,SACE,qBAAC,SAAI,WAAW,GAAG,8BAA8B,SAAS,GACvD;AAAA,iBAAa,CAAC,iBACb,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAM,iBAAM;AAAA,MACb,qBAAC,UAAK,WAAU,gBAAgB;AAAA,aAAK,MAAM,OAAO;AAAA,QAAE;AAAA,SAAC;AAAA,OACvD;AAAA,IAEF;AAAA,MAAC,kBAAkB;AAAA,MAAlB;AAAA,QACC,OAAO,gBAAgB,OAAO;AAAA,QAC9B;AAAA,QACA,cAAY;AAAA,QACZ,WAAU;AAAA,QACT,GAAG;AAAA,QAEH,0BACC;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YAcjB,WAAU;AAAA;AAAA,QACZ,IAEA;AAAA,UAAC,kBAAkB;AAAA,UAAlB;AAAA,YAKC,WAAU;AAAA,YACV,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI;AAAA;AAAA,QAChC;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
@@ -11,6 +11,12 @@ type RadioGroupProps = ComponentProps<typeof RadioGroupPrimitive.Root>;
11
11
  * requires and what a stack of hand-rolled `<input type="radio">` wrappers
12
12
  * usually gets wrong.
13
13
  *
14
+ * Inside a `Field` the group takes its name from that label, by pointing back
15
+ * at it: the root is a `<div role="radiogroup">` and `<label for>` does not bind
16
+ * to one, so the words above it click through no more than a `<legend>` does.
17
+ * Standing alone, it still needs an `aria-label` of its own — an unnamed group
18
+ * is three unlabelled radios.
19
+ *
14
20
  * @example
15
21
  * <RadioGroup defaultValue="light" aria-label="Theme">
16
22
  * <RadioGroupItem value="light">Light</RadioGroupItem>
@@ -3,9 +3,19 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
4
4
  import { useEffect, useRef } from "react";
5
5
  import { cn } from "../../lib/cn.js";
6
+ import { useFieldControl } from "../Field/field-control.js";
6
7
  const ARROW_GRACE_MS = 300;
7
8
  function RadioGroup({ className, ...props }) {
8
- return /* @__PURE__ */ jsx(RadioGroupPrimitive.Root, { className: cn("flex flex-col gap-2.5", className), ...props });
9
+ const field = useFieldControl();
10
+ const named = props["aria-label"] != null || props["aria-labelledby"] != null;
11
+ return /* @__PURE__ */ jsx(
12
+ RadioGroupPrimitive.Root,
13
+ {
14
+ "aria-labelledby": named ? void 0 : field?.labelId,
15
+ className: cn("flex flex-col gap-2.5", className),
16
+ ...props
17
+ }
18
+ );
9
19
  }
10
20
  function RadioGroupItem({ children, className, id, onFocus, ...props }) {
11
21
  const arrowPressedAt = useRef(0);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/RadioGroup/RadioGroup.tsx"],"sourcesContent":["'use client'\n\nimport * as RadioGroupPrimitive from '@radix-ui/react-radio-group'\nimport { useEffect, useRef, type ComponentProps, type FocusEvent, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/**\n * How recently an arrow key was pressed, in milliseconds, for selection to\n * still count as \"caused by\" it.\n *\n * A boolean cleared on `keyup` — which is how the upstream primitive tracks\n * this — loses the race: the roving focus moves through a React state update,\n * and by the time the commit lands and the element takes focus, a normal\n * keypress has already released. The result is a group whose arrow keys move\n * the outline and select nothing, which is half the ARIA radiogroup pattern\n * missing. A timestamp cannot be cleared out from under the focus handler.\n */\nconst ARROW_GRACE_MS = 300\n\nexport type RadioGroupProps = ComponentProps<typeof RadioGroupPrimitive.Root>\n\n/**\n * A set of mutually exclusive choices.\n *\n * Radix owns the roving tabindex, so the whole group is ONE tab stop and the\n * arrow keys move between options — which is what the ARIA radiogroup pattern\n * requires and what a stack of hand-rolled `<input type=\"radio\">` wrappers\n * usually gets wrong.\n *\n * @example\n * <RadioGroup defaultValue=\"light\" aria-label=\"Theme\">\n * <RadioGroupItem value=\"light\">Light</RadioGroupItem>\n * <RadioGroupItem value=\"dark\">Dark</RadioGroupItem>\n * </RadioGroup>\n */\nexport function RadioGroup({ className, ...props }: RadioGroupProps) {\n return <RadioGroupPrimitive.Root className={cn('flex flex-col gap-2.5', className)} {...props} />\n}\n\nexport interface RadioGroupItemProps\n extends Omit<ComponentProps<typeof RadioGroupPrimitive.Item>, 'children'> {\n /** The visible label. Rendered inside the `<label>` that wraps the control. */\n children: ReactNode\n}\n\n/**\n * One option, and its label, as a single click target.\n *\n * The `<label>` wraps both, so the whole row is clickable — a bare 18px circle\n * is below every pointer-target guideline and is miserable on a phone. It is\n * also what gives the control its accessible name: remove the wrapper and the\n * radio has no name at all.\n *\n * Selection follows focus, which is the half of the pattern that makes a\n * radiogroup usable from the keyboard: moving to an option chooses it, so\n * nobody has to press an extra key to commit. That is implemented here rather\n * than inherited, because the upstream primitive gates it on a flag cleared by\n * `keyup` and loses the race against its own focus move — see ARROW_GRACE_MS.\n * Re-selecting an already-selected option is a no-op, so this stays correct\n * even where the upstream path does fire.\n */\nexport function RadioGroupItem({ children, className, id, onFocus, ...props }: RadioGroupItemProps) {\n const arrowPressedAt = useRef(0)\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key.startsWith('Arrow')) arrowPressedAt.current = Date.now()\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [])\n\n const selectOnArrowFocus = (event: FocusEvent<HTMLButtonElement>) => {\n onFocus?.(event)\n if (Date.now() - arrowPressedAt.current < ARROW_GRACE_MS) event.currentTarget.click()\n }\n\n return (\n <label className={cn('flex cursor-pointer items-center gap-2.5 text-sm text-(--ink-2)', className)}>\n <RadioGroupPrimitive.Item\n id={id}\n onFocus={selectOnArrowFocus}\n className=\"grid size-[18px] shrink-0 place-items-center rounded-full border border-(--rule-2) bg-(--paper) transition-colors duration-(--duration-fast) data-[state=checked]:border-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none\"\n {...props}\n >\n <RadioGroupPrimitive.Indicator className=\"size-2.5 rounded-full bg-(--accent)\" />\n </RadioGroupPrimitive.Item>\n {children}\n </label>\n )\n}\n\nexport default RadioGroup\n"],"mappings":";AAoCS,cA0CL,YA1CK;AAlCT,YAAY,yBAAyB;AACrC,SAAS,WAAW,cAAoE;AACxF,SAAS,UAAU;AAanB,MAAM,iBAAiB;AAkBhB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AACnE,SAAO,oBAAC,oBAAoB,MAApB,EAAyB,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,OAAO;AACjG;AAwBO,SAAS,eAAe,EAAE,UAAU,WAAW,IAAI,SAAS,GAAG,MAAM,GAAwB;AAClG,QAAM,iBAAiB,OAAO,CAAC;AAE/B,YAAU,MAAM;AACd,UAAM,YAAY,CAAC,UAAyB;AAC1C,UAAI,MAAM,IAAI,WAAW,OAAO,EAAG,gBAAe,UAAU,KAAK,IAAI;AAAA,IACvE;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,CAAC;AAEL,QAAM,qBAAqB,CAAC,UAAyC;AACnE,cAAU,KAAK;AACf,QAAI,KAAK,IAAI,IAAI,eAAe,UAAU,eAAgB,OAAM,cAAc,MAAM;AAAA,EACtF;AAEA,SACE,qBAAC,WAAM,WAAW,GAAG,mEAAmE,SAAS,GAC/F;AAAA;AAAA,MAAC,oBAAoB;AAAA,MAApB;AAAA,QACC;AAAA,QACA,SAAS;AAAA,QACT,WAAU;AAAA,QACT,GAAG;AAAA,QAEJ,8BAAC,oBAAoB,WAApB,EAA8B,WAAU,uCAAsC;AAAA;AAAA,IACjF;AAAA,IACC;AAAA,KACH;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/RadioGroup/RadioGroup.tsx"],"sourcesContent":["'use client'\n\nimport * as RadioGroupPrimitive from '@radix-ui/react-radio-group'\nimport { useEffect, useRef, type ComponentProps, type FocusEvent, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useFieldControl } from '../Field/field-control'\n\n/**\n * How recently an arrow key was pressed, in milliseconds, for selection to\n * still count as \"caused by\" it.\n *\n * A boolean cleared on `keyup` — which is how the upstream primitive tracks\n * this — loses the race: the roving focus moves through a React state update,\n * and by the time the commit lands and the element takes focus, a normal\n * keypress has already released. The result is a group whose arrow keys move\n * the outline and select nothing, which is half the ARIA radiogroup pattern\n * missing. A timestamp cannot be cleared out from under the focus handler.\n */\nconst ARROW_GRACE_MS = 300\n\nexport type RadioGroupProps = ComponentProps<typeof RadioGroupPrimitive.Root>\n\n/**\n * A set of mutually exclusive choices.\n *\n * Radix owns the roving tabindex, so the whole group is ONE tab stop and the\n * arrow keys move between options — which is what the ARIA radiogroup pattern\n * requires and what a stack of hand-rolled `<input type=\"radio\">` wrappers\n * usually gets wrong.\n *\n * Inside a `Field` the group takes its name from that label, by pointing back\n * at it: the root is a `<div role=\"radiogroup\">` and `<label for>` does not bind\n * to one, so the words above it click through no more than a `<legend>` does.\n * Standing alone, it still needs an `aria-label` of its own — an unnamed group\n * is three unlabelled radios.\n *\n * @example\n * <RadioGroup defaultValue=\"light\" aria-label=\"Theme\">\n * <RadioGroupItem value=\"light\">Light</RadioGroupItem>\n * <RadioGroupItem value=\"dark\">Dark</RadioGroupItem>\n * </RadioGroup>\n */\nexport function RadioGroup({ className, ...props }: RadioGroupProps) {\n const field = useFieldControl()\n const named = props['aria-label'] != null || props['aria-labelledby'] != null\n\n return (\n <RadioGroupPrimitive.Root\n aria-labelledby={named ? undefined : field?.labelId}\n className={cn('flex flex-col gap-2.5', className)}\n {...props}\n />\n )\n}\n\nexport interface RadioGroupItemProps\n extends Omit<ComponentProps<typeof RadioGroupPrimitive.Item>, 'children'> {\n /** The visible label. Rendered inside the `<label>` that wraps the control. */\n children: ReactNode\n}\n\n/**\n * One option, and its label, as a single click target.\n *\n * The `<label>` wraps both, so the whole row is clickable — a bare 18px circle\n * is below every pointer-target guideline and is miserable on a phone. It is\n * also what gives the control its accessible name: remove the wrapper and the\n * radio has no name at all.\n *\n * Selection follows focus, which is the half of the pattern that makes a\n * radiogroup usable from the keyboard: moving to an option chooses it, so\n * nobody has to press an extra key to commit. That is implemented here rather\n * than inherited, because the upstream primitive gates it on a flag cleared by\n * `keyup` and loses the race against its own focus move — see ARROW_GRACE_MS.\n * Re-selecting an already-selected option is a no-op, so this stays correct\n * even where the upstream path does fire.\n */\nexport function RadioGroupItem({ children, className, id, onFocus, ...props }: RadioGroupItemProps) {\n const arrowPressedAt = useRef(0)\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key.startsWith('Arrow')) arrowPressedAt.current = Date.now()\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [])\n\n const selectOnArrowFocus = (event: FocusEvent<HTMLButtonElement>) => {\n onFocus?.(event)\n if (Date.now() - arrowPressedAt.current < ARROW_GRACE_MS) event.currentTarget.click()\n }\n\n return (\n <label className={cn('flex cursor-pointer items-center gap-2.5 text-sm text-(--ink-2)', className)}>\n <RadioGroupPrimitive.Item\n id={id}\n onFocus={selectOnArrowFocus}\n className=\"grid size-[18px] shrink-0 place-items-center rounded-full border border-(--rule-2) bg-(--paper) transition-colors duration-(--duration-fast) data-[state=checked]:border-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none\"\n {...props}\n >\n <RadioGroupPrimitive.Indicator className=\"size-2.5 rounded-full bg-(--accent)\" />\n </RadioGroupPrimitive.Item>\n {children}\n </label>\n )\n}\n\nexport default RadioGroup\n"],"mappings":";AA+CI,cA+CA,YA/CA;AA7CJ,YAAY,yBAAyB;AACrC,SAAS,WAAW,cAAoE;AACxF,SAAS,UAAU;AACnB,SAAS,uBAAuB;AAahC,MAAM,iBAAiB;AAwBhB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AACnE,QAAM,QAAQ,gBAAgB;AAC9B,QAAM,QAAQ,MAAM,YAAY,KAAK,QAAQ,MAAM,iBAAiB,KAAK;AAEzE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,mBAAiB,QAAQ,SAAY,OAAO;AAAA,MAC5C,WAAW,GAAG,yBAAyB,SAAS;AAAA,MAC/C,GAAG;AAAA;AAAA,EACN;AAEJ;AAwBO,SAAS,eAAe,EAAE,UAAU,WAAW,IAAI,SAAS,GAAG,MAAM,GAAwB;AAClG,QAAM,iBAAiB,OAAO,CAAC;AAE/B,YAAU,MAAM;AACd,UAAM,YAAY,CAAC,UAAyB;AAC1C,UAAI,MAAM,IAAI,WAAW,OAAO,EAAG,gBAAe,UAAU,KAAK,IAAI;AAAA,IACvE;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,CAAC;AAEL,QAAM,qBAAqB,CAAC,UAAyC;AACnE,cAAU,KAAK;AACf,QAAI,KAAK,IAAI,IAAI,eAAe,UAAU,eAAgB,OAAM,cAAc,MAAM;AAAA,EACtF;AAEA,SACE,qBAAC,WAAM,WAAW,GAAG,mEAAmE,SAAS,GAC/F;AAAA;AAAA,MAAC,oBAAoB;AAAA,MAApB;AAAA,QACC;AAAA,QACA,SAAS;AAAA,QACT,WAAU;AAAA,QACT,GAAG;AAAA,QAEJ,8BAAC,oBAAoB,WAApB,EAA8B,WAAU,uCAAsC;AAAA;AAAA,IACjF;AAAA,IACC;AAAA,KACH;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -8,6 +8,15 @@ interface ScrollAreaProps extends ComponentProps<typeof ScrollAreaPrimitive.Root
8
8
  * keyboard stop, and an unnamed stop announces "group" and nothing else.
9
9
  */
10
10
  label: string;
11
+ /**
12
+ * Which axes get a bar — and therefore which axes scroll at all. Radix sets
13
+ * the viewport's overflow from the bars that are mounted, so an axis without
14
+ * one is `hidden`: what is past that edge is not merely unmarked, it is
15
+ * unreachable by every key and every gesture.
16
+ *
17
+ * `both` is the default for that reason. Narrow it only when clipping the
18
+ * other axis is the thing you meant.
19
+ */
11
20
  orientation?: 'vertical' | 'horizontal' | 'both';
12
21
  }
13
22
  /**
@@ -23,6 +32,12 @@ interface ScrollAreaProps extends ComponentProps<typeof ScrollAreaPrimitive.Root
23
32
  * needs no component. This is for a bounded panel: a long option list, a log,
24
33
  * a sidebar that outgrows its column.
25
34
  *
35
+ * Both axes scroll unless a caller says otherwise. The old vertical default
36
+ * read as a statement about which bar to draw and was in fact a statement about
37
+ * which half of the content existed: Radix sets the viewport's overflow from
38
+ * the mounted bars, so the axis without one was `hidden` and everything past it
39
+ * was unreachable — silently, and with the content perfectly well rendered.
40
+ *
26
41
  * @example
27
42
  * <ScrollArea label="Deploy log" className="h-48">…</ScrollArea>
28
43
  */
@@ -4,7 +4,7 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4
4
  import { cn } from "../../lib/cn.js";
5
5
  function ScrollArea({
6
6
  label,
7
- orientation = "vertical",
7
+ orientation = "both",
8
8
  className,
9
9
  children,
10
10
  ...props
@@ -22,7 +22,7 @@ function ScrollArea({
22
22
  tabIndex: 0,
23
23
  role: "region",
24
24
  "aria-label": label,
25
- className: "size-full rounded-[inherit]",
25
+ className: "relative size-full rounded-[inherit]",
26
26
  children
27
27
  }
28
28
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"sourcesContent":["'use client'\n\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ScrollAreaProps extends ComponentProps<typeof ScrollAreaPrimitive.Root> {\n /**\n * Names the region. Required, and not decoration: a scroll container is a\n * keyboard stop, and an unnamed stop announces \"group\" and nothing else.\n */\n label: string\n orientation?: 'vertical' | 'horizontal' | 'both'\n}\n\n/**\n * A box that scrolls, with a scrollbar that looks the same on every platform.\n *\n * The reason to reach for this over `overflow-auto` is not the scrollbar — it\n * is that Radix keeps the viewport focusable and the bar operable, which a bare\n * overflow container does not. A scrollable region whose contents are not\n * themselves focusable is unreachable by keyboard: there is nothing to Tab to,\n * so everything past the fold does not exist without a mouse.\n *\n * For a page-level or prose scroll, the `scroll-slim` utility is lighter and\n * needs no component. This is for a bounded panel: a long option list, a log,\n * a sidebar that outgrows its column.\n *\n * @example\n * <ScrollArea label=\"Deploy log\" className=\"h-48\">…</ScrollArea>\n */\nexport function ScrollArea({\n label,\n orientation = 'vertical',\n className,\n children,\n ...props\n}: ScrollAreaProps) {\n return (\n <ScrollAreaPrimitive.Root\n type=\"hover\"\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n className=\"size-full rounded-[inherit]\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n {(orientation === 'vertical' || orientation === 'both') && <Bar orientation=\"vertical\" />}\n {(orientation === 'horizontal' || orientation === 'both') && <Bar orientation=\"horizontal\" />}\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction Bar({ orientation }: { orientation: 'vertical' | 'horizontal' }) {\n return (\n <ScrollAreaPrimitive.Scrollbar\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none p-0.5 transition-colors duration-(--duration-fast)',\n orientation === 'vertical' ? 'h-full w-2' : 'h-2 flex-col',\n )}\n >\n <ScrollAreaPrimitive.Thumb className=\"relative flex-1 rounded-(--radius-pill) bg-(--rule-2)\" />\n </ScrollAreaPrimitive.Scrollbar>\n )\n}\n\nexport default ScrollArea\n"],"mappings":";AAuCI,SAKE,KALF;AArCJ,YAAY,yBAAyB;AAErC,SAAS,UAAU;AA2BZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,MAAK;AAAA,MACL,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC,oBAAoB;AAAA,UAApB;AAAA,YACC,UAAU;AAAA,YACV,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,SACE,gBAAgB,cAAc,gBAAgB,WAAW,oBAAC,OAAI,aAAY,YAAW;AAAA,SACrF,gBAAgB,gBAAgB,gBAAgB,WAAW,oBAAC,OAAI,aAAY,cAAa;AAAA,QAC3F,oBAAC,oBAAoB,QAApB,EAA2B;AAAA;AAAA;AAAA,EAC9B;AAEJ;AAEA,SAAS,IAAI,EAAE,YAAY,GAA+C;AACxE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aAAa,eAAe;AAAA,MAC9C;AAAA,MAEA,8BAAC,oBAAoB,OAApB,EAA0B,WAAU,yDAAwD;AAAA;AAAA,EAC/F;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"sourcesContent":["'use client'\n\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ScrollAreaProps extends ComponentProps<typeof ScrollAreaPrimitive.Root> {\n /**\n * Names the region. Required, and not decoration: a scroll container is a\n * keyboard stop, and an unnamed stop announces \"group\" and nothing else.\n */\n label: string\n /**\n * Which axes get a bar — and therefore which axes scroll at all. Radix sets\n * the viewport's overflow from the bars that are mounted, so an axis without\n * one is `hidden`: what is past that edge is not merely unmarked, it is\n * unreachable by every key and every gesture.\n *\n * `both` is the default for that reason. Narrow it only when clipping the\n * other axis is the thing you meant.\n */\n orientation?: 'vertical' | 'horizontal' | 'both'\n}\n\n/**\n * A box that scrolls, with a scrollbar that looks the same on every platform.\n *\n * The reason to reach for this over `overflow-auto` is not the scrollbar — it\n * is that Radix keeps the viewport focusable and the bar operable, which a bare\n * overflow container does not. A scrollable region whose contents are not\n * themselves focusable is unreachable by keyboard: there is nothing to Tab to,\n * so everything past the fold does not exist without a mouse.\n *\n * For a page-level or prose scroll, the `scroll-slim` utility is lighter and\n * needs no component. This is for a bounded panel: a long option list, a log,\n * a sidebar that outgrows its column.\n *\n * Both axes scroll unless a caller says otherwise. The old vertical default\n * read as a statement about which bar to draw and was in fact a statement about\n * which half of the content existed: Radix sets the viewport's overflow from\n * the mounted bars, so the axis without one was `hidden` and everything past it\n * was unreachable — silently, and with the content perfectly well rendered.\n *\n * @example\n * <ScrollArea label=\"Deploy log\" className=\"h-48\">…</ScrollArea>\n */\nexport function ScrollArea({\n label,\n orientation = 'both',\n className,\n children,\n ...props\n}: ScrollAreaProps) {\n return (\n <ScrollAreaPrimitive.Root\n type=\"hover\"\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n // Positioned, so an absolutely-positioned descendant — an `sr-only`\n // label, a pinned marker — resolves against the thing that scrolls and\n // travels with the content, rather than against the root and hanging\n // still over it.\n className=\"relative size-full rounded-[inherit]\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n {(orientation === 'vertical' || orientation === 'both') && <Bar orientation=\"vertical\" />}\n {(orientation === 'horizontal' || orientation === 'both') && <Bar orientation=\"horizontal\" />}\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction Bar({ orientation }: { orientation: 'vertical' | 'horizontal' }) {\n return (\n <ScrollAreaPrimitive.Scrollbar\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none p-0.5 transition-colors duration-(--duration-fast)',\n orientation === 'vertical' ? 'h-full w-2' : 'h-2 flex-col',\n )}\n >\n <ScrollAreaPrimitive.Thumb className=\"relative flex-1 rounded-(--radius-pill) bg-(--rule-2)\" />\n </ScrollAreaPrimitive.Scrollbar>\n )\n}\n\nexport default ScrollArea\n"],"mappings":";AAsDI,SAKE,KALF;AApDJ,YAAY,yBAAyB;AAErC,SAAS,UAAU;AA0CZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,MAAK;AAAA,MACL,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC,oBAAoB;AAAA,UAApB;AAAA,YACC,UAAU;AAAA,YACV,MAAK;AAAA,YACL,cAAY;AAAA,YAKZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,SACE,gBAAgB,cAAc,gBAAgB,WAAW,oBAAC,OAAI,aAAY,YAAW;AAAA,SACrF,gBAAgB,gBAAgB,gBAAgB,WAAW,oBAAC,OAAI,aAAY,cAAa;AAAA,QAC3F,oBAAC,oBAAoB,QAApB,EAA2B;AAAA;AAAA;AAAA,EAC9B;AAEJ;AAEA,SAAS,IAAI,EAAE,YAAY,GAA+C;AACxE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aAAa,eAAe;AAAA,MAC9C;AAAA,MAEA,8BAAC,oBAAoB,OAApB,EAA0B,WAAU,yDAAwD;AAAA;AAAA,EAC/F;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}