@misoto22/design 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +50 -5
  4. package/bin/misoto22-design.mjs +59 -13
  5. package/dist/agent/Accordion.md +24 -1
  6. package/dist/agent/Alert.md +23 -1
  7. package/dist/agent/AppShell.md +30 -2
  8. package/dist/agent/ArchitectureFigure.md +25 -2
  9. package/dist/agent/AreaChart.md +27 -2
  10. package/dist/agent/Article.md +26 -3
  11. package/dist/agent/AspectRatio.md +57 -0
  12. package/dist/agent/Avatar.md +25 -5
  13. package/dist/agent/Badge.md +22 -1
  14. package/dist/agent/BarChart.md +29 -2
  15. package/dist/agent/BarList.md +27 -1
  16. package/dist/agent/BigNumber.md +26 -2
  17. package/dist/agent/BoxPlot.md +24 -1
  18. package/dist/agent/Breadcrumb.md +24 -2
  19. package/dist/agent/BulletChart.md +23 -1
  20. package/dist/agent/Button.md +25 -1
  21. package/dist/agent/Calendar.md +37 -3
  22. package/dist/agent/Card.md +24 -2
  23. package/dist/agent/Checkbox.md +23 -2
  24. package/dist/agent/Code.md +50 -0
  25. package/dist/agent/CodeBlock.md +74 -0
  26. package/dist/agent/Collapsible.md +22 -1
  27. package/dist/agent/Combobox.md +29 -3
  28. package/dist/agent/Command.md +25 -2
  29. package/dist/agent/ComposedChart.md +26 -2
  30. package/dist/agent/ContextMenu.md +37 -4
  31. package/dist/agent/DataflowFigure.md +22 -1
  32. package/dist/agent/DatePicker.md +34 -6
  33. package/dist/agent/DescriptionList.md +68 -0
  34. package/dist/agent/Diagram.md +27 -3
  35. package/dist/agent/DiagramCanvas.md +24 -1
  36. package/dist/agent/DiagramExportMenu.md +29 -3
  37. package/dist/agent/DiagramInspector.md +23 -1
  38. package/dist/agent/DiagramLegend.md +22 -1
  39. package/dist/agent/DiagramMinimap.md +24 -4
  40. package/dist/agent/DiagramToolbar.md +23 -1
  41. package/dist/agent/Dialog.md +26 -3
  42. package/dist/agent/DropdownMenu.md +45 -6
  43. package/dist/agent/EmptyState.md +26 -1
  44. package/dist/agent/ErrorState.md +27 -2
  45. package/dist/agent/Facet.md +25 -1
  46. package/dist/agent/Field.md +45 -4
  47. package/dist/agent/FigureBand.md +24 -1
  48. package/dist/agent/FloatingIconButton.md +23 -1
  49. package/dist/agent/FunnelChart.md +23 -1
  50. package/dist/agent/Heading.md +64 -0
  51. package/dist/agent/Heatmap.md +25 -1
  52. package/dist/agent/Histogram.md +26 -2
  53. package/dist/agent/Input.md +22 -1
  54. package/dist/agent/Kbd.md +19 -1
  55. package/dist/agent/LifecycleFigure.md +22 -1
  56. package/dist/agent/LineChart.md +26 -2
  57. package/dist/agent/LinkArrow.md +20 -1
  58. package/dist/agent/Markdown.md +68 -0
  59. package/dist/agent/NativeSelect.md +23 -2
  60. package/dist/agent/NavItem.md +21 -1
  61. package/dist/agent/Pagination.md +28 -2
  62. package/dist/agent/PieChart.md +26 -2
  63. package/dist/agent/Popover.md +23 -1
  64. package/dist/agent/Progress.md +27 -3
  65. package/dist/agent/RadarChart.md +26 -2
  66. package/dist/agent/RadialChart.md +26 -3
  67. package/dist/agent/RadioGroup.md +23 -2
  68. package/dist/agent/SankeyChart.md +25 -2
  69. package/dist/agent/ScatterChart.md +27 -2
  70. package/dist/agent/ScrollArea.md +24 -3
  71. package/dist/agent/SearchableMenu.md +23 -1
  72. package/dist/agent/Select.md +29 -3
  73. package/dist/agent/Separator.md +28 -4
  74. package/dist/agent/SequenceFigure.md +23 -1
  75. package/dist/agent/Sheet.md +25 -2
  76. package/dist/agent/Skeleton.md +24 -2
  77. package/dist/agent/Slider.md +27 -4
  78. package/dist/agent/Sparkline.md +23 -1
  79. package/dist/agent/Spinner.md +24 -2
  80. package/dist/agent/StatusDot.md +20 -1
  81. package/dist/agent/StatusPill.md +28 -2
  82. package/dist/agent/Steps.md +24 -1
  83. package/dist/agent/Switch.md +21 -1
  84. package/dist/agent/Table.md +26 -3
  85. package/dist/agent/Tabs.md +24 -2
  86. package/dist/agent/Tag.md +40 -9
  87. package/dist/agent/Text.md +65 -0
  88. package/dist/agent/Textarea.md +20 -1
  89. package/dist/agent/Timestamp.md +64 -0
  90. package/dist/agent/Toast.md +26 -2
  91. package/dist/agent/ToggleGroup.md +24 -2
  92. package/dist/agent/Toolbar.md +71 -0
  93. package/dist/agent/Tooltip.md +23 -1
  94. package/dist/agent/TreemapChart.md +24 -1
  95. package/dist/agent/WaterfallChart.md +24 -1
  96. package/dist/agent/WorkflowFigure.md +24 -2
  97. package/dist/agent/catalog.json +5602 -745
  98. package/dist/agent/index.md +13 -4
  99. package/dist/article.css +16 -4
  100. package/dist/charts/AreaChart/AreaChart.d.ts +9 -2
  101. package/dist/charts/AreaChart/AreaChart.js +12 -13
  102. package/dist/charts/AreaChart/AreaChart.js.map +1 -1
  103. package/dist/charts/BarChart/BarChart.d.ts +14 -3
  104. package/dist/charts/BarChart/BarChart.js +20 -12
  105. package/dist/charts/BarChart/BarChart.js.map +1 -1
  106. package/dist/charts/BarList/BarList.d.ts +9 -1
  107. package/dist/charts/BarList/BarList.js +9 -1
  108. package/dist/charts/BarList/BarList.js.map +1 -1
  109. package/dist/charts/BigNumber/BigNumber.d.ts +19 -3
  110. package/dist/charts/BigNumber/BigNumber.js +25 -5
  111. package/dist/charts/BigNumber/BigNumber.js.map +1 -1
  112. package/dist/charts/BoxPlot/BoxPlot.js +4 -2
  113. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -1
  114. package/dist/charts/BulletChart/BulletChart.js +28 -7
  115. package/dist/charts/BulletChart/BulletChart.js.map +1 -1
  116. package/dist/charts/ComposedChart/ComposedChart.d.ts +9 -2
  117. package/dist/charts/ComposedChart/ComposedChart.js +10 -10
  118. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -1
  119. package/dist/charts/FunnelChart/FunnelChart.d.ts +7 -2
  120. package/dist/charts/FunnelChart/FunnelChart.js +6 -16
  121. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -1
  122. package/dist/charts/Heatmap/Heatmap.d.ts +17 -3
  123. package/dist/charts/Heatmap/Heatmap.js +20 -11
  124. package/dist/charts/Heatmap/Heatmap.js.map +1 -1
  125. package/dist/charts/Histogram/Histogram.d.ts +24 -1
  126. package/dist/charts/Histogram/Histogram.js +43 -11
  127. package/dist/charts/Histogram/Histogram.js.map +1 -1
  128. package/dist/charts/LineChart/LineChart.d.ts +9 -2
  129. package/dist/charts/LineChart/LineChart.js +11 -12
  130. package/dist/charts/LineChart/LineChart.js.map +1 -1
  131. package/dist/charts/PieChart/PieChart.d.ts +15 -3
  132. package/dist/charts/PieChart/PieChart.js +14 -6
  133. package/dist/charts/PieChart/PieChart.js.map +1 -1
  134. package/dist/charts/RadarChart/RadarChart.d.ts +15 -3
  135. package/dist/charts/RadarChart/RadarChart.js +11 -9
  136. package/dist/charts/RadarChart/RadarChart.js.map +1 -1
  137. package/dist/charts/RadialChart/RadialChart.d.ts +22 -4
  138. package/dist/charts/RadialChart/RadialChart.js +28 -6
  139. package/dist/charts/RadialChart/RadialChart.js.map +1 -1
  140. package/dist/charts/SankeyChart/SankeyChart.d.ts +16 -4
  141. package/dist/charts/SankeyChart/SankeyChart.js +14 -6
  142. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -1
  143. package/dist/charts/ScatterChart/ScatterChart.d.ts +24 -3
  144. package/dist/charts/ScatterChart/ScatterChart.js +21 -15
  145. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -1
  146. package/dist/charts/Sparkline/Sparkline.js +5 -3
  147. package/dist/charts/Sparkline/Sparkline.js.map +1 -1
  148. package/dist/charts/TreemapChart/TreemapChart.d.ts +7 -2
  149. package/dist/charts/TreemapChart/TreemapChart.js +20 -11
  150. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -1
  151. package/dist/charts/WaterfallChart/WaterfallChart.js +4 -2
  152. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -1
  153. package/dist/charts/lib/export.d.ts +1 -0
  154. package/dist/charts/lib/figure.d.ts +21 -1
  155. package/dist/charts/lib/figure.js +5 -2
  156. package/dist/charts/lib/figure.js.map +1 -1
  157. package/dist/charts/lib/scale.d.ts +23 -0
  158. package/dist/charts/lib/scale.js +13 -0
  159. package/dist/charts/lib/scale.js.map +1 -0
  160. package/dist/charts/lib/selection.d.ts +20 -0
  161. package/dist/charts/lib/selection.js +18 -0
  162. package/dist/charts/lib/selection.js.map +1 -0
  163. package/dist/charts/lib/toolbar.d.ts +1 -0
  164. package/dist/components/Accordion/Accordion.js +8 -1
  165. package/dist/components/Accordion/Accordion.js.map +1 -1
  166. package/dist/components/AppShell/AppShell.d.ts +6 -0
  167. package/dist/components/AppShell/AppShell.js +34 -4
  168. package/dist/components/AppShell/AppShell.js.map +1 -1
  169. package/dist/components/Article/Article.d.ts +11 -4
  170. package/dist/components/Article/Article.js +13 -0
  171. package/dist/components/Article/Article.js.map +1 -1
  172. package/dist/components/AspectRatio/AspectRatio.d.ts +46 -0
  173. package/dist/components/AspectRatio/AspectRatio.js +33 -0
  174. package/dist/components/AspectRatio/AspectRatio.js.map +1 -0
  175. package/dist/components/Avatar/Avatar.d.ts +12 -3
  176. package/dist/components/Avatar/Avatar.js +6 -1
  177. package/dist/components/Avatar/Avatar.js.map +1 -1
  178. package/dist/components/Breadcrumb/Breadcrumb.d.ts +6 -0
  179. package/dist/components/Breadcrumb/Breadcrumb.js +15 -0
  180. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
  181. package/dist/components/Button/Button.js +14 -0
  182. package/dist/components/Button/Button.js.map +1 -1
  183. package/dist/components/Calendar/Calendar.d.ts +29 -3
  184. package/dist/components/Calendar/Calendar.js +60 -12
  185. package/dist/components/Calendar/Calendar.js.map +1 -1
  186. package/dist/components/Card/Card.d.ts +6 -0
  187. package/dist/components/Card/Card.js +1 -1
  188. package/dist/components/Card/Card.js.map +1 -1
  189. package/dist/components/Checkbox/Checkbox.d.ts +5 -2
  190. package/dist/components/Checkbox/Checkbox.js +12 -2
  191. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  192. package/dist/components/Code/Code.d.ts +30 -0
  193. package/dist/components/Code/Code.js +21 -0
  194. package/dist/components/Code/Code.js.map +1 -0
  195. package/dist/components/CodeBlock/CodeBlock.d.ts +141 -0
  196. package/dist/components/CodeBlock/CodeBlock.js +123 -0
  197. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  198. package/dist/components/Collapsible/Collapsible.js +10 -2
  199. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  200. package/dist/components/Combobox/Combobox.d.ts +15 -1
  201. package/dist/components/Combobox/Combobox.js +25 -5
  202. package/dist/components/Combobox/Combobox.js.map +1 -1
  203. package/dist/components/Command/Command.d.ts +4 -2
  204. package/dist/components/Command/Command.js +13 -1
  205. package/dist/components/Command/Command.js.map +1 -1
  206. package/dist/components/ContextMenu/ContextMenu.d.ts +30 -5
  207. package/dist/components/ContextMenu/ContextMenu.js +32 -2
  208. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  209. package/dist/components/DatePicker/DatePicker.d.ts +36 -4
  210. package/dist/components/DatePicker/DatePicker.js +55 -12
  211. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  212. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  213. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  214. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  215. package/dist/components/Diagram/Diagram.d.ts +8 -3
  216. package/dist/components/Diagram/Diagram.js +82 -10
  217. package/dist/components/Diagram/Diagram.js.map +1 -1
  218. package/dist/components/Dialog/Dialog.d.ts +18 -3
  219. package/dist/components/Dialog/Dialog.js +20 -2
  220. package/dist/components/Dialog/Dialog.js.map +1 -1
  221. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -11
  222. package/dist/components/DropdownMenu/DropdownMenu.js +32 -2
  223. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  224. package/dist/components/EmptyState/EmptyState.d.ts +25 -1
  225. package/dist/components/EmptyState/EmptyState.js +3 -1
  226. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  227. package/dist/components/ErrorState/ErrorState.d.ts +22 -1
  228. package/dist/components/ErrorState/ErrorState.js +11 -2
  229. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  230. package/dist/components/Field/Field.d.ts +70 -3
  231. package/dist/components/Field/Field.js +53 -12
  232. package/dist/components/Field/Field.js.map +1 -1
  233. package/dist/components/Field/field-control.d.ts +31 -0
  234. package/dist/components/Field/field-control.js +12 -0
  235. package/dist/components/Field/field-control.js.map +1 -0
  236. package/dist/components/FloatingIconButton/FloatingIconButton.js +2 -0
  237. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -1
  238. package/dist/components/Heading/Heading.d.ts +62 -0
  239. package/dist/components/Heading/Heading.js +44 -0
  240. package/dist/components/Heading/Heading.js.map +1 -0
  241. package/dist/components/Markdown/Markdown.d.ts +139 -0
  242. package/dist/components/Markdown/Markdown.js +135 -0
  243. package/dist/components/Markdown/Markdown.js.map +1 -0
  244. package/dist/components/Markdown/inline.d.ts +29 -0
  245. package/dist/components/Markdown/inline.js +74 -0
  246. package/dist/components/Markdown/inline.js.map +1 -0
  247. package/dist/components/Markdown/nodes.d.ts +65 -0
  248. package/dist/components/Markdown/nodes.js +1 -0
  249. package/dist/components/Markdown/nodes.js.map +1 -0
  250. package/dist/components/Markdown/parse.d.ts +24 -0
  251. package/dist/components/Markdown/parse.js +110 -0
  252. package/dist/components/Markdown/parse.js.map +1 -0
  253. package/dist/components/Markdown/slug.d.ts +15 -0
  254. package/dist/components/Markdown/slug.js +7 -0
  255. package/dist/components/Markdown/slug.js.map +1 -0
  256. package/dist/components/NativeSelect/NativeSelect.d.ts +6 -0
  257. package/dist/components/NativeSelect/NativeSelect.js +2 -3
  258. package/dist/components/NativeSelect/NativeSelect.js.map +1 -1
  259. package/dist/components/Pagination/Pagination.d.ts +24 -1
  260. package/dist/components/Pagination/Pagination.js +6 -3
  261. package/dist/components/Pagination/Pagination.js.map +1 -1
  262. package/dist/components/Progress/Progress.d.ts +17 -5
  263. package/dist/components/Progress/Progress.js +18 -6
  264. package/dist/components/Progress/Progress.js.map +1 -1
  265. package/dist/components/RadioGroup/RadioGroup.d.ts +6 -0
  266. package/dist/components/RadioGroup/RadioGroup.js +11 -1
  267. package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
  268. package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
  269. package/dist/components/ScrollArea/ScrollArea.js +2 -2
  270. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  271. package/dist/components/SearchableMenu/SearchableMenu.d.ts +11 -2
  272. package/dist/components/SearchableMenu/SearchableMenu.js +24 -2
  273. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -1
  274. package/dist/components/Select/Select.d.ts +18 -2
  275. package/dist/components/Select/Select.js +20 -3
  276. package/dist/components/Select/Select.js.map +1 -1
  277. package/dist/components/Separator/Separator.d.ts +21 -3
  278. package/dist/components/Separator/Separator.js +9 -1
  279. package/dist/components/Separator/Separator.js.map +1 -1
  280. package/dist/components/Sheet/Sheet.d.ts +3 -0
  281. package/dist/components/Sheet/Sheet.js +11 -2
  282. package/dist/components/Sheet/Sheet.js.map +1 -1
  283. package/dist/components/Skeleton/Skeleton.d.ts +8 -1
  284. package/dist/components/Skeleton/Skeleton.js +1 -1
  285. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  286. package/dist/components/Slider/Slider.d.ts +21 -3
  287. package/dist/components/Slider/Slider.js +54 -27
  288. package/dist/components/Slider/Slider.js.map +1 -1
  289. package/dist/components/Spinner/Spinner.d.ts +8 -0
  290. package/dist/components/Spinner/Spinner.js +3 -2
  291. package/dist/components/Spinner/Spinner.js.map +1 -1
  292. package/dist/components/StatusPill/StatusPill.d.ts +5 -0
  293. package/dist/components/StatusPill/StatusPill.js +6 -0
  294. package/dist/components/StatusPill/StatusPill.js.map +1 -1
  295. package/dist/components/Table/Table.d.ts +6 -0
  296. package/dist/components/Table/Table.js +3 -1
  297. package/dist/components/Table/Table.js.map +1 -1
  298. package/dist/components/Tag/Tag.d.ts +68 -9
  299. package/dist/components/Tag/Tag.js +50 -10
  300. package/dist/components/Tag/Tag.js.map +1 -1
  301. package/dist/components/Text/Text.d.ts +62 -0
  302. package/dist/components/Text/Text.js +30 -0
  303. package/dist/components/Text/Text.js.map +1 -0
  304. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  305. package/dist/components/Timestamp/Timestamp.js +56 -0
  306. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  307. package/dist/components/Toast/Toast.d.ts +9 -1
  308. package/dist/components/Toast/Toast.js +27 -4
  309. package/dist/components/Toast/Toast.js.map +1 -1
  310. package/dist/components/ToggleGroup/ToggleGroup.d.ts +6 -1
  311. package/dist/components/ToggleGroup/ToggleGroup.js +11 -1
  312. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  313. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  314. package/dist/components/Toolbar/Toolbar.js +43 -0
  315. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  316. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +6 -0
  317. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +48 -8
  318. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -1
  319. package/dist/diagrams/DataflowFigure/DataflowFigure.js +21 -2
  320. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -1
  321. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +14 -0
  322. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +14 -7
  323. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -1
  324. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +38 -4
  325. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +14 -16
  326. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -1
  327. package/dist/diagrams/DiagramInspector/DiagramInspector.js +2 -2
  328. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -1
  329. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +23 -7
  330. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +45 -18
  331. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -1
  332. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +44 -12
  333. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -1
  334. package/dist/diagrams/SequenceFigure/SequenceFigure.js +35 -5
  335. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -1
  336. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +3 -1
  337. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +76 -28
  338. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -1
  339. package/dist/diagrams/index.d.ts +1 -0
  340. package/dist/diagrams/index.js +10 -0
  341. package/dist/diagrams/index.js.map +1 -1
  342. package/dist/diagrams/lib/dev.d.ts +61 -0
  343. package/dist/diagrams/lib/dev.js +96 -0
  344. package/dist/diagrams/lib/dev.js.map +1 -0
  345. package/dist/diagrams/lib/frame.d.ts +48 -7
  346. package/dist/diagrams/lib/frame.js +17 -2
  347. package/dist/diagrams/lib/frame.js.map +1 -1
  348. package/dist/diagrams/lib/wires.js.map +1 -1
  349. package/dist/diagrams/spec.d.ts +64 -12
  350. package/dist/index.d.ts +16 -4
  351. package/dist/index.js +9 -0
  352. package/dist/index.js.map +1 -1
  353. package/dist/keyframes.css +57 -3
  354. package/dist/lib/overlay-container.d.ts +14 -4
  355. package/dist/lib/overlay-container.js.map +1 -1
  356. package/dist/lib/warn.d.ts +73 -0
  357. package/dist/lib/warn.js +38 -0
  358. package/dist/lib/warn.js.map +1 -0
  359. package/dist/semantic.css +17 -6
  360. package/dist/styles.css +1 -1
  361. package/dist/tokens.css +20 -2
  362. package/dist/tokens.d.ts +1 -1
  363. package/dist/tokens.js +39 -36
  364. package/dist/tokens.json +39 -36
  365. package/llms.txt +35 -0
  366. package/package.json +5 -2
  367. package/skills/misoto22-design/evals/evals.json +84 -0
  368. package/skills/misoto22-design/rules/a11y.md +55 -3
  369. package/skills/misoto22-design/rules/forms.md +5 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Diagram/Diagram.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface DiagramNode {\n /** Only needed when an edge names this node. */\n id?: string\n /** The node's name, set in the interface face. */\n label: string\n /** What it is — one short line, a step back. */\n note?: string\n /** The one node the diagram is about. At most one or two per diagram. */\n accent?: boolean\n /** How this node's children are laid out. Defaults to a row. */\n direction?: 'row' | 'column'\n children?: DiagramNode[]\n /** A line printed under the frame, outside it — what the box means. */\n footnote?: string\n}\n\nexport interface DiagramEdge {\n from: string\n to: string\n label?: string\n}\n\nexport interface DiagramSpec {\n /** How the top-level nodes are laid out. Defaults to a row. */\n direction?: 'row' | 'column'\n nodes: DiagramNode[]\n /** Arrows between adjacent top-level nodes, by id. */\n edges?: DiagramEdge[]\n /** The figure's caption. */\n caption?: string\n /** Names the figure for assistive tech when the caption does not. */\n label?: string\n}\n\nexport interface DiagramProps {\n spec: DiagramSpec\n className?: string\n}\n\n/**\n * A flow or architecture diagram, drawn out of the system's own parts.\n *\n * The alternative it replaces is a fenced block of box-drawing characters —\n * `┌──────┬──────┐` — which is a picture rendered in a font chosen for code. It\n * inherits the code block's frame and scrollbar, so a diagram reads as terminal\n * output; it cannot wrap, so on a phone it either overflows or is scaled to\n * nothing; the box edges are text, so a screen reader reads the rules out loud;\n * and none of it responds to the theme.\n *\n * The other alternative is a diagramming library, which is several hundred\n * kilobytes of layout engine, renders after hydration, and draws in its own\n * palette. This is neither: hairline frames on the radius scale, mono labels,\n * the muted step for anything supporting — so a diagram belongs to the page it\n * sits on. It server-renders, because it is markup.\n *\n * NESTING IS CONTAINMENT, which is what most architecture diagrams actually\n * describe: this is inside that, and these two sit beside each other. Edges are\n * for the sequence between siblings, not for arbitrary wiring — a diagram that\n * needs arbitrary wiring is a diagram that wants a drawing, and this will not\n * pretend otherwise.\n *\n * It takes a spec rather than markup, so a fenced ```diagram block in an\n * article and a hand-written figure on a page are one renderer and one look —\n * and a wrong diagram is corrected by editing data.\n *\n * @example\n * <Diagram\n * spec={{\n * caption: 'One request, end to end.',\n * edges: [{ from: 'edge', to: 'app', label: 'HTTPS' }],\n * nodes: [\n * { id: 'edge', label: 'Edge', note: 'CDN' },\n * { id: 'app', label: 'Application', accent: true, children: [\n * { label: 'Router' },\n * { label: 'Handlers' },\n * ] },\n * ],\n * }}\n * />\n */\nexport function Diagram({ spec, className }: DiagramProps) {\n const { nodes, edges = [], direction = 'row', caption, label } = spec\n\n return (\n <figure className={cn('m-0', className)} role=\"group\" aria-label={label ?? caption ?? undefined}>\n <div className=\"overflow-x-auto rounded-(--radius-lg) border border-(--rule) bg-(--paper-2) p-[clamp(1.125rem,calc(2.5*var(--fluid)),1.875rem)] scroll-slim\">\n <NodeList nodes={nodes} edges={edges} direction={direction} depth={0} />\n </div>\n {caption && (\n // A <div>, not a <figcaption>. A long-form stylesheet dropped around\n // this will style `figcaption` and `p` unlayered, and an unlayered rule\n // beats a utility whatever the specificity — inside an article a real\n // figcaption came out uppercased and every label in the diagram\n // inherited a paragraph margin. Nothing here uses a tag prose reaches\n // for.\n <div className=\"mt-3 mono-meta leading-[1.6] text-(--ink-3-aa)\">{caption}</div>\n )}\n </figure>\n )\n}\n\n/** One rank of the diagram: the nodes, and any arrows between them. */\nfunction NodeList({\n nodes,\n edges,\n direction,\n depth,\n}: {\n nodes: DiagramNode[]\n edges: DiagramEdge[]\n direction: 'row' | 'column'\n depth: number\n}) {\n const row = direction === 'row'\n\n return (\n <div className={cn('flex min-w-0 gap-3', row ? 'flex-col sm:flex-row' : 'flex-col')}>\n {nodes.map((node, index) => (\n <NodeFrame\n key={node.id ?? `${node.label}-${index}`}\n node={node}\n edges={edges}\n depth={depth}\n lead={index > 0 ? findEdge(edges, nodes[index - 1]!, node) : undefined}\n row={row}\n />\n ))}\n </div>\n )\n}\n\n/** The arrow that sits between two adjacent nodes, when an edge names them. */\nfunction findEdge(edges: DiagramEdge[], from: DiagramNode, to: DiagramNode): DiagramEdge | undefined {\n if (!from.id || !to.id) return undefined\n return edges.find((edge) => edge.from === from.id && edge.to === to.id)\n}\n\n/**\n * A node is one of two things, and they are drawn differently on purpose.\n *\n * A LEAF is a plate: a bordered card on paper, or ink-filled when it is what\n * the diagram is about. A CONTAINER is a band: a labelled hairline with its\n * children underneath, and NO frame of its own.\n *\n * That distinction is the whole design. Drawing a container as another box puts\n * three borders around anything two levels deep — the figure's frame, the\n * group's, and the plate's — and the result reads as packaging rather than as\n * structure. A rule with a name on it says \"these belong together\" using the\n * device every section head already uses, and leaves the borders to the things\n * that are actually objects.\n */\nfunction NodeFrame({\n node,\n edges,\n depth,\n lead,\n row,\n}: {\n node: DiagramNode\n edges: DiagramEdge[]\n depth: number\n lead?: DiagramEdge\n row: boolean\n}) {\n const children = node.children ?? []\n const isGroup = children.length > 0\n\n return (\n <>\n {lead && <EdgeMark label={lead.label} row={row} />}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n {isGroup ? (\n <div\n className={cn(\n 'min-w-0 flex-1 border-t pt-3',\n depth === 0 ? 'border-(--ink)' : 'border-(--rule-2)',\n )}\n >\n <div className=\"flex flex-wrap items-baseline gap-x-3 gap-y-0.5\">\n <span className=\"font-sans text-[13.5px] leading-[1.35] text-(--ink)\">\n {node.label}\n </span>\n {node.note && <span className=\"mono-meta text-(--ink-3-aa)\">{node.note}</span>}\n </div>\n <div className=\"mt-3\">\n <NodeList\n nodes={children}\n edges={edges}\n direction={node.direction ?? 'row'}\n depth={depth + 1}\n />\n </div>\n </div>\n ) : (\n <div\n className={cn(\n 'flex min-w-0 flex-1 flex-col justify-center rounded-(--radius) px-3.5 py-3',\n node.accent ? 'bg-(--accent)' : 'border border-(--rule-2) bg-(--paper)',\n )}\n >\n {/* An identifier can be one long word — `TenantMainMiddleware` —\n with no break opportunity, so it runs into the plate's edge\n unless it is allowed to break inside itself. */}\n <span\n className={cn(\n 'block break-words font-sans text-[13.5px] leading-[1.35]',\n node.accent ? 'text-(--accent-foreground)' : 'text-(--ink)',\n )}\n >\n {node.label}\n </span>\n {node.note && (\n <span\n className={cn(\n 'mt-1 block mono-meta',\n node.accent ? 'text-(--accent-foreground) opacity-70' : 'text-(--ink-3-aa)',\n )}\n >\n {node.note}\n </span>\n )}\n </div>\n )}\n\n {node.footnote && (\n <span className=\"mt-2 block mono-meta text-(--ink-3-aa)\">{node.footnote}</span>\n )}\n </div>\n </>\n )\n}\n\n/**\n * The connector. An arrow and, when the edge is named, the name under it —\n * hidden from assistive tech, which reads the nodes in document order anyway\n * and has no use for a glyph pointing at the next one.\n */\nfunction EdgeMark({ label, row }: { label?: string; row: boolean }): ReactNode {\n return (\n <div\n aria-hidden=\"true\"\n className={cn(\n 'flex flex-none items-center justify-center gap-1.5 text-(--ink-3-aa)',\n row ? 'sm:flex-col' : 'flex-col',\n )}\n >\n {/* One glyph, turned. A row stacks into a column below `sm`, so the arrow\n has to point the way the layout actually runs at that width. In RTL the\n inline axis reverses, so the flat arrow is mirrored with it. */}\n <span\n className={cn(\n 'font-mono text-[13px] leading-none',\n row ? 'rotate-90 sm:rotate-0 sm:rtl:-scale-x-100' : 'rotate-90',\n )}\n >\n →\n </span>\n {label && <span className=\"mono-meta whitespace-nowrap\">{label}</span>}\n </div>\n )\n}\n\nexport default Diagram\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Diagram/Diagram.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { DEV, warn } from '../../lib/warn'\n\nexport interface DiagramNode {\n /** Only needed when an edge names this node. */\n id?: string\n /** The node's name, set in the interface face. */\n label: string\n /** What it is — one short line, a step back. */\n note?: string\n /** The one node the diagram is about. At most one or two per diagram. */\n accent?: boolean\n /** How this node's children are laid out. Defaults to a row. */\n direction?: 'row' | 'column'\n children?: DiagramNode[]\n /** A line printed under the frame, outside it — what the box means. */\n footnote?: string\n}\n\nexport interface DiagramEdge {\n from: string\n to: string\n label?: string\n}\n\nexport interface DiagramSpec {\n /** How the top-level nodes are laid out. Defaults to a row. */\n direction?: 'row' | 'column'\n nodes: DiagramNode[]\n /**\n * Arrows between adjacent siblings, by id and in the order the rank runs.\n * Each edge is spent the first time a pair matches it, at whatever depth\n * that pair sits — and an edge that matches nothing says so in development\n * rather than drawing nothing and reporting nothing.\n */\n edges?: DiagramEdge[]\n /** The figure's caption. */\n caption?: string\n /** Names the figure for assistive tech when the caption does not. */\n label?: string\n}\n\nexport interface DiagramProps {\n spec: DiagramSpec\n className?: string\n}\n\n/**\n * A flow or architecture diagram, drawn out of the system's own parts.\n *\n * The alternative it replaces is a fenced block of box-drawing characters —\n * `┌──────┬──────┐` — which is a picture rendered in a font chosen for code. It\n * inherits the code block's frame and scrollbar, so a diagram reads as terminal\n * output; it cannot wrap, so on a phone it either overflows or is scaled to\n * nothing; the box edges are text, so a screen reader reads the rules out loud;\n * and none of it responds to the theme.\n *\n * The other alternative is a diagramming library, which is several hundred\n * kilobytes of layout engine, renders after hydration, and draws in its own\n * palette. This is neither: hairline frames on the radius scale, mono labels,\n * the muted step for anything supporting — so a diagram belongs to the page it\n * sits on. It server-renders, because it is markup.\n *\n * NESTING IS CONTAINMENT, which is what most architecture diagrams actually\n * describe: this is inside that, and these two sit beside each other. Edges are\n * for the sequence between siblings, not for arbitrary wiring — a diagram that\n * needs arbitrary wiring is a diagram that wants a drawing, and this will not\n * pretend otherwise.\n *\n * It takes a spec rather than markup, so a fenced ```diagram block in an\n * article and a hand-written figure on a page are one renderer and one look —\n * and a wrong diagram is corrected by editing data.\n *\n * @example\n * <Diagram\n * spec={{\n * caption: 'One request, end to end.',\n * edges: [{ from: 'edge', to: 'app', label: 'HTTPS' }],\n * nodes: [\n * { id: 'edge', label: 'Edge', note: 'CDN' },\n * { id: 'app', label: 'Application', children: [\n * { label: 'Router', accent: true },\n * { label: 'Handlers' },\n * ] },\n * ],\n * }}\n * />\n */\nexport function Diagram({ spec, className }: DiagramProps) {\n const { nodes, edges = [], direction = 'row', caption, label } = spec\n const { leads, drawn } = resolveLeads(nodes, edges)\n\n if (DEV) warnSpec(nodes, edges, drawn)\n\n return (\n <figure className={cn('m-0', className)} role=\"group\" aria-label={label ?? caption ?? undefined}>\n <div className=\"overflow-x-auto rounded-(--radius-lg) border border-(--rule) bg-(--paper-2) p-[clamp(1.125rem,calc(2.5*var(--fluid)),1.875rem)] scroll-slim\">\n <NodeList nodes={nodes} leads={leads} direction={direction} depth={0} />\n </div>\n {caption && (\n // A <div>, not a <figcaption>. A long-form stylesheet dropped around\n // this will style `figcaption` and `p` unlayered, and an unlayered rule\n // beats a utility whatever the specificity — inside an article a real\n // figcaption came out uppercased and every label in the diagram\n // inherited a paragraph margin. Nothing here uses a tag prose reaches\n // for.\n <div className=\"mt-3 mono-meta leading-[1.6] text-(--ink-3-aa)\">{caption}</div>\n )}\n </figure>\n )\n}\n\n/** The arrow that leads INTO a node, for every node that earned one. */\ntype Leads = Map<DiagramNode, DiagramEdge>\n\n/**\n * Every arrow the spec earns, resolved once for the whole figure.\n *\n * Keyed by the node the arrow leads into, by OBJECT rather than by id: two\n * nodes sharing an id are a defect in the spec, not a licence to draw the same\n * arrow in two places. The same `edges` array used to be handed to every rank,\n * so a pair of ids reused two levels down drew the arrow again down there — an\n * arrow the author asked for once and the figure asserted twice.\n *\n * Ranks are walked breadth-first from the top and an edge is spent at the first\n * pair that matches it, which makes \"one edge, one arrow\" a property of the\n * renderer rather than a rule the author has to keep.\n */\nfunction resolveLeads(nodes: DiagramNode[], edges: DiagramEdge[]): { leads: Leads; drawn: Set<DiagramEdge> } {\n const leads: Leads = new Map()\n const drawn = new Set<DiagramEdge>()\n const ranks: DiagramNode[][] = [nodes]\n\n while (ranks.length > 0) {\n const rank = ranks.shift()!\n for (let index = 1; index < rank.length; index += 1) {\n const from = rank[index - 1]!\n const to = rank[index]!\n if (!from.id || !to.id) continue\n const edge = edges.find(\n (candidate) =>\n !drawn.has(candidate) && candidate.from === from.id && candidate.to === to.id,\n )\n if (!edge) continue\n drawn.add(edge)\n leads.set(to, edge)\n }\n for (const node of rank) if (node.children?.length) ranks.push(node.children)\n }\n\n return { leads, drawn }\n}\n\n/** One rank of the diagram: the nodes, and any arrows between them. */\nfunction NodeList({\n nodes,\n leads,\n direction,\n depth,\n}: {\n nodes: DiagramNode[]\n leads: Leads\n direction: 'row' | 'column'\n depth: number\n}) {\n const row = direction === 'row'\n\n return (\n <div className={cn('flex min-w-0 gap-3', row ? 'flex-col sm:flex-row' : 'flex-col')}>\n {nodes.map((node, index) => (\n <NodeFrame\n key={node.id ?? `${node.label}-${index}`}\n node={node}\n leads={leads}\n depth={depth}\n lead={leads.get(node)}\n row={row}\n />\n ))}\n </div>\n )\n}\n\n/**\n * A node is one of two things, and they are drawn differently on purpose.\n *\n * A LEAF is a plate: a bordered card on paper, or ink-filled when it is what\n * the diagram is about. A CONTAINER is a band: a labelled hairline with its\n * children underneath, and NO frame of its own.\n *\n * That distinction is the whole design. Drawing a container as another box puts\n * three borders around anything two levels deep — the figure's frame, the\n * group's, and the plate's — and the result reads as packaging rather than as\n * structure. A rule with a name on it says \"these belong together\" using the\n * device every section head already uses, and leaves the borders to the things\n * that are actually objects.\n */\nfunction NodeFrame({\n node,\n leads,\n depth,\n lead,\n row,\n}: {\n node: DiagramNode\n leads: Leads\n depth: number\n lead?: DiagramEdge\n row: boolean\n}) {\n const children = node.children ?? []\n const isGroup = children.length > 0\n\n return (\n <>\n {lead && <EdgeMark label={lead.label} row={row} />}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n {isGroup ? (\n <div\n className={cn(\n 'min-w-0 flex-1 border-t pt-3',\n depth === 0 ? 'border-(--ink)' : 'border-(--rule-2)',\n )}\n >\n <div className=\"flex flex-wrap items-baseline gap-x-3 gap-y-0.5\">\n <span className=\"font-sans text-[13.5px] leading-[1.35] text-(--ink)\">\n {node.label}\n </span>\n {node.note && <span className=\"mono-meta text-(--ink-3-aa)\">{node.note}</span>}\n </div>\n <div className=\"mt-3\">\n <NodeList\n nodes={children}\n leads={leads}\n direction={node.direction ?? 'row'}\n depth={depth + 1}\n />\n </div>\n </div>\n ) : (\n <div\n className={cn(\n 'flex min-w-0 flex-1 flex-col justify-center rounded-(--radius) px-3.5 py-3',\n node.accent ? 'bg-(--accent)' : 'border border-(--rule-2) bg-(--paper)',\n )}\n >\n {/* An identifier can be one long word — `TenantMainMiddleware` —\n with no break opportunity, so it runs into the plate's edge\n unless it is allowed to break inside itself. */}\n <span\n className={cn(\n 'block break-words font-sans text-[13.5px] leading-[1.35]',\n node.accent ? 'text-(--accent-foreground)' : 'text-(--ink)',\n )}\n >\n {node.label}\n </span>\n {node.note && (\n <span\n className={cn(\n 'mt-1 block mono-meta',\n node.accent ? 'text-(--accent-foreground) opacity-70' : 'text-(--ink-3-aa)',\n )}\n >\n {node.note}\n </span>\n )}\n </div>\n )}\n\n {node.footnote && (\n <span className=\"mt-2 block mono-meta text-(--ink-3-aa)\">{node.footnote}</span>\n )}\n </div>\n </>\n )\n}\n\n/**\n * The connector. An arrow and, when the edge is named, the name under it —\n * hidden from assistive tech, which reads the nodes in document order anyway\n * and has no use for a glyph pointing at the next one.\n */\nfunction EdgeMark({ label, row }: { label?: string; row: boolean }): ReactNode {\n return (\n <div\n aria-hidden=\"true\"\n className={cn(\n 'flex flex-none items-center justify-center gap-1.5 text-(--ink-3-aa)',\n row ? 'sm:flex-col' : 'flex-col',\n )}\n >\n {/* One glyph, turned. A row stacks into a column below `sm`, so the arrow\n has to point the way the layout actually runs at that width. In RTL the\n inline axis reverses, so the flat arrow is mirrored with it. */}\n <span\n className={cn(\n 'font-mono text-[13px] leading-none',\n row ? 'rotate-90 sm:rotate-0 sm:rtl:-scale-x-100' : 'rotate-90',\n )}\n >\n →\n </span>\n {label && <span className=\"mono-meta whitespace-nowrap\">{label}</span>}\n </div>\n )\n}\n\n/**\n * Everything the spec asked for that the renderer will not do, said out loud.\n *\n * All three failures here are silent by construction: the figure renders, it\n * renders beautifully, and it describes something other than what was written.\n * An author reading the picture has no way to tell an edge that was ignored\n * from an edge they forgot, so the console is the only place left to say it.\n *\n * Dev only, and guarded at the call site so a production bundle drops the walk\n * along with the messages.\n */\nfunction warnSpec(nodes: DiagramNode[], edges: DiagramEdge[], drawn: Set<DiagramEdge>): void {\n const ids = new Set<string>()\n warnNodes(nodes, ids)\n\n for (const edge of edges) {\n if (drawn.has(edge)) continue\n const pair = `${edge.from} → ${edge.to}`\n const unknown = !ids.has(edge.from) || !ids.has(edge.to)\n\n warn({\n code: unknown ? 'DIAGRAM_EDGE_UNKNOWN_NODE' : 'DIAGRAM_EDGE_NOT_ADJACENT',\n problem: unknown\n ? `The edge ${pair} names an id no node in this spec carries, so no arrow is drawn and nothing else changes.`\n : `The edge ${pair} names two nodes that are never consecutive siblings, so no arrow is drawn anywhere.`,\n field: `spec.edges: ${pair}`,\n fix: unknown\n ? 'Give the node the id the edge names, or correct the edge to an id the spec already has.'\n : 'Write the edge from a node to the node immediately after it in the same rank, in that order. Nesting is containment here; an edge is a step between siblings.',\n component: 'Diagram',\n })\n }\n}\n\n/** The node half of {@link warnSpec}: duplicate ids, and props read by nobody. */\nfunction warnNodes(nodes: DiagramNode[], ids: Set<string>): void {\n for (const node of nodes) {\n if (node.id) {\n if (ids.has(node.id)) {\n warn({\n code: 'DIAGRAM_DUPLICATE_ID',\n problem: `Two nodes carry the id \"${node.id}\", so an edge naming it lands on whichever comes first and silently means the other.`,\n field: `spec.nodes: id \"${node.id}\"`,\n fix: 'Make every id unique across the whole spec, or drop it from the node no edge names.',\n component: 'Diagram',\n })\n }\n ids.add(node.id)\n }\n\n const isGroup = (node.children?.length ?? 0) > 0\n\n if (isGroup && node.accent) {\n warn({\n code: 'DIAGRAM_ACCENT_ON_CONTAINER',\n problem: `\"${node.label}\" has children, so it is drawn as a band — and a band has no fill for accent to take. It type-checks and paints nothing.`,\n field: `spec.nodes: accent on \"${node.label}\"`,\n fix: 'Move accent to the leaf the diagram is actually about, or drop the children so this node becomes that leaf.',\n component: 'Diagram',\n })\n }\n\n if (!isGroup && node.direction) {\n warn({\n code: 'DIAGRAM_DIRECTION_ON_LEAF',\n problem: `\"${node.label}\" has no children, so there is nothing here to lay out — direction is read only from a node that has some.`,\n field: `spec.nodes: direction on \"${node.label}\"`,\n fix: 'Set direction on the parent whose axis you meant, or on spec.direction for the top rank.',\n component: 'Diagram',\n })\n }\n\n if (node.children) warnNodes(node.children, ids)\n }\n}\n\nexport default Diagram\n"],"mappings":"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","names":[]}
@@ -7,7 +7,14 @@ declare const Dialog: react.FC<DialogPrimitive.DialogProps>;
7
7
  declare const DialogTrigger: react.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
8
8
  declare const DialogClose: react.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>;
9
9
  interface DialogContentProps extends Omit<ComponentProps<typeof DialogPrimitive.Content>, 'title' | 'className'> {
10
- /** Heading text. When omitted, a visually-hidden title is rendered for a11y. */
10
+ /**
11
+ * Heading text.
12
+ *
13
+ * Pass one even when `hideTitle` is set. Radix requires a title, so omitting
14
+ * it renders a hidden fallback reading the literal word "Dialog" — which
15
+ * satisfies an automated accessibility check and announces every unnamed
16
+ * modal in the application as the same thing. Development says so out loud.
17
+ */
11
18
  title?: ReactNode;
12
19
  /** Sub-heading under the title. */
13
20
  description?: ReactNode;
@@ -32,8 +39,16 @@ interface DialogContentProps extends Omit<ComponentProps<typeof DialogPrimitive.
32
39
  * usually by leaving focus behind in the page underneath.
33
40
  *
34
41
  * Radix requires a `Dialog.Title` whether or not one is shown, so a dialog
35
- * without a visible heading still renders a hidden one rather than logging a
36
- * warning and shipping an unnamed modal.
42
+ * without a visible heading still renders a hidden one rather than shipping an
43
+ * unnamed modal — and warns in development, because the fallback it renders is
44
+ * the literal word "Dialog" and a placeholder that passes an accessibility
45
+ * check is how the problem survives a review.
46
+ *
47
+ * Portals into the element an enclosing `OverlayContainer` names, and switches
48
+ * from viewport positioning to container positioning when there is one. A
49
+ * `fixed` panel covers the page whatever it is portalled into, so honouring
50
+ * the container without that swap would have moved the markup and left the
51
+ * picture unchanged.
37
52
  *
38
53
  * @example
39
54
  * <Dialog>
@@ -3,6 +3,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as DialogPrimitive from "@radix-ui/react-dialog";
4
4
  import { X } from "lucide-react";
5
5
  import { cn } from "../../lib/cn.js";
6
+ import { useOverlayContainer } from "../../lib/overlay-container.js";
7
+ import { DEV, warn } from "../../lib/warn.js";
6
8
  const Dialog = DialogPrimitive.Root;
7
9
  const DialogTrigger = DialogPrimitive.Trigger;
8
10
  const DialogClose = DialogPrimitive.Close;
@@ -26,12 +28,25 @@ function DialogContent({
26
28
  hideTitle = false,
27
29
  ...rest
28
30
  }) {
29
- return /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [
31
+ const container = useOverlayContainer();
32
+ if (DEV && title === void 0) {
33
+ warn({
34
+ code: "DIALOG_TITLE_MISSING",
35
+ problem: 'DialogContent has no title, so its accessible name is the fallback string "Dialog". Every unnamed modal in the app announces identically, and the check that would have caught it passes.',
36
+ field: "title",
37
+ fix: "Give it a title that says what the dialog asks. Add hideTitle when the heading would be furniture on screen.",
38
+ component: "Dialog"
39
+ });
40
+ }
41
+ return /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { container: container ?? void 0, children: [
30
42
  /* @__PURE__ */ jsx(
31
43
  DialogPrimitive.Overlay,
32
44
  {
33
45
  "data-m22-animated": true,
34
- className: "fixed inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]"
46
+ className: cn(
47
+ "inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]",
48
+ container ? "absolute" : "fixed"
49
+ )
35
50
  }
36
51
  ),
37
52
  /* @__PURE__ */ jsxs(
@@ -41,6 +56,9 @@ function DialogContent({
41
56
  className: cn(
42
57
  "fixed left-1/2 top-1/2 z-(--z-modal) max-h-[85vh] w-[min(92vw,32rem)] -translate-x-1/2 -translate-y-1/2 overflow-auto rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-6 panel-blur scroll-slim",
43
58
  "data-[state=open]:animate-[m22-panel-in_var(--duration-base)_var(--ease)]",
59
+ // Against the container's box rather than the viewport's, and capped
60
+ // by it: 92vw inside a 400px frame is not a cap at all.
61
+ container && "absolute max-h-[calc(100%-2rem)] max-w-[calc(100%-2rem)]",
44
62
  className
45
63
  ),
46
64
  ...rest,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/** Radix Dialog root + trigger + close, re-exported as typed passthroughs. */\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogClose = DialogPrimitive.Close\n\n/** Title + description stack; renders nothing when both are omitted. */\nfunction DialogHeader({\n title,\n description,\n hidden,\n}: {\n title?: ReactNode\n description?: ReactNode\n hidden: boolean\n}) {\n if (title === undefined && description === undefined) return null\n return (\n <div className={cn('mb-4 flex flex-col gap-1 pe-8', hidden && 'sr-only')}>\n {title !== undefined && (\n <DialogPrimitive.Title className=\"m-0 font-heading text-[length:var(--fs-item)] font-normal text-(--ink)\">\n {title}\n </DialogPrimitive.Title>\n )}\n {description !== undefined && (\n <DialogPrimitive.Description className=\"m-0 text-sm leading-relaxed text-(--ink-3-aa)\">\n {description}\n </DialogPrimitive.Description>\n )}\n </div>\n )\n}\n\nexport interface DialogContentProps\n extends Omit<ComponentProps<typeof DialogPrimitive.Content>, 'title' | 'className'> {\n /** Heading text. When omitted, a visually-hidden title is rendered for a11y. */\n title?: ReactNode\n /** Sub-heading under the title. */\n description?: ReactNode\n className?: string\n /** Show the top-right close control (default true). */\n showClose?: boolean\n /**\n * Keeps the title for assistive tech and hides it visually.\n *\n * For a surface whose purpose is obvious to anyone who can see it — a command\n * palette, a media lightbox — where a printed heading would be furniture. The\n * title itself is never optional: Radix requires one, and a modal with no\n * accessible name drops a screen reader into an unnamed region.\n */\n hideTitle?: boolean\n}\n\n/**\n * A modal surface: portal → scrim → centred panel.\n *\n * Radix owns the focus trap, the escape key, the scroll lock and the\n * `aria-modal` wiring — all of which a hand-rolled dialog gets subtly wrong,\n * usually by leaving focus behind in the page underneath.\n *\n * Radix requires a `Dialog.Title` whether or not one is shown, so a dialog\n * without a visible heading still renders a hidden one rather than logging a\n * warning and shipping an unnamed modal.\n *\n * @example\n * <Dialog>\n * <DialogTrigger asChild><Button>Delete</Button></DialogTrigger>\n * <DialogContent title=\"Delete file\" description=\"This cannot be undone.\">\n * <DialogClose asChild><Button variant=\"secondary\">Cancel</Button></DialogClose>\n * </DialogContent>\n * </Dialog>\n */\nexport function DialogContent({\n title,\n description,\n children,\n className,\n showClose = true,\n hideTitle = false,\n ...rest\n}: DialogContentProps) {\n return (\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay\n data-m22-animated\n className=\"fixed inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]\"\n />\n <DialogPrimitive.Content\n data-m22-animated\n className={cn(\n 'fixed left-1/2 top-1/2 z-(--z-modal) max-h-[85vh] w-[min(92vw,32rem)] -translate-x-1/2 -translate-y-1/2 overflow-auto rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-6 panel-blur scroll-slim',\n 'data-[state=open]:animate-[m22-panel-in_var(--duration-base)_var(--ease)]',\n className,\n )}\n {...rest}\n >\n <DialogHeader title={title} description={description} hidden={hideTitle} />\n\n {title === undefined && (\n <DialogPrimitive.Title className=\"sr-only\">Dialog</DialogPrimitive.Title>\n )}\n\n {showClose && (\n <DialogPrimitive.Close\n aria-label=\"Close\"\n className=\"absolute end-3 top-3 grid size-9 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={16} strokeWidth={1.5} aria-hidden />\n </DialogPrimitive.Close>\n )}\n\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n )\n}\n\nexport default Dialog\n"],"mappings":";AAwBI,SAEI,KAFJ;AAtBJ,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAElB,SAAS,UAAU;AAGZ,MAAM,SAAS,gBAAgB;AAC/B,MAAM,gBAAgB,gBAAgB;AACtC,MAAM,cAAc,gBAAgB;AAG3C,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,UAAU,UAAa,gBAAgB,OAAW,QAAO;AAC7D,SACE,qBAAC,SAAI,WAAW,GAAG,iCAAiC,UAAU,SAAS,GACpE;AAAA,cAAU,UACT,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,0EAC9B,iBACH;AAAA,IAED,gBAAgB,UACf,oBAAC,gBAAgB,aAAhB,EAA4B,WAAU,iDACpC,uBACH;AAAA,KAEJ;AAEJ;AAyCO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,GAAG;AACL,GAAuB;AACrB,SACE,qBAAC,gBAAgB,QAAhB,EACC;AAAA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,qBAAiB;AAAA,QACjB,WAAU;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,qBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEJ;AAAA,8BAAC,gBAAa,OAAc,aAA0B,QAAQ,WAAW;AAAA,UAExE,UAAU,UACT,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,WAAU,oBAAM;AAAA,UAGlD,aACC;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cACC,cAAW;AAAA,cACX,WAAU;AAAA,cAEV,8BAAC,KAAE,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC;AAAA;AAAA,UAC7C;AAAA,UAGD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\nimport { DEV, warn } from '../../lib/warn'\n\n/** Radix Dialog root + trigger + close, re-exported as typed passthroughs. */\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogClose = DialogPrimitive.Close\n\n/** Title + description stack; renders nothing when both are omitted. */\nfunction DialogHeader({\n title,\n description,\n hidden,\n}: {\n title?: ReactNode\n description?: ReactNode\n hidden: boolean\n}) {\n if (title === undefined && description === undefined) return null\n return (\n <div className={cn('mb-4 flex flex-col gap-1 pe-8', hidden && 'sr-only')}>\n {title !== undefined && (\n <DialogPrimitive.Title className=\"m-0 font-heading text-[length:var(--fs-item)] font-normal text-(--ink)\">\n {title}\n </DialogPrimitive.Title>\n )}\n {description !== undefined && (\n <DialogPrimitive.Description className=\"m-0 text-sm leading-relaxed text-(--ink-3-aa)\">\n {description}\n </DialogPrimitive.Description>\n )}\n </div>\n )\n}\n\nexport interface DialogContentProps\n extends Omit<ComponentProps<typeof DialogPrimitive.Content>, 'title' | 'className'> {\n /**\n * Heading text.\n *\n * Pass one even when `hideTitle` is set. Radix requires a title, so omitting\n * it renders a hidden fallback reading the literal word \"Dialog\" — which\n * satisfies an automated accessibility check and announces every unnamed\n * modal in the application as the same thing. Development says so out loud.\n */\n title?: ReactNode\n /** Sub-heading under the title. */\n description?: ReactNode\n className?: string\n /** Show the top-right close control (default true). */\n showClose?: boolean\n /**\n * Keeps the title for assistive tech and hides it visually.\n *\n * For a surface whose purpose is obvious to anyone who can see it — a command\n * palette, a media lightbox — where a printed heading would be furniture. The\n * title itself is never optional: Radix requires one, and a modal with no\n * accessible name drops a screen reader into an unnamed region.\n */\n hideTitle?: boolean\n}\n\n/**\n * A modal surface: portal → scrim → centred panel.\n *\n * Radix owns the focus trap, the escape key, the scroll lock and the\n * `aria-modal` wiring — all of which a hand-rolled dialog gets subtly wrong,\n * usually by leaving focus behind in the page underneath.\n *\n * Radix requires a `Dialog.Title` whether or not one is shown, so a dialog\n * without a visible heading still renders a hidden one rather than shipping an\n * unnamed modal — and warns in development, because the fallback it renders is\n * the literal word \"Dialog\" and a placeholder that passes an accessibility\n * check is how the problem survives a review.\n *\n * Portals into the element an enclosing `OverlayContainer` names, and switches\n * from viewport positioning to container positioning when there is one. A\n * `fixed` panel covers the page whatever it is portalled into, so honouring\n * the container without that swap would have moved the markup and left the\n * picture unchanged.\n *\n * @example\n * <Dialog>\n * <DialogTrigger asChild><Button>Delete</Button></DialogTrigger>\n * <DialogContent title=\"Delete file\" description=\"This cannot be undone.\">\n * <DialogClose asChild><Button variant=\"secondary\">Cancel</Button></DialogClose>\n * </DialogContent>\n * </Dialog>\n */\nexport function DialogContent({\n title,\n description,\n children,\n className,\n showClose = true,\n hideTitle = false,\n ...rest\n}: DialogContentProps) {\n const container = useOverlayContainer()\n\n if (DEV && title === undefined) {\n warn({\n code: 'DIALOG_TITLE_MISSING',\n problem:\n 'DialogContent has no title, so its accessible name is the fallback string \"Dialog\". Every unnamed modal in the app announces identically, and the check that would have caught it passes.',\n field: 'title',\n fix: 'Give it a title that says what the dialog asks. Add hideTitle when the heading would be furniture on screen.',\n component: 'Dialog',\n })\n }\n\n return (\n <DialogPrimitive.Portal container={container ?? undefined}>\n <DialogPrimitive.Overlay\n data-m22-animated\n className={cn(\n 'inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]',\n container ? 'absolute' : 'fixed',\n )}\n />\n <DialogPrimitive.Content\n data-m22-animated\n className={cn(\n 'fixed left-1/2 top-1/2 z-(--z-modal) max-h-[85vh] w-[min(92vw,32rem)] -translate-x-1/2 -translate-y-1/2 overflow-auto rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-6 panel-blur scroll-slim',\n 'data-[state=open]:animate-[m22-panel-in_var(--duration-base)_var(--ease)]',\n // Against the container's box rather than the viewport's, and capped\n // by it: 92vw inside a 400px frame is not a cap at all.\n container && 'absolute max-h-[calc(100%-2rem)] max-w-[calc(100%-2rem)]',\n className,\n )}\n {...rest}\n >\n <DialogHeader title={title} description={description} hidden={hideTitle} />\n\n {title === undefined && (\n <DialogPrimitive.Title className=\"sr-only\">Dialog</DialogPrimitive.Title>\n )}\n\n {showClose && (\n <DialogPrimitive.Close\n aria-label=\"Close\"\n className=\"absolute end-3 top-3 grid size-9 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={16} strokeWidth={1.5} aria-hidden />\n </DialogPrimitive.Close>\n )}\n\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n )\n}\n\nexport default Dialog\n"],"mappings":";AA0BI,SAEI,KAFJ;AAxBJ,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAElB,SAAS,UAAU;AACnB,SAAS,2BAA2B;AACpC,SAAS,KAAK,YAAY;AAGnB,MAAM,SAAS,gBAAgB;AAC/B,MAAM,gBAAgB,gBAAgB;AACtC,MAAM,cAAc,gBAAgB;AAG3C,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,UAAU,UAAa,gBAAgB,OAAW,QAAO;AAC7D,SACE,qBAAC,SAAI,WAAW,GAAG,iCAAiC,UAAU,SAAS,GACpE;AAAA,cAAU,UACT,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,0EAC9B,iBACH;AAAA,IAED,gBAAgB,UACf,oBAAC,gBAAgB,aAAhB,EAA4B,WAAU,iDACpC,uBACH;AAAA,KAEJ;AAEJ;AAwDO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,GAAG;AACL,GAAuB;AACrB,QAAM,YAAY,oBAAoB;AAEtC,MAAI,OAAO,UAAU,QAAW;AAC9B,SAAK;AAAA,MACH,MAAM;AAAA,MACN,SACE;AAAA,MACF,OAAO;AAAA,MACP,KAAK;AAAA,MACL,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,gBAAgB,QAAhB,EAAuB,WAAW,aAAa,QAC9C;AAAA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,qBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA,YAAY,aAAa;AAAA,QAC3B;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,qBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA;AAAA;AAAA;AAAA,UAGA,aAAa;AAAA,UACb;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEJ;AAAA,8BAAC,gBAAa,OAAc,aAA0B,QAAQ,WAAW;AAAA,UAExE,UAAU,UACT,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,WAAU,oBAAM;AAAA,UAGlD,aACC;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cACC,cAAW;AAAA,cACX,WAAU;AAAA,cAEV,8BAAC,KAAE,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC;AAAA;AAAA,UAC7C;AAAA,UAGD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ComponentProps } from 'react';
2
+ import { ComponentProps, ReactNode } from 'react';
3
3
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
4
4
  import { LucideIcon } from 'lucide-react';
5
5
 
@@ -11,16 +11,17 @@ type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive.Cont
11
11
  * A menu surface.
12
12
  *
13
13
  * A menu is a list of ACTIONS. If the items navigate somewhere, they belong in
14
- * a nav; if they set a value, that is a `Select` or a `RadioGroup` — Radix has
15
- * menu variants for both, and a plain item pretending to be a choice loses the
16
- * checked state a screen reader needs.
14
+ * a nav; if they set a value, that is a `Select` or a `RadioGroup` — this
15
+ * package ships no checkbox or radio menu item, and a plain item pretending to
16
+ * be a choice loses the checked state a screen reader needs.
17
17
  *
18
18
  * @example
19
19
  * <DropdownMenu>
20
20
  * <DropdownMenuTrigger asChild><Button variant="secondary">Menu</Button></DropdownMenuTrigger>
21
21
  * <DropdownMenuContent>
22
- * <DropdownMenuLabel>Account</DropdownMenuLabel>
23
- * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>
22
+ * <DropdownMenuGroup label="Account">
23
+ * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>
24
+ * </DropdownMenuGroup>
24
25
  * <DropdownMenuSeparator />
25
26
  * <DropdownMenuItem icon={LogOut}>Sign out</DropdownMenuItem>
26
27
  * </DropdownMenuContent>
@@ -28,8 +29,16 @@ type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive.Cont
28
29
  */
29
30
  declare function DropdownMenuContent({ className, sideOffset, children, ...rest }: DropdownMenuContentProps): react.JSX.Element;
30
31
  interface DropdownMenuItemProps extends ComponentProps<typeof DropdownMenuPrimitive.Item> {
31
- /** Optional leading icon, rendered before the label. */
32
- icon?: LucideIcon;
32
+ /**
33
+ * Optional leading icon, rendered before the label.
34
+ *
35
+ * Either spelling: `icon={Settings}` passes the component and this sizes it,
36
+ * `icon={<Settings size={16} />}` passes the element and this places it. The
37
+ * two used to mean opposite things one import apart — `CommandItem.icon` took
38
+ * the element while this took the component — and the wrong one did not fail
39
+ * a type check into anything actionable, it failed at render.
40
+ */
41
+ icon?: LucideIcon | ReactNode;
33
42
  /** Paints the row as destructive. Use for delete, revoke, disconnect. */
34
43
  destructive?: boolean;
35
44
  }
@@ -40,10 +49,39 @@ interface DropdownMenuItemProps extends ComponentProps<typeof DropdownMenuPrimit
40
49
  * hover and keyboard focus — styling `:hover` alone leaves the keyboard user
41
50
  * unable to see where they are.
42
51
  */
43
- declare function DropdownMenuItem({ icon: Icon, destructive, className, children, ...rest }: DropdownMenuItemProps): react.JSX.Element;
52
+ declare function DropdownMenuItem({ icon, destructive, className, children, ...rest }: DropdownMenuItemProps): react.JSX.Element;
44
53
  /** Hairline divider between menu groups. */
45
54
  declare function DropdownMenuSeparator({ className, ...rest }: ComponentProps<typeof DropdownMenuPrimitive.Separator>): react.JSX.Element;
46
- /** Mono eyebrow heading for a group of items. */
55
+ /**
56
+ * Mono eyebrow heading, on its own.
57
+ *
58
+ * Visual only: Radix renders it as a bare `<div>` with no role, so it segments
59
+ * the menu for a reader who can see it and for nobody else. Reach for
60
+ * `DropdownMenuGroup` when the eyebrow is a HEADING over rows; this is right
61
+ * for a line that heads nothing — the signed-in address at the top of an
62
+ * account menu.
63
+ */
47
64
  declare function DropdownMenuLabel({ className, ...rest }: ComponentProps<typeof DropdownMenuPrimitive.Label>): react.JSX.Element;
65
+ interface DropdownMenuGroupProps extends ComponentProps<typeof DropdownMenuPrimitive.Group> {
66
+ /** The eyebrow over the rows, and the group's accessible name. */
67
+ label?: ReactNode;
68
+ }
69
+ /**
70
+ * A named section of a menu.
71
+ *
72
+ * The eyebrow alone was a picture of a heading. Radix's `MenuLabel` carries no
73
+ * role and no `aria-labelledby` wiring, and `MenuGroup` — which does carry
74
+ * `role="group"` — was not re-exported by this package at all, so a sighted
75
+ * reader saw three labelled sections and a screen-reader user got one
76
+ * undifferentiated list. This renders the group, renders the label inside it,
77
+ * and points the one at the other, which is the whole of the fix and not
78
+ * something a caller should have to remember.
79
+ *
80
+ * @example
81
+ * <DropdownMenuGroup label="Account">
82
+ * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>
83
+ * </DropdownMenuGroup>
84
+ */
85
+ declare function DropdownMenuGroup({ label, children, ...rest }: DropdownMenuGroupProps): react.JSX.Element;
48
86
 
49
- export { DropdownMenu, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenu as default };
87
+ export { DropdownMenu, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, type DropdownMenuGroupProps, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenu as default };
@@ -1,8 +1,23 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
4
+ import { isValidElement, useId } from "react";
4
5
  import { cn } from "../../lib/cn.js";
5
6
  import { useOverlayContainer } from "../../lib/overlay-container.js";
7
+ function isIconComponent(icon) {
8
+ if (isValidElement(icon)) return false;
9
+ if (typeof icon === "function") return true;
10
+ if (typeof icon !== "object" || icon === null) return false;
11
+ return !Array.isArray(icon) && !(Symbol.iterator in icon) && !("then" in icon);
12
+ }
13
+ function iconNode(icon) {
14
+ if (icon === void 0 || icon === null || icon === false) return null;
15
+ if (!isIconComponent(icon)) {
16
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "grid size-4 shrink-0 place-items-center [&_svg]:size-4", children: icon });
17
+ }
18
+ const Icon = icon;
19
+ return /* @__PURE__ */ jsx(Icon, { size: 16, strokeWidth: 1.5, className: "shrink-0", "aria-hidden": true });
20
+ }
6
21
  const DropdownMenu = DropdownMenuPrimitive.Root;
7
22
  const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
8
23
  function DropdownMenuContent({
@@ -30,7 +45,7 @@ function DropdownMenuContent({
30
45
  ) });
31
46
  }
32
47
  function DropdownMenuItem({
33
- icon: Icon,
48
+ icon,
34
49
  destructive = false,
35
50
  className,
36
51
  children,
@@ -46,7 +61,7 @@ function DropdownMenuItem({
46
61
  ),
47
62
  ...rest,
48
63
  children: [
49
- Icon && /* @__PURE__ */ jsx(Icon, { size: 16, strokeWidth: 1.5, className: "shrink-0", "aria-hidden": true }),
64
+ iconNode(icon),
50
65
  children
51
66
  ]
52
67
  }
@@ -76,10 +91,25 @@ function DropdownMenuLabel({
76
91
  }
77
92
  );
78
93
  }
94
+ function DropdownMenuGroup({ label, children, ...rest }) {
95
+ const id = useId();
96
+ return /* @__PURE__ */ jsxs(
97
+ DropdownMenuPrimitive.Group,
98
+ {
99
+ "aria-labelledby": label === void 0 ? void 0 : id,
100
+ ...rest,
101
+ children: [
102
+ label !== void 0 && /* @__PURE__ */ jsx(DropdownMenuLabel, { id, children: label }),
103
+ children
104
+ ]
105
+ }
106
+ );
107
+ }
79
108
  var DropdownMenu_default = DropdownMenu;
80
109
  export {
81
110
  DropdownMenu,
82
111
  DropdownMenuContent,
112
+ DropdownMenuGroup,
83
113
  DropdownMenuItem,
84
114
  DropdownMenuLabel,
85
115
  DropdownMenuSeparator,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["'use client'\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'\nimport type { LucideIcon } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\n\n/** Radix DropdownMenu root + trigger, re-exported as typed passthroughs. */\nexport const DropdownMenu = DropdownMenuPrimitive.Root\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nexport type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive.Content>\n\n/**\n * A menu surface.\n *\n * A menu is a list of ACTIONS. If the items navigate somewhere, they belong in\n * a nav; if they set a value, that is a `Select` or a `RadioGroup` — Radix has\n * menu variants for both, and a plain item pretending to be a choice loses the\n * checked state a screen reader needs.\n *\n * @example\n * <DropdownMenu>\n * <DropdownMenuTrigger asChild><Button variant=\"secondary\">Menu</Button></DropdownMenuTrigger>\n * <DropdownMenuContent>\n * <DropdownMenuLabel>Account</DropdownMenuLabel>\n * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>\n * <DropdownMenuSeparator />\n * <DropdownMenuItem icon={LogOut}>Sign out</DropdownMenuItem>\n * </DropdownMenuContent>\n * </DropdownMenu>\n */\nexport function DropdownMenuContent({\n className,\n sideOffset = 6,\n children,\n ...rest\n}: DropdownMenuContentProps) {\n const container = useOverlayContainer()\n\n return (\n <DropdownMenuPrimitive.Portal container={container ?? undefined}>\n <DropdownMenuPrimitive.Content\n collisionBoundary={container ?? undefined}\n collisionPadding={8}\n sideOffset={sideOffset}\n data-m22-animated\n className={cn(\n 'z-(--z-dropdown) min-w-44 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-1.5 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 </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n )\n}\n\nexport interface DropdownMenuItemProps extends ComponentProps<typeof DropdownMenuPrimitive.Item> {\n /** Optional leading icon, rendered before the label. */\n icon?: LucideIcon\n /** Paints the row as destructive. Use for delete, revoke, disconnect. */\n destructive?: boolean\n}\n\n/**\n * A menu row.\n *\n * Highlight is driven by Radix's `data-highlighted`, which covers both pointer\n * hover and keyboard focus — styling `:hover` alone leaves the keyboard user\n * unable to see where they are.\n */\nexport function DropdownMenuItem({\n icon: Icon,\n destructive = false,\n className,\n children,\n ...rest\n}: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n className={cn(\n 'flex cursor-pointer items-center gap-2.5 rounded-(--radius-row) px-2.5 py-2 text-sm outline-none transition-colors duration-(--duration-fast) data-[highlighted]:bg-(--stone) data-[disabled]:pointer-events-none data-[disabled]:opacity-(--disabled-opacity)',\n destructive ? 'text-(--danger)' : 'text-(--ink-2) data-[highlighted]:text-(--ink)',\n className,\n )}\n {...rest}\n >\n {Icon && <Icon size={16} strokeWidth={1.5} className=\"shrink-0\" aria-hidden />}\n {children}\n </DropdownMenuPrimitive.Item>\n )\n}\n\n/** Hairline divider between menu groups. */\nexport function DropdownMenuSeparator({\n className,\n ...rest\n}: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n className={cn('my-1 h-px bg-(--rule)', className)}\n {...rest}\n />\n )\n}\n\n/** Mono eyebrow heading for a group of items. */\nexport function DropdownMenuLabel({\n className,\n ...rest\n}: ComponentProps<typeof DropdownMenuPrimitive.Label>) {\n return (\n <DropdownMenuPrimitive.Label\n className={cn('px-2.5 py-1.5 eyebrow text-(--ink-3-aa)', className)}\n {...rest}\n />\n )\n}\n\nexport default DropdownMenu\n"],"mappings":";AA2CM,cAwCF,YAxCE;AAzCN,YAAY,2BAA2B;AAGvC,SAAS,UAAU;AACnB,SAAS,2BAA2B;AAG7B,MAAM,eAAe,sBAAsB;AAC3C,MAAM,sBAAsB,sBAAsB;AAuBlD,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAA6B;AAC3B,QAAM,YAAY,oBAAoB;AAEtC,SACE,oBAAC,sBAAsB,QAAtB,EAA6B,WAAW,aAAa,QACpD;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,mBAAmB,aAAa;AAAA,MAChC,kBAAkB;AAAA,MAClB;AAAA,MACA,qBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH,GACF;AAEJ;AAgBO,SAAS,iBAAiB;AAAA,EAC/B,MAAM;AAAA,EACN,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,cAAc,oBAAoB;AAAA,QAClC;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,gBAAQ,oBAAC,QAAK,MAAM,IAAI,aAAa,KAAK,WAAU,YAAW,eAAW,MAAC;AAAA,QAC3E;AAAA;AAAA;AAAA,EACH;AAEJ;AAGO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA,GAAG;AACL,GAA2D;AACzD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW,GAAG,yBAAyB,SAAS;AAAA,MAC/C,GAAG;AAAA;AAAA,EACN;AAEJ;AAGO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,GAAG;AACL,GAAuD;AACrD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW,GAAG,2CAA2C,SAAS;AAAA,MACjE,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,IAAO,uBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["'use client'\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'\nimport type { LucideIcon } from 'lucide-react'\nimport { isValidElement, useId, type ComponentProps, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\n\n/**\n * True for a component type — `icon={Settings}` — rather than a rendered element.\n *\n * Everything a `ReactNode` can be is an element, a primitive, an array, another\n * iterable or a promise; a Lucide icon is none of those, it is a `forwardRef`\n * object. So both spellings are tellable apart at runtime, which is what lets\n * one prop take either — and it has to, because this prop meant the opposite\n * thing one import away and the wrong spelling failed at render, not at the\n * type.\n */\nfunction isIconComponent(icon: LucideIcon | ReactNode): icon is LucideIcon {\n if (isValidElement(icon)) return false\n if (typeof icon === 'function') return true\n if (typeof icon !== 'object' || icon === null) return false\n return !Array.isArray(icon) && !(Symbol.iterator in icon) && !('then' in icon)\n}\n\n/** The leading glyph, sized by this component when it was handed a component. */\nfunction iconNode(icon: LucideIcon | ReactNode): ReactNode {\n if (icon === undefined || icon === null || icon === false) return null\n if (!isIconComponent(icon)) {\n return (\n <span aria-hidden className=\"grid size-4 shrink-0 place-items-center [&_svg]:size-4\">\n {icon}\n </span>\n )\n }\n const Icon = icon\n return <Icon size={16} strokeWidth={1.5} className=\"shrink-0\" aria-hidden />\n}\n\n/** Radix DropdownMenu root + trigger, re-exported as typed passthroughs. */\nexport const DropdownMenu = DropdownMenuPrimitive.Root\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nexport type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive.Content>\n\n/**\n * A menu surface.\n *\n * A menu is a list of ACTIONS. If the items navigate somewhere, they belong in\n * a nav; if they set a value, that is a `Select` or a `RadioGroup` — this\n * package ships no checkbox or radio menu item, and a plain item pretending to\n * be a choice loses the checked state a screen reader needs.\n *\n * @example\n * <DropdownMenu>\n * <DropdownMenuTrigger asChild><Button variant=\"secondary\">Menu</Button></DropdownMenuTrigger>\n * <DropdownMenuContent>\n * <DropdownMenuGroup label=\"Account\">\n * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>\n * </DropdownMenuGroup>\n * <DropdownMenuSeparator />\n * <DropdownMenuItem icon={LogOut}>Sign out</DropdownMenuItem>\n * </DropdownMenuContent>\n * </DropdownMenu>\n */\nexport function DropdownMenuContent({\n className,\n sideOffset = 6,\n children,\n ...rest\n}: DropdownMenuContentProps) {\n const container = useOverlayContainer()\n\n return (\n <DropdownMenuPrimitive.Portal container={container ?? undefined}>\n <DropdownMenuPrimitive.Content\n collisionBoundary={container ?? undefined}\n collisionPadding={8}\n sideOffset={sideOffset}\n data-m22-animated\n className={cn(\n 'z-(--z-dropdown) min-w-44 rounded-(--radius-lg) border border-(--panel-border) bg-(--panel-bg) p-1.5 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 </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n )\n}\n\nexport interface DropdownMenuItemProps extends ComponentProps<typeof DropdownMenuPrimitive.Item> {\n /**\n * Optional leading icon, rendered before the label.\n *\n * Either spelling: `icon={Settings}` passes the component and this sizes it,\n * `icon={<Settings size={16} />}` passes the element and this places it. The\n * two used to mean opposite things one import apart — `CommandItem.icon` took\n * the element while this took the component — and the wrong one did not fail\n * a type check into anything actionable, it failed at render.\n */\n icon?: LucideIcon | ReactNode\n /** Paints the row as destructive. Use for delete, revoke, disconnect. */\n destructive?: boolean\n}\n\n/**\n * A menu row.\n *\n * Highlight is driven by Radix's `data-highlighted`, which covers both pointer\n * hover and keyboard focus — styling `:hover` alone leaves the keyboard user\n * unable to see where they are.\n */\nexport function DropdownMenuItem({\n icon,\n destructive = false,\n className,\n children,\n ...rest\n}: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n className={cn(\n 'flex cursor-pointer items-center gap-2.5 rounded-(--radius-row) px-2.5 py-2 text-sm outline-none transition-colors duration-(--duration-fast) data-[highlighted]:bg-(--stone) data-[disabled]:pointer-events-none data-[disabled]:opacity-(--disabled-opacity)',\n destructive ? 'text-(--danger)' : 'text-(--ink-2) data-[highlighted]:text-(--ink)',\n className,\n )}\n {...rest}\n >\n {iconNode(icon)}\n {children}\n </DropdownMenuPrimitive.Item>\n )\n}\n\n/** Hairline divider between menu groups. */\nexport function DropdownMenuSeparator({\n className,\n ...rest\n}: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n className={cn('my-1 h-px bg-(--rule)', className)}\n {...rest}\n />\n )\n}\n\n/**\n * Mono eyebrow heading, on its own.\n *\n * Visual only: Radix renders it as a bare `<div>` with no role, so it segments\n * the menu for a reader who can see it and for nobody else. Reach for\n * `DropdownMenuGroup` when the eyebrow is a HEADING over rows; this is right\n * for a line that heads nothing — the signed-in address at the top of an\n * account menu.\n */\nexport function DropdownMenuLabel({\n className,\n ...rest\n}: ComponentProps<typeof DropdownMenuPrimitive.Label>) {\n return (\n <DropdownMenuPrimitive.Label\n className={cn('px-2.5 py-1.5 eyebrow text-(--ink-3-aa)', className)}\n {...rest}\n />\n )\n}\n\nexport interface DropdownMenuGroupProps\n extends ComponentProps<typeof DropdownMenuPrimitive.Group> {\n /** The eyebrow over the rows, and the group's accessible name. */\n label?: ReactNode\n}\n\n/**\n * A named section of a menu.\n *\n * The eyebrow alone was a picture of a heading. Radix's `MenuLabel` carries no\n * role and no `aria-labelledby` wiring, and `MenuGroup` — which does carry\n * `role=\"group\"` — was not re-exported by this package at all, so a sighted\n * reader saw three labelled sections and a screen-reader user got one\n * undifferentiated list. This renders the group, renders the label inside it,\n * and points the one at the other, which is the whole of the fix and not\n * something a caller should have to remember.\n *\n * @example\n * <DropdownMenuGroup label=\"Account\">\n * <DropdownMenuItem icon={Settings}>Settings</DropdownMenuItem>\n * </DropdownMenuGroup>\n */\nexport function DropdownMenuGroup({ label, children, ...rest }: DropdownMenuGroupProps) {\n const id = useId()\n\n return (\n <DropdownMenuPrimitive.Group\n aria-labelledby={label === undefined ? undefined : id}\n {...rest}\n >\n {label !== undefined && <DropdownMenuLabel id={id}>{label}</DropdownMenuLabel>}\n {children}\n </DropdownMenuPrimitive.Group>\n )\n}\n\nexport default DropdownMenu\n"],"mappings":";AA8BM,cA6FF,YA7FE;AA5BN,YAAY,2BAA2B;AAEvC,SAAS,gBAAgB,aAAkD;AAC3E,SAAS,UAAU;AACnB,SAAS,2BAA2B;AAYpC,SAAS,gBAAgB,MAAkD;AACzE,MAAI,eAAe,IAAI,EAAG,QAAO;AACjC,MAAI,OAAO,SAAS,WAAY,QAAO;AACvC,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,SAAO,CAAC,MAAM,QAAQ,IAAI,KAAK,EAAE,OAAO,YAAY,SAAS,EAAE,UAAU;AAC3E;AAGA,SAAS,SAAS,MAAyC;AACzD,MAAI,SAAS,UAAa,SAAS,QAAQ,SAAS,MAAO,QAAO;AAClE,MAAI,CAAC,gBAAgB,IAAI,GAAG;AAC1B,WACE,oBAAC,UAAK,eAAW,MAAC,WAAU,0DACzB,gBACH;AAAA,EAEJ;AACA,QAAM,OAAO;AACb,SAAO,oBAAC,QAAK,MAAM,IAAI,aAAa,KAAK,WAAU,YAAW,eAAW,MAAC;AAC5E;AAGO,MAAM,eAAe,sBAAsB;AAC3C,MAAM,sBAAsB,sBAAsB;AAwBlD,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAA6B;AAC3B,QAAM,YAAY,oBAAoB;AAEtC,SACE,oBAAC,sBAAsB,QAAtB,EAA6B,WAAW,aAAa,QACpD;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,mBAAmB,aAAa;AAAA,MAChC,kBAAkB;AAAA,MAClB;AAAA,MACA,qBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH,GACF;AAEJ;AAwBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,cAAc,oBAAoB;AAAA,QAClC;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,iBAAS,IAAI;AAAA,QACb;AAAA;AAAA;AAAA,EACH;AAEJ;AAGO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA,GAAG;AACL,GAA2D;AACzD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW,GAAG,yBAAyB,SAAS;AAAA,MAC/C,GAAG;AAAA;AAAA,EACN;AAEJ;AAWO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,GAAG;AACL,GAAuD;AACrD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,WAAW,GAAG,2CAA2C,SAAS;AAAA,MACjE,GAAG;AAAA;AAAA,EACN;AAEJ;AAwBO,SAAS,kBAAkB,EAAE,OAAO,UAAU,GAAG,KAAK,GAA2B;AACtF,QAAM,KAAK,MAAM;AAEjB,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,mBAAiB,UAAU,SAAY,SAAY;AAAA,MAClD,GAAG;AAAA,MAEH;AAAA,kBAAU,UAAa,oBAAC,qBAAkB,IAAS,iBAAM;AAAA,QACzD;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,uBAAQ;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
  import { LucideIcon } from 'lucide-react';
4
+ import { HeadingLevel } from '../Heading/Heading.js';
4
5
 
5
6
  interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
6
7
  /** Optional lucide icon, shown muted inside a quiet circle. */
@@ -9,6 +10,26 @@ interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>
9
10
  description?: ReactNode;
10
11
  /** The one thing to do next. An empty state without an action is a dead end. */
11
12
  action?: ReactNode;
13
+ /**
14
+ * The heading level `title` opens, in the document the empty state lands in.
15
+ *
16
+ * Defaults to `2`, which is the placement this component is sized for: it
17
+ * stands in for a whole view inside a page that already has an `h1`, so the
18
+ * level below that one is the level that does not leave a hole in heading
19
+ * navigation. Under a section that already has its own `h2`, pass `3`.
20
+ *
21
+ * The default was `3` and fixed, which was wrong in the ordinary case and
22
+ * impossible to correct — the documentation could name the rule but a caller
23
+ * had no prop to follow it with. `2` is the common case rather than a
24
+ * compromise, and it is optional rather than required because the correct
25
+ * placement has one answer often enough that making every existing call site
26
+ * restate it would buy nothing.
27
+ *
28
+ * The size does not follow the level. `title` renders at `--fs-sub` at every
29
+ * level, because moving a state down the outline is a fact about the document
30
+ * and not a request for smaller type.
31
+ */
32
+ level?: HeadingLevel;
12
33
  }
13
34
  /**
14
35
  * A collection with nothing in it — yet.
@@ -25,7 +46,10 @@ interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>
25
46
  * description="Create your first project to get started."
26
47
  * action={<Button>New project</Button>}
27
48
  * />
49
+ * @example
50
+ * // Inside a section that already has its own heading.
51
+ * <EmptyState level={3} title="No invoices yet" />
28
52
  */
29
- declare function EmptyState({ icon: Icon, title, description, action, className, ...rest }: EmptyStateProps): react.JSX.Element;
53
+ declare function EmptyState({ icon: Icon, title, description, action, level, className, ...rest }: EmptyStateProps): react.JSX.Element;
30
54
 
31
55
  export { EmptyState, type EmptyStateProps, EmptyState as default };
@@ -1,10 +1,12 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Heading } from "../Heading/Heading.js";
2
3
  import { cn } from "../../lib/cn.js";
3
4
  function EmptyState({
4
5
  icon: Icon,
5
6
  title,
6
7
  description,
7
8
  action,
9
+ level = 2,
8
10
  className,
9
11
  ...rest
10
12
  }) {
@@ -15,7 +17,7 @@ function EmptyState({
15
17
  ...rest,
16
18
  children: [
17
19
  Icon && /* @__PURE__ */ jsx("span", { className: "mb-6 flex size-14 items-center justify-center rounded-(--radius-pill) bg-(--stone) text-(--ink-3-aa)", children: /* @__PURE__ */ jsx(Icon, { size: 24, strokeWidth: 1.5, "aria-hidden": true }) }),
18
- /* @__PURE__ */ jsx("h3", { className: "m-0 font-heading text-[length:var(--fs-sub)] font-normal text-(--ink)", children: title }),
20
+ /* @__PURE__ */ jsx(Heading, { level, size: "sub", children: title }),
19
21
  description && /* @__PURE__ */ jsx("p", { className: "m-0 mt-2 max-w-sm text-sm leading-relaxed text-(--ink-3-aa)", children: description }),
20
22
  action && /* @__PURE__ */ jsx("div", { className: "mt-8", children: action })
21
23
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import type { LucideIcon } from 'lucide-react'\nimport type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Optional lucide icon, shown muted inside a quiet circle. */\n icon?: LucideIcon\n title: ReactNode\n description?: ReactNode\n /** The one thing to do next. An empty state without an action is a dead end. */\n action?: ReactNode\n}\n\n/**\n * A collection with nothing in it — yet.\n *\n * Distinct from `ErrorState`: nothing went wrong here, so the copy says what to\n * do rather than what failed. The two are different components because the\n * shared temptation — one component with a `variant` — produces error pages\n * that read like empty folders.\n *\n * @example\n * <EmptyState\n * icon={Inbox}\n * title=\"No projects yet\"\n * description=\"Create your first project to get started.\"\n * action={<Button>New project</Button>}\n * />\n */\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n action,\n className,\n ...rest\n}: EmptyStateProps) {\n return (\n <div\n className={cn('flex flex-col items-center px-(--page-pad) py-20 text-center', className)}\n {...rest}\n >\n {Icon && (\n <span className=\"mb-6 flex size-14 items-center justify-center rounded-(--radius-pill) bg-(--stone) text-(--ink-3-aa)\">\n <Icon size={24} strokeWidth={1.5} aria-hidden />\n </span>\n )}\n <h3 className=\"m-0 font-heading text-[length:var(--fs-sub)] font-normal text-(--ink)\">\n {title}\n </h3>\n {description && (\n <p className=\"m-0 mt-2 max-w-sm text-sm leading-relaxed text-(--ink-3-aa)\">{description}</p>\n )}\n {action && <div className=\"mt-8\">{action}</div>}\n </div>\n )\n}\n\nexport default EmptyState\n"],"mappings":"AAsCI,SAMM,KANN;AApCJ,SAAS,UAAU;AA2BZ,SAAS,WAAW;AAAA,EACzB,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACtF,GAAG;AAAA,MAEH;AAAA,gBACC,oBAAC,UAAK,WAAU,wGACd,8BAAC,QAAK,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,GAChD;AAAA,QAEF,oBAAC,QAAG,WAAU,yEACX,iBACH;AAAA,QACC,eACC,oBAAC,OAAE,WAAU,+DAA+D,uBAAY;AAAA,QAEzF,UAAU,oBAAC,SAAI,WAAU,QAAQ,kBAAO;AAAA;AAAA;AAAA,EAC3C;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import type { LucideIcon } from 'lucide-react'\nimport type { HTMLAttributes, ReactNode } from 'react'\nimport { Heading, type HeadingLevel } from '../Heading/Heading'\nimport { cn } from '../../lib/cn'\n\nexport interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Optional lucide icon, shown muted inside a quiet circle. */\n icon?: LucideIcon\n title: ReactNode\n description?: ReactNode\n /** The one thing to do next. An empty state without an action is a dead end. */\n action?: ReactNode\n /**\n * The heading level `title` opens, in the document the empty state lands in.\n *\n * Defaults to `2`, which is the placement this component is sized for: it\n * stands in for a whole view inside a page that already has an `h1`, so the\n * level below that one is the level that does not leave a hole in heading\n * navigation. Under a section that already has its own `h2`, pass `3`.\n *\n * The default was `3` and fixed, which was wrong in the ordinary case and\n * impossible to correct — the documentation could name the rule but a caller\n * had no prop to follow it with. `2` is the common case rather than a\n * compromise, and it is optional rather than required because the correct\n * placement has one answer often enough that making every existing call site\n * restate it would buy nothing.\n *\n * The size does not follow the level. `title` renders at `--fs-sub` at every\n * level, because moving a state down the outline is a fact about the document\n * and not a request for smaller type.\n */\n level?: HeadingLevel\n}\n\n/**\n * A collection with nothing in it — yet.\n *\n * Distinct from `ErrorState`: nothing went wrong here, so the copy says what to\n * do rather than what failed. The two are different components because the\n * shared temptation — one component with a `variant` — produces error pages\n * that read like empty folders.\n *\n * @example\n * <EmptyState\n * icon={Inbox}\n * title=\"No projects yet\"\n * description=\"Create your first project to get started.\"\n * action={<Button>New project</Button>}\n * />\n * @example\n * // Inside a section that already has its own heading.\n * <EmptyState level={3} title=\"No invoices yet\" />\n */\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n action,\n level = 2,\n className,\n ...rest\n}: EmptyStateProps) {\n return (\n <div\n className={cn('flex flex-col items-center px-(--page-pad) py-20 text-center', className)}\n {...rest}\n >\n {Icon && (\n <span className=\"mb-6 flex size-14 items-center justify-center rounded-(--radius-pill) bg-(--stone) text-(--ink-3-aa)\">\n <Icon size={24} strokeWidth={1.5} aria-hidden />\n </span>\n )}\n <Heading level={level} size=\"sub\">\n {title}\n </Heading>\n {description && (\n <p className=\"m-0 mt-2 max-w-sm text-sm leading-relaxed text-(--ink-3-aa)\">{description}</p>\n )}\n {action && <div className=\"mt-8\">{action}</div>}\n </div>\n )\n}\n\nexport default EmptyState\n"],"mappings":"AA+DI,SAMM,KANN;AA7DJ,SAAS,eAAkC;AAC3C,SAAS,UAAU;AAkDZ,SAAS,WAAW;AAAA,EACzB,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACtF,GAAG;AAAA,MAEH;AAAA,gBACC,oBAAC,UAAK,WAAU,wGACd,8BAAC,QAAK,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,GAChD;AAAA,QAEF,oBAAC,WAAQ,OAAc,MAAK,OACzB,iBACH;AAAA,QACC,eACC,oBAAC,OAAE,WAAU,+DAA+D,uBAAY;AAAA,QAEzF,UAAU,oBAAC,SAAI,WAAU,QAAQ,kBAAO;AAAA;AAAA;AAAA,EAC3C;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
+ import { HeadingLevel } from '../Heading/Heading.js';
3
4
 
4
5
  interface ErrorStateProps extends HTMLAttributes<HTMLElement> {
5
6
  /** The status, set large — "404", "500". Decorative; the heading carries the meaning. */
@@ -8,6 +9,23 @@ interface ErrorStateProps extends HTMLAttributes<HTMLElement> {
8
9
  message: ReactNode;
9
10
  /** The way back. Render it with {@link ERROR_ACTION_CLASS}. */
10
11
  action: ReactNode;
12
+ /**
13
+ * The heading level `heading` opens, in the document the error state lands in.
14
+ *
15
+ * Defaults to `1`, and that is not a compromise: this component replaces the
16
+ * page rather than sitting inside one — its own ground, its own viewport, its
17
+ * own top clearance — so the page's single `h1` is the one it renders. Kept
18
+ * as the default, existing call sites render exactly the markup they did.
19
+ *
20
+ * The prop exists because the level was previously fixed, and the one piece
21
+ * of advice this component's documentation gives about headings — do not put
22
+ * it inside a shell that already has an `h1` — was advice a caller had no way
23
+ * to take. Now they do: inside a shell that owns the page heading, pass `2`.
24
+ *
25
+ * The size does not follow the level. `heading` renders at `--fs-heading` at
26
+ * every level; a failure demoted in the outline is the same failure.
27
+ */
28
+ level?: HeadingLevel;
11
29
  }
12
30
  /**
13
31
  * The recovery affordance's look, exported so every error page offers the same
@@ -28,7 +46,10 @@ declare const ERROR_ACTION_CLASS = "text-sm text-(--ink) underline decoration-(-
28
46
  * message="The page you're looking for has moved, or never existed."
29
47
  * action={<a href="/" className={ERROR_ACTION_CLASS}>Back home</a>}
30
48
  * />
49
+ * @example
50
+ * // Inside a shell that already owns the page's h1.
51
+ * <ErrorState level={2} code="503" heading="Invoices could not be loaded" … />
31
52
  */
32
- declare function ErrorState({ code, heading, message, action, className, ...rest }: ErrorStateProps): react.JSX.Element;
53
+ declare function ErrorState({ code, heading, message, action, level, className, ...rest }: ErrorStateProps): react.JSX.Element;
33
54
 
34
55
  export { ERROR_ACTION_CLASS, ErrorState, type ErrorStateProps, ErrorState as default };
@@ -1,7 +1,16 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Heading } from "../Heading/Heading.js";
2
3
  import { cn } from "../../lib/cn.js";
3
4
  const ERROR_ACTION_CLASS = "text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)";
4
- function ErrorState({ code, heading, message, action, className, ...rest }) {
5
+ function ErrorState({
6
+ code,
7
+ heading,
8
+ message,
9
+ action,
10
+ level = 1,
11
+ className,
12
+ ...rest
13
+ }) {
5
14
  return /* @__PURE__ */ jsx(
6
15
  "section",
7
16
  {
@@ -16,7 +25,7 @@ function ErrorState({ code, heading, message, action, className, ...rest }) {
16
25
  children: code
17
26
  }
18
27
  ),
19
- /* @__PURE__ */ jsx("h1", { className: "mb-4 mt-6 font-heading text-[length:var(--fs-heading)] font-normal text-(--ink)", children: heading }),
28
+ /* @__PURE__ */ jsx(Heading, { level, size: "heading", className: "mb-4 mt-6", children: heading }),
20
29
  /* @__PURE__ */ jsx("p", { className: "mb-10 max-w-(--measure-record) text-base leading-relaxed text-(--ink-3-aa)", children: message }),
21
30
  action
22
31
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ErrorState/ErrorState.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ErrorStateProps extends HTMLAttributes<HTMLElement> {\n /** The status, set large — \"404\", \"500\". Decorative; the heading carries the meaning. */\n code: ReactNode\n heading: ReactNode\n message: ReactNode\n /** The way back. Render it with {@link ERROR_ACTION_CLASS}. */\n action: ReactNode\n}\n\n/**\n * The recovery affordance's look, exported so every error page offers the same\n * one rather than each inventing a link style.\n */\nexport const ERROR_ACTION_CLASS =\n 'text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)'\n\n/**\n * A page that could not be shown.\n *\n * The code is set at the top of the type ladder and marked `aria-hidden` — read\n * aloud, \"404\" before the sentence explaining it is noise, and the heading\n * immediately after says the same thing in words.\n *\n * @example\n * <ErrorState\n * code=\"404\"\n * heading=\"Page not found\"\n * message=\"The page you're looking for has moved, or never existed.\"\n * action={<a href=\"/\" className={ERROR_ACTION_CLASS}>Back home</a>}\n * />\n */\nexport function ErrorState({ code, heading, message, action, className, ...rest }: ErrorStateProps) {\n return (\n <section\n className={cn('flex min-h-svh flex-col justify-center bg-(--paper) pt-24', className)}\n {...rest}\n >\n <div className=\"mx-auto w-full max-w-(--w-page) px-(--page-pad)\">\n <p\n aria-hidden=\"true\"\n className=\"m-0 font-heading text-[length:var(--fs-title)] leading-none text-(--ink)\"\n >\n {code}\n </p>\n <h1 className=\"mb-4 mt-6 font-heading text-[length:var(--fs-heading)] font-normal text-(--ink)\">\n {heading}\n </h1>\n <p className=\"mb-10 max-w-(--measure-record) text-base leading-relaxed text-(--ink-3-aa)\">\n {message}\n </p>\n {action}\n </div>\n </section>\n )\n}\n\nexport default ErrorState\n"],"mappings":"AAwCM,SACE,KADF;AAvCN,SAAS,UAAU;AAeZ,MAAM,qBACX;AAiBK,SAAS,WAAW,EAAE,MAAM,SAAS,SAAS,QAAQ,WAAW,GAAG,KAAK,GAAoB;AAClG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,6DAA6D,SAAS;AAAA,MACnF,GAAG;AAAA,MAEJ,+BAAC,SAAI,WAAU,mDACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,QACA,oBAAC,QAAG,WAAU,mFACX,mBACH;AAAA,QACA,oBAAC,OAAE,WAAU,8EACV,mBACH;AAAA,QACC;AAAA,SACH;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ErrorState/ErrorState.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { Heading, type HeadingLevel } from '../Heading/Heading'\nimport { cn } from '../../lib/cn'\n\nexport interface ErrorStateProps extends HTMLAttributes<HTMLElement> {\n /** The status, set large — \"404\", \"500\". Decorative; the heading carries the meaning. */\n code: ReactNode\n heading: ReactNode\n message: ReactNode\n /** The way back. Render it with {@link ERROR_ACTION_CLASS}. */\n action: ReactNode\n /**\n * The heading level `heading` opens, in the document the error state lands in.\n *\n * Defaults to `1`, and that is not a compromise: this component replaces the\n * page rather than sitting inside one — its own ground, its own viewport, its\n * own top clearance — so the page's single `h1` is the one it renders. Kept\n * as the default, existing call sites render exactly the markup they did.\n *\n * The prop exists because the level was previously fixed, and the one piece\n * of advice this component's documentation gives about headings — do not put\n * it inside a shell that already has an `h1` — was advice a caller had no way\n * to take. Now they do: inside a shell that owns the page heading, pass `2`.\n *\n * The size does not follow the level. `heading` renders at `--fs-heading` at\n * every level; a failure demoted in the outline is the same failure.\n */\n level?: HeadingLevel\n}\n\n/**\n * The recovery affordance's look, exported so every error page offers the same\n * one rather than each inventing a link style.\n */\nexport const ERROR_ACTION_CLASS =\n 'text-sm text-(--ink) underline decoration-(--rule-2) underline-offset-4 transition-colors duration-(--duration-fast) hover:decoration-(--ink)'\n\n/**\n * A page that could not be shown.\n *\n * The code is set at the top of the type ladder and marked `aria-hidden` — read\n * aloud, \"404\" before the sentence explaining it is noise, and the heading\n * immediately after says the same thing in words.\n *\n * @example\n * <ErrorState\n * code=\"404\"\n * heading=\"Page not found\"\n * message=\"The page you're looking for has moved, or never existed.\"\n * action={<a href=\"/\" className={ERROR_ACTION_CLASS}>Back home</a>}\n * />\n * @example\n * // Inside a shell that already owns the page's h1.\n * <ErrorState level={2} code=\"503\" heading=\"Invoices could not be loaded\" … />\n */\nexport function ErrorState({\n code,\n heading,\n message,\n action,\n level = 1,\n className,\n ...rest\n}: ErrorStateProps) {\n return (\n <section\n className={cn('flex min-h-svh flex-col justify-center bg-(--paper) pt-24', className)}\n {...rest}\n >\n <div className=\"mx-auto w-full max-w-(--w-page) px-(--page-pad)\">\n <p\n aria-hidden=\"true\"\n className=\"m-0 font-heading text-[length:var(--fs-title)] leading-none text-(--ink)\"\n >\n {code}\n </p>\n <Heading level={level} size=\"heading\" className=\"mb-4 mt-6\">\n {heading}\n </Heading>\n <p className=\"mb-10 max-w-(--measure-record) text-base leading-relaxed text-(--ink-3-aa)\">\n {message}\n </p>\n {action}\n </div>\n </section>\n )\n}\n\nexport default ErrorState\n"],"mappings":"AAqEM,SACE,KADF;AApEN,SAAS,eAAkC;AAC3C,SAAS,UAAU;AAgCZ,MAAM,qBACX;AAoBK,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,6DAA6D,SAAS;AAAA,MACnF,GAAG;AAAA,MAEJ,+BAAC,SAAI,WAAU,mDACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,QACA,oBAAC,WAAQ,OAAc,MAAK,WAAU,WAAU,aAC7C,mBACH;AAAA,QACA,oBAAC,OAAE,WAAU,8EACV,mBACH;AAAA,QACC;AAAA,SACH;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}