@dashforge/tw 1.0.0 → 1.1.1

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 (692) hide show
  1. package/CHANGELOG.md +241 -0
  2. package/README.md +74 -15
  3. package/dist/index.d.ts +1 -146
  4. package/dist/index.esm.js +3127 -748
  5. package/dist/src/components/Alert/Alert.d.ts +65 -0
  6. package/dist/src/components/Alert/Alert.d.ts.map +1 -0
  7. package/dist/src/components/Alert/alert.types.d.ts +130 -0
  8. package/dist/src/components/Alert/alert.types.d.ts.map +1 -0
  9. package/dist/src/components/Alert/alert.variants.d.ts +85 -0
  10. package/dist/src/components/Alert/alert.variants.d.ts.map +1 -0
  11. package/dist/src/components/Avatar/Avatar.d.ts +48 -0
  12. package/dist/src/components/Avatar/Avatar.d.ts.map +1 -0
  13. package/dist/src/components/Avatar/avatar.types.d.ts +162 -0
  14. package/dist/src/components/Avatar/avatar.types.d.ts.map +1 -0
  15. package/dist/src/components/Avatar/avatar.variants.d.ts +208 -0
  16. package/dist/src/components/Avatar/avatar.variants.d.ts.map +1 -0
  17. package/dist/src/components/Badge/Badge.d.ts +63 -0
  18. package/dist/src/components/Badge/Badge.d.ts.map +1 -0
  19. package/dist/src/components/Badge/badge.types.d.ts +124 -0
  20. package/dist/src/components/Badge/badge.types.d.ts.map +1 -0
  21. package/dist/src/components/Badge/badge.variants.d.ts +262 -0
  22. package/dist/src/components/Badge/badge.variants.d.ts.map +1 -0
  23. package/dist/src/components/Box/Box.d.ts.map +1 -1
  24. package/dist/src/components/Box/box.types.d.ts +53 -0
  25. package/dist/src/components/Box/box.types.d.ts.map +1 -1
  26. package/dist/src/components/Button/Button.d.ts.map +1 -1
  27. package/dist/src/components/Button/button.types.d.ts +35 -0
  28. package/dist/src/components/Button/button.types.d.ts.map +1 -1
  29. package/dist/src/components/Card/Card.d.ts +107 -0
  30. package/dist/src/components/Card/Card.d.ts.map +1 -0
  31. package/dist/src/components/Card/card.types.d.ts +119 -0
  32. package/dist/src/components/Card/card.types.d.ts.map +1 -0
  33. package/dist/src/components/Chip/Chip.d.ts +67 -0
  34. package/dist/src/components/Chip/Chip.d.ts.map +1 -0
  35. package/dist/src/components/Chip/chip.types.d.ts +113 -0
  36. package/dist/src/components/Chip/chip.types.d.ts.map +1 -0
  37. package/dist/src/components/Chip/chip.variants.d.ts +155 -0
  38. package/dist/src/components/Chip/chip.variants.d.ts.map +1 -0
  39. package/dist/src/components/IconButton/IconButton.d.ts +57 -0
  40. package/dist/src/components/IconButton/IconButton.d.ts.map +1 -0
  41. package/dist/src/components/IconButton/iconButton.types.d.ts +96 -0
  42. package/dist/src/components/IconButton/iconButton.types.d.ts.map +1 -0
  43. package/dist/src/components/IconButton/iconButton.variants.d.ts +26 -0
  44. package/dist/src/components/IconButton/iconButton.variants.d.ts.map +1 -0
  45. package/dist/src/components/Menu/Menu.d.ts +118 -0
  46. package/dist/src/components/Menu/Menu.d.ts.map +1 -0
  47. package/dist/src/components/Menu/menu.types.d.ts +179 -0
  48. package/dist/src/components/Menu/menu.types.d.ts.map +1 -0
  49. package/dist/src/components/Menu/menu.variants.d.ts +129 -0
  50. package/dist/src/components/Menu/menu.variants.d.ts.map +1 -0
  51. package/dist/src/components/Snackbar/Snackbar.d.ts.map +1 -1
  52. package/dist/src/components/Snackbar/snackbar.types.d.ts +40 -3
  53. package/dist/src/components/Snackbar/snackbar.types.d.ts.map +1 -1
  54. package/dist/src/components/Snackbar/snackbar.variants.d.ts +12 -57
  55. package/dist/src/components/Snackbar/snackbar.variants.d.ts.map +1 -1
  56. package/dist/src/components/Spinner/Spinner.d.ts +44 -0
  57. package/dist/src/components/Spinner/Spinner.d.ts.map +1 -0
  58. package/dist/src/components/Spinner/spinner.types.d.ts +89 -0
  59. package/dist/src/components/Spinner/spinner.types.d.ts.map +1 -0
  60. package/dist/src/components/Spinner/spinner.variants.d.ts +111 -0
  61. package/dist/src/components/Spinner/spinner.variants.d.ts.map +1 -0
  62. package/dist/src/components/Table/cells/RenderChip.d.ts +15 -72
  63. package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -1
  64. package/dist/src/components/_shared/severity/index.d.ts +21 -0
  65. package/dist/src/components/_shared/severity/index.d.ts.map +1 -0
  66. package/dist/src/components/_shared/severity/severity.types.d.ts +48 -0
  67. package/dist/src/components/_shared/severity/severity.types.d.ts.map +1 -0
  68. package/dist/src/components/_shared/severity/severityIcons.d.ts +45 -0
  69. package/dist/src/components/_shared/severity/severityIcons.d.ts.map +1 -0
  70. package/dist/src/components/_shared/severity/severityVariants.d.ts +22 -0
  71. package/dist/src/components/_shared/severity/severityVariants.d.ts.map +1 -0
  72. package/dist/src/index.d.ts +27 -2
  73. package/dist/src/index.d.ts.map +1 -1
  74. package/package.json +9 -3
  75. package/.swcrc +0 -29
  76. package/A11Y.md +0 -130
  77. package/CONSUMER-VALIDATION.md +0 -130
  78. package/COVERAGE.md +0 -63
  79. package/LICENSE +0 -21
  80. package/PARITY.md +0 -481
  81. package/PERFORMANCE.md +0 -227
  82. package/REFINEMENT-NOTES.md +0 -50
  83. package/THEME-AUDIT.md +0 -174
  84. package/dist/components/Accordion/Accordion.d.ts +0 -21
  85. package/dist/components/Accordion/Accordion.d.ts.map +0 -1
  86. package/dist/components/Accordion/accordion.types.d.ts +0 -74
  87. package/dist/components/Accordion/accordion.types.d.ts.map +0 -1
  88. package/dist/components/Accordion/accordion.variants.d.ts +0 -58
  89. package/dist/components/Accordion/accordion.variants.d.ts.map +0 -1
  90. package/dist/components/AppShell/AppShell.d.ts +0 -46
  91. package/dist/components/AppShell/AppShell.d.ts.map +0 -1
  92. package/dist/components/AppShell/appShell.types.d.ts +0 -62
  93. package/dist/components/AppShell/appShell.types.d.ts.map +0 -1
  94. package/dist/components/AppShell/appShell.variants.d.ts +0 -65
  95. package/dist/components/AppShell/appShell.variants.d.ts.map +0 -1
  96. package/dist/components/AspectRatio/AspectRatio.d.ts +0 -36
  97. package/dist/components/AspectRatio/AspectRatio.d.ts.map +0 -1
  98. package/dist/components/AspectRatio/aspectRatio.types.d.ts +0 -50
  99. package/dist/components/AspectRatio/aspectRatio.types.d.ts.map +0 -1
  100. package/dist/components/Autocomplete/Autocomplete.d.ts +0 -32
  101. package/dist/components/Autocomplete/Autocomplete.d.ts.map +0 -1
  102. package/dist/components/Autocomplete/autocomplete.types.d.ts +0 -214
  103. package/dist/components/Autocomplete/autocomplete.types.d.ts.map +0 -1
  104. package/dist/components/Autocomplete/autocomplete.variants.d.ts +0 -214
  105. package/dist/components/Autocomplete/autocomplete.variants.d.ts.map +0 -1
  106. package/dist/components/Box/Box.d.ts +0 -36
  107. package/dist/components/Box/Box.d.ts.map +0 -1
  108. package/dist/components/Box/box.types.d.ts +0 -54
  109. package/dist/components/Box/box.types.d.ts.map +0 -1
  110. package/dist/components/Box/box.variants.d.ts +0 -402
  111. package/dist/components/Box/box.variants.d.ts.map +0 -1
  112. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +0 -23
  113. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts.map +0 -1
  114. package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts +0 -98
  115. package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts.map +0 -1
  116. package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts +0 -85
  117. package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +0 -1
  118. package/dist/components/Button/Button.d.ts +0 -44
  119. package/dist/components/Button/Button.d.ts.map +0 -1
  120. package/dist/components/Button/button.types.d.ts +0 -66
  121. package/dist/components/Button/button.types.d.ts.map +0 -1
  122. package/dist/components/Button/button.variants.d.ts +0 -104
  123. package/dist/components/Button/button.variants.d.ts.map +0 -1
  124. package/dist/components/Calendar/Calendar.d.ts +0 -14
  125. package/dist/components/Calendar/Calendar.d.ts.map +0 -1
  126. package/dist/components/Calendar/calendar.types.d.ts +0 -72
  127. package/dist/components/Calendar/calendar.types.d.ts.map +0 -1
  128. package/dist/components/Calendar/calendar.variants.d.ts +0 -99
  129. package/dist/components/Calendar/calendar.variants.d.ts.map +0 -1
  130. package/dist/components/Checkbox/Checkbox.d.ts +0 -31
  131. package/dist/components/Checkbox/Checkbox.d.ts.map +0 -1
  132. package/dist/components/Checkbox/checkbox.types.d.ts +0 -86
  133. package/dist/components/Checkbox/checkbox.types.d.ts.map +0 -1
  134. package/dist/components/Checkbox/checkbox.variants.d.ts +0 -109
  135. package/dist/components/Checkbox/checkbox.variants.d.ts.map +0 -1
  136. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts +0 -38
  137. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +0 -1
  138. package/dist/components/ConfirmDialog/confirmDialog.types.d.ts +0 -80
  139. package/dist/components/ConfirmDialog/confirmDialog.types.d.ts.map +0 -1
  140. package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts +0 -90
  141. package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts.map +0 -1
  142. package/dist/components/Container/Container.d.ts +0 -35
  143. package/dist/components/Container/Container.d.ts.map +0 -1
  144. package/dist/components/Container/container.types.d.ts +0 -41
  145. package/dist/components/Container/container.types.d.ts.map +0 -1
  146. package/dist/components/Container/container.variants.d.ts +0 -93
  147. package/dist/components/Container/container.variants.d.ts.map +0 -1
  148. package/dist/components/DataGrid/DataGrid.d.ts +0 -29
  149. package/dist/components/DataGrid/DataGrid.d.ts.map +0 -1
  150. package/dist/components/DataGrid/dataGrid.types.d.ts +0 -214
  151. package/dist/components/DataGrid/dataGrid.types.d.ts.map +0 -1
  152. package/dist/components/DataGrid/dataGrid.variants.d.ts +0 -222
  153. package/dist/components/DataGrid/dataGrid.variants.d.ts.map +0 -1
  154. package/dist/components/DataGrid/filters/ColumnFilters.d.ts +0 -23
  155. package/dist/components/DataGrid/filters/ColumnFilters.d.ts.map +0 -1
  156. package/dist/components/DataGrid/reorder/useColumnReorder.d.ts +0 -50
  157. package/dist/components/DataGrid/reorder/useColumnReorder.d.ts.map +0 -1
  158. package/dist/components/DataGrid/resize/useColumnResize.d.ts +0 -39
  159. package/dist/components/DataGrid/resize/useColumnResize.d.ts.map +0 -1
  160. package/dist/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts +0 -44
  161. package/dist/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts.map +0 -1
  162. package/dist/components/DatePicker/DatePicker.d.ts +0 -14
  163. package/dist/components/DatePicker/DatePicker.d.ts.map +0 -1
  164. package/dist/components/DatePicker/datePicker.types.d.ts +0 -88
  165. package/dist/components/DatePicker/datePicker.types.d.ts.map +0 -1
  166. package/dist/components/DatePicker/datePicker.variants.d.ts +0 -97
  167. package/dist/components/DatePicker/datePicker.variants.d.ts.map +0 -1
  168. package/dist/components/DateRangePicker/DateRangePicker.d.ts +0 -13
  169. package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +0 -1
  170. package/dist/components/DateRangePicker/dateRangePicker.types.d.ts +0 -85
  171. package/dist/components/DateRangePicker/dateRangePicker.types.d.ts.map +0 -1
  172. package/dist/components/DateRangePicker/dateRangePicker.variants.d.ts +0 -183
  173. package/dist/components/DateRangePicker/dateRangePicker.variants.d.ts.map +0 -1
  174. package/dist/components/DateTimePicker/DateTimePicker.d.ts +0 -13
  175. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  176. package/dist/components/DateTimePicker/dateTimePicker.types.d.ts +0 -90
  177. package/dist/components/DateTimePicker/dateTimePicker.types.d.ts.map +0 -1
  178. package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts +0 -107
  179. package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts.map +0 -1
  180. package/dist/components/Dialog/Dialog.d.ts +0 -17
  181. package/dist/components/Dialog/Dialog.d.ts.map +0 -1
  182. package/dist/components/Dialog/dialog.types.d.ts +0 -75
  183. package/dist/components/Dialog/dialog.types.d.ts.map +0 -1
  184. package/dist/components/Dialog/dialog.variants.d.ts +0 -80
  185. package/dist/components/Dialog/dialog.variants.d.ts.map +0 -1
  186. package/dist/components/Divider/Divider.d.ts +0 -50
  187. package/dist/components/Divider/Divider.d.ts.map +0 -1
  188. package/dist/components/Divider/divider.types.d.ts +0 -47
  189. package/dist/components/Divider/divider.types.d.ts.map +0 -1
  190. package/dist/components/Divider/divider.variants.d.ts +0 -129
  191. package/dist/components/Divider/divider.variants.d.ts.map +0 -1
  192. package/dist/components/Grid/Grid.d.ts +0 -32
  193. package/dist/components/Grid/Grid.d.ts.map +0 -1
  194. package/dist/components/Grid/grid.types.d.ts +0 -103
  195. package/dist/components/Grid/grid.types.d.ts.map +0 -1
  196. package/dist/components/Grid/grid.variants.d.ts +0 -453
  197. package/dist/components/Grid/grid.variants.d.ts.map +0 -1
  198. package/dist/components/LeftNav/LeftNav.d.ts +0 -34
  199. package/dist/components/LeftNav/LeftNav.d.ts.map +0 -1
  200. package/dist/components/LeftNav/leftNav.types.d.ts +0 -136
  201. package/dist/components/LeftNav/leftNav.types.d.ts.map +0 -1
  202. package/dist/components/LeftNav/leftNav.variants.d.ts +0 -143
  203. package/dist/components/LeftNav/leftNav.variants.d.ts.map +0 -1
  204. package/dist/components/NumberField/NumberField.d.ts +0 -18
  205. package/dist/components/NumberField/NumberField.d.ts.map +0 -1
  206. package/dist/components/NumberField/numberField.types.d.ts +0 -79
  207. package/dist/components/NumberField/numberField.types.d.ts.map +0 -1
  208. package/dist/components/NumberField/numberField.variants.d.ts +0 -169
  209. package/dist/components/NumberField/numberField.variants.d.ts.map +0 -1
  210. package/dist/components/OTPField/OTPField.d.ts +0 -25
  211. package/dist/components/OTPField/OTPField.d.ts.map +0 -1
  212. package/dist/components/OTPField/otpField.types.d.ts +0 -74
  213. package/dist/components/OTPField/otpField.types.d.ts.map +0 -1
  214. package/dist/components/OTPField/otpField.variants.d.ts +0 -105
  215. package/dist/components/OTPField/otpField.variants.d.ts.map +0 -1
  216. package/dist/components/Pagination/Pagination.d.ts +0 -32
  217. package/dist/components/Pagination/Pagination.d.ts.map +0 -1
  218. package/dist/components/Pagination/pagination.helpers.d.ts +0 -23
  219. package/dist/components/Pagination/pagination.helpers.d.ts.map +0 -1
  220. package/dist/components/Pagination/pagination.types.d.ts +0 -120
  221. package/dist/components/Pagination/pagination.types.d.ts.map +0 -1
  222. package/dist/components/Pagination/pagination.variants.d.ts +0 -150
  223. package/dist/components/Pagination/pagination.variants.d.ts.map +0 -1
  224. package/dist/components/Popover/Popover.d.ts +0 -15
  225. package/dist/components/Popover/Popover.d.ts.map +0 -1
  226. package/dist/components/Popover/popover.types.d.ts +0 -45
  227. package/dist/components/Popover/popover.types.d.ts.map +0 -1
  228. package/dist/components/Popover/popover.variants.d.ts +0 -34
  229. package/dist/components/Popover/popover.variants.d.ts.map +0 -1
  230. package/dist/components/RadioGroup/RadioGroup.d.ts +0 -35
  231. package/dist/components/RadioGroup/RadioGroup.d.ts.map +0 -1
  232. package/dist/components/RadioGroup/radioGroup.types.d.ts +0 -103
  233. package/dist/components/RadioGroup/radioGroup.types.d.ts.map +0 -1
  234. package/dist/components/RadioGroup/radioGroup.variants.d.ts +0 -166
  235. package/dist/components/RadioGroup/radioGroup.variants.d.ts.map +0 -1
  236. package/dist/components/Skeleton/Skeleton.d.ts +0 -23
  237. package/dist/components/Skeleton/Skeleton.d.ts.map +0 -1
  238. package/dist/components/Skeleton/skeleton.types.d.ts +0 -56
  239. package/dist/components/Skeleton/skeleton.types.d.ts.map +0 -1
  240. package/dist/components/Skeleton/skeleton.variants.d.ts +0 -110
  241. package/dist/components/Skeleton/skeleton.variants.d.ts.map +0 -1
  242. package/dist/components/Snackbar/Snackbar.d.ts +0 -37
  243. package/dist/components/Snackbar/Snackbar.d.ts.map +0 -1
  244. package/dist/components/Snackbar/snackbar.types.d.ts +0 -86
  245. package/dist/components/Snackbar/snackbar.types.d.ts.map +0 -1
  246. package/dist/components/Snackbar/snackbar.variants.d.ts +0 -153
  247. package/dist/components/Snackbar/snackbar.variants.d.ts.map +0 -1
  248. package/dist/components/Stack/Stack.d.ts +0 -24
  249. package/dist/components/Stack/Stack.d.ts.map +0 -1
  250. package/dist/components/Stack/stack.types.d.ts +0 -61
  251. package/dist/components/Stack/stack.types.d.ts.map +0 -1
  252. package/dist/components/Stack/stack.variants.d.ts +0 -163
  253. package/dist/components/Stack/stack.variants.d.ts.map +0 -1
  254. package/dist/components/Switch/Switch.d.ts +0 -15
  255. package/dist/components/Switch/Switch.d.ts.map +0 -1
  256. package/dist/components/Switch/switch.types.d.ts +0 -47
  257. package/dist/components/Switch/switch.types.d.ts.map +0 -1
  258. package/dist/components/Switch/switch.variants.d.ts +0 -105
  259. package/dist/components/Switch/switch.variants.d.ts.map +0 -1
  260. package/dist/components/Table/Table.d.ts +0 -24
  261. package/dist/components/Table/Table.d.ts.map +0 -1
  262. package/dist/components/Table/cells/RenderButton.d.ts +0 -13
  263. package/dist/components/Table/cells/RenderButton.d.ts.map +0 -1
  264. package/dist/components/Table/cells/RenderChip.d.ts +0 -80
  265. package/dist/components/Table/cells/RenderChip.d.ts.map +0 -1
  266. package/dist/components/Table/cells/RenderText.d.ts +0 -21
  267. package/dist/components/Table/cells/RenderText.d.ts.map +0 -1
  268. package/dist/components/Table/cells/RenderTwoLine.d.ts +0 -20
  269. package/dist/components/Table/cells/RenderTwoLine.d.ts.map +0 -1
  270. package/dist/components/Table/cells/RowActionsMenu.d.ts +0 -36
  271. package/dist/components/Table/cells/RowActionsMenu.d.ts.map +0 -1
  272. package/dist/components/Table/cells/index.d.ts +0 -11
  273. package/dist/components/Table/cells/index.d.ts.map +0 -1
  274. package/dist/components/Table/table.types.d.ts +0 -399
  275. package/dist/components/Table/table.types.d.ts.map +0 -1
  276. package/dist/components/Table/table.variants.d.ts +0 -285
  277. package/dist/components/Table/table.variants.d.ts.map +0 -1
  278. package/dist/components/Tabs/Tabs.d.ts +0 -19
  279. package/dist/components/Tabs/Tabs.d.ts.map +0 -1
  280. package/dist/components/Tabs/tabs.types.d.ts +0 -61
  281. package/dist/components/Tabs/tabs.types.d.ts.map +0 -1
  282. package/dist/components/Tabs/tabs.variants.d.ts +0 -82
  283. package/dist/components/Tabs/tabs.variants.d.ts.map +0 -1
  284. package/dist/components/Tabs/useTabs.d.ts +0 -55
  285. package/dist/components/Tabs/useTabs.d.ts.map +0 -1
  286. package/dist/components/TextField/TextField.d.ts +0 -25
  287. package/dist/components/TextField/TextField.d.ts.map +0 -1
  288. package/dist/components/TextField/textField.types.d.ts +0 -98
  289. package/dist/components/TextField/textField.types.d.ts.map +0 -1
  290. package/dist/components/TextField/textField.variants.d.ts +0 -166
  291. package/dist/components/TextField/textField.variants.d.ts.map +0 -1
  292. package/dist/components/Textarea/Textarea.d.ts +0 -16
  293. package/dist/components/Textarea/Textarea.d.ts.map +0 -1
  294. package/dist/components/Textarea/textarea.types.d.ts +0 -63
  295. package/dist/components/Textarea/textarea.types.d.ts.map +0 -1
  296. package/dist/components/Textarea/textarea.variants.d.ts +0 -197
  297. package/dist/components/Textarea/textarea.variants.d.ts.map +0 -1
  298. package/dist/components/TimePicker/TimePicker.d.ts +0 -12
  299. package/dist/components/TimePicker/TimePicker.d.ts.map +0 -1
  300. package/dist/components/TimePicker/timePicker.types.d.ts +0 -80
  301. package/dist/components/TimePicker/timePicker.types.d.ts.map +0 -1
  302. package/dist/components/TimePicker/timePicker.variants.d.ts +0 -103
  303. package/dist/components/TimePicker/timePicker.variants.d.ts.map +0 -1
  304. package/dist/components/Tooltip/Tooltip.d.ts +0 -29
  305. package/dist/components/Tooltip/Tooltip.d.ts.map +0 -1
  306. package/dist/components/Tooltip/tooltip.types.d.ts +0 -47
  307. package/dist/components/Tooltip/tooltip.types.d.ts.map +0 -1
  308. package/dist/components/Tooltip/tooltip.variants.d.ts +0 -34
  309. package/dist/components/Tooltip/tooltip.variants.d.ts.map +0 -1
  310. package/dist/components/TopBar/TopBar.d.ts +0 -33
  311. package/dist/components/TopBar/TopBar.d.ts.map +0 -1
  312. package/dist/components/TopBar/topBar.types.d.ts +0 -49
  313. package/dist/components/TopBar/topBar.types.d.ts.map +0 -1
  314. package/dist/components/TopBar/topBar.variants.d.ts +0 -79
  315. package/dist/components/TopBar/topBar.variants.d.ts.map +0 -1
  316. package/dist/components/Typography/Typography.d.ts +0 -42
  317. package/dist/components/Typography/Typography.d.ts.map +0 -1
  318. package/dist/components/Typography/typography.types.d.ts +0 -61
  319. package/dist/components/Typography/typography.types.d.ts.map +0 -1
  320. package/dist/components/Typography/typography.variants.d.ts +0 -167
  321. package/dist/components/Typography/typography.variants.d.ts.map +0 -1
  322. package/dist/components/VisuallyHidden/VisuallyHidden.d.ts +0 -42
  323. package/dist/components/VisuallyHidden/VisuallyHidden.d.ts.map +0 -1
  324. package/dist/components/VisuallyHidden/visuallyHidden.types.d.ts +0 -42
  325. package/dist/components/VisuallyHidden/visuallyHidden.types.d.ts.map +0 -1
  326. package/dist/components/_shared/data/getNestedValue.d.ts +0 -20
  327. package/dist/components/_shared/data/getNestedValue.d.ts.map +0 -1
  328. package/dist/components/_shared/data/useColumnAutoDetect.d.ts +0 -45
  329. package/dist/components/_shared/data/useColumnAutoDetect.d.ts.map +0 -1
  330. package/dist/components/_shared/data/useControllableState.d.ts +0 -23
  331. package/dist/components/_shared/data/useControllableState.d.ts.map +0 -1
  332. package/dist/components/_shared/data/useDebouncedValue.d.ts +0 -10
  333. package/dist/components/_shared/data/useDebouncedValue.d.ts.map +0 -1
  334. package/dist/components/_shared/data/useTableFilter.d.ts +0 -36
  335. package/dist/components/_shared/data/useTableFilter.d.ts.map +0 -1
  336. package/dist/components/_shared/data/useTableSearch.d.ts +0 -23
  337. package/dist/components/_shared/data/useTableSearch.d.ts.map +0 -1
  338. package/dist/components/_shared/data/useTableSelection.d.ts +0 -25
  339. package/dist/components/_shared/data/useTableSelection.d.ts.map +0 -1
  340. package/dist/components/_shared/data/useTableSort.d.ts +0 -20
  341. package/dist/components/_shared/data/useTableSort.d.ts.map +0 -1
  342. package/dist/components/_shared/data/useVirtualizer.d.ts +0 -107
  343. package/dist/components/_shared/data/useVirtualizer.d.ts.map +0 -1
  344. package/dist/components/_shared/resolveValidationState.d.ts +0 -42
  345. package/dist/components/_shared/resolveValidationState.d.ts.map +0 -1
  346. package/dist/hooks/useAccessState.d.ts +0 -37
  347. package/dist/hooks/useAccessState.d.ts.map +0 -1
  348. package/dist/index.d.ts.map +0 -1
  349. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  350. package/dist/utils/cn.d.ts +0 -26
  351. package/dist/utils/cn.d.ts.map +0 -1
  352. package/eslint.config.mjs +0 -62
  353. package/project.json +0 -15
  354. package/rollup.config.cjs +0 -16
  355. package/src/components/Accordion/Accordion.test.tsx +0 -95
  356. package/src/components/Accordion/Accordion.tsx +0 -97
  357. package/src/components/Accordion/accordion.types.ts +0 -66
  358. package/src/components/Accordion/accordion.variants.ts +0 -32
  359. package/src/components/AppShell/AppShell.test.tsx +0 -223
  360. package/src/components/AppShell/AppShell.tsx +0 -239
  361. package/src/components/AppShell/appShell.types.ts +0 -49
  362. package/src/components/AppShell/appShell.variants.ts +0 -54
  363. package/src/components/AspectRatio/AspectRatio.test.tsx +0 -173
  364. package/src/components/AspectRatio/AspectRatio.tsx +0 -73
  365. package/src/components/AspectRatio/aspectRatio.types.ts +0 -54
  366. package/src/components/Autocomplete/Autocomplete.perf.test.tsx +0 -225
  367. package/src/components/Autocomplete/Autocomplete.test.tsx +0 -556
  368. package/src/components/Autocomplete/Autocomplete.tsx +0 -1069
  369. package/src/components/Autocomplete/autocomplete.types.ts +0 -198
  370. package/src/components/Autocomplete/autocomplete.variants.ts +0 -141
  371. package/src/components/Box/Box.test.tsx +0 -370
  372. package/src/components/Box/Box.tsx +0 -83
  373. package/src/components/Box/box.types.ts +0 -61
  374. package/src/components/Box/box.variants.ts +0 -230
  375. package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +0 -128
  376. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +0 -231
  377. package/src/components/Breadcrumbs/Breadcrumbs.tsx +0 -203
  378. package/src/components/Breadcrumbs/breadcrumbs.types.ts +0 -87
  379. package/src/components/Breadcrumbs/breadcrumbs.variants.ts +0 -54
  380. package/src/components/Button/Button.test.tsx +0 -183
  381. package/src/components/Button/Button.tsx +0 -150
  382. package/src/components/Button/button.types.ts +0 -74
  383. package/src/components/Button/button.variants.ts +0 -159
  384. package/src/components/Calendar/Calendar.test.tsx +0 -75
  385. package/src/components/Calendar/Calendar.tsx +0 -264
  386. package/src/components/Calendar/calendar.types.ts +0 -65
  387. package/src/components/Calendar/calendar.variants.ts +0 -64
  388. package/src/components/Checkbox/Checkbox.test.tsx +0 -146
  389. package/src/components/Checkbox/Checkbox.tsx +0 -286
  390. package/src/components/Checkbox/checkbox.types.ts +0 -88
  391. package/src/components/Checkbox/checkbox.variants.ts +0 -68
  392. package/src/components/ConfirmDialog/ConfirmDialog.perf.test.tsx +0 -172
  393. package/src/components/ConfirmDialog/ConfirmDialog.test.tsx +0 -311
  394. package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -252
  395. package/src/components/ConfirmDialog/confirmDialog.types.ts +0 -70
  396. package/src/components/ConfirmDialog/confirmDialog.variants.ts +0 -76
  397. package/src/components/Container/Container.test.tsx +0 -209
  398. package/src/components/Container/Container.tsx +0 -74
  399. package/src/components/Container/container.types.ts +0 -45
  400. package/src/components/Container/container.variants.ts +0 -81
  401. package/src/components/DataGrid/DataGrid.perf.test.tsx +0 -114
  402. package/src/components/DataGrid/DataGrid.test.tsx +0 -1153
  403. package/src/components/DataGrid/DataGrid.tsx +0 -1282
  404. package/src/components/DataGrid/dataGrid.types.ts +0 -238
  405. package/src/components/DataGrid/dataGrid.variants.ts +0 -152
  406. package/src/components/DataGrid/filters/ColumnFilters.test.tsx +0 -330
  407. package/src/components/DataGrid/filters/ColumnFilters.tsx +0 -456
  408. package/src/components/DataGrid/reorder/useColumnReorder.test.ts +0 -130
  409. package/src/components/DataGrid/reorder/useColumnReorder.ts +0 -144
  410. package/src/components/DataGrid/resize/useColumnResize.test.ts +0 -144
  411. package/src/components/DataGrid/resize/useColumnResize.ts +0 -135
  412. package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +0 -186
  413. package/src/components/DatePicker/DatePicker.test.tsx +0 -76
  414. package/src/components/DatePicker/DatePicker.tsx +0 -305
  415. package/src/components/DatePicker/datePicker.types.ts +0 -77
  416. package/src/components/DatePicker/datePicker.variants.ts +0 -50
  417. package/src/components/DateRangePicker/DateRangePicker.test.tsx +0 -92
  418. package/src/components/DateRangePicker/DateRangePicker.tsx +0 -510
  419. package/src/components/DateRangePicker/dateRangePicker.types.ts +0 -74
  420. package/src/components/DateRangePicker/dateRangePicker.variants.ts +0 -101
  421. package/src/components/DateTimePicker/DateTimePicker.test.tsx +0 -78
  422. package/src/components/DateTimePicker/DateTimePicker.tsx +0 -347
  423. package/src/components/DateTimePicker/dateTimePicker.types.ts +0 -79
  424. package/src/components/DateTimePicker/dateTimePicker.variants.ts +0 -55
  425. package/src/components/Dialog/Dialog.test.tsx +0 -96
  426. package/src/components/Dialog/Dialog.tsx +0 -102
  427. package/src/components/Dialog/dialog.types.ts +0 -62
  428. package/src/components/Dialog/dialog.variants.ts +0 -66
  429. package/src/components/Divider/Divider.test.tsx +0 -243
  430. package/src/components/Divider/Divider.tsx +0 -140
  431. package/src/components/Divider/divider.types.ts +0 -52
  432. package/src/components/Divider/divider.variants.ts +0 -116
  433. package/src/components/Grid/Grid.test.tsx +0 -321
  434. package/src/components/Grid/Grid.tsx +0 -148
  435. package/src/components/Grid/grid.types.ts +0 -112
  436. package/src/components/Grid/grid.variants.ts +0 -133
  437. package/src/components/LeftNav/LeftNav.perf.test.tsx +0 -172
  438. package/src/components/LeftNav/LeftNav.test.tsx +0 -302
  439. package/src/components/LeftNav/LeftNav.tsx +0 -330
  440. package/src/components/LeftNav/leftNav.types.ts +0 -120
  441. package/src/components/LeftNav/leftNav.variants.ts +0 -107
  442. package/src/components/NumberField/NumberField.test.tsx +0 -83
  443. package/src/components/NumberField/NumberField.tsx +0 -321
  444. package/src/components/NumberField/numberField.types.ts +0 -72
  445. package/src/components/NumberField/numberField.variants.ts +0 -94
  446. package/src/components/OTPField/OTPField.test.tsx +0 -121
  447. package/src/components/OTPField/OTPField.tsx +0 -250
  448. package/src/components/OTPField/otpField.types.ts +0 -58
  449. package/src/components/OTPField/otpField.variants.ts +0 -72
  450. package/src/components/Pagination/Pagination.test.tsx +0 -314
  451. package/src/components/Pagination/Pagination.tsx +0 -277
  452. package/src/components/Pagination/pagination.helpers.ts +0 -102
  453. package/src/components/Pagination/pagination.types.ts +0 -117
  454. package/src/components/Pagination/pagination.variants.ts +0 -117
  455. package/src/components/Popover/Popover.test.tsx +0 -72
  456. package/src/components/Popover/Popover.tsx +0 -55
  457. package/src/components/Popover/popover.types.ts +0 -42
  458. package/src/components/Popover/popover.variants.ts +0 -24
  459. package/src/components/RadioGroup/RadioGroup.test.tsx +0 -140
  460. package/src/components/RadioGroup/RadioGroup.tsx +0 -283
  461. package/src/components/RadioGroup/radioGroup.types.ts +0 -85
  462. package/src/components/RadioGroup/radioGroup.variants.ts +0 -87
  463. package/src/components/Skeleton/Skeleton.test.tsx +0 -127
  464. package/src/components/Skeleton/Skeleton.tsx +0 -49
  465. package/src/components/Skeleton/skeleton.types.ts +0 -59
  466. package/src/components/Skeleton/skeleton.variants.ts +0 -87
  467. package/src/components/Snackbar/Snackbar.perf.test.tsx +0 -144
  468. package/src/components/Snackbar/Snackbar.test.tsx +0 -327
  469. package/src/components/Snackbar/Snackbar.tsx +0 -298
  470. package/src/components/Snackbar/snackbar.types.ts +0 -83
  471. package/src/components/Snackbar/snackbar.variants.ts +0 -74
  472. package/src/components/Stack/Stack.test.tsx +0 -309
  473. package/src/components/Stack/Stack.tsx +0 -115
  474. package/src/components/Stack/stack.types.ts +0 -68
  475. package/src/components/Stack/stack.variants.ts +0 -80
  476. package/src/components/Switch/Switch.test.tsx +0 -131
  477. package/src/components/Switch/Switch.tsx +0 -158
  478. package/src/components/Switch/switch.types.ts +0 -36
  479. package/src/components/Switch/switch.variants.ts +0 -70
  480. package/src/components/Table/Table.perf.test.tsx +0 -81
  481. package/src/components/Table/Table.test.tsx +0 -674
  482. package/src/components/Table/Table.tsx +0 -863
  483. package/src/components/Table/cells/RenderButton.tsx +0 -23
  484. package/src/components/Table/cells/RenderChip.tsx +0 -100
  485. package/src/components/Table/cells/RenderText.tsx +0 -37
  486. package/src/components/Table/cells/RenderTwoLine.tsx +0 -34
  487. package/src/components/Table/cells/RowActionsMenu.tsx +0 -147
  488. package/src/components/Table/cells/cells.test.tsx +0 -247
  489. package/src/components/Table/cells/index.ts +0 -14
  490. package/src/components/Table/table.types.ts +0 -438
  491. package/src/components/Table/table.variants.ts +0 -207
  492. package/src/components/Tabs/Tabs.test.tsx +0 -145
  493. package/src/components/Tabs/Tabs.tsx +0 -77
  494. package/src/components/Tabs/tabs.types.ts +0 -55
  495. package/src/components/Tabs/tabs.variants.ts +0 -69
  496. package/src/components/Tabs/useTabs.ts +0 -167
  497. package/src/components/TextField/TextField.test.tsx +0 -198
  498. package/src/components/TextField/TextField.tsx +0 -245
  499. package/src/components/TextField/textField.types.ts +0 -90
  500. package/src/components/TextField/textField.variants.ts +0 -105
  501. package/src/components/Textarea/Textarea.test.tsx +0 -75
  502. package/src/components/Textarea/Textarea.tsx +0 -190
  503. package/src/components/Textarea/textarea.types.ts +0 -60
  504. package/src/components/Textarea/textarea.variants.ts +0 -85
  505. package/src/components/TimePicker/TimePicker.test.tsx +0 -65
  506. package/src/components/TimePicker/TimePicker.tsx +0 -274
  507. package/src/components/TimePicker/timePicker.types.ts +0 -69
  508. package/src/components/TimePicker/timePicker.variants.ts +0 -53
  509. package/src/components/Tooltip/Tooltip.test.tsx +0 -73
  510. package/src/components/Tooltip/Tooltip.tsx +0 -70
  511. package/src/components/Tooltip/tooltip.types.ts +0 -44
  512. package/src/components/Tooltip/tooltip.variants.ts +0 -28
  513. package/src/components/TopBar/TopBar.test.tsx +0 -129
  514. package/src/components/TopBar/TopBar.tsx +0 -66
  515. package/src/components/TopBar/topBar.types.ts +0 -42
  516. package/src/components/TopBar/topBar.variants.ts +0 -38
  517. package/src/components/Typography/Typography.test.tsx +0 -247
  518. package/src/components/Typography/Typography.tsx +0 -112
  519. package/src/components/Typography/typography.types.ts +0 -65
  520. package/src/components/Typography/typography.variants.ts +0 -130
  521. package/src/components/VisuallyHidden/VisuallyHidden.test.tsx +0 -86
  522. package/src/components/VisuallyHidden/VisuallyHidden.tsx +0 -59
  523. package/src/components/VisuallyHidden/visuallyHidden.types.ts +0 -45
  524. package/src/components/_shared/data/getNestedValue.test.ts +0 -50
  525. package/src/components/_shared/data/getNestedValue.ts +0 -32
  526. package/src/components/_shared/data/useColumnAutoDetect.test.ts +0 -123
  527. package/src/components/_shared/data/useColumnAutoDetect.ts +0 -100
  528. package/src/components/_shared/data/useControllableState.ts +0 -48
  529. package/src/components/_shared/data/useDebouncedValue.ts +0 -24
  530. package/src/components/_shared/data/useTableFilter.test.ts +0 -166
  531. package/src/components/_shared/data/useTableFilter.ts +0 -118
  532. package/src/components/_shared/data/useTableSearch.test.ts +0 -115
  533. package/src/components/_shared/data/useTableSearch.ts +0 -66
  534. package/src/components/_shared/data/useTableSelection.test.ts +0 -100
  535. package/src/components/_shared/data/useTableSelection.ts +0 -100
  536. package/src/components/_shared/data/useTableSort.test.ts +0 -150
  537. package/src/components/_shared/data/useTableSort.ts +0 -141
  538. package/src/components/_shared/data/useVirtualizer.test.ts +0 -158
  539. package/src/components/_shared/data/useVirtualizer.ts +0 -199
  540. package/src/components/_shared/resolveValidationState.ts +0 -60
  541. package/src/components/_shared/themeIdentity.test.ts +0 -113
  542. package/src/hooks/useAccessState.spec.tsx +0 -161
  543. package/src/hooks/useAccessState.ts +0 -81
  544. package/src/index.ts +0 -388
  545. package/src/utils/cn.spec.ts +0 -16
  546. package/src/utils/cn.ts +0 -29
  547. package/test-output/vitest/coverage/base.css +0 -224
  548. package/test-output/vitest/coverage/block-navigation.js +0 -87
  549. package/test-output/vitest/coverage/clover.xml +0 -2418
  550. package/test-output/vitest/coverage/coverage-final.json +0 -89
  551. package/test-output/vitest/coverage/favicon.png +0 -0
  552. package/test-output/vitest/coverage/index.html +0 -746
  553. package/test-output/vitest/coverage/prettify.css +0 -1
  554. package/test-output/vitest/coverage/prettify.js +0 -2
  555. package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
  556. package/test-output/vitest/coverage/sorter.js +0 -210
  557. package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +0 -376
  558. package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +0 -181
  559. package/test-output/vitest/coverage/src/components/Accordion/index.html +0 -131
  560. package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +0 -802
  561. package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +0 -247
  562. package/test-output/vitest/coverage/src/components/AppShell/index.html +0 -131
  563. package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +0 -304
  564. package/test-output/vitest/coverage/src/components/AspectRatio/index.html +0 -116
  565. package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +0 -3292
  566. package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +0 -508
  567. package/test-output/vitest/coverage/src/components/Autocomplete/index.html +0 -131
  568. package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +0 -334
  569. package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +0 -775
  570. package/test-output/vitest/coverage/src/components/Box/index.html +0 -131
  571. package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +0 -694
  572. package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +0 -247
  573. package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +0 -131
  574. package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +0 -535
  575. package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +0 -562
  576. package/test-output/vitest/coverage/src/components/Button/index.html +0 -131
  577. package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +0 -943
  578. package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +0 -289
  579. package/test-output/vitest/coverage/src/components/Checkbox/index.html +0 -131
  580. package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +0 -841
  581. package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +0 -313
  582. package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +0 -131
  583. package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +0 -307
  584. package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +0 -328
  585. package/test-output/vitest/coverage/src/components/Container/index.html +0 -131
  586. package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +0 -3937
  587. package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +0 -541
  588. package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +0 -1453
  589. package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +0 -116
  590. package/test-output/vitest/coverage/src/components/DataGrid/index.html +0 -131
  591. package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +0 -116
  592. package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +0 -517
  593. package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +0 -116
  594. package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +0 -490
  595. package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +0 -592
  596. package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +0 -116
  597. package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +0 -1042
  598. package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +0 -337
  599. package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +0 -131
  600. package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +0 -391
  601. package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +0 -283
  602. package/test-output/vitest/coverage/src/components/Dialog/index.html +0 -131
  603. package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +0 -505
  604. package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +0 -433
  605. package/test-output/vitest/coverage/src/components/Divider/index.html +0 -131
  606. package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +0 -529
  607. package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +0 -484
  608. package/test-output/vitest/coverage/src/components/Grid/index.html +0 -131
  609. package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +0 -1075
  610. package/test-output/vitest/coverage/src/components/LeftNav/index.html +0 -131
  611. package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +0 -406
  612. package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +0 -1048
  613. package/test-output/vitest/coverage/src/components/NumberField/index.html +0 -131
  614. package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +0 -367
  615. package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +0 -835
  616. package/test-output/vitest/coverage/src/components/OTPField/index.html +0 -131
  617. package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +0 -301
  618. package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +0 -916
  619. package/test-output/vitest/coverage/src/components/Pagination/index.html +0 -146
  620. package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +0 -391
  621. package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +0 -436
  622. package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +0 -250
  623. package/test-output/vitest/coverage/src/components/Popover/index.html +0 -131
  624. package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +0 -157
  625. package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +0 -934
  626. package/test-output/vitest/coverage/src/components/RadioGroup/index.html +0 -131
  627. package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +0 -346
  628. package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +0 -232
  629. package/test-output/vitest/coverage/src/components/Skeleton/index.html +0 -131
  630. package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +0 -346
  631. package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +0 -979
  632. package/test-output/vitest/coverage/src/components/Snackbar/index.html +0 -131
  633. package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +0 -307
  634. package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +0 -430
  635. package/test-output/vitest/coverage/src/components/Stack/index.html +0 -131
  636. package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +0 -325
  637. package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +0 -559
  638. package/test-output/vitest/coverage/src/components/Switch/index.html +0 -131
  639. package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +0 -295
  640. package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +0 -2677
  641. package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +0 -154
  642. package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +0 -385
  643. package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +0 -196
  644. package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +0 -187
  645. package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +0 -526
  646. package/test-output/vitest/coverage/src/components/Table/cells/index.html +0 -191
  647. package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +0 -127
  648. package/test-output/vitest/coverage/src/components/Table/index.html +0 -131
  649. package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +0 -706
  650. package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +0 -283
  651. package/test-output/vitest/coverage/src/components/Tabs/index.html +0 -131
  652. package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +0 -292
  653. package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +0 -820
  654. package/test-output/vitest/coverage/src/components/TextField/index.html +0 -131
  655. package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +0 -400
  656. package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +0 -655
  657. package/test-output/vitest/coverage/src/components/Textarea/index.html +0 -131
  658. package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +0 -340
  659. package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +0 -295
  660. package/test-output/vitest/coverage/src/components/Tooltip/index.html +0 -131
  661. package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +0 -169
  662. package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +0 -283
  663. package/test-output/vitest/coverage/src/components/TopBar/index.html +0 -131
  664. package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +0 -199
  665. package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +0 -421
  666. package/test-output/vitest/coverage/src/components/Typography/index.html +0 -131
  667. package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +0 -436
  668. package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +0 -262
  669. package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +0 -116
  670. package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +0 -181
  671. package/test-output/vitest/coverage/src/components/_shared/data/index.html +0 -236
  672. package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +0 -385
  673. package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +0 -229
  674. package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +0 -157
  675. package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +0 -439
  676. package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +0 -283
  677. package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +0 -385
  678. package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +0 -508
  679. package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +0 -682
  680. package/test-output/vitest/coverage/src/components/_shared/index.html +0 -116
  681. package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +0 -265
  682. package/test-output/vitest/coverage/src/hooks/index.html +0 -116
  683. package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +0 -328
  684. package/test-output/vitest/coverage/src/index.html +0 -116
  685. package/test-output/vitest/coverage/src/index.ts.html +0 -1153
  686. package/test-output/vitest/coverage/src/utils/cn.ts.html +0 -172
  687. package/test-output/vitest/coverage/src/utils/index.html +0 -116
  688. package/tsconfig.json +0 -13
  689. package/tsconfig.lib.json +0 -58
  690. package/tsconfig.spec.json +0 -34
  691. package/vite.config.ts +0 -33
  692. package/vitest.config.mts +0 -29
package/dist/index.esm.js CHANGED
@@ -1,18 +1,19 @@
1
- import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
- import { useMemo, forwardRef, useContext, useId, useRef, useEffect, useCallback, useState, Fragment, createContext, Children, isValidElement, cloneElement, useLayoutEffect } from 'react';
1
+ import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
+ import { useMemo, forwardRef, useContext, useState, useEffect, useId, useRef, useCallback, Fragment, createContext, memo, Children, isValidElement, cloneElement, useLayoutEffect } from 'react';
3
3
  import { Slot } from '@radix-ui/react-slot';
4
+ import { DashFormContext, useEngineVisibility } from '@dashforge/ui-core';
4
5
  import clsx from 'clsx';
5
6
  import { twMerge } from 'tailwind-merge';
6
7
  import { useRbacOptional, resolveAccessState } from '@dashforge/rbac';
7
8
  import { tv } from 'tailwind-variants';
8
9
  export { tv } from 'tailwind-variants';
9
- import { DashFormContext, useEngineVisibility } from '@dashforge/ui-core';
10
10
  import { useDashFieldMeta } from '@dashforge/forms';
11
11
  import * as RadixCheckbox from '@radix-ui/react-checkbox';
12
12
  import * as RadixSwitch from '@radix-ui/react-switch';
13
13
  import * as RadixRadioGroup from '@radix-ui/react-radio-group';
14
14
  import { useCalendar, resolveCalendarKey, formatLongDate, parseISODate, formatTime, generateTimeOptions, timeStringToMinutes, getTodayISODate, useDateRange } from '@dashforge/calendar-core';
15
15
  import * as RadixPopover from '@radix-ui/react-popover';
16
+ import * as RadixMenu from '@radix-ui/react-dropdown-menu';
16
17
  import * as RadixDialog from '@radix-ui/react-dialog';
17
18
  import * as RadixTooltip from '@radix-ui/react-tooltip';
18
19
  import * as RadixAccordion from '@radix-ui/react-accordion';
@@ -132,6 +133,190 @@ function _object_without_properties_loose(source, excluded) {
132
133
  ]);
133
134
  }
134
135
 
136
+ /**
137
+ * Tailwind-variants recipe for `<Spinner>`.
138
+ *
139
+ * Axes:
140
+ * - `size` — 5 steps (xs/sm/md/lg/xl) → w/h spacing tokens
141
+ * - `thickness` — 3 steps (thin/md/thick) → SVG stroke-width
142
+ * - `color` — 7 intents (or omitted → `currentColor`)
143
+ *
144
+ * The stroke + fill of the SVG paths are governed by
145
+ * `stroke="currentColor"` (set inline in Spinner.tsx), so the root
146
+ * `text-*` class drives the color. When `color` is omitted, no
147
+ * `text-*` class is emitted at all — `currentColor` resolves to
148
+ * whatever the parent's text color is.
149
+ */ const spinnerVariants = tv({
150
+ base: 'inline-block animate-spin motion-reduce:animate-none',
151
+ variants: {
152
+ size: {
153
+ xs: 'w-3 h-3',
154
+ sm: 'w-4 h-4',
155
+ md: 'w-5 h-5',
156
+ lg: 'w-6 h-6',
157
+ xl: 'w-8 h-8'
158
+ },
159
+ /**
160
+ * Color → text-{color}-600 (mid-tone, readable on most surfaces).
161
+ * Note: text-{color}-600 acts on currentColor inside the SVG via
162
+ * `stroke="currentColor"`. Neutral uses `text-neutral-600` which
163
+ * auto-inverts via the dashforgePreset CSS-var swap (no `dark:`
164
+ * needed). Color rows do NOT carry `dark:` either — the chosen
165
+ * 600 step works in both modes for the spinner's specific use
166
+ * (small element, brief visibility).
167
+ */ color: {
168
+ neutral: 'text-neutral-600',
169
+ primary: 'text-primary-600',
170
+ secondary: 'text-secondary-600',
171
+ success: 'text-success-600',
172
+ warning: 'text-warning-600',
173
+ danger: 'text-danger-600',
174
+ info: 'text-info-600'
175
+ }
176
+ },
177
+ defaultVariants: {
178
+ size: 'md'
179
+ }
180
+ });
181
+ /**
182
+ * SVG stroke-width per thickness step. Picked to read well at every
183
+ * size — thin (1.5px) is the minimum readable; thick (3px) saturates
184
+ * the available radius at sizes xs/sm.
185
+ */ const SPINNER_STROKE_WIDTH = {
186
+ thin: 1.5,
187
+ md: 2.25,
188
+ thick: 3
189
+ };
190
+ /**
191
+ * The track ring renders at this opacity of `currentColor`. 0.2 is
192
+ * the sweet spot — visible enough to anchor the eye, faint enough
193
+ * to read as background.
194
+ */ const SPINNER_TRACK_OPACITY = 0.2;
195
+ /** Re-export the color name list so playground demos can iterate. */ const SPINNER_COLORS = [
196
+ 'neutral',
197
+ 'primary',
198
+ 'secondary',
199
+ 'success',
200
+ 'warning',
201
+ 'danger',
202
+ 'info'
203
+ ];
204
+
205
+ /**
206
+ * `<Spinner>` — rotating-arc loading indicator.
207
+ *
208
+ * Visual: a partial-arc SVG rotating via Tailwind's `animate-spin`
209
+ * (pure CSS, GPU-accelerated). Optional `withTrack` renders a faint
210
+ * ghost ring (20% opacity of currentColor) behind the arc — the
211
+ * standard "premium spinner" pattern used by Stripe / Vercel / Linear.
212
+ *
213
+ * Color resolution:
214
+ * - `color="primary"` (or any 7 intent) → `text-{color}-600` class
215
+ * → SVG `stroke="currentColor"` picks it up
216
+ * - `color` omitted → no `text-*` class emitted → SVG inherits
217
+ * parent's text color via `currentColor`. **This is the right
218
+ * default for nested usage** (inside Button, Alert, Card).
219
+ *
220
+ * A11y:
221
+ * - `role="status"` on the wrapper
222
+ * - `aria-live="polite"` so SR queues the announcement
223
+ * - visually-hidden text label inside (`'Loading'` default)
224
+ * - `animate-spin` is gated on `motion-reduce` (WCAG 2.3.3)
225
+ *
226
+ * Delay (anti-flash):
227
+ * - `delay={150}` mounts as `null` for the first 150ms, then swaps
228
+ * to the actual SVG. Quick operations that finish before 150ms
229
+ * never render the spinner — eliminates the "flash" UX bug.
230
+ *
231
+ * @example
232
+ * ```tsx
233
+ * // 1. Inside a Button — inherits text color
234
+ * <button><Spinner size="sm" /> Saving...</button>
235
+ *
236
+ * // 2. Standalone with explicit color
237
+ * <Spinner color="primary" size="lg" label="Loading dashboard data" />
238
+ *
239
+ * // 3. Anti-flash on quick submits
240
+ * <Spinner visibleWhen={() => isSubmitting} delay={150} />
241
+ *
242
+ * // 4. With track for busy backgrounds
243
+ * <Spinner color="primary" withTrack size="xl" />
244
+ * ```
245
+ */ const Spinner = /*#__PURE__*/ forwardRef(function Spinner(props, ref) {
246
+ const { size = 'md', color, thickness = 'md', withTrack = false, label = 'Loading', delay = 0, visibleWhen, sx, className } = props;
247
+ // Bridge — engine-reactive visibility. Hook called unconditionally
248
+ // (rules-of-hooks). Outside a `<DashForm>`, predicate evaluated as
249
+ // a plain closure.
250
+ const bridge = useContext(DashFormContext);
251
+ const isBridgeVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
252
+ // Anti-flash delay — render `null` for the first `delay` ms after
253
+ // the spinner becomes visible. Skipping the timer entirely when
254
+ // delay=0 keeps the synchronous render path for the common case.
255
+ const [delayElapsed, setDelayElapsed] = useState(delay <= 0);
256
+ useEffect(()=>{
257
+ if (delay <= 0) {
258
+ setDelayElapsed(true);
259
+ return;
260
+ }
261
+ setDelayElapsed(false);
262
+ const handle = setTimeout(()=>setDelayElapsed(true), delay);
263
+ return ()=>clearTimeout(handle);
264
+ }, [
265
+ delay
266
+ ]);
267
+ if (!isBridgeVisible || !delayElapsed) return null;
268
+ const classes = cn(spinnerVariants({
269
+ size,
270
+ color
271
+ }), sx, className);
272
+ const strokeWidth = SPINNER_STROKE_WIDTH[thickness];
273
+ // Decorative mode — when label is explicitly empty (""), suppress
274
+ // role="status" and the visually-hidden label entirely. Used when
275
+ // the Spinner is embedded inside another component that already
276
+ // announces its loading state via `aria-busy` (e.g., Button) — the
277
+ // inner spinner is then purely visual chrome.
278
+ const isDecorative = label === '';
279
+ return jsxs("span", {
280
+ ref: ref,
281
+ className: classes,
282
+ role: isDecorative ? undefined : 'status',
283
+ "aria-live": isDecorative ? undefined : 'polite',
284
+ "aria-hidden": isDecorative ? true : undefined,
285
+ children: [
286
+ jsxs("svg", {
287
+ viewBox: "0 0 24 24",
288
+ fill: "none",
289
+ "aria-hidden": "true",
290
+ width: "100%",
291
+ height: "100%",
292
+ children: [
293
+ withTrack && jsx("circle", {
294
+ cx: "12",
295
+ cy: "12",
296
+ r: "9",
297
+ stroke: "currentColor",
298
+ strokeWidth: strokeWidth,
299
+ strokeOpacity: SPINNER_TRACK_OPACITY,
300
+ fill: "none"
301
+ }),
302
+ jsx("path", {
303
+ d: "M21 12a9 9 0 0 0-9-9",
304
+ stroke: "currentColor",
305
+ strokeWidth: strokeWidth,
306
+ strokeLinecap: "round",
307
+ fill: "none"
308
+ })
309
+ ]
310
+ }),
311
+ !isDecorative && jsx("span", {
312
+ className: "absolute -m-px h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)]",
313
+ children: label
314
+ })
315
+ ]
316
+ });
317
+ });
318
+ Spinner.displayName = 'Spinner';
319
+
135
320
  /**
136
321
  * Tailwind-variants recipe for `<Button>`.
137
322
  *
@@ -308,36 +493,13 @@ function _object_without_properties_loose(source, excluded) {
308
493
  });
309
494
 
310
495
  /**
311
- * Inline spinner shown when `loading` is true. Kept inline (≈10 lines
312
- * of SVG) so the package adds no icon-library dependency.
496
+ * Map Button size to Spinner size. Button has 3 sizes (sm/md/lg);
497
+ * Spinner has 5 (xs/sm/md/lg/xl). Picking the closest match keeps
498
+ * the spinner visually proportional to the button label.
313
499
  *
314
500
  * @internal
315
- */ function LoadingSpinner({ size }) {
316
- const dim = size === 'sm' ? 14 : size === 'lg' ? 20 : 16;
317
- return jsxs("svg", {
318
- "aria-hidden": "true",
319
- width: dim,
320
- height: dim,
321
- viewBox: "0 0 24 24",
322
- fill: "none",
323
- className: "animate-spin",
324
- children: [
325
- jsx("circle", {
326
- cx: "12",
327
- cy: "12",
328
- r: "9",
329
- stroke: "currentColor",
330
- strokeWidth: "2.5",
331
- strokeOpacity: "0.25"
332
- }),
333
- jsx("path", {
334
- d: "M21 12a9 9 0 0 0-9-9",
335
- stroke: "currentColor",
336
- strokeWidth: "2.5",
337
- strokeLinecap: "round"
338
- })
339
- ]
340
- });
501
+ */ function buttonSizeToSpinnerSize(size) {
502
+ return size === 'sm' ? 'xs' : size === 'lg' ? 'md' : 'sm';
341
503
  }
342
504
  /**
343
505
  * Dashforge TW Button — action component with RBAC support.
@@ -381,8 +543,9 @@ function _object_without_properties_loose(source, excluded) {
381
543
  * ```
382
544
  */ const Button = /*#__PURE__*/ forwardRef(function Button(props, ref) {
383
545
  var _rest_type;
384
- const { access, disabled, variant, color, size, fullWidth, loading, asChild, sx, children } = props, rest = _object_without_properties_loose(props, [
546
+ const { access, visibleWhen, disabled, variant, color, size, fullWidth, loading, asChild, sx, children } = props, rest = _object_without_properties_loose(props, [
385
547
  "access",
548
+ "visibleWhen",
386
549
  "disabled",
387
550
  "variant",
388
551
  "color",
@@ -393,10 +556,17 @@ function _object_without_properties_loose(source, excluded) {
393
556
  "sx",
394
557
  "children"
395
558
  ]);
396
- // RBAC always called unconditionally (rules-of-hooks compliance).
559
+ // Bridge — both hooks called unconditionally (rules-of-hooks).
560
+ // Inside a `<DashForm>`, `useEngineVisibility` subscribes to engine
561
+ // state and re-evaluates the predicate on changes; outside a form,
562
+ // the predicate is called with `null` engine (consumer captures
563
+ // external state in the closure).
564
+ const bridge = useContext(DashFormContext);
565
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
397
566
  const accessState = useAccessState(access);
398
- // Early return for hidden state.
399
- if (!accessState.visible) return null;
567
+ // Early return for hidden state — either RBAC denies visibility or
568
+ // the engine-reactive predicate returned false.
569
+ if (!isVisible || !accessState.visible) return null;
400
570
  // Effective disabled = explicit OR RBAC-disabled OR RBAC-readonly OR loading.
401
571
  // Buttons do not support a true readonly state — RBAC readonly is mapped
402
572
  // to disabled (matches the MUI-side semantics in @dashforge/ui).
@@ -435,8 +605,11 @@ function _object_without_properties_loose(source, excluded) {
435
605
  className: classes
436
606
  }, rest, {
437
607
  children: [
438
- loading && jsx(LoadingSpinner, {
439
- size: size != null ? size : 'md'
608
+ loading && jsx(Spinner, {
609
+ size: buttonSizeToSpinnerSize(size != null ? size : 'md'),
610
+ thickness: "thick",
611
+ withTrack: true,
612
+ label: ""
440
613
  }),
441
614
  children
442
615
  ]
@@ -2502,7 +2675,7 @@ function _object_without_properties_loose(source, excluded) {
2502
2675
  'rounded-md border bg-neutral-50 border-neutral-300 shadow-lg',
2503
2676
  'min-w-[var(--trigger-width)]'
2504
2677
  ],
2505
- listBox: 'outline-none py-1',
2678
+ listBox: 'outline-none py-1 list-none pl-0 m-0',
2506
2679
  listItem: [
2507
2680
  'cursor-pointer select-none px-3 py-2 text-sm text-neutral-900',
2508
2681
  'outline-none',
@@ -5425,7 +5598,7 @@ function formatRange(range, locale) {
5425
5598
  'flex items-center gap-2 px-3 h-14 shrink-0',
5426
5599
  'border-b border-neutral-200 text-neutral-900 font-semibold'
5427
5600
  ],
5428
- list: 'flex flex-col gap-0.5 p-2 flex-1 overflow-y-auto',
5601
+ list: 'flex flex-col gap-0.5 p-2 flex-1 overflow-y-auto list-none m-0',
5429
5602
  item: 'list-none',
5430
5603
  itemLink: [
5431
5604
  'flex items-center gap-2 px-2 py-2 rounded-md',
@@ -5458,7 +5631,7 @@ function formatRange(range, locale) {
5458
5631
  'outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
5459
5632
  'transition-colors w-full'
5460
5633
  ],
5461
- groupChildren: 'flex flex-col gap-0.5 pl-6',
5634
+ groupChildren: 'flex flex-col gap-0.5 pl-6 list-none m-0',
5462
5635
  footer: [
5463
5636
  'border-t border-neutral-200 p-3 shrink-0',
5464
5637
  'flex items-center gap-2'
@@ -6357,13 +6530,22 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
6357
6530
  /**
6358
6531
  * Tailwind-variants recipe for `<Snackbar>` (stacked toast surface).
6359
6532
  *
6533
+ * **Sprint 4.4 refactor (1.1.0):** the severity color block has been
6534
+ * REMOVED from this recipe. The 3×4 (variant × severity) color matrix
6535
+ * now lives in `_shared/severity/severityVariants` and is consumed by
6536
+ * Snackbar AND Alert (and any future Banner). This recipe owns layout
6537
+ * + spacing + transitions only — the surface / border / icon colors
6538
+ * are merged in at render time via `cn()`.
6539
+ *
6360
6540
  * Slots:
6361
6541
  * - `container` — fixed-position outer stack
6362
- * - `item` — single snackbar surface
6363
- * - `icon` — leading severity icon
6542
+ * - `item` — single snackbar surface (layout only; severity
6543
+ * colors injected at render via `getSeverityClasses`)
6544
+ * - `icon` — leading severity icon (layout only; tone class
6545
+ * injected at render)
6364
6546
  * - `message` — main text
6365
6547
  * - `action` — optional action button
6366
- * - `closeButton` — trailing `×` button
6548
+ * - `closeButton` — trailing close `×` button
6367
6549
  */ const snackbarVariants = tv({
6368
6550
  slots: {
6369
6551
  container: [
@@ -6374,28 +6556,33 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
6374
6556
  'pointer-events-auto',
6375
6557
  'flex items-start gap-3 px-4 py-3 w-full',
6376
6558
  'rounded-lg border shadow-lg',
6377
- 'text-sm bg-neutral-50 text-neutral-900',
6378
- // Subtle enter transition — opacity + translate, kept short so a
6379
- // burst of snackbars feels snappy. Gated on motion-reduce
6559
+ 'text-sm',
6560
+ // Subtle enter transition — opacity-only, kept short so a burst
6561
+ // of snackbars feels snappy. Gated on motion-reduce
6380
6562
  // (WCAG 2.3.3) — users who request reduced motion see snackbars
6381
- // pop in instantly without the slide animation.
6382
- 'transition-all duration-200 motion-reduce:transition-none motion-reduce:duration-0',
6563
+ // pop in instantly without the fade.
6564
+ 'transition-opacity duration-200 motion-reduce:transition-none motion-reduce:duration-0',
6383
6565
  'data-[state=entered]:opacity-100 data-[state=exited]:opacity-0'
6384
6566
  ],
6385
- icon: 'shrink-0 mt-0.5 w-5 h-5 inline-flex items-center justify-center',
6567
+ icon: 'shrink-0 mt-0.5 inline-flex items-center justify-center',
6386
6568
  message: 'flex-1 min-w-0 leading-relaxed',
6387
6569
  action: [
6388
6570
  'shrink-0 inline-flex items-center justify-center px-2 h-7',
6389
6571
  'rounded-md text-xs font-medium',
6390
- 'text-primary-700 hover:bg-primary-100',
6391
- 'outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
6392
- 'transition-colors'
6572
+ // Action button uses currentColor + a soft hover tint so it
6573
+ // adapts to whatever (variant, severity) surface it's on. The
6574
+ // hover tint relies on a 10% opacity overlay of the current
6575
+ // foreground — works in both light and dark severity surfaces.
6576
+ 'opacity-90 hover:opacity-100 hover:bg-current/10',
6577
+ 'outline-none focus-visible:ring-2 focus-visible:ring-current',
6578
+ 'transition-opacity'
6393
6579
  ],
6394
6580
  closeButton: [
6395
6581
  'shrink-0 inline-flex items-center justify-center w-6 h-6',
6396
- 'rounded-md text-neutral-500 hover:bg-neutral-200 hover:text-neutral-900',
6397
- 'outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
6398
- 'transition-colors'
6582
+ 'rounded-md',
6583
+ 'opacity-70 hover:opacity-100',
6584
+ 'outline-none focus-visible:ring-2 focus-visible:ring-current',
6585
+ 'transition-opacity'
6399
6586
  ]
6400
6587
  },
6401
6588
  variants: {
@@ -6418,32 +6605,208 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
6418
6605
  'bottom-right': {
6419
6606
  container: 'bottom-0 right-0 items-end'
6420
6607
  }
6421
- },
6422
- severity: {
6423
- info: {
6424
- item: 'border-primary-200',
6425
- icon: 'text-primary-600'
6426
- },
6427
- success: {
6428
- item: 'border-success-200',
6429
- icon: 'text-success-600'
6430
- },
6431
- warning: {
6432
- item: 'border-warning-200',
6433
- icon: 'text-warning-600'
6434
- },
6435
- danger: {
6436
- item: 'border-danger-200',
6437
- icon: 'text-danger-600'
6438
- }
6439
6608
  }
6440
6609
  },
6441
6610
  defaultVariants: {
6442
- position: 'bottom-right',
6443
- severity: 'info'
6611
+ position: 'bottom-right'
6444
6612
  }
6445
6613
  });
6446
6614
 
6615
+ /**
6616
+ * The 3×4 severity color matrix.
6617
+ *
6618
+ * Every class is a Tailwind utility that resolves through
6619
+ * `dashforgePreset()` to a CSS variable on the host — patchable at
6620
+ * runtime via `patchTheme({ <severity>: { …scale } })` without
6621
+ * recompiling. **Zero hardcoded hex values, zero `dark:` variants**:
6622
+ * the dashforge preset auto-inverts the neutral surface ladder via CSS
6623
+ * variable swap; the severity scales themselves do NOT auto-invert
6624
+ * (they read the same scale in light + dark, which is the desired
6625
+ * behaviour for semantic colors).
6626
+ *
6627
+ * The matrix is indexed `[variant][severity]` so a consumer-facing
6628
+ * `<Alert variant="filled" severity="warning">` resolves with a single
6629
+ * lookup.
6630
+ */ const SEVERITY_MATRIX = {
6631
+ standard: {
6632
+ info: {
6633
+ surface: 'bg-info-50 text-info-900',
6634
+ border: 'border-info-100',
6635
+ icon: 'text-info-600'
6636
+ },
6637
+ success: {
6638
+ surface: 'bg-success-50 text-success-900',
6639
+ border: 'border-success-100',
6640
+ icon: 'text-success-600'
6641
+ },
6642
+ warning: {
6643
+ surface: 'bg-warning-50 text-warning-900',
6644
+ border: 'border-warning-100',
6645
+ icon: 'text-warning-600'
6646
+ },
6647
+ danger: {
6648
+ surface: 'bg-danger-50 text-danger-900',
6649
+ border: 'border-danger-100',
6650
+ icon: 'text-danger-600'
6651
+ }
6652
+ },
6653
+ filled: {
6654
+ info: {
6655
+ surface: 'bg-info-600 text-info-50',
6656
+ border: 'border-info-700',
6657
+ icon: 'text-info-50'
6658
+ },
6659
+ success: {
6660
+ surface: 'bg-success-600 text-success-50',
6661
+ border: 'border-success-700',
6662
+ icon: 'text-success-50'
6663
+ },
6664
+ warning: {
6665
+ surface: 'bg-warning-600 text-warning-50',
6666
+ border: 'border-warning-700',
6667
+ icon: 'text-warning-50'
6668
+ },
6669
+ danger: {
6670
+ surface: 'bg-danger-600 text-danger-50',
6671
+ border: 'border-danger-700',
6672
+ icon: 'text-danger-50'
6673
+ }
6674
+ },
6675
+ outlined: {
6676
+ info: {
6677
+ surface: 'bg-transparent text-info-700',
6678
+ border: 'border-info-300',
6679
+ icon: 'text-info-600'
6680
+ },
6681
+ success: {
6682
+ surface: 'bg-transparent text-success-700',
6683
+ border: 'border-success-300',
6684
+ icon: 'text-success-600'
6685
+ },
6686
+ warning: {
6687
+ surface: 'bg-transparent text-warning-700',
6688
+ border: 'border-warning-300',
6689
+ icon: 'text-warning-600'
6690
+ },
6691
+ danger: {
6692
+ surface: 'bg-transparent text-danger-700',
6693
+ border: 'border-danger-300',
6694
+ icon: 'text-danger-600'
6695
+ }
6696
+ }
6697
+ };
6698
+ /**
6699
+ * Resolve the Tailwind classes for one (variant, severity) pair.
6700
+ *
6701
+ * @example
6702
+ * const { surface, border, icon } = getSeverityClasses('standard', 'warning');
6703
+ * // surface: 'bg-warning-50 text-warning-900'
6704
+ * // border: 'border-warning-100'
6705
+ * // icon: 'text-warning-600'
6706
+ */ function getSeverityClasses(variant, severity) {
6707
+ return SEVERITY_MATRIX[variant][severity];
6708
+ }
6709
+ /**
6710
+ * ARIA role for a severity. Follows the W3C WAI-ARIA spec:
6711
+ * - `alert` for assertive states (warning / danger) — interrupts the
6712
+ * screen-reader announcement queue.
6713
+ * - `status` for polite states (info / success) — queued behind other
6714
+ * announcements.
6715
+ *
6716
+ * Consumers can override via an explicit `role` prop.
6717
+ */ function getSeverityRole(severity) {
6718
+ return severity === 'warning' || severity === 'danger' ? 'alert' : 'status';
6719
+ }
6720
+
6721
+ const BASE_PROPS = {
6722
+ width: '1.25em',
6723
+ height: '1.25em',
6724
+ viewBox: '0 0 20 20',
6725
+ fill: 'none',
6726
+ stroke: 'currentColor',
6727
+ strokeWidth: 1.5,
6728
+ strokeLinecap: 'round',
6729
+ strokeLinejoin: 'round',
6730
+ 'aria-hidden': 'true'
6731
+ };
6732
+ function InfoIcon(props) {
6733
+ return jsxs("svg", _extends({}, BASE_PROPS, props, {
6734
+ children: [
6735
+ jsx("circle", {
6736
+ cx: "10",
6737
+ cy: "10",
6738
+ r: "8.25"
6739
+ }),
6740
+ jsx("path", {
6741
+ d: "M10 6.5h.01M10 9.5v4"
6742
+ })
6743
+ ]
6744
+ }));
6745
+ }
6746
+ function SuccessIcon(props) {
6747
+ return jsxs("svg", _extends({}, BASE_PROPS, props, {
6748
+ children: [
6749
+ jsx("circle", {
6750
+ cx: "10",
6751
+ cy: "10",
6752
+ r: "8.25"
6753
+ }),
6754
+ jsx("path", {
6755
+ d: "m6.5 10 2.5 2.5 4.5-5"
6756
+ })
6757
+ ]
6758
+ }));
6759
+ }
6760
+ function WarningIcon(props) {
6761
+ return jsxs("svg", _extends({}, BASE_PROPS, props, {
6762
+ children: [
6763
+ jsx("path", {
6764
+ d: "M10 2.5 18 17H2L10 2.5Z"
6765
+ }),
6766
+ jsx("path", {
6767
+ d: "M10 8v3.5M10 14.5h.01"
6768
+ })
6769
+ ]
6770
+ }));
6771
+ }
6772
+ function DangerIcon(props) {
6773
+ return jsxs("svg", _extends({}, BASE_PROPS, props, {
6774
+ children: [
6775
+ jsx("circle", {
6776
+ cx: "10",
6777
+ cy: "10",
6778
+ r: "8.25"
6779
+ }),
6780
+ jsx("path", {
6781
+ d: "m7 7 6 6M13 7l-6 6"
6782
+ })
6783
+ ]
6784
+ }));
6785
+ }
6786
+ /**
6787
+ * Map of severity → default Icon component. Used by Alert (and
6788
+ * eventually Snackbar / Banner / etc.) to render the per-severity
6789
+ * default when the consumer doesn't pass a custom `icon` prop and
6790
+ * doesn't opt-out with `icon={false}`.
6791
+ */ const SEVERITY_DEFAULT_ICON = {
6792
+ info: InfoIcon,
6793
+ success: SuccessIcon,
6794
+ warning: WarningIcon,
6795
+ danger: DangerIcon
6796
+ };
6797
+ /**
6798
+ * Convenience helper — render the default icon for a severity as a
6799
+ * `ReactNode`, ready to inject into a JSX slot.
6800
+ *
6801
+ * @example
6802
+ * <span className={severityClasses.icon}>
6803
+ * {getDefaultSeverityIcon('warning')}
6804
+ * </span>
6805
+ */ function getDefaultSeverityIcon(severity) {
6806
+ const Icon = SEVERITY_DEFAULT_ICON[severity];
6807
+ return jsx(Icon, {});
6808
+ }
6809
+
6447
6810
  const SnackbarContext = /*#__PURE__*/ createContext(null);
6448
6811
  /**
6449
6812
  * `useSnackbar()` — returns `{ enqueue, dismiss, dismissAll }`.
@@ -6457,17 +6820,6 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
6457
6820
  }
6458
6821
  return ctx;
6459
6822
  }
6460
- /**
6461
- * Unicode glyphs for severities — keeps the bundle small (no icon dep)
6462
- * and stays accessible because consumers pass their own message text.
6463
- * The glyph is `aria-hidden`; the screen reader announces the message
6464
- * via the live region.
6465
- */ const SEVERITY_GLYPH = {
6466
- info: 'ⓘ',
6467
- success: '✓',
6468
- warning: '⚠',
6469
- danger: '✕'
6470
- };
6471
6823
  /**
6472
6824
  * Dashforge TW Snackbar provider — transient, stacked toast notifications.
6473
6825
  *
@@ -6630,23 +6982,31 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
6630
6982
  "aria-live": "polite",
6631
6983
  "aria-atomic": "false",
6632
6984
  children: visible.map((rec)=>{
6633
- var _ref, _rec_severity, _ref1, _rec_showClose;
6985
+ var _ref, _rec_severity, _ref1, _rec_variant, _ref2, _rec_showClose;
6634
6986
  var _slotProps_item, _slotProps_icon, _slotProps_message, _slotProps_action, _slotProps_closeButton;
6635
6987
  const sev = (_ref = (_rec_severity = rec.severity) != null ? _rec_severity : defaults == null ? void 0 : defaults.severity) != null ? _ref : 'info';
6636
- const showClose = (_ref1 = (_rec_showClose = rec.showClose) != null ? _rec_showClose : defaults == null ? void 0 : defaults.showClose) != null ? _ref1 : true;
6988
+ const variant = (_ref1 = (_rec_variant = rec.variant) != null ? _rec_variant : defaults == null ? void 0 : defaults.variant) != null ? _ref1 : 'standard';
6989
+ const showClose = (_ref2 = (_rec_showClose = rec.showClose) != null ? _rec_showClose : defaults == null ? void 0 : defaults.showClose) != null ? _ref2 : true;
6637
6990
  const itemClasses = snackbarVariants({
6638
- position,
6639
- severity: sev
6991
+ position
6640
6992
  });
6993
+ // Severity color classes — sourced from the shared 3×4 matrix
6994
+ // (`_shared/severity/`), same as Alert. Keeps the two
6995
+ // components visually in lockstep.
6996
+ const severityClasses = getSeverityClasses(variant, sev);
6997
+ // Icon resolution — same tristate as Alert:
6998
+ // undefined → default per-severity SVG (shared with Alert)
6999
+ // ReactNode → consumer's icon
7000
+ // false → no icon
7001
+ const renderedIcon = rec.icon === false ? null : rec.icon !== undefined ? rec.icon : getDefaultSeverityIcon(sev);
6641
7002
  return jsxs("div", {
6642
7003
  "data-state": "entered",
6643
7004
  "data-tick": rec.tick,
6644
- className: cn(itemClasses.item(), slotProps == null ? void 0 : (_slotProps_item = slotProps.item) == null ? void 0 : _slotProps_item.className),
7005
+ className: cn(itemClasses.item(), severityClasses.surface, severityClasses.border, slotProps == null ? void 0 : (_slotProps_item = slotProps.item) == null ? void 0 : _slotProps_item.className),
6645
7006
  children: [
6646
- jsx("span", {
6647
- "aria-hidden": "true",
6648
- className: cn(itemClasses.icon(), slotProps == null ? void 0 : (_slotProps_icon = slotProps.icon) == null ? void 0 : _slotProps_icon.className),
6649
- children: SEVERITY_GLYPH[sev]
7007
+ renderedIcon !== null && jsx("span", {
7008
+ className: cn(itemClasses.icon(), severityClasses.icon, slotProps == null ? void 0 : (_slotProps_icon = slotProps.icon) == null ? void 0 : _slotProps_icon.className),
7009
+ children: renderedIcon
6650
7010
  }),
6651
7011
  jsx("div", {
6652
7012
  className: cn(itemClasses.message(), slotProps == null ? void 0 : (_slotProps_message = slotProps.message) == null ? void 0 : _slotProps_message.className),
@@ -6667,7 +7027,20 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
6667
7027
  "aria-label": "Dismiss notification",
6668
7028
  onClick: ()=>dismiss(rec.id),
6669
7029
  className: cn(itemClasses.closeButton(), slotProps == null ? void 0 : (_slotProps_closeButton = slotProps.closeButton) == null ? void 0 : _slotProps_closeButton.className),
6670
- children: "\u00D7"
7030
+ children: jsx("svg", {
7031
+ width: "1em",
7032
+ height: "1em",
7033
+ viewBox: "0 0 20 20",
7034
+ fill: "none",
7035
+ stroke: "currentColor",
7036
+ strokeWidth: 1.5,
7037
+ strokeLinecap: "round",
7038
+ strokeLinejoin: "round",
7039
+ "aria-hidden": "true",
7040
+ children: jsx("path", {
7041
+ d: "m6 6 8 8M14 6l-8 8"
7042
+ })
7043
+ })
6671
7044
  })
6672
7045
  ]
6673
7046
  }, rec.id);
@@ -6678,277 +7051,1446 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
6678
7051
  }
6679
7052
 
6680
7053
  /**
6681
- * `typographyVariants` — the full type scale for the @dashforge/tw library.
7054
+ * Tailwind-variants recipe for `<Alert>`.
6682
7055
  *
6683
- * Mirrors the MUI Typography variant set (h1–h6 · subtitle1/2 · body1/2 ·
6684
- * caption · overline) so the mental model carries over for developers
6685
- * moving between the two ecosystems. Each variant baseline maps to a
6686
- * Tailwind utility chain that resolves through the @dashforge/tw-tokens
6687
- * scale (so the visual stays in sync with the rest of the system when the
6688
- * token theme is patched).
7056
+ * Slots:
7057
+ * - `root` — outer surface, severity + variant color classes
7058
+ * come from `_shared/severity/severityVariants`
7059
+ * (NOT baked into this recipe, to keep the matrix
7060
+ * shared across Alert / Snackbar / future Banner)
7061
+ * - `icon` — leading severity icon container
7062
+ * - `titleSlot` — `<AlertTitle>` rendered as the first content row
7063
+ * - `content` — body text container
7064
+ * - `action` — trailing action / close button container
7065
+ * - `closeButton` — built-in close `×` (when `onClose` is defined)
7066
+ *
7067
+ * The severity color classes are merged at render time via `cn()` in
7068
+ * `Alert.tsx` — see the call sites of `getSeverityClasses(variant,
7069
+ * severity)`. This recipe owns layout + spacing + typography only.
7070
+ */ const alertVariants = tv({
7071
+ slots: {
7072
+ root: [
7073
+ 'relative flex items-start gap-3',
7074
+ 'px-4 py-3',
7075
+ 'rounded-lg border',
7076
+ 'text-sm',
7077
+ // Smooth visibility transition (opacity only) — matches the
7078
+ // Snackbar entry timing, gated on motion-reduce per WCAG 2.3.3.
7079
+ 'transition-opacity duration-150 motion-reduce:transition-none motion-reduce:duration-0'
7080
+ ],
7081
+ icon: [
7082
+ 'shrink-0 mt-0.5',
7083
+ 'inline-flex items-center justify-center'
7084
+ ],
7085
+ titleSlot: [
7086
+ 'block font-medium leading-snug mb-1'
7087
+ ],
7088
+ content: [
7089
+ 'flex-1 min-w-0',
7090
+ 'leading-relaxed'
7091
+ ],
7092
+ action: [
7093
+ 'shrink-0 inline-flex items-center',
7094
+ 'ml-auto'
7095
+ ],
7096
+ closeButton: [
7097
+ 'shrink-0 inline-flex items-center justify-center w-6 h-6',
7098
+ 'rounded-md',
7099
+ 'opacity-70 hover:opacity-100',
7100
+ 'outline-none focus-visible:ring-2 focus-visible:ring-current',
7101
+ 'transition-opacity'
7102
+ ]
7103
+ },
7104
+ variants: {
7105
+ /**
7106
+ * Layout density. Defaults to comfortable spacing; `compact`
7107
+ * tightens the vertical padding for inline contexts (form
7108
+ * validation summary at the top of a tight modal, etc.).
7109
+ */ density: {
7110
+ comfortable: {
7111
+ root: 'px-4 py-3'
7112
+ },
7113
+ compact: {
7114
+ root: 'px-3 py-2 text-[13px]'
7115
+ }
7116
+ }
7117
+ },
7118
+ defaultVariants: {
7119
+ density: 'comfortable'
7120
+ }
7121
+ });
7122
+
7123
+ /**
7124
+ * `<AlertTitle>` — optional title block, rendered as the first child
7125
+ * of `<Alert>`. Pattern mirrors MUI exactly (sub-component, not a
7126
+ * prop), so the JSX shape ports 1:1 from `@mui/material`.
6689
7127
  *
6690
- * Variant axes are intentionally ORTHOGONAL — `variant` chooses the type
6691
- * scale, `weight` overrides the variant's default weight (useful for "h2
6692
- * but lighter"), `color` picks the intent, `align` picks the axis. They
6693
- * never collide, so consumers can mix them freely.
7128
+ * @example
7129
+ * <Alert severity="warning">
7130
+ * <AlertTitle>Subscription expires soon</AlertTitle>
7131
+ * Renew before 2026-06-20 to keep access.
7132
+ * </Alert>
7133
+ */ function AlertTitle({ children, className }) {
7134
+ const { titleSlot } = alertVariants();
7135
+ return jsx("div", {
7136
+ className: cn(titleSlot(), className),
7137
+ children: children
7138
+ });
7139
+ }
7140
+ /**
7141
+ * `<Alert>` — inline persistent status surface.
6694
7142
  *
6695
- * Two boolean flags (`truncate`, `noWrap`) encode the most common one-line
6696
- * patterns; `gutterBottom` adds the conventional bottom margin used when a
6697
- * heading precedes a paragraph block (mirror of MUI's same flag).
6698
- */ const typographyVariants = tv({
6699
- base: 'text-inherit',
6700
- variants: {
6701
- /*
6702
- * `variant` is the type-scale axis.
6703
- *
6704
- * The default font-weight is baked into each variant (headings come
6705
- * with semibold/bold by default). Consumers override per-instance via
6706
- * the `weight` axis below — when set, `weight` wins because it's
6707
- * declared later in the cn() chain and tailwind-merge resolves the
6708
- * last `font-*` to win.
6709
- */ variant: {
6710
- h1: 'text-5xl font-bold leading-[1.05] tracking-[-0.025em]',
6711
- h2: 'text-4xl font-bold leading-[1.1] tracking-[-0.022em]',
6712
- h3: 'text-3xl font-semibold leading-[1.15] tracking-[-0.02em]',
6713
- h4: 'text-2xl font-semibold leading-snug tracking-[-0.015em]',
6714
- h5: 'text-xl font-semibold leading-snug',
6715
- h6: 'text-lg font-semibold leading-normal',
6716
- subtitle1: 'text-base font-medium leading-relaxed',
6717
- subtitle2: 'text-sm font-medium leading-relaxed',
6718
- body1: 'text-base font-normal leading-relaxed',
6719
- body2: 'text-sm font-normal leading-relaxed',
6720
- caption: 'text-xs font-normal leading-normal',
6721
- overline: 'text-xs font-semibold uppercase tracking-[0.12em] leading-normal'
6722
- },
6723
- /*
6724
- * `color` is the intent axis. Pairs with the @dashforge/tw-theme
6725
- * reactive colour vars so the choice survives theme patches and dark
6726
- * mode flips. `inherit` is the escape hatch — used inside a Box that
6727
- * has set its own color (e.g. `<Box variant="solid" color="primary">`
6728
- * paints white text).
6729
- */ color: {
6730
- // `inherit` is the explicit escape hatch — use it on a
6731
- // `<Typography>` inside a coloured surface (e.g.
6732
- // `<Box variant="solid" color="primary">` paints white text)
6733
- // so the text picks up the surface colour. It is NO LONGER the
6734
- // default: a bare `<Typography>` would otherwise inherit the
6735
- // consuming app's arbitrary root colour (often OS-`prefers-
6736
- // color-scheme` driven), which diverges from the Dashforge
6737
- // `data-dash-tw-theme` mode and breaks colour consistency in
6738
- // dark mode. The default is now `neutral` — see `defaultVariants`.
6739
- inherit: 'text-inherit',
6740
- primary: 'text-primary-700 dark:text-primary-400',
6741
- secondary: 'text-secondary-700 dark:text-secondary-400',
6742
- success: 'text-success-700 dark:text-success-400',
6743
- warning: 'text-warning-700 dark:text-warning-400',
6744
- danger: 'text-danger-700 dark:text-danger-400',
6745
- info: 'text-info-700 dark:text-info-400',
6746
- // Neutral palette auto-inverts via the dashforgePreset() CSS-var
6747
- // swap — no `dark:` variants. `text-neutral-900` resolves to
6748
- // dark text in light mode and light text in dark mode; same
6749
- // applies to `text-neutral-600` (muted).
6750
- neutral: 'text-neutral-900',
6751
- muted: 'text-neutral-600'
6752
- },
6753
- /*
6754
- * `weight` overrides the variant's default weight. When unset, the
6755
- * variant's own weight wins. When set, this axis appears LATER in the
6756
- * cn() chain so tailwind-merge resolves to this value.
6757
- */ weight: {
6758
- normal: 'font-normal',
6759
- medium: 'font-medium',
6760
- semibold: 'font-semibold',
6761
- bold: 'font-bold',
6762
- extrabold: 'font-extrabold'
6763
- },
6764
- align: {
6765
- left: 'text-left',
6766
- center: 'text-center',
6767
- right: 'text-right',
6768
- justify: 'text-justify'
6769
- },
6770
- /*
6771
- * `truncate` collapses to a one-line ellipsis. `noWrap` is the looser
6772
- * sibling — keeps the text on one line but lets it overflow without
6773
- * the `…`. Mutually-exclusive intent-wise; if both are passed,
6774
- * `truncate` wins (later in the cn() chain).
6775
- */ truncate: {
6776
- true: 'truncate'
6777
- },
6778
- noWrap: {
6779
- true: 'whitespace-nowrap'
6780
- },
6781
- /*
6782
- * `gutterBottom` adds the conventional bottom margin used when a
6783
- * heading precedes a paragraph block. Mirror of MUI's same prop —
6784
- * familiar to developers crossing from the MUI side.
6785
- */ gutterBottom: {
6786
- true: 'mb-3'
6787
- }
6788
- },
6789
- defaultVariants: {
6790
- variant: 'body1',
6791
- // `neutral` (not `inherit`): a bare `<Typography>` emits
6792
- // `text-neutral-900`, which auto-inverts via the dashforgePreset()
6793
- // CSS-var swap and follows the Dashforge mode. Pass
6794
- // `color="inherit"` explicitly inside a coloured surface.
6795
- color: 'neutral',
6796
- align: 'left'
6797
- }
6798
- });
7143
+ * **API**: full MUI Alert parity (`severity`, `variant`, `icon`,
7144
+ * `onClose`, `action`, `role`, `<AlertTitle>` sub-component). One
7145
+ * deliberate divergence: `severity="danger"` instead of MUI's
7146
+ * `severity="error"` — aligned to the `danger.*` token palette.
7147
+ *
7148
+ * **Bridge integration** (Dashforge extras):
7149
+ * - `visibleWhen` — engine-reactive predicate (re-evaluated on
7150
+ * every engine state change when mounted inside a `<DashForm>`)
7151
+ * - `access` — RBAC gating via `@dashforge/rbac`
7152
+ *
7153
+ * **Token-driven**: every color class flows from `dashforgePreset()`
7154
+ * via CSS variables. No hardcoded hex, no `dark:` variants. The
7155
+ * 3×4 color matrix lives in `_shared/severity/severityVariants.ts`
7156
+ * and is shared with the refactored `<Snackbar>` (and future Banner).
7157
+ *
7158
+ * **A11y**: `role="alert"` for assertive severities (warning, danger)
7159
+ * and `role="status"` for polite ones (info, success). Override via
7160
+ * the `role` prop. The icon is `aria-hidden` — the message text is
7161
+ * the semantic carrier.
7162
+ *
7163
+ * @example
7164
+ * // Default — soft tinted surface, default icon
7165
+ * <Alert severity="info">New feature: export to CSV is now in beta.</Alert>
7166
+ *
7167
+ * // With title (sub-component) + close button
7168
+ * <Alert severity="warning" onClose={dismiss}>
7169
+ * <AlertTitle>Subscription expires soon</AlertTitle>
7170
+ * Your Pro plan ends in 3 days.
7171
+ * </Alert>
7172
+ *
7173
+ * // Custom icon
7174
+ * import { Bell } from 'lucide-react';
7175
+ * <Alert severity="info" icon={<Bell size={20} />}>3 new notifications</Alert>
7176
+ *
7177
+ * // No icon — colored surface alone carries the severity
7178
+ * <Alert severity="success" icon={false}>Saved 2 minutes ago.</Alert>
7179
+ *
7180
+ * // Custom action — CTA replaces the auto-close button
7181
+ * <Alert severity="warning" action={<button onClick={renew}>Renew</button>}>
7182
+ * Trial expires in 3 days.
7183
+ * </Alert>
7184
+ *
7185
+ * // Filled variant — for high-priority callouts
7186
+ * <Alert severity="danger" variant="filled">
7187
+ * Couldn't save — 3 fields below contain validation errors.
7188
+ * </Alert>
7189
+ */ function Alert({ severity, variant = 'standard', children, icon: iconProp, onClose, closeText = 'Close', action, role: roleProp, visibleWhen, access, sx, className, slotProps }) {
7190
+ var _slotProps_root, _slotProps_icon, _slotProps_content, _slotProps_action, _slotProps_closeButton;
7191
+ // Bridge — both hooks safely return defaults outside a DashForm /
7192
+ // RbacProvider, matching the TextField pattern.
7193
+ const bridge = useContext(DashFormContext);
7194
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
7195
+ const accessState = useAccessState(access);
7196
+ if (!isVisible || !accessState.visible) return null;
7197
+ const v = alertVariants();
7198
+ const severityClasses = getSeverityClasses(variant, severity);
7199
+ const role = roleProp != null ? roleProp : getSeverityRole(severity);
7200
+ // Icon resolution:
7201
+ // - false → no icon
7202
+ // - undefined → default per severity (lib's inline SVG)
7203
+ // - ReactNode → consumer's custom icon
7204
+ const renderedIcon = iconProp === false ? null : iconProp !== undefined ? iconProp : getDefaultSeverityIcon(severity);
7205
+ return jsxs("div", _extends({
7206
+ role: role,
7207
+ className: cn(v.root(), severityClasses.surface, severityClasses.border, sx, className, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className)
7208
+ }, slotProps == null ? void 0 : slotProps.root, {
7209
+ children: [
7210
+ renderedIcon !== null && jsx("span", _extends({
7211
+ className: cn(v.icon(), severityClasses.icon, slotProps == null ? void 0 : (_slotProps_icon = slotProps.icon) == null ? void 0 : _slotProps_icon.className)
7212
+ }, slotProps == null ? void 0 : slotProps.icon, {
7213
+ children: renderedIcon
7214
+ })),
7215
+ jsx("div", _extends({
7216
+ className: cn(v.content(), slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className)
7217
+ }, slotProps == null ? void 0 : slotProps.content, {
7218
+ children: children
7219
+ })),
7220
+ action ? jsx("div", _extends({
7221
+ className: cn(v.action(), slotProps == null ? void 0 : (_slotProps_action = slotProps.action) == null ? void 0 : _slotProps_action.className)
7222
+ }, slotProps == null ? void 0 : slotProps.action, {
7223
+ children: action
7224
+ })) : onClose ? jsx("button", _extends({
7225
+ type: "button",
7226
+ "aria-label": closeText,
7227
+ onClick: onClose,
7228
+ className: cn(v.closeButton(), slotProps == null ? void 0 : (_slotProps_closeButton = slotProps.closeButton) == null ? void 0 : _slotProps_closeButton.className)
7229
+ }, slotProps == null ? void 0 : slotProps.closeButton, {
7230
+ children: jsx("svg", {
7231
+ width: "1em",
7232
+ height: "1em",
7233
+ viewBox: "0 0 20 20",
7234
+ fill: "none",
7235
+ stroke: "currentColor",
7236
+ strokeWidth: 1.5,
7237
+ strokeLinecap: "round",
7238
+ strokeLinejoin: "round",
7239
+ "aria-hidden": "true",
7240
+ children: jsx("path", {
7241
+ d: "m6 6 8 8M14 6l-8 8"
7242
+ })
7243
+ })
7244
+ })) : null
7245
+ ]
7246
+ }));
7247
+ }
7248
+
7249
+ const ICON_BUTTON_SIZE_OVERRIDES = {
7250
+ sm: 'w-8 px-0',
7251
+ md: 'w-10 px-0',
7252
+ lg: 'w-12 px-0'
7253
+ };
7254
+ /** Always-applied layout primitives — not token-driven (geometric CSS). */ const ICON_BUTTON_BASE = 'aspect-square inline-flex items-center justify-center';
6799
7255
 
6800
7256
  /**
6801
- * Default HTML tag per variant. Headings get their semantic level (h1→h1
6802
- * etc.); subtitle/body get `<p>` (block, paragraph semantics);
6803
- * caption/overline get `<span>` (inline, no implicit block break).
7257
+ * Map IconButton size to Spinner size. Same mapping as Button —
7258
+ * Spinner has 5 steps, the parent has 3.
6804
7259
  *
6805
- * Override per-instance via the `as` prop — useful when the semantic
6806
- * heading level should differ from the visual scale (e.g. a hero
6807
- * rendered as `<h2>` but visually styled `h1`).
6808
- */ const VARIANT_TO_TAG = {
6809
- h1: 'h1',
6810
- h2: 'h2',
6811
- h3: 'h3',
6812
- h4: 'h4',
6813
- h5: 'h5',
6814
- h6: 'h6',
6815
- subtitle1: 'p',
6816
- subtitle2: 'p',
6817
- body1: 'p',
6818
- body2: 'p',
6819
- caption: 'span',
6820
- overline: 'span'
6821
- };
7260
+ * @internal
7261
+ */ function iconButtonSizeToSpinnerSize(size) {
7262
+ return size === 'sm' ? 'xs' : size === 'lg' ? 'md' : 'sm';
7263
+ }
6822
7264
  /**
6823
- * `<Typography>` — semantic typed text, the foundation of every readable
6824
- * surface in @dashforge/tw.
7265
+ * Dashforge TW IconButton — square, icon-only action button.
6825
7266
  *
6826
- * Why this exists:
6827
- * Tailwind ships a typographic scale (`text-xl`, `font-bold`,
6828
- * `leading-relaxed`) but leaves the SEMANTIC HTML tag and the
6829
- * intent-coloured palette to the consumer. That's fine for one-off
6830
- * marketing surfaces, but at app scale it means every `<h2>` and every
6831
- * body paragraph re-derives its own utility chain — and they drift.
7267
+ * Reuses `buttonVariants` 1:1 (same `variant` / `color` / `size` /
7268
+ * `loading` axes) so every `<Button>` style maps to the equivalent
7269
+ * `<IconButton>` with zero code duplication. The ONLY additions are:
6832
7270
  *
6833
- * Typography moves that decision into a typed prop set: the visual
6834
- * scale, the intent colour, the alignment, the truncation all live in
6835
- * `tailwind-variants` and resolve to the same utility chain everywhere
6836
- * in the app. The default HTML tag is inferred from `variant` so the
6837
- * semantic layer follows the visual layer; `as` and `asChild` are the
6838
- * two escape hatches when you need something else.
7271
+ * - `ICON_BUTTON_SIZE_OVERRIDES` — forces `w-N + px-0` so the button
7272
+ * is square at every size token
7273
+ * - `ICON_BUTTON_BASE` — `aspect-square inline-flex items-center
7274
+ * justify-center` for layout
6839
7275
  *
6840
- * Layering:
6841
- * • Sits BENEATH every component that renders text — `<Button>`'s
6842
- * label, `<TextField>`'s helper text, MDX prose in our own docs.
6843
- * • Composes ON TOP of `@dashforge/tw-tokens` colour scales (so
6844
- * `color="primary"` paints `text-primary-700` in light, `-400` in dark,
6845
- * reactive to `setMode()`).
6846
- * • Polymorphic via Radix Slot — pairs cleanly with router `<Link>`,
6847
- * `<button>`, or `<a>` without injecting an extra wrapper element.
7276
+ * Both additions are token-driven (spacing) or layout-primitive
7277
+ * (geometry) — no hardcoded hex, no `dark:` variants.
6848
7278
  *
6849
- * Polymorphism rules — `as` vs `asChild`:
6850
- * • `as` swaps the rendered tag (we still render the element ourselves).
6851
- * • `asChild` removes our element entirely — the single React child
6852
- * becomes the rendered tag, with our className/ref merged onto it.
6853
- * This is the Radix Slot pattern; use when you need a router Link
6854
- * to style as a heading.
6855
- * When BOTH are passed, `asChild` wins. We considered making this a
6856
- * compile-time error via discriminated unions, but the API surface
6857
- * already has 8 axes and adding one more dimension to the props type
6858
- * would inflate IntelliSense suggestions for marginal benefit. The
6859
- * runtime preference is documented; the test asserts it.
6860
- */ const Typography = /*#__PURE__*/ forwardRef(function Typography(props, ref) {
6861
- var _ref;
6862
- const { variant = 'body1', color, weight, align, truncate, noWrap, gutterBottom, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
7279
+ * **`aria-label` is REQUIRED** at the type level — icon-only buttons
7280
+ * are invisible to assistive tech without an explicit accessible
7281
+ * name. The TypeScript compiler enforces this rather than relying on
7282
+ * runtime warnings.
7283
+ *
7284
+ * RBAC integration mirrors `<Button>`: `access` with
7285
+ * `onUnauthorized: 'hide' | 'disable' | 'readonly'`. No `visibleWhen`
7286
+ * (mirror Button — buttons should disable, not hide, on
7287
+ * state-driven unavailability; permission-driven hiding is `access`).
7288
+ *
7289
+ * @example
7290
+ * ```tsx
7291
+ * import { Trash2 } from 'lucide-react';
7292
+ *
7293
+ * <IconButton aria-label="Delete row" color="danger" onClick={onDelete}>
7294
+ * <Trash2 size={20} />
7295
+ * </IconButton>
7296
+ *
7297
+ * // Polymorphism via Radix Slot
7298
+ * <IconButton asChild aria-label="Open settings" variant="ghost">
7299
+ * <Link to="/settings"><SettingsIcon size={20} /></Link>
7300
+ * </IconButton>
7301
+ *
7302
+ * // RBAC-gated
7303
+ * <IconButton
7304
+ * aria-label="Publish article"
7305
+ * variant="solid"
7306
+ * color="primary"
7307
+ * access={{ resource: 'article', action: 'publish', onUnauthorized: 'disable' }}
7308
+ * >
7309
+ * <PublishIcon size={20} />
7310
+ * </IconButton>
7311
+ *
7312
+ * // Loading swap
7313
+ * <IconButton aria-label="Save" loading={isSaving}>
7314
+ * <SaveIcon size={20} />
7315
+ * </IconButton>
7316
+ * ```
7317
+ */ const IconButton = /*#__PURE__*/ forwardRef(function IconButton(props, ref) {
7318
+ var _rest_type;
7319
+ const { access, visibleWhen, disabled, variant, color, size = 'md', loading, asChild, sx, className, children } = props, rest = _object_without_properties_loose(props, [
7320
+ "access",
7321
+ "visibleWhen",
7322
+ "disabled",
6863
7323
  "variant",
6864
7324
  "color",
6865
- "weight",
6866
- "align",
6867
- "truncate",
6868
- "noWrap",
6869
- "gutterBottom",
6870
- "as",
7325
+ "size",
7326
+ "loading",
6871
7327
  "asChild",
6872
7328
  "sx",
7329
+ "className",
6873
7330
  "children"
6874
7331
  ]);
6875
- const classes = cn(typographyVariants({
7332
+ // Bridge — both hooks called unconditionally (rules-of-hooks).
7333
+ const bridge = useContext(DashFormContext);
7334
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
7335
+ const accessState = useAccessState(access);
7336
+ if (!isVisible || !accessState.visible) return null;
7337
+ // Effective disabled = explicit OR RBAC.disabled OR RBAC.readonly OR loading.
7338
+ // Mirrors `<Button>` exactly — IconButton has no true read-only
7339
+ // state, so RBAC readonly maps to disabled (same as MUI side).
7340
+ const effectiveDisabled = Boolean(disabled) || accessState.disabled || accessState.readonly || Boolean(loading);
7341
+ const classes = cn(buttonVariants({
6876
7342
  variant,
6877
7343
  color,
6878
- weight,
6879
- align,
6880
- truncate,
6881
- noWrap,
6882
- gutterBottom
6883
- }), sx);
6884
- // asChild wins over `as` when both are passed (see component header).
7344
+ size,
7345
+ loading
7346
+ }), ICON_BUTTON_SIZE_OVERRIDES[size], ICON_BUTTON_BASE, sx, className);
7347
+ // `aria-busy` while loading — distinguishes "wait for action to
7348
+ // finish" from "you can't do this" in screen-reader announcements.
7349
+ const ariaBusy = loading ? true : undefined;
6885
7350
  if (asChild) {
6886
- return jsx(Slot, _extends({
7351
+ return jsx(Slot, {
6887
7352
  ref: ref,
6888
- className: classes
6889
- }, rest, {
7353
+ className: classes,
7354
+ "data-disabled": effectiveDisabled || undefined,
7355
+ "aria-disabled": effectiveDisabled || undefined,
7356
+ "aria-busy": ariaBusy,
6890
7357
  children: children
6891
- }));
7358
+ });
6892
7359
  }
6893
- // Resolve the rendered tag: explicit `as` > variant default > fallback.
6894
- const Tag = (_ref = as != null ? as : VARIANT_TO_TAG[variant]) != null ? _ref : 'span';
6895
- return jsx(Tag, _extends({
7360
+ return jsx("button", _extends({
6896
7361
  ref: ref,
7362
+ type: (_rest_type = rest.type) != null ? _rest_type : 'button',
7363
+ disabled: effectiveDisabled,
7364
+ "aria-busy": ariaBusy,
6897
7365
  className: classes
6898
7366
  }, rest, {
6899
- children: children
7367
+ children: loading ? jsx(Spinner, {
7368
+ size: iconButtonSizeToSpinnerSize(size),
7369
+ thickness: "thick",
7370
+ withTrack: true,
7371
+ label: ""
7372
+ }) : children
6900
7373
  }));
6901
7374
  });
6902
- Typography.displayName = 'Typography';
6903
7375
 
6904
7376
  /**
6905
- * `boxVariants` — the surface primitive for @dashforge/tw.
6906
- *
6907
- * Architectural choice (planned with the user — F9 deep dive):
6908
- *
6909
- * Box replaces FOUR overlapping concepts from MUI in one component:
6910
- * • Box (typed div)
6911
- * • Paper (surface with elevation)
6912
- * • Card (Paper specialisation)
6913
- * • Surface (Joy UI's outlined / soft / solid / plain variants)
6914
- *
6915
- * The reason for the consolidation: in MUI you have to compose two or
6916
- * three of these to express even basic intent ("an outlined card with
6917
- * warning tone"). Here, one `<Box variant="outlined" color="warning">`
6918
- * says exactly that.
6919
- *
6920
- * Variant taxonomy (5 axes, intentionally non-overlapping with Stack/Grid):
7377
+ * Tailwind-variants recipe for the Menu family.
6921
7378
  *
6922
- * • plain — bare div + padding + radius. The escape hatch.
6923
- * • outlined — 1px border + subtle bg tint. The "card lite".
6924
- * • elevated — bg surface + shadow scale (0-5). The "floating panel".
6925
- * • soft — semi-transparent intent bg + intent text. The "callout".
6926
- * • solid — solid intent bg + contrasting text. The "CTA banner".
7379
+ * The whole menu family is styled here in one TV instance so the
7380
+ * compound rules (item color × selected × danger, etc.) live in
7381
+ * one source of truth.
6927
7382
  *
6928
- * `color` applies to outlined/soft/solid (each gets the 7 intent
6929
- * variants). `elevated` is color-agnostic (always neutral surface +
6930
- * shadow scale). `plain` is everything-agnostic.
7383
+ * Slots:
7384
+ * - `content` — floating panel (DropdownMenu.Content)
7385
+ * - `item` — clickable row (DropdownMenu.Item)
7386
+ * - `itemIcon` — leading icon container
7387
+ * - `itemLabel` — text content slot
7388
+ * - `itemEnd` — trailing slot (shortcut hint, chevron, …)
7389
+ * - `label` — non-interactive heading (DropdownMenu.Label)
7390
+ * - `separator` — divider (DropdownMenu.Separator)
7391
+ * - `skeleton` — placeholder item (visual only, no Radix slot)
7392
+ */ const menuVariants = tv({
7393
+ slots: {
7394
+ content: [
7395
+ 'z-50 min-w-[180px] py-1.5',
7396
+ // Surface elevation: `bg-white` is hardcoded (does NOT
7397
+ // auto-invert via the dashforgePreset CSS-var swap), so we
7398
+ // pair it with `dark:bg-neutral-100` (target #171717 — one
7399
+ // elevation tier above page surface in dark mode). This is
7400
+ // Category B of THEME-AUDIT.md — must stay on the same
7401
+ // string literal so the whitelist regex matches.
7402
+ 'rounded-md border border-neutral-200 bg-white dark:bg-neutral-100 shadow-lg',
7403
+ // Smooth fade-in via Radix's data-state attributes. Motion-
7404
+ // reduce gated.
7405
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
7406
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
7407
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
7408
+ 'motion-reduce:animate-none motion-reduce:transition-none',
7409
+ // List reset (defensive — same defensive pattern as
7410
+ // Autocomplete listbox after the listbox-bullets fix in 1.0.1).
7411
+ 'list-none pl-0 m-0'
7412
+ ],
7413
+ item: [
7414
+ 'group relative flex cursor-pointer select-none items-center gap-2',
7415
+ 'px-3 py-1.5 mx-1 my-0.5',
7416
+ 'rounded-md text-sm text-neutral-900',
7417
+ 'outline-none',
7418
+ // Radix sets data-highlighted when the item is keyboard-focused
7419
+ // OR hovered. We anchor the active visual state to that single
7420
+ // attribute so keyboard + mouse interactions look identical.
7421
+ 'data-[highlighted]:bg-neutral-100',
7422
+ // Disabled (Radix sets data-disabled).
7423
+ 'data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none',
7424
+ 'transition-colors duration-100',
7425
+ 'motion-reduce:transition-none'
7426
+ ],
7427
+ itemIcon: 'shrink-0 inline-flex items-center justify-center w-4 h-4',
7428
+ itemLabel: 'flex-1 truncate',
7429
+ itemEnd: 'shrink-0 ml-3 inline-flex items-center text-xs text-neutral-500',
7430
+ label: [
7431
+ 'px-3 py-1 mx-1 mt-1 text-xs font-medium uppercase tracking-wide',
7432
+ 'text-neutral-500'
7433
+ ],
7434
+ separator: 'h-px my-1 mx-1 bg-neutral-200',
7435
+ skeleton: [
7436
+ 'flex items-center gap-2 px-3 py-1.5 mx-1 my-0.5',
7437
+ 'animate-pulse motion-reduce:animate-none'
7438
+ ]
7439
+ },
7440
+ variants: {
7441
+ /**
7442
+ * Item color intent. `'danger'` highlights destructive actions
7443
+ * (Delete, Sign out) with red bg on hover. Both bg and text
7444
+ * shift to the danger palette when the item is highlighted.
7445
+ */ color: {
7446
+ default: {},
7447
+ danger: {
7448
+ item: [
7449
+ 'text-danger-700',
7450
+ // Hover + keyboard focus → red soft bg + saturated text.
7451
+ 'data-[highlighted]:bg-danger-50 data-[highlighted]:text-danger-800',
7452
+ // Dark mode: bg-danger-50 doesn't auto-invert (color
7453
+ // palettes don't), so we add the explicit dark variant.
7454
+ // The `text-danger-700` base also gets a dark counterpart
7455
+ // for readability on the dark surface.
7456
+ 'dark:text-danger-300',
7457
+ 'dark:data-[highlighted]:bg-danger-950 dark:data-[highlighted]:text-danger-200'
7458
+ ],
7459
+ itemIcon: 'text-danger-600 dark:text-danger-400'
7460
+ }
7461
+ },
7462
+ /**
7463
+ * Visual `selected` hint — adds a faint primary bg + the
7464
+ * leading edge gets a primary accent. Pure visual; v1 has no
7465
+ * controlled selection state machine.
7466
+ */ selected: {
7467
+ true: {
7468
+ item: [
7469
+ 'bg-primary-50 text-primary-900',
7470
+ 'dark:bg-primary-950 dark:text-primary-100'
7471
+ ],
7472
+ itemIcon: 'text-primary-600 dark:text-primary-400'
7473
+ }
7474
+ }
7475
+ },
7476
+ defaultVariants: {
7477
+ color: 'default'
7478
+ }
7479
+ });
7480
+ /**
7481
+ * Resolve a `MenuPlacement` enum to Radix `side` + `align` props.
7482
+ * Single source of truth so Menu.tsx doesn't need a switch statement.
7483
+ */ function placementToRadix(placement) {
7484
+ // The format is `<side>` or `<side>-<align>`. Center is the default
7485
+ // when no `-start` / `-end` suffix is present.
7486
+ const [side, alignSuffix] = placement.split('-');
7487
+ return {
7488
+ side,
7489
+ align: alignSuffix != null ? alignSuffix : 'center'
7490
+ };
7491
+ }
7492
+
7493
+ const MenuContext = /*#__PURE__*/ createContext({
7494
+ closeOnItemClick: true
7495
+ });
7496
+ // ─── Menu (Root provider) ────────────────────────────────────────
7497
+ /**
7498
+ * `<Menu>` — top-level provider that wraps Radix `DropdownMenu.Root`.
6931
7499
  *
6932
- * Spacing axes (p/px/py/m/mx/my): mapped explicitly to the 11 token
6933
- * steps from @dashforge/tw-tokens (0, 0.5, 1, 2, 3, 4, 6, 8, 12, 16, 24).
6934
- * Tailwind JIT requires literal class strings — building them dynamically
6935
- * with template literals would purge them. The verbosity below is the
6936
- * cost of keeping the bundle CSS-pure and rebuild-free.
7500
+ * Compound API:
7501
+ * ```tsx
7502
+ * <Menu>
7503
+ * <MenuTrigger>
7504
+ * <IconButton aria-label="Actions"><MoreIcon /></IconButton>
7505
+ * </MenuTrigger>
7506
+ * <MenuContent>
7507
+ * <MenuLabel>Actions</MenuLabel>
7508
+ * <MenuItem onClick={onEdit}>Edit</MenuItem>
7509
+ * <MenuSeparator />
7510
+ * <MenuItem color="danger" onClick={onDelete}>Delete</MenuItem>
7511
+ * </MenuContent>
7512
+ * </Menu>
7513
+ * ```
6937
7514
  *
6938
- * What this does NOT do (deliberate, see component docs):
6939
- * • No display / flex / grid props → use Stack or Grid
6940
- * • No position / overflow / z-index → use `sx`
6941
- * • No animation / transition → use `sx`
7515
+ * Built on `@radix-ui/react-dropdown-menu` (purpose-built menu
7516
+ * primitive with full WAI-ARIA menu pattern + keyboard nav
7517
+ * + type-ahead + portal mount).
6942
7518
  *
6943
- * The "Box is not flex" rule is the spine of the layout layer. Without
6944
- * it, every `<div>` in an app gravitates back to Box and the surface
6945
- * vs layout distinction collapses — exactly the failure mode this
6946
- * primitive exists to prevent.
6947
- */ const boxVariants = tv({
6948
- base: 'block',
6949
- variants: {
6950
- /*
6951
- * Surface variant. Compound with `color` for outlined / soft / solid;
7519
+ * Sub-menus and controlled selection mode are deliberately out of
7520
+ * scope for v1 (Sprint 4.4) — composable via Radix primitives if
7521
+ * needed via explicit sub-component opt-in (future).
7522
+ */ function Menu({ children, open, defaultOpen, onOpenChange, closeOnItemClick = true, modal = false }) {
7523
+ return jsx(MenuContext.Provider, {
7524
+ value: {
7525
+ closeOnItemClick
7526
+ },
7527
+ children: jsx(RadixMenu.Root, {
7528
+ open: open,
7529
+ defaultOpen: defaultOpen,
7530
+ onOpenChange: onOpenChange,
7531
+ modal: modal,
7532
+ children: children
7533
+ })
7534
+ });
7535
+ }
7536
+ // ─── MenuTrigger ─────────────────────────────────────────────────
7537
+ /**
7538
+ * `<MenuTrigger>` — the element that opens the menu.
7539
+ *
7540
+ * Always renders the immediate child via Radix Slot (`asChild`) — the
7541
+ * child receives the click handler + `aria-haspopup="menu"` +
7542
+ * `aria-expanded` reactive to open state. Pass any interactive
7543
+ * component as the child (`<IconButton>`, `<Button>`, `<Avatar>`,
7544
+ * custom).
7545
+ */ function MenuTrigger({ children }) {
7546
+ return jsx(RadixMenu.Trigger, {
7547
+ asChild: true,
7548
+ children: children
7549
+ });
7550
+ }
7551
+ // ─── MenuContent ─────────────────────────────────────────────────
7552
+ /**
7553
+ * `<MenuContent>` — the floating panel.
7554
+ *
7555
+ * Lazy-mounted via Radix Portal — zero DOM cost when the menu is
7556
+ * closed (one of the perf properties we inherit from Radix +
7557
+ * Atlaskit-style menus).
7558
+ *
7559
+ * Placement / sideOffset are read from the parent `<Menu>` via
7560
+ * Radix context — we re-pull the `<Menu>` props via the
7561
+ * `MenuContext` here.
7562
+ *
7563
+ * NB: placement is read from PARENT MENU PROPS, not from
7564
+ * `<MenuContent>` directly — keeps the floating-positioning API on
7565
+ * the provider where it belongs.
7566
+ */ const MenuContent = /*#__PURE__*/ forwardRef(function MenuContent(props, ref) {
7567
+ const { children, minWidth, className, sx } = props;
7568
+ const v = menuVariants();
7569
+ // NB: we don't read `placement` from MenuContext (Radix Root
7570
+ // doesn't surface those props to descendants). Instead, the
7571
+ // consumer passes them on `<Menu>` and they're forwarded into
7572
+ // `<MenuContent>` via context-style props — to keep the API
7573
+ // simple, we DON'T expose placement on MenuContent itself; the
7574
+ // user sets it on Menu. We achieve this by reading the MenuProps
7575
+ // off the Radix-internal context… actually, simpler: we just
7576
+ // accept the default `bottom-start` from Radix and let the
7577
+ // consumer pass `side`/`align` via MenuContent if they want fine
7578
+ // control. For now, hardcode the most common defaults; Sprint
7579
+ // 4.4-bis can lift this if real demand emerges.
7580
+ const { side, align } = placementToRadix('bottom-start');
7581
+ return jsx(RadixMenu.Portal, {
7582
+ children: jsx(RadixMenu.Content, {
7583
+ ref: ref,
7584
+ side: side,
7585
+ align: align,
7586
+ sideOffset: 6,
7587
+ style: minWidth !== undefined ? {
7588
+ minWidth: typeof minWidth === 'number' ? `${minWidth}px` : minWidth
7589
+ } : undefined,
7590
+ className: cn(v.content(), sx, className),
7591
+ children: children
7592
+ })
7593
+ });
7594
+ });
7595
+ MenuContent.displayName = 'MenuContent';
7596
+ // ─── MenuItem ────────────────────────────────────────────────────
7597
+ /**
7598
+ * `<MenuItem>` — clickable row inside the menu.
7599
+ *
7600
+ * Memoized via `React.memo` for performance — re-renders only when
7601
+ * its own props change, not when sibling items mutate. Pattern
7602
+ * inspired by Atlaskit's `ButtonItem` optimization (one of the
7603
+ * reasons Atlaskit menus stay responsive on large item lists).
7604
+ *
7605
+ * Bridge integration:
7606
+ * - `access` — hides / disables the item per RBAC
7607
+ * - `visibleWhen` — engine-reactive predicate
7608
+ *
7609
+ * Both gates fold into the visible/disabled state; when the
7610
+ * predicate returns `false` OR `access` denies visibility, the
7611
+ * item renders `null` (does NOT occupy a row in the menu, vs
7612
+ * disabled which renders dimmed-but-present).
7613
+ */ function MenuItemImpl(props) {
7614
+ const { children, onClick, icon, endIcon, disabled: disabledProp, selected, color = 'default', access, visibleWhen, className, sx } = props;
7615
+ const { closeOnItemClick } = useContext(MenuContext);
7616
+ // Bridge hooks — always called (rules-of-hooks).
7617
+ const bridge = useContext(DashFormContext);
7618
+ const isBridgeVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
7619
+ const accessState = useAccessState(access);
7620
+ if (!isBridgeVisible || !accessState.visible) return null;
7621
+ const isDisabled = Boolean(disabledProp) || accessState.disabled || accessState.readonly;
7622
+ const v = menuVariants({
7623
+ color,
7624
+ selected
7625
+ });
7626
+ return jsxs(RadixMenu.Item, {
7627
+ disabled: isDisabled,
7628
+ onSelect: (e)=>{
7629
+ if (isDisabled) return;
7630
+ // Suppress default close behaviour when the parent <Menu>
7631
+ // explicitly opted out (filter / multi-select pattern).
7632
+ if (!closeOnItemClick) e.preventDefault();
7633
+ onClick == null ? void 0 : onClick();
7634
+ },
7635
+ className: cn(v.item(), sx, className),
7636
+ children: [
7637
+ icon != null && jsx("span", {
7638
+ className: v.itemIcon(),
7639
+ children: icon
7640
+ }),
7641
+ jsx("span", {
7642
+ className: v.itemLabel(),
7643
+ children: children
7644
+ }),
7645
+ endIcon != null && jsx("span", {
7646
+ className: v.itemEnd(),
7647
+ children: endIcon
7648
+ })
7649
+ ]
7650
+ });
7651
+ }
7652
+ const MenuItem = /*#__PURE__*/ memo(MenuItemImpl);
7653
+ MenuItem.displayName = 'MenuItem';
7654
+ // ─── MenuLabel ───────────────────────────────────────────────────
7655
+ /**
7656
+ * `<MenuLabel>` — non-interactive group heading.
7657
+ *
7658
+ * Renders via Radix `DropdownMenu.Label` which carries the right
7659
+ * ARIA semantics (`role="presentation"` — not announced as a
7660
+ * `menuitem` by screen readers).
7661
+ */ function MenuLabel({ children, className }) {
7662
+ const v = menuVariants();
7663
+ return jsx(RadixMenu.Label, {
7664
+ className: cn(v.label(), className),
7665
+ children: children
7666
+ });
7667
+ }
7668
+ // ─── MenuSeparator ───────────────────────────────────────────────
7669
+ /**
7670
+ * `<MenuSeparator>` — horizontal divider between items.
7671
+ *
7672
+ * Renders via Radix `DropdownMenu.Separator` — semantically correct
7673
+ * `role="separator"` for screen readers navigating the menu.
7674
+ */ function MenuSeparator({ className }) {
7675
+ const v = menuVariants();
7676
+ return jsx(RadixMenu.Separator, {
7677
+ className: cn(v.separator(), className)
7678
+ });
7679
+ }
7680
+ // ─── MenuSkeleton ────────────────────────────────────────────────
7681
+ /**
7682
+ * `<MenuSkeleton>` — Atlaskit-style loading state.
7683
+ *
7684
+ * Renders `count` placeholder rows (default 3) inside the menu while
7685
+ * real items are being fetched. Each row matches the geometry of a
7686
+ * real `<MenuItem>` (height, padding, icon slot) so the layout
7687
+ * doesn't jump when the real data arrives.
7688
+ *
7689
+ * `withHeading=true` adds a skeleton heading at the top, mimicking a
7690
+ * `<MenuLabel>` — useful when the real menu starts with a section
7691
+ * label.
7692
+ *
7693
+ * Pure visual — no animation overhead beyond Tailwind's
7694
+ * `animate-pulse` (motion-reduce-safe).
7695
+ *
7696
+ * @example
7697
+ * ```tsx
7698
+ * <MenuContent>
7699
+ * {isLoading ? (
7700
+ * <MenuSkeleton count={5} withHeading />
7701
+ * ) : (
7702
+ * items.map((it) => <MenuItem key={it.id}>{it.label}</MenuItem>)
7703
+ * )}
7704
+ * </MenuContent>
7705
+ * ```
7706
+ */ function MenuSkeleton({ count = 3, withHeading = false, className }) {
7707
+ const v = menuVariants();
7708
+ return jsxs("div", {
7709
+ role: "presentation",
7710
+ "aria-hidden": "true",
7711
+ className: cn('animate-pulse motion-reduce:animate-none', className),
7712
+ children: [
7713
+ withHeading && jsx("div", {
7714
+ className: cn(v.label(), 'pointer-events-none'),
7715
+ children: jsx("span", {
7716
+ className: "inline-block h-2.5 w-16 rounded-sm bg-neutral-200"
7717
+ })
7718
+ }),
7719
+ Array.from({
7720
+ length: count
7721
+ }).map((_, i)=>jsx(SkeletonRow, {}, i))
7722
+ ]
7723
+ });
7724
+ }
7725
+ /**
7726
+ * One skeleton row — matches the dimensions of a real `<MenuItem>`
7727
+ * (icon slot + label slot). Internal — not exported.
7728
+ *
7729
+ * @internal
7730
+ */ function SkeletonRow() {
7731
+ return jsxs("div", {
7732
+ className: "flex items-center gap-2 px-3 py-1.5 mx-1 my-0.5",
7733
+ children: [
7734
+ jsx("span", {
7735
+ className: "inline-block h-4 w-4 shrink-0 rounded-sm bg-neutral-200"
7736
+ }),
7737
+ jsx("span", {
7738
+ className: "inline-block h-3 flex-1 rounded-sm bg-neutral-200"
7739
+ })
7740
+ ]
7741
+ });
7742
+ }
7743
+
7744
+ /**
7745
+ * Tailwind-variants recipe for `<Badge>`.
7746
+ *
7747
+ * Slot layout:
7748
+ * - `root` — outer span (relative-positioned wrapper around the
7749
+ * anchor). `inline-flex` so the wrapper shrinks to fit
7750
+ * the anchor element.
7751
+ * - `badge` — the floating absolute-positioned pill / dot. Two
7752
+ * variants (`dot=true` → tiny circle, `dot=false` →
7753
+ * pill with min-width to fit content).
7754
+ *
7755
+ * Color axis emits the `badge` background + text classes. The 7
7756
+ * intents map to:
7757
+ * - solid bg: `bg-{intent}-500` (vivid, attention-grabbing for
7758
+ * notifications — different from Chip which uses 600 for solid
7759
+ * because chip surface is larger; small badge needs the brighter
7760
+ * mid-tone)
7761
+ * - text: `text-white` for primary/secondary/success/warning/danger/
7762
+ * info; `text-neutral-50` for neutral (auto-inverts via CSS-var
7763
+ * swap, no `dark:` needed)
7764
+ *
7765
+ * Placement axis emits absolute-position + translate utilities:
7766
+ * - top-right (default): `top-0 right-0 -translate-y-1/2 translate-x-1/2`
7767
+ * - top-left: `top-0 left-0 -translate-y-1/2 -translate-x-1/2`
7768
+ * - bottom-right: `bottom-0 right-0 translate-y-1/2 translate-x-1/2`
7769
+ * - bottom-left: `bottom-0 left-0 translate-y-1/2 -translate-x-1/2`
7770
+ *
7771
+ * Overlap axis adds an extra inset for circular anchors so the
7772
+ * badge sits along the round edge instead of the bounding box corner.
7773
+ */ const badgeVariants = tv({
7774
+ slots: {
7775
+ root: 'relative inline-flex shrink-0',
7776
+ badge: [
7777
+ // Absolute positioning is owned by the placement compound rules.
7778
+ 'absolute z-10',
7779
+ // Layout / typography — pill defaults; dot mode overrides
7780
+ // dimensions below.
7781
+ 'inline-flex items-center justify-center',
7782
+ 'rounded-full',
7783
+ 'text-[10px] font-medium leading-none',
7784
+ 'pointer-events-none select-none',
7785
+ // Smooth show/hide for `invisible` toggle (consumer-side
7786
+ // animation hook). Gated on motion-reduce per WCAG 2.3.3.
7787
+ 'transition-transform duration-150 motion-reduce:transition-none motion-reduce:duration-0'
7788
+ ]
7789
+ },
7790
+ variants: {
7791
+ /**
7792
+ * `dot=true` → tiny circle (8px), no content. The badge slot
7793
+ * collapses to a fixed-size circle without the pill min-width.
7794
+ */ dot: {
7795
+ true: {
7796
+ badge: 'w-2 h-2 p-0'
7797
+ },
7798
+ false: {
7799
+ // Content pill — 16px tall, padded inline, min-width so
7800
+ // single-digit counts still read centered.
7801
+ badge: 'min-w-4 h-4 px-1'
7802
+ }
7803
+ },
7804
+ /** Whether the floating badge is hidden via `display: none`. */ invisible: {
7805
+ true: {
7806
+ badge: 'hidden'
7807
+ }
7808
+ },
7809
+ /** Halo ring for visual separation from the anchor surface. */ withRing: {
7810
+ true: {
7811
+ // ring-neutral-50 auto-inverts via dashforgePreset CSS var
7812
+ // swap (no `dark:` needed for the neutral palette).
7813
+ badge: 'ring-2 ring-neutral-50'
7814
+ }
7815
+ },
7816
+ /** 7 intents — drives the badge background + text colour. */ color: {
7817
+ neutral: {
7818
+ // Both classes on the neutral palette — auto-invert via CSS
7819
+ // var swap. No `dark:` variants needed.
7820
+ badge: 'bg-neutral-700 text-neutral-50'
7821
+ },
7822
+ primary: {
7823
+ badge: 'bg-primary-500 text-white'
7824
+ },
7825
+ secondary: {
7826
+ badge: 'bg-secondary-500 text-white'
7827
+ },
7828
+ success: {
7829
+ badge: 'bg-success-500 text-white'
7830
+ },
7831
+ warning: {
7832
+ badge: 'bg-warning-500 text-white'
7833
+ },
7834
+ danger: {
7835
+ badge: 'bg-danger-500 text-white'
7836
+ },
7837
+ info: {
7838
+ badge: 'bg-info-500 text-white'
7839
+ }
7840
+ },
7841
+ placement: {
7842
+ 'top-right': {
7843
+ badge: 'top-0 right-0 -translate-y-1/2 translate-x-1/2'
7844
+ },
7845
+ 'top-left': {
7846
+ badge: 'top-0 left-0 -translate-y-1/2 -translate-x-1/2'
7847
+ },
7848
+ 'bottom-right': {
7849
+ badge: 'bottom-0 right-0 translate-y-1/2 translate-x-1/2'
7850
+ },
7851
+ 'bottom-left': {
7852
+ badge: 'bottom-0 left-0 translate-y-1/2 -translate-x-1/2'
7853
+ }
7854
+ },
7855
+ /**
7856
+ * `overlap='circular'` adds an inward inset so the badge sits
7857
+ * along the circular anchor edge instead of the bounding-box
7858
+ * corner. The 14% offset is empirically tuned to match the
7859
+ * standard avatar / round-button geometry (matches MUI's value).
7860
+ */ overlap: {
7861
+ rectangular: {},
7862
+ circular: {}
7863
+ }
7864
+ },
7865
+ compoundVariants: [
7866
+ // ─── overlap='circular' × placement — additional inset ─────────
7867
+ // The inward shift is in addition to the placement translate,
7868
+ // achieved via an extra `-{translate-corner}` adjustment.
7869
+ {
7870
+ overlap: 'circular',
7871
+ placement: 'top-right',
7872
+ class: {
7873
+ badge: 'top-[14%] right-[14%] translate-x-1/2 -translate-y-1/2'
7874
+ }
7875
+ },
7876
+ {
7877
+ overlap: 'circular',
7878
+ placement: 'top-left',
7879
+ class: {
7880
+ badge: 'top-[14%] left-[14%] -translate-x-1/2 -translate-y-1/2'
7881
+ }
7882
+ },
7883
+ {
7884
+ overlap: 'circular',
7885
+ placement: 'bottom-right',
7886
+ class: {
7887
+ badge: 'bottom-[14%] right-[14%] translate-x-1/2 translate-y-1/2'
7888
+ }
7889
+ },
7890
+ {
7891
+ overlap: 'circular',
7892
+ placement: 'bottom-left',
7893
+ class: {
7894
+ badge: 'bottom-[14%] left-[14%] -translate-x-1/2 translate-y-1/2'
7895
+ }
7896
+ }
7897
+ ],
7898
+ defaultVariants: {
7899
+ dot: false,
7900
+ invisible: false,
7901
+ withRing: true,
7902
+ color: 'danger',
7903
+ placement: 'top-right',
7904
+ overlap: 'rectangular'
7905
+ }
7906
+ });
7907
+
7908
+ /**
7909
+ * Resolve the displayed badge content based on `content`, `max`, and
7910
+ * `showZero` props.
7911
+ *
7912
+ * Returns `null` when nothing should render (omitted content + no dot,
7913
+ * or `content=0` without `showZero`). Otherwise returns the string
7914
+ * to display (formatted overflow for numbers above `max`).
7915
+ *
7916
+ * @internal
7917
+ */ function resolveDisplayContent(content, max, showZero, dot) {
7918
+ // Dot mode — content is irrelevant, badge always shows (unless
7919
+ // gated by visibleWhen / access / invisible higher up).
7920
+ if (dot) return '';
7921
+ // No content + no dot → nothing to render.
7922
+ if (content === undefined || content === null) return null;
7923
+ // Numeric content — apply max overflow + showZero gate.
7924
+ if (typeof content === 'number') {
7925
+ if (content === 0 && !showZero) return null;
7926
+ if (max >= 0 && content > max) return `${max}+`;
7927
+ return String(content);
7928
+ }
7929
+ // String content — render verbatim. Empty string → hide
7930
+ // (counts as "no content" — consumer should pass `dot` for plain dot).
7931
+ return content === '' ? null : content;
7932
+ }
7933
+ /**
7934
+ * `<Badge>` — anchored indicator overlaying an anchor element.
7935
+ *
7936
+ * Common patterns:
7937
+ * - Notification count on a bell IconButton
7938
+ * - Presence dot on an Avatar
7939
+ * - "NEW" label on a Card or feature tile
7940
+ * - Unsaved-changes indicator on a save button
7941
+ *
7942
+ * For inline standalone pills (status labels, tags, removable
7943
+ * chips), use `<Chip>` instead — Chip and Badge are complementary
7944
+ * primitives. Chip is interactive (`onClick`, `onDelete`,
7945
+ * `selected`); Badge is passive overlay.
7946
+ *
7947
+ * Bridge integration (Sprint 4.4):
7948
+ * - `access` — hide the badge for unauthorized subjects (anchor
7949
+ * stays visible; wrap anchor in a separate Box `access` to gate
7950
+ * it too)
7951
+ * - `visibleWhen` — engine-reactive predicate
7952
+ *
7953
+ * The wrapped anchor element ALWAYS renders; only the floating
7954
+ * badge dot/count is gated by `access`, `visibleWhen`, `invisible`,
7955
+ * and content-resolution logic.
7956
+ *
7957
+ * @example
7958
+ * ```tsx
7959
+ * // Notification count on bell
7960
+ * <Badge content={unreadCount}>
7961
+ * <IconButton aria-label="Notifications" variant="ghost">
7962
+ * <BellIcon />
7963
+ * </IconButton>
7964
+ * </Badge>
7965
+ *
7966
+ * // Presence dot on avatar
7967
+ * <Badge dot color="success" overlap="circular">
7968
+ * <Avatar name="Maya Rodriguez" />
7969
+ * </Badge>
7970
+ *
7971
+ * // Overflow
7972
+ * <Badge content={1284} max={999}>
7973
+ * <IconButton aria-label="Messages"><MailIcon /></IconButton>
7974
+ * </Badge>
7975
+ *
7976
+ * // RBAC-gated
7977
+ * <Badge
7978
+ * content={adminQueueCount}
7979
+ * access={{ resource: 'admin', action: 'read', onUnauthorized: 'hide' }}
7980
+ * >
7981
+ * <IconButton aria-label="Admin queue"><SettingsIcon /></IconButton>
7982
+ * </Badge>
7983
+ *
7984
+ * // Engine-reactive
7985
+ * <Badge
7986
+ * dot color="warning"
7987
+ * visibleWhen={(engine) => !!engine.getNode('unsavedChanges')?.value}
7988
+ * >
7989
+ * <IconButton aria-label="Save"><SaveIcon /></IconButton>
7990
+ * </Badge>
7991
+ * ```
7992
+ */ const Badge = /*#__PURE__*/ forwardRef(function Badge(props, ref) {
7993
+ var _slotProps_root, _slotProps_badge;
7994
+ const { children, content, dot = false, max = 99, showZero = false, invisible = false, color = 'danger', placement = 'top-right', overlap = 'rectangular', withRing = true, access, visibleWhen, sx, className, slotProps } = props;
7995
+ // Bridge — both hooks called unconditionally (rules-of-hooks).
7996
+ const bridge = useContext(DashFormContext);
7997
+ const isBridgeVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
7998
+ const accessState = useAccessState(access);
7999
+ // Resolve the actual content to render (string or null = hide).
8000
+ // Computed regardless of access/visibleWhen so we can keep the
8001
+ // hook order stable; the final "render or not" check folds them in.
8002
+ const displayContent = resolveDisplayContent(content, max, showZero, dot);
8003
+ // The wrapped anchor ALWAYS renders. Only the floating badge is
8004
+ // gated. This is the right contract — gating the anchor too would
8005
+ // belong to a Box `access` wrapper one level up.
8006
+ const shouldRenderBadge = isBridgeVisible && accessState.visible && displayContent !== null;
8007
+ const v = badgeVariants({
8008
+ dot,
8009
+ invisible,
8010
+ withRing,
8011
+ color,
8012
+ placement,
8013
+ overlap
8014
+ });
8015
+ return jsxs("span", {
8016
+ ref: ref,
8017
+ className: cn(v.root(), sx, className, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
8018
+ children: [
8019
+ children,
8020
+ shouldRenderBadge && jsx("span", {
8021
+ className: cn(v.badge(), slotProps == null ? void 0 : (_slotProps_badge = slotProps.badge) == null ? void 0 : _slotProps_badge.className),
8022
+ "aria-hidden": "true",
8023
+ children: displayContent
8024
+ })
8025
+ ]
8026
+ });
8027
+ });
8028
+ Badge.displayName = 'Badge';
8029
+
8030
+ /**
8031
+ * Tailwind-variants recipe for `<Avatar>`.
8032
+ *
8033
+ * Axes:
8034
+ * - `size` — 5 steps (xs/sm/md/lg/xl), maps to w/h spacing tokens
8035
+ * + matching text-size for the initials inside.
8036
+ * - `radius` — 7 steps (none/sm/md/lg/xl/2xl/full), same scale as
8037
+ * `<Box rounded>`. `shape` prop in the component maps
8038
+ * semantically: circle → full, rounded → lg, square → none.
8039
+ * - `color` — 7 intents. Drives the FALLBACK bg+text only (image
8040
+ * surface is independent of color).
8041
+ *
8042
+ * The `color` × `tone` combo is resolved at render time in
8043
+ * `Avatar.tsx` via compound lookup — see the SOFT_TINT and TONE
8044
+ * tables there. We do NOT expand the matrix exhaustively in this TV
8045
+ * recipe (it would be 7 colors × 10 tones × 5 sizes = 350 rules; the
8046
+ * runtime resolver keeps the recipe slim).
8047
+ */ const avatarVariants = tv({
8048
+ slots: {
8049
+ /** Outer wrapper — sets dimensions + radius + ring + alignment. */ root: [
8050
+ 'relative inline-flex items-center justify-center shrink-0',
8051
+ 'overflow-hidden align-middle',
8052
+ 'select-none',
8053
+ // The font weight + leading are baked into root so initials slot
8054
+ // inherits them without an extra class layer.
8055
+ 'font-medium leading-none'
8056
+ ],
8057
+ /** `<img>` slot — fills the root, object-cover for crop-to-square. */ img: 'block w-full h-full object-cover',
8058
+ /** Initials text slot. */ initials: 'uppercase tracking-tight',
8059
+ /** Generic user icon SVG fallback. */ icon: 'w-1/2 h-1/2 opacity-80'
8060
+ },
8061
+ variants: {
8062
+ size: {
8063
+ xs: {
8064
+ root: 'w-5 h-5 text-[10px]',
8065
+ initials: ''
8066
+ },
8067
+ sm: {
8068
+ root: 'w-7 h-7 text-xs',
8069
+ initials: ''
8070
+ },
8071
+ md: {
8072
+ root: 'w-9 h-9 text-sm',
8073
+ initials: ''
8074
+ },
8075
+ lg: {
8076
+ root: 'w-12 h-12 text-base',
8077
+ initials: ''
8078
+ },
8079
+ xl: {
8080
+ root: 'w-16 h-16 text-xl',
8081
+ initials: ''
8082
+ }
8083
+ },
8084
+ radius: {
8085
+ none: {
8086
+ root: 'rounded-none'
8087
+ },
8088
+ sm: {
8089
+ root: 'rounded-sm'
8090
+ },
8091
+ md: {
8092
+ root: 'rounded-md'
8093
+ },
8094
+ lg: {
8095
+ root: 'rounded-lg'
8096
+ },
8097
+ xl: {
8098
+ root: 'rounded-xl'
8099
+ },
8100
+ '2xl': {
8101
+ root: 'rounded-2xl'
8102
+ },
8103
+ full: {
8104
+ root: 'rounded-full'
8105
+ }
8106
+ }
8107
+ },
8108
+ defaultVariants: {
8109
+ size: 'md',
8110
+ radius: 'full'
8111
+ }
8112
+ });
8113
+ /**
8114
+ * Default "soft" fallback treatment per intent — what you get when
8115
+ * `tone` is omitted. Light tinted bg + dark text for legibility.
8116
+ * Neutral row uses no `dark:` because the dashforgePreset() CSS-var
8117
+ * swap auto-inverts neutral; color rows keep `dark:` for the
8118
+ * intentional palette shift (palette colors don't auto-invert).
8119
+ */ const AVATAR_SOFT_FALLBACK = {
8120
+ neutral: 'bg-neutral-100 text-neutral-900',
8121
+ primary: 'bg-primary-100 text-primary-900 dark:bg-primary-950 dark:text-primary-100',
8122
+ secondary: 'bg-secondary-100 text-secondary-900 dark:bg-secondary-950 dark:text-secondary-100',
8123
+ success: 'bg-success-100 text-success-900 dark:bg-success-950 dark:text-success-100',
8124
+ warning: 'bg-warning-100 text-warning-900 dark:bg-warning-950 dark:text-warning-100',
8125
+ danger: 'bg-danger-100 text-danger-900 dark:bg-danger-950 dark:text-danger-100',
8126
+ info: 'bg-info-100 text-info-900 dark:bg-info-950 dark:text-info-100'
8127
+ };
8128
+ /**
8129
+ * Specific-tone resolver — when the consumer sets `tone={500}` (or
8130
+ * any other value), this lookup builds the matching bg + text class.
8131
+ *
8132
+ * Text color logic:
8133
+ * - tones 50-400 → dark text (uses the 900 step)
8134
+ * - tones 500-900 → light text (uses the 50 step)
8135
+ *
8136
+ * Tailwind JIT requires literal class strings, so we enumerate them
8137
+ * explicitly here. The bundle cost is ~70 lines, acceptable for the
8138
+ * type-safety win of letting consumers pick any palette shade.
8139
+ */ const AVATAR_TONE_FALLBACK = {
8140
+ neutral: {
8141
+ 50: 'bg-neutral-50 text-neutral-900',
8142
+ 100: 'bg-neutral-100 text-neutral-900',
8143
+ 200: 'bg-neutral-200 text-neutral-900',
8144
+ 300: 'bg-neutral-300 text-neutral-900',
8145
+ 400: 'bg-neutral-400 text-neutral-900',
8146
+ 500: 'bg-neutral-500 text-neutral-50',
8147
+ 600: 'bg-neutral-600 text-neutral-50',
8148
+ 700: 'bg-neutral-700 text-neutral-50',
8149
+ 800: 'bg-neutral-800 text-neutral-50',
8150
+ 900: 'bg-neutral-900 text-neutral-50'
8151
+ },
8152
+ primary: {
8153
+ 50: 'bg-primary-50 text-primary-900',
8154
+ 100: 'bg-primary-100 text-primary-900',
8155
+ 200: 'bg-primary-200 text-primary-900',
8156
+ 300: 'bg-primary-300 text-primary-900',
8157
+ 400: 'bg-primary-400 text-primary-900',
8158
+ 500: 'bg-primary-500 text-primary-50',
8159
+ 600: 'bg-primary-600 text-primary-50',
8160
+ 700: 'bg-primary-700 text-primary-50',
8161
+ 800: 'bg-primary-800 text-primary-50',
8162
+ 900: 'bg-primary-900 text-primary-50'
8163
+ },
8164
+ secondary: {
8165
+ 50: 'bg-secondary-50 text-secondary-900',
8166
+ 100: 'bg-secondary-100 text-secondary-900',
8167
+ 200: 'bg-secondary-200 text-secondary-900',
8168
+ 300: 'bg-secondary-300 text-secondary-900',
8169
+ 400: 'bg-secondary-400 text-secondary-900',
8170
+ 500: 'bg-secondary-500 text-secondary-50',
8171
+ 600: 'bg-secondary-600 text-secondary-50',
8172
+ 700: 'bg-secondary-700 text-secondary-50',
8173
+ 800: 'bg-secondary-800 text-secondary-50',
8174
+ 900: 'bg-secondary-900 text-secondary-50'
8175
+ },
8176
+ success: {
8177
+ 50: 'bg-success-50 text-success-900',
8178
+ 100: 'bg-success-100 text-success-900',
8179
+ 200: 'bg-success-200 text-success-900',
8180
+ 300: 'bg-success-300 text-success-900',
8181
+ 400: 'bg-success-400 text-success-900',
8182
+ 500: 'bg-success-500 text-success-50',
8183
+ 600: 'bg-success-600 text-success-50',
8184
+ 700: 'bg-success-700 text-success-50',
8185
+ 800: 'bg-success-800 text-success-50',
8186
+ 900: 'bg-success-900 text-success-50'
8187
+ },
8188
+ warning: {
8189
+ 50: 'bg-warning-50 text-warning-900',
8190
+ 100: 'bg-warning-100 text-warning-900',
8191
+ 200: 'bg-warning-200 text-warning-900',
8192
+ 300: 'bg-warning-300 text-warning-900',
8193
+ 400: 'bg-warning-400 text-warning-900',
8194
+ 500: 'bg-warning-500 text-warning-50',
8195
+ 600: 'bg-warning-600 text-warning-50',
8196
+ 700: 'bg-warning-700 text-warning-50',
8197
+ 800: 'bg-warning-800 text-warning-50',
8198
+ 900: 'bg-warning-900 text-warning-50'
8199
+ },
8200
+ danger: {
8201
+ 50: 'bg-danger-50 text-danger-900',
8202
+ 100: 'bg-danger-100 text-danger-900',
8203
+ 200: 'bg-danger-200 text-danger-900',
8204
+ 300: 'bg-danger-300 text-danger-900',
8205
+ 400: 'bg-danger-400 text-danger-900',
8206
+ 500: 'bg-danger-500 text-danger-50',
8207
+ 600: 'bg-danger-600 text-danger-50',
8208
+ 700: 'bg-danger-700 text-danger-50',
8209
+ 800: 'bg-danger-800 text-danger-50',
8210
+ 900: 'bg-danger-900 text-danger-50'
8211
+ },
8212
+ info: {
8213
+ 50: 'bg-info-50 text-info-900',
8214
+ 100: 'bg-info-100 text-info-900',
8215
+ 200: 'bg-info-200 text-info-900',
8216
+ 300: 'bg-info-300 text-info-900',
8217
+ 400: 'bg-info-400 text-info-900',
8218
+ 500: 'bg-info-500 text-info-50',
8219
+ 600: 'bg-info-600 text-info-50',
8220
+ 700: 'bg-info-700 text-info-50',
8221
+ 800: 'bg-info-800 text-info-50',
8222
+ 900: 'bg-info-900 text-info-50'
8223
+ }
8224
+ };
8225
+
8226
+ /**
8227
+ * Generic user icon — inline SVG fallback when no `src` and no
8228
+ * `name` are provided. Stroke uses `currentColor` so it inherits the
8229
+ * resolved fallback text color. Same convention as Calendar +
8230
+ * Autocomplete + Alert icons (no icon-library dep).
8231
+ *
8232
+ * @internal
8233
+ */ function DefaultUserIcon() {
8234
+ return jsxs("svg", {
8235
+ viewBox: "0 0 24 24",
8236
+ fill: "none",
8237
+ stroke: "currentColor",
8238
+ strokeWidth: 1.75,
8239
+ strokeLinecap: "round",
8240
+ strokeLinejoin: "round",
8241
+ "aria-hidden": "true",
8242
+ children: [
8243
+ jsx("circle", {
8244
+ cx: "12",
8245
+ cy: "8",
8246
+ r: "3.75"
8247
+ }),
8248
+ jsx("path", {
8249
+ d: "M4.5 20a7.5 7.5 0 0 1 15 0"
8250
+ })
8251
+ ]
8252
+ });
8253
+ }
8254
+ /**
8255
+ * Map the semantic `shape` shortcut to a Box-style `radius` token.
8256
+ * Used only when `radius` is NOT explicitly passed (explicit wins).
8257
+ *
8258
+ * @internal
8259
+ */ function shapeToRadius(shape) {
8260
+ switch(shape){
8261
+ case 'circle':
8262
+ return 'full';
8263
+ case 'rounded':
8264
+ return 'lg';
8265
+ case 'square':
8266
+ return 'none';
8267
+ }
8268
+ }
8269
+ /**
8270
+ * Extract initials from a name string. Strategy:
8271
+ * - Split by whitespace, filter empty.
8272
+ * - Take the first letter of the first 2 tokens, uppercased.
8273
+ * - "Maya Rodriguez" → "MR"
8274
+ * - "Cher" → "C"
8275
+ * - " multiple spaces " → "MS" (if 2+ words after trim)
8276
+ * - "" → "" (empty string — caller falls back to icon)
8277
+ *
8278
+ * @internal
8279
+ */ function initialsFromName(name) {
8280
+ var _ref;
8281
+ var _tokens_;
8282
+ if (!name) return '';
8283
+ const tokens = name.trim().split(/\s+/).filter(Boolean);
8284
+ if (tokens.length === 0) return '';
8285
+ return (tokens[0][0] + ((_ref = (_tokens_ = tokens[1]) == null ? void 0 : _tokens_[0]) != null ? _ref : '')).toUpperCase();
8286
+ }
8287
+ /**
8288
+ * `<Avatar>` — image / initials / icon visual identity.
8289
+ *
8290
+ * Fallback chain (top to bottom — first match wins):
8291
+ * 1. `src` set + image loads OK → render `<img>`
8292
+ * 2. `src` set + image fails → render initials from `name`
8293
+ * 3. `src` absent + `name` set → render initials from `name`
8294
+ * 4. `fallbackIcon` set → render that ReactNode
8295
+ * 5. Nothing → render generic user SVG
8296
+ *
8297
+ * Color resolution (only for fallback content, image surface is
8298
+ * independent):
8299
+ * - `color="primary"` only → soft default (bg-100, text-900)
8300
+ * - `color="primary" tone={500}` → bg-primary-500 + text-primary-50
8301
+ * - Outside the palette → use `sx`
8302
+ *
8303
+ * No `access` / `visibleWhen` — Avatar is display-only by category.
8304
+ * For visibility gating, wrap in `<Box>` (which has both since
8305
+ * Sprint 4.4).
8306
+ */ const Avatar = /*#__PURE__*/ forwardRef(function Avatar(props, ref) {
8307
+ var _ref, _ref1;
8308
+ var _AVATAR_TONE_FALLBACK_color;
8309
+ const { src, alt, imgProps, name, shape = 'circle', radius, size = 'md', color = 'neutral', tone, fallbackIcon, sx, className } = props;
8310
+ // Track image-load failures so we can fall through to initials/icon
8311
+ // dynamically. `imgLoaded` distinguishes between "still loading"
8312
+ // (don't show fallback yet — would flash) and "loaded successfully"
8313
+ // (don't paint a fallback color underneath, which would peek through
8314
+ // transparent PNGs).
8315
+ const [imgFailed, setImgFailed] = useState(false);
8316
+ // Resolve final radius: explicit `radius` prop wins over `shape`.
8317
+ const finalRadius = radius != null ? radius : shapeToRadius(shape);
8318
+ // Compute base classes from the TV recipe.
8319
+ const v = avatarVariants({
8320
+ size,
8321
+ radius: finalRadius
8322
+ });
8323
+ // Fallback color resolution. `tone` (when set) drives the specific
8324
+ // shade; otherwise the soft-default lookup is used.
8325
+ const fallbackTone = tone !== undefined ? (_ref = (_AVATAR_TONE_FALLBACK_color = AVATAR_TONE_FALLBACK[color]) == null ? void 0 : _AVATAR_TONE_FALLBACK_color[tone]) != null ? _ref : AVATAR_SOFT_FALLBACK[color] : AVATAR_SOFT_FALLBACK[color];
8326
+ // Image is "successful" when src is set AND the load hasn't failed.
8327
+ const showImage = !!src && !imgFailed;
8328
+ const initials = initialsFromName(name);
8329
+ // The root carries the fallback color ONLY when we're showing the
8330
+ // fallback content. Painting it underneath a successful image is
8331
+ // wrong if the image has transparency.
8332
+ const fallbackBgClasses = !showImage ? fallbackTone : '';
8333
+ const rootClasses = cn(v.root(), fallbackBgClasses, sx, className);
8334
+ // Pick which fallback to render — priority: initials > custom icon > generic SVG.
8335
+ const fallbackContent = initials ? jsx("span", {
8336
+ className: v.initials(),
8337
+ children: initials
8338
+ }) : fallbackIcon ? jsx("span", {
8339
+ className: "inline-flex items-center justify-center w-full h-full",
8340
+ children: fallbackIcon
8341
+ }) : jsx("span", {
8342
+ className: v.icon() + ' inline-flex items-center justify-center',
8343
+ children: jsx(DefaultUserIcon, {})
8344
+ });
8345
+ return jsx("span", {
8346
+ ref: ref,
8347
+ className: rootClasses,
8348
+ role: "img",
8349
+ "aria-label": (_ref1 = alt != null ? alt : name) != null ? _ref1 : undefined,
8350
+ children: showImage ? // Inner img is DECORATIVE (alt="") — the parent span carries
8351
+ // the accessible name via role="img" + aria-label. This avoids
8352
+ // a duplicate role="img" announcement and matches MUI's a11y
8353
+ // pattern.
8354
+ jsx("img", _extends({
8355
+ src: src,
8356
+ alt: "",
8357
+ onError: ()=>setImgFailed(true)
8358
+ }, imgProps, {
8359
+ className: cn(v.img(), imgProps == null ? void 0 : imgProps.className)
8360
+ })) : fallbackContent
8361
+ });
8362
+ });
8363
+ Avatar.displayName = 'Avatar';
8364
+ // ─── AvatarGroup ─────────────────────────────────────────────────
8365
+ /**
8366
+ * Horizontal spacing tokens for the group overlap. Negative margin
8367
+ * pulls each child onto the previous, simulating the "stack of
8368
+ * avatars" pattern. Tokens map to spacing scale.
8369
+ *
8370
+ * @internal
8371
+ */ const GROUP_SPACING = {
8372
+ xs: '-space-x-1',
8373
+ sm: '-space-x-2',
8374
+ md: '-space-x-3'
8375
+ };
8376
+ /**
8377
+ * `<AvatarGroup>` — thin horizontal wrapper for overlapping avatars.
8378
+ *
8379
+ * Slices children to `max - 1` visible items and renders a trailing
8380
+ * "+N" overflow indicator with the remaining count (when more than
8381
+ * `max` children are passed).
8382
+ *
8383
+ * Each child Avatar inherits the group's `size` unless it explicitly
8384
+ * overrides. When `withRing` is `true` (default), each visible
8385
+ * Avatar gets a `ring-2 ring-white` halo so overlapping borders
8386
+ * stay visually separated.
8387
+ *
8388
+ * @example
8389
+ * ```tsx
8390
+ * <AvatarGroup max={3}>
8391
+ * <Avatar name="Maya Rodriguez" color="primary" />
8392
+ * <Avatar name="John Doe" color="success" />
8393
+ * <Avatar name="Sara Chen" color="warning" />
8394
+ * <Avatar name="Lila Park" color="info" />
8395
+ * <Avatar name="Eve Kim" color="danger" />
8396
+ * </AvatarGroup>
8397
+ * // Renders: MR, JD, SC + a "+2" overflow chip
8398
+ * ```
8399
+ */ const AvatarGroup = /*#__PURE__*/ forwardRef(function AvatarGroup(props, ref) {
8400
+ const { children, max = 4, size = 'md', spacing = 'sm', withRing = true, className, sx } = props;
8401
+ const allChildren = Children.toArray(children).filter(isValidElement);
8402
+ const total = allChildren.length;
8403
+ const visibleCount = Math.min(total, max);
8404
+ const overflow = Math.max(0, total - visibleCount);
8405
+ // Decorate each visible child with size (if not overridden by the
8406
+ // child) and optional ring halo. cloneElement is the standard
8407
+ // React pattern for prop-injection across a children array.
8408
+ const visible = allChildren.slice(0, visibleCount).map((child, idx)=>{
8409
+ var _childProps_size;
8410
+ const childProps = child.props;
8411
+ const inheritedSize = (_childProps_size = childProps.size) != null ? _childProps_size : size;
8412
+ const ringClass = withRing ? 'ring-2 ring-neutral-50' : '';
8413
+ const mergedSx = cn(ringClass, childProps.sx, childProps.className);
8414
+ return /*#__PURE__*/ cloneElement(child, {
8415
+ key: idx,
8416
+ size: inheritedSize,
8417
+ sx: mergedSx
8418
+ });
8419
+ });
8420
+ return jsxs("div", {
8421
+ ref: ref,
8422
+ className: cn('inline-flex items-center', GROUP_SPACING[spacing], sx, className),
8423
+ children: [
8424
+ visible,
8425
+ overflow > 0 && jsx(Avatar, {
8426
+ // The overflow indicator IS an avatar (same dimensions /
8427
+ // radius), but with a +N text fallback rather than initials.
8428
+ // We pass an empty `name` and a custom `fallbackIcon` that
8429
+ // renders the count.
8430
+ size: size,
8431
+ color: "neutral",
8432
+ sx: cn(withRing && 'ring-2 ring-neutral-50'),
8433
+ fallbackIcon: jsxs("span", {
8434
+ className: "text-xs font-medium",
8435
+ children: [
8436
+ "+",
8437
+ overflow
8438
+ ]
8439
+ })
8440
+ })
8441
+ ]
8442
+ });
8443
+ });
8444
+ AvatarGroup.displayName = 'AvatarGroup';
8445
+
8446
+ /**
8447
+ * `boxVariants` — the surface primitive for @dashforge/tw.
8448
+ *
8449
+ * Architectural choice (planned with the user — F9 deep dive):
8450
+ *
8451
+ * Box replaces FOUR overlapping concepts from MUI in one component:
8452
+ * • Box (typed div)
8453
+ * • Paper (surface with elevation)
8454
+ * • Card (Paper specialisation)
8455
+ * • Surface (Joy UI's outlined / soft / solid / plain variants)
8456
+ *
8457
+ * The reason for the consolidation: in MUI you have to compose two or
8458
+ * three of these to express even basic intent ("an outlined card with
8459
+ * warning tone"). Here, one `<Box variant="outlined" color="warning">`
8460
+ * says exactly that.
8461
+ *
8462
+ * Variant taxonomy (5 axes, intentionally non-overlapping with Stack/Grid):
8463
+ *
8464
+ * • plain — bare div + padding + radius. The escape hatch.
8465
+ * • outlined — 1px border + subtle bg tint. The "card lite".
8466
+ * • elevated — bg surface + shadow scale (0-5). The "floating panel".
8467
+ * • soft — semi-transparent intent bg + intent text. The "callout".
8468
+ * • solid — solid intent bg + contrasting text. The "CTA banner".
8469
+ *
8470
+ * `color` applies to outlined/soft/solid (each gets the 7 intent
8471
+ * variants). `elevated` is color-agnostic (always neutral surface +
8472
+ * shadow scale). `plain` is everything-agnostic.
8473
+ *
8474
+ * Spacing axes (p/px/py/m/mx/my): mapped explicitly to the 11 token
8475
+ * steps from @dashforge/tw-tokens (0, 0.5, 1, 2, 3, 4, 6, 8, 12, 16, 24).
8476
+ * Tailwind JIT requires literal class strings — building them dynamically
8477
+ * with template literals would purge them. The verbosity below is the
8478
+ * cost of keeping the bundle CSS-pure and rebuild-free.
8479
+ *
8480
+ * What this does NOT do (deliberate, see component docs):
8481
+ * • No display / flex / grid props → use Stack or Grid
8482
+ * • No position / overflow / z-index → use `sx`
8483
+ * • No animation / transition → use `sx`
8484
+ *
8485
+ * The "Box is not flex" rule is the spine of the layout layer. Without
8486
+ * it, every `<div>` in an app gravitates back to Box and the surface
8487
+ * vs layout distinction collapses — exactly the failure mode this
8488
+ * primitive exists to prevent.
8489
+ */ const boxVariants = tv({
8490
+ base: 'block',
8491
+ variants: {
8492
+ /*
8493
+ * Surface variant. Compound with `color` for outlined / soft / solid;
6952
8494
  * standalone for plain / elevated.
6953
8495
  */ variant: {
6954
8496
  plain: '',
@@ -6961,337 +8503,1337 @@ Typography.displayName = 'Typography';
6961
8503
  // in dark mode — overpowering elevation).
6962
8504
  elevated: 'bg-white dark:bg-neutral-100',
6963
8505
  soft: '',
6964
- solid: ''
8506
+ solid: ''
8507
+ },
8508
+ /*
8509
+ * Intent color. Only meaningful when variant is outlined / soft /
8510
+ * solid (resolved via compoundVariants below). For plain / elevated
8511
+ * this axis is ignored at the visual level — but kept in the type
8512
+ * so the prop is always available without conditional typing.
8513
+ */ color: {
8514
+ primary: '',
8515
+ secondary: '',
8516
+ success: '',
8517
+ warning: '',
8518
+ danger: '',
8519
+ info: '',
8520
+ neutral: ''
8521
+ },
8522
+ /*
8523
+ * Shadow scale — relevant for `variant='elevated'`. We keep elevation
8524
+ * as a separate axis (not folded into `variant`) so consumers can
8525
+ * dial it up/down without changing the variant. Default `0` = no
8526
+ * shadow (consistent with MUI's elevation=0).
8527
+ */ elevation: {
8528
+ 0: 'shadow-none',
8529
+ 1: 'shadow-sm',
8530
+ 2: 'shadow',
8531
+ 3: 'shadow-md',
8532
+ 4: 'shadow-lg',
8533
+ 5: 'shadow-xl'
8534
+ },
8535
+ rounded: {
8536
+ none: 'rounded-none',
8537
+ sm: 'rounded-sm',
8538
+ md: 'rounded-md',
8539
+ lg: 'rounded-lg',
8540
+ xl: 'rounded-xl',
8541
+ '2xl': 'rounded-2xl',
8542
+ full: 'rounded-full'
8543
+ },
8544
+ /*
8545
+ * Spacing — six axes (p/px/py/m/mx/my), 11 token steps each.
8546
+ * Literals enumerated explicitly so Tailwind's JIT scanner finds
8547
+ * every class. Token steps mirror @dashforge/tw-tokens spacing scale.
8548
+ */ p: {
8549
+ 0: 'p-0',
8550
+ '0.5': 'p-0.5',
8551
+ 1: 'p-1',
8552
+ 2: 'p-2',
8553
+ 3: 'p-3',
8554
+ 4: 'p-4',
8555
+ 6: 'p-6',
8556
+ 8: 'p-8',
8557
+ 12: 'p-12',
8558
+ 16: 'p-16',
8559
+ 24: 'p-24'
8560
+ },
8561
+ px: {
8562
+ 0: 'px-0',
8563
+ '0.5': 'px-0.5',
8564
+ 1: 'px-1',
8565
+ 2: 'px-2',
8566
+ 3: 'px-3',
8567
+ 4: 'px-4',
8568
+ 6: 'px-6',
8569
+ 8: 'px-8',
8570
+ 12: 'px-12',
8571
+ 16: 'px-16',
8572
+ 24: 'px-24'
8573
+ },
8574
+ py: {
8575
+ 0: 'py-0',
8576
+ '0.5': 'py-0.5',
8577
+ 1: 'py-1',
8578
+ 2: 'py-2',
8579
+ 3: 'py-3',
8580
+ 4: 'py-4',
8581
+ 6: 'py-6',
8582
+ 8: 'py-8',
8583
+ 12: 'py-12',
8584
+ 16: 'py-16',
8585
+ 24: 'py-24'
8586
+ },
8587
+ m: {
8588
+ 0: 'm-0',
8589
+ '0.5': 'm-0.5',
8590
+ 1: 'm-1',
8591
+ 2: 'm-2',
8592
+ 3: 'm-3',
8593
+ 4: 'm-4',
8594
+ 6: 'm-6',
8595
+ 8: 'm-8',
8596
+ 12: 'm-12',
8597
+ 16: 'm-16',
8598
+ 24: 'm-24'
8599
+ },
8600
+ mx: {
8601
+ 0: 'mx-0',
8602
+ '0.5': 'mx-0.5',
8603
+ 1: 'mx-1',
8604
+ 2: 'mx-2',
8605
+ 3: 'mx-3',
8606
+ 4: 'mx-4',
8607
+ 6: 'mx-6',
8608
+ 8: 'mx-8',
8609
+ 12: 'mx-12',
8610
+ 16: 'mx-16',
8611
+ 24: 'mx-24'
8612
+ },
8613
+ my: {
8614
+ 0: 'my-0',
8615
+ '0.5': 'my-0.5',
8616
+ 1: 'my-1',
8617
+ 2: 'my-2',
8618
+ 3: 'my-3',
8619
+ 4: 'my-4',
8620
+ 6: 'my-6',
8621
+ 8: 'my-8',
8622
+ 12: 'my-12',
8623
+ 16: 'my-16',
8624
+ 24: 'my-24'
8625
+ },
8626
+ fullWidth: {
8627
+ true: 'w-full'
8628
+ },
8629
+ fullHeight: {
8630
+ true: 'h-full'
8631
+ }
8632
+ },
8633
+ /*
8634
+ * Compound variants — where surface × color get their actual visual.
8635
+ * Twenty-one entries: 7 (outlined) + 7 (soft) + 7 (solid).
8636
+ * `plain` and `elevated` don't appear here (no color contribution).
8637
+ *
8638
+ * Dark-mode pair is baked in: light-mode picks the 50-300 steps,
8639
+ * dark-mode picks the 800-950 steps — both reactive to setMode()
8640
+ * via the @dashforge/tw-theme CSS variables.
8641
+ */ compoundVariants: [
8642
+ // ─── outlined × color ─────────────────────────────────────────────
8643
+ {
8644
+ variant: 'outlined',
8645
+ color: 'primary',
8646
+ class: 'border-primary-300 bg-primary-50/40 dark:border-primary-800 dark:bg-primary-950/30'
8647
+ },
8648
+ {
8649
+ variant: 'outlined',
8650
+ color: 'secondary',
8651
+ class: 'border-secondary-300 bg-secondary-50/40 dark:border-secondary-800 dark:bg-secondary-950/30'
8652
+ },
8653
+ {
8654
+ variant: 'outlined',
8655
+ color: 'success',
8656
+ class: 'border-success-300 bg-success-50/40 dark:border-success-800 dark:bg-success-950/30'
8657
+ },
8658
+ {
8659
+ variant: 'outlined',
8660
+ color: 'warning',
8661
+ class: 'border-warning-300 bg-warning-50/40 dark:border-warning-800 dark:bg-warning-950/30'
8662
+ },
8663
+ {
8664
+ variant: 'outlined',
8665
+ color: 'danger',
8666
+ class: 'border-danger-300 bg-danger-50/40 dark:border-danger-800 dark:bg-danger-950/30'
8667
+ },
8668
+ {
8669
+ variant: 'outlined',
8670
+ color: 'info',
8671
+ class: 'border-info-300 bg-info-50/40 dark:border-info-800 dark:bg-info-950/30'
8672
+ },
8673
+ {
8674
+ variant: 'outlined',
8675
+ color: 'neutral',
8676
+ // border-neutral-200 auto-inverts via CSS var swap (no `dark:`
8677
+ // needed). bg-white is hardcoded — `dark:bg-neutral-100` keeps
8678
+ // elevation tier above page surface in dark mode (NOT
8679
+ // `dark:bg-neutral-900` which renders near-white).
8680
+ class: 'border-neutral-200 bg-white dark:bg-neutral-100'
8681
+ },
8682
+ // ─── soft × color ─────────────────────────────────────────────────
8683
+ {
8684
+ variant: 'soft',
8685
+ color: 'primary',
8686
+ class: 'bg-primary-100 text-primary-900 dark:bg-primary-950/50 dark:text-primary-100'
8687
+ },
8688
+ {
8689
+ variant: 'soft',
8690
+ color: 'secondary',
8691
+ class: 'bg-secondary-100 text-secondary-900 dark:bg-secondary-950/50 dark:text-secondary-100'
8692
+ },
8693
+ {
8694
+ variant: 'soft',
8695
+ color: 'success',
8696
+ class: 'bg-success-100 text-success-900 dark:bg-success-950/50 dark:text-success-100'
8697
+ },
8698
+ {
8699
+ variant: 'soft',
8700
+ color: 'warning',
8701
+ class: 'bg-warning-100 text-warning-900 dark:bg-warning-950/50 dark:text-warning-100'
8702
+ },
8703
+ {
8704
+ variant: 'soft',
8705
+ color: 'danger',
8706
+ class: 'bg-danger-100 text-danger-900 dark:bg-danger-950/50 dark:text-danger-100'
8707
+ },
8708
+ {
8709
+ variant: 'soft',
8710
+ color: 'info',
8711
+ class: 'bg-info-100 text-info-900 dark:bg-info-950/50 dark:text-info-100'
8712
+ },
8713
+ {
8714
+ variant: 'soft',
8715
+ color: 'neutral',
8716
+ // Both neutral classes auto-invert via the dashforgePreset() CSS
8717
+ // var swap — no `dark:` variants needed.
8718
+ class: 'bg-neutral-100 text-neutral-900'
8719
+ },
8720
+ // ─── solid × color ────────────────────────────────────────────────
8721
+ {
8722
+ variant: 'solid',
8723
+ color: 'primary',
8724
+ class: 'bg-primary-600 text-white dark:bg-primary-500'
8725
+ },
8726
+ {
8727
+ variant: 'solid',
8728
+ color: 'secondary',
8729
+ class: 'bg-secondary-600 text-white dark:bg-secondary-500'
8730
+ },
8731
+ {
8732
+ variant: 'solid',
8733
+ color: 'success',
8734
+ class: 'bg-success-600 text-white dark:bg-success-500'
8735
+ },
8736
+ {
8737
+ variant: 'solid',
8738
+ color: 'warning',
8739
+ class: 'bg-warning-500 text-white dark:bg-warning-600'
8740
+ },
8741
+ {
8742
+ variant: 'solid',
8743
+ color: 'danger',
8744
+ class: 'bg-danger-600 text-white dark:bg-danger-500'
8745
+ },
8746
+ {
8747
+ variant: 'solid',
8748
+ color: 'info',
8749
+ class: 'bg-info-600 text-white dark:bg-info-500'
8750
+ },
8751
+ {
8752
+ variant: 'solid',
8753
+ color: 'neutral',
8754
+ // Solid neutral is the high-emphasis "always-foreground accent"
8755
+ // variant. Both classes auto-invert: dark surface + light text
8756
+ // in light mode (`#171717` / `#fafafa`) flips to light surface +
8757
+ // dark text in dark mode (`#f5f5f5` / `#0a0a0a`). The
8758
+ // accent inverts WITH the page surface — consistent with the
8759
+ // identity rule. Previously used `text-white` (no auto-invert)
8760
+ // which forced double-inversion to keep readable.
8761
+ class: 'bg-neutral-900 text-neutral-50'
8762
+ }
8763
+ ],
8764
+ defaultVariants: {
8765
+ variant: 'plain',
8766
+ color: 'neutral',
8767
+ elevation: 0,
8768
+ rounded: 'none'
8769
+ }
8770
+ });
8771
+
8772
+ /**
8773
+ * `<Box>` — the surface primitive of @dashforge/tw.
8774
+ *
8775
+ * What it IS:
8776
+ * A polymorphic container with typed surface variants (plain /
8777
+ * outlined / elevated / soft / solid) × 7 intent colours, plus
8778
+ * spacing / sizing / rounded / elevation as enumerated token-scale
8779
+ * props. Replaces MUI's Box + Paper + Card + Surface in one component.
8780
+ *
8781
+ * What it is NOT (deliberate, enforced by the prop set):
8782
+ * • Not a flex container → use <Stack>
8783
+ * • Not a grid container → use <Grid>
8784
+ * • Not a paragraph → use <Typography>
8785
+ * • Not a button → use <Button>
8786
+ *
8787
+ * The "Box is not flex" rule is the spine of the layout layer. Without
8788
+ * it, every `<div>` in an app gravitates back to Box and the surface
8789
+ * vs layout distinction collapses. Box's job is the SURFACE around
8790
+ * content; Stack/Grid's job is the ARRANGEMENT of content. Two
8791
+ * primitives, two responsibilities.
8792
+ *
8793
+ * When BOTH `as` and `asChild` are passed, `asChild` wins. Same rule
8794
+ * as Typography — documented to avoid the dimension-bloat of a
8795
+ * discriminated union over an 11-axis props type.
8796
+ *
8797
+ * Layering:
8798
+ * • Sits BENEATH every visual chrome — every card, every panel,
8799
+ * every section background.
8800
+ * • Composes ON TOP of @dashforge/tw-tokens (color + spacing + radius
8801
+ * + shadow scales) via the dashforgePreset CSS variables.
8802
+ * • Pairs with Typography for text content, Stack/Grid for layout
8803
+ * children inside it.
8804
+ */ const Box = /*#__PURE__*/ forwardRef(function Box(props, ref) {
8805
+ const { variant, color, elevation, rounded, p, px, py, m, mx, my, fullWidth, fullHeight, as, asChild = false, sx, visibleWhen, access, children } = props, rest = _object_without_properties_loose(props, [
8806
+ "variant",
8807
+ "color",
8808
+ "elevation",
8809
+ "rounded",
8810
+ "p",
8811
+ "px",
8812
+ "py",
8813
+ "m",
8814
+ "mx",
8815
+ "my",
8816
+ "fullWidth",
8817
+ "fullHeight",
8818
+ "as",
8819
+ "asChild",
8820
+ "sx",
8821
+ "visibleWhen",
8822
+ "access",
8823
+ "children"
8824
+ ]);
8825
+ // Bridge — hooks called unconditionally (rules-of-hooks).
8826
+ // `useEngineVisibility` subscribes to engine state inside a
8827
+ // `<DashForm>` and re-evaluates the predicate reactively; outside
8828
+ // a form, it evaluates the predicate as a plain closure.
8829
+ // `useAccessState` consults the @dashforge/rbac policy engine.
8830
+ // Both default to no-op when the corresponding prop is omitted.
8831
+ const bridge = useContext(DashFormContext);
8832
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
8833
+ const accessState = useAccessState(access);
8834
+ // Early return — predicate false OR RBAC denies visibility.
8835
+ if (!isVisible || !accessState.visible) return null;
8836
+ const classes = cn(boxVariants({
8837
+ variant,
8838
+ color,
8839
+ elevation,
8840
+ rounded,
8841
+ p,
8842
+ px,
8843
+ py,
8844
+ m,
8845
+ mx,
8846
+ my,
8847
+ fullWidth,
8848
+ fullHeight
8849
+ }), // RBAC-disabled / readonly surfaces dim visually + carry
8850
+ // semantic ARIA attributes (assigned below) so descendants and
8851
+ // assistive tech can react.
8852
+ accessState.disabled && 'opacity-60', accessState.readonly && 'opacity-80', sx);
8853
+ const ariaProps = {
8854
+ 'aria-disabled': accessState.disabled || undefined,
8855
+ 'aria-readonly': accessState.readonly || undefined,
8856
+ 'data-disabled': accessState.disabled || undefined,
8857
+ 'data-readonly': accessState.readonly || undefined
8858
+ };
8859
+ if (asChild) {
8860
+ return jsx(Slot, _extends({
8861
+ ref: ref,
8862
+ className: classes
8863
+ }, ariaProps, rest, {
8864
+ children: children
8865
+ }));
8866
+ }
8867
+ const Tag = as != null ? as : 'div';
8868
+ return jsx(Tag, _extends({
8869
+ ref: ref,
8870
+ className: classes
8871
+ }, ariaProps, rest, {
8872
+ children: children
8873
+ }));
8874
+ });
8875
+ Box.displayName = 'Box';
8876
+
8877
+ /**
8878
+ * `<Card>` — opinionated surface preset over `<Box>`.
8879
+ *
8880
+ * Thin alias that pre-fills card-shaped Box defaults (`outlined`,
8881
+ * `rounded='lg'`, `elevation=1`, no padding). Reads as `<Card>` in
8882
+ * consumer code while reusing every Box affordance: surface variants,
8883
+ * 7 intent colors, spacing scale, polymorphism, and the bridge
8884
+ * integration (`access` + `visibleWhen`) retrofitted to Box in 1.1.0.
8885
+ *
8886
+ * Restricted variant axis: only `'outlined' | 'elevated' | 'plain'`.
8887
+ * `'soft'` and `'solid'` are deliberately excluded — they belong to
8888
+ * Box (as `<Alert>`-shaped callouts) and to banners, not to cards.
8889
+ *
8890
+ * @example
8891
+ * ```tsx
8892
+ * // Most common — outlined card with content padding via CardContent
8893
+ * <Card>
8894
+ * <CardContent>
8895
+ * <Typography variant="h3">Title</Typography>
8896
+ * <Typography variant="body">Body</Typography>
8897
+ * </CardContent>
8898
+ * </Card>
8899
+ *
8900
+ * // Elevated, clickable, gated by RBAC (inherits access from Box)
8901
+ * <Card
8902
+ * variant="elevated"
8903
+ * elevation={2}
8904
+ * access={{ resource: 'billing', action: 'read', onUnauthorized: 'hide' }}
8905
+ * >
8906
+ * <CardActionArea onClick={openBilling}>
8907
+ * <CardContent>
8908
+ * <Typography variant="h3">Revenue</Typography>
8909
+ * </CardContent>
8910
+ * </CardActionArea>
8911
+ * </Card>
8912
+ * ```
8913
+ */ const Card = /*#__PURE__*/ forwardRef(function Card(props, ref) {
8914
+ const { variant = 'outlined', rounded = 'lg', elevation = 1, p = 0, children } = props, rest = _object_without_properties_loose(props, [
8915
+ "variant",
8916
+ "rounded",
8917
+ "elevation",
8918
+ "p",
8919
+ "children"
8920
+ ]);
8921
+ return jsx(Box, _extends({
8922
+ ref: ref,
8923
+ variant: variant,
8924
+ rounded: rounded,
8925
+ elevation: elevation,
8926
+ p: p
8927
+ }, rest, {
8928
+ children: children
8929
+ }));
8930
+ });
8931
+ Card.displayName = 'Card';
8932
+ /**
8933
+ * `<CardContent>` — padded inner section of a Card.
8934
+ *
8935
+ * Semantic alias for an inner `<Box p={4}>`. The default padding step
8936
+ * (4) maps to `--df-tw-spacing-4` (1rem) via the dashforgePreset,
8937
+ * matching common card-content paddings across MUI / shadcn / Chakra.
8938
+ * Override `p` to dial up/down per the spacing token scale.
8939
+ *
8940
+ * @example
8941
+ * ```tsx
8942
+ * <Card>
8943
+ * <CardContent p={6}> {/* bigger inner padding *}/}
8944
+ * <Typography variant="h3">Spacious card</Typography>
8945
+ * </CardContent>
8946
+ * </Card>
8947
+ * ```
8948
+ */ function CardContent({ p = 4, children, className, sx }) {
8949
+ return jsx(Box, {
8950
+ p: p,
8951
+ sx: cn(className, sx),
8952
+ children: children
8953
+ });
8954
+ }
8955
+ CardContent.displayName = 'CardContent';
8956
+ /**
8957
+ * `<CardActionArea>` — clickable wrapper for Card content.
8958
+ *
8959
+ * Adds focus ring + hover tint + Radix Slot polymorphism. Renders as
8960
+ * `<button type="button">` by default, or as the immediate child via
8961
+ * `asChild` (typical pattern: wrap a router `<Link>`).
8962
+ *
8963
+ * Bridge integration is local — CardActionArea is an action trigger
8964
+ * (not a surface), so it carries `access` + `visibleWhen` directly
8965
+ * (rather than relying on the wrapping Card's props). This lets the
8966
+ * consumer differentiate "the card is visible but not clickable" from
8967
+ * "the card is not visible at all".
8968
+ *
8969
+ * @example
8970
+ * ```tsx
8971
+ * // Card-as-CTA
8972
+ * <Card>
8973
+ * <CardActionArea onClick={navigate}>
8974
+ * <CardContent>
8975
+ * <Typography variant="h3">Open dashboard</Typography>
8976
+ * <Typography variant="body">Hover to see the focus ring.</Typography>
8977
+ * </CardContent>
8978
+ * </CardActionArea>
8979
+ * </Card>
8980
+ *
8981
+ * // Polymorphic — wrap a router Link
8982
+ * <Card>
8983
+ * <CardActionArea asChild>
8984
+ * <Link to="/billing">
8985
+ * <CardContent>
8986
+ * <Typography variant="h3">Billing</Typography>
8987
+ * </CardContent>
8988
+ * </Link>
8989
+ * </CardActionArea>
8990
+ * </Card>
8991
+ *
8992
+ * // Selectable card (filter / option pattern)
8993
+ * <CardActionArea
8994
+ * selected={selected === 'monthly'}
8995
+ * onClick={() => setSelected('monthly')}
8996
+ * >
8997
+ * <CardContent>Monthly plan</CardContent>
8998
+ * </CardActionArea>
8999
+ * ```
9000
+ */ const CardActionArea = /*#__PURE__*/ forwardRef(function CardActionArea(props, ref) {
9001
+ const { children, asChild, selected, access, visibleWhen, disabled, sx, className, onClick } = props, rest = _object_without_properties_loose(props, [
9002
+ "children",
9003
+ "asChild",
9004
+ "selected",
9005
+ "access",
9006
+ "visibleWhen",
9007
+ "disabled",
9008
+ "sx",
9009
+ "className",
9010
+ "onClick"
9011
+ ]);
9012
+ // Bridge — both hooks called unconditionally (rules-of-hooks).
9013
+ const bridge = useContext(DashFormContext);
9014
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
9015
+ const accessState = useAccessState(access);
9016
+ if (!isVisible || !accessState.visible) return null;
9017
+ const isDisabled = Boolean(disabled) || accessState.disabled || accessState.readonly;
9018
+ // The action area must SPAN the parent Card so the entire surface
9019
+ // is clickable. Includes focus ring, hover, and a subtle pressed
9020
+ // tint via `data-pressed`. Token-driven via dashforgePreset.
9021
+ const classes = cn('block w-full text-left', 'outline-none rounded-[inherit]', 'transition-colors', // Hover — gentle bg tint on top of the card surface. Uses
9022
+ // currentColor with 5% alpha so it composes with any Card
9023
+ // variant/color without hardcoding.
9024
+ 'hover:bg-current/5', // Focus ring — keyboard-navigable, primary tone, inset slightly
9025
+ // so it traces the card's inherited radius.
9026
+ 'focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1', // Selected — adds a primary tone ring + soft tint so the card
9027
+ // visually reads as "active option" without changing the parent
9028
+ // Card's variant.
9029
+ selected && 'ring-2 ring-primary-500 ring-offset-1 bg-primary-50/60 dark:bg-primary-950/40', // Disabled — dims content and suppresses clicks.
9030
+ isDisabled && 'opacity-50 cursor-not-allowed pointer-events-none', sx, className);
9031
+ const handleClick = (e)=>{
9032
+ if (isDisabled) return;
9033
+ onClick == null ? void 0 : onClick(e);
9034
+ };
9035
+ if (asChild) {
9036
+ return jsx(Slot, {
9037
+ ref: ref,
9038
+ className: classes,
9039
+ "data-disabled": isDisabled || undefined,
9040
+ "aria-disabled": isDisabled || undefined,
9041
+ "aria-pressed": selected ? true : undefined,
9042
+ onClick: handleClick,
9043
+ children: children
9044
+ });
9045
+ }
9046
+ return jsx("button", _extends({
9047
+ ref: ref,
9048
+ type: "button",
9049
+ className: classes,
9050
+ disabled: isDisabled,
9051
+ "aria-pressed": selected ? true : undefined,
9052
+ onClick: handleClick
9053
+ }, rest, {
9054
+ children: children
9055
+ }));
9056
+ });
9057
+ CardActionArea.displayName = 'CardActionArea';
9058
+
9059
+ /**
9060
+ * Tailwind-variants recipe for `<Chip>` — promoted from the internal
9061
+ * `Table/cells/RenderChip` (Sprint 4.4). The visual treatment is
9062
+ * preserved byte-identical: the same 3 × 7 compound matrix
9063
+ * (variant × color) emitted by RenderChip is now the single source
9064
+ * of truth, consumed by both the public `<Chip>` and (via thin
9065
+ * wrapper) the legacy `RenderChip` internal.
9066
+ *
9067
+ * Slots (only `root` slot used by the public Chip; sub-slots
9068
+ * surfaced via separate inline classes in `Chip.tsx`):
9069
+ * - `root` — outer chip surface
9070
+ *
9071
+ * Variants axis (3 × 7, plus orthogonal `size`):
9072
+ * - `color`: 7 intents (`neutral / primary / secondary / success /
9073
+ * warning / danger / info`)
9074
+ * - `variant`: `soft` (default) / `solid` / `outline`
9075
+ * - `size`: `sm` (default) / `md`
9076
+ *
9077
+ * Token-driven: every Tailwind utility resolves through
9078
+ * `dashforgePreset()` → CSS variables. No hardcoded hex.
9079
+ *
9080
+ * `dark:` variants on color palettes (lines 49-77 of legacy
9081
+ * RenderChip, preserved here) are INTENTIONAL — color palettes do not
9082
+ * auto-invert via the CSS-var swap (only the `neutral` palette does),
9083
+ * so the `dark:` shift is a deliberate design choice (darker bg +
9084
+ * lighter text in dark mode for visual cohesion). `dark:` on the
9085
+ * `neutral` row is AVOIDED — that would double-invert.
9086
+ */ const chipVariants = tv({
9087
+ base: [
9088
+ 'inline-flex items-center gap-1 rounded-full font-medium',
9089
+ 'whitespace-nowrap',
9090
+ // Smooth transitions for clickable / selected state changes.
9091
+ 'transition-colors',
9092
+ 'motion-reduce:transition-none'
9093
+ ],
9094
+ variants: {
9095
+ color: {
9096
+ neutral: '',
9097
+ primary: '',
9098
+ secondary: '',
9099
+ success: '',
9100
+ warning: '',
9101
+ danger: '',
9102
+ info: ''
9103
+ },
9104
+ variant: {
9105
+ soft: '',
9106
+ solid: '',
9107
+ outline: ''
9108
+ },
9109
+ size: {
9110
+ sm: 'h-5 px-2 text-xs',
9111
+ md: 'h-6 px-2.5 text-xs'
9112
+ },
9113
+ /**
9114
+ * Clickable state — adds focus ring + hover transition. Applied
9115
+ * when the chip renders as `<button>` (i.e. `onClick` or
9116
+ * explicit `clickable` is set).
9117
+ */ clickable: {
9118
+ true: [
9119
+ 'cursor-pointer select-none',
9120
+ 'outline-none focus-visible:ring-2 focus-visible:ring-offset-1'
9121
+ ]
9122
+ },
9123
+ /**
9124
+ * Selected state — used on filter chips to indicate active
9125
+ * selection. Visually shifts the chip to a heavier emphasis
9126
+ * (one step up the elevation ladder of its current variant).
9127
+ * Compound rules below apply the actual shift per (variant, color)
9128
+ * cell.
9129
+ */ selected: {
9130
+ true: 'ring-2 ring-offset-1'
9131
+ },
9132
+ /** Disabled state for clickable chips. */ disabled: {
9133
+ true: 'opacity-50 cursor-not-allowed pointer-events-none'
9134
+ }
9135
+ },
9136
+ compoundVariants: [
9137
+ // ─── soft × color ──────────────────────────────────────────────
9138
+ // Subtle bg + readable text. Neutral row uses NO `dark:` (auto-
9139
+ // inverts via the dashforgePreset CSS-var swap). Color rows KEEP
9140
+ // `dark:` (color palettes don't auto-invert).
9141
+ {
9142
+ variant: 'soft',
9143
+ color: 'neutral',
9144
+ class: 'bg-neutral-100 text-neutral-700'
9145
+ },
9146
+ {
9147
+ variant: 'soft',
9148
+ color: 'primary',
9149
+ class: 'bg-primary-100 dark:bg-primary-950 text-primary-700 dark:text-primary-300'
9150
+ },
9151
+ {
9152
+ variant: 'soft',
9153
+ color: 'secondary',
9154
+ class: 'bg-secondary-100 dark:bg-secondary-950 text-secondary-700 dark:text-secondary-300'
9155
+ },
9156
+ {
9157
+ variant: 'soft',
9158
+ color: 'success',
9159
+ class: 'bg-success-100 dark:bg-success-950 text-success-700 dark:text-success-300'
9160
+ },
9161
+ {
9162
+ variant: 'soft',
9163
+ color: 'warning',
9164
+ class: 'bg-warning-100 dark:bg-warning-950 text-warning-700 dark:text-warning-300'
9165
+ },
9166
+ {
9167
+ variant: 'soft',
9168
+ color: 'danger',
9169
+ class: 'bg-danger-100 dark:bg-danger-950 text-danger-700 dark:text-danger-300'
9170
+ },
9171
+ {
9172
+ variant: 'soft',
9173
+ color: 'info',
9174
+ class: 'bg-info-100 dark:bg-info-950 text-info-700 dark:text-info-300'
9175
+ },
9176
+ // ─── solid × color ─────────────────────────────────────────────
9177
+ // Full bg + contrasting text. Solid neutral uses neutral-700/50
9178
+ // (both on neutral palette → auto-invert together).
9179
+ // `text-white` is deliberately NOT used: white does not auto-invert,
9180
+ // which would flip the contrast in dark mode for the neutral row.
9181
+ {
9182
+ variant: 'solid',
9183
+ color: 'neutral',
9184
+ class: 'bg-neutral-700 text-neutral-50'
6965
9185
  },
6966
- /*
6967
- * Intent color. Only meaningful when variant is outlined / soft /
6968
- * solid (resolved via compoundVariants below). For plain / elevated
6969
- * this axis is ignored at the visual level — but kept in the type
6970
- * so the prop is always available without conditional typing.
6971
- */ color: {
6972
- primary: '',
6973
- secondary: '',
6974
- success: '',
6975
- warning: '',
6976
- danger: '',
6977
- info: '',
6978
- neutral: ''
9186
+ {
9187
+ variant: 'solid',
9188
+ color: 'primary',
9189
+ class: 'bg-primary-500 text-white'
6979
9190
  },
6980
- /*
6981
- * Shadow scale — relevant for `variant='elevated'`. We keep elevation
6982
- * as a separate axis (not folded into `variant`) so consumers can
6983
- * dial it up/down without changing the variant. Default `0` = no
6984
- * shadow (consistent with MUI's elevation=0).
6985
- */ elevation: {
6986
- 0: 'shadow-none',
6987
- 1: 'shadow-sm',
6988
- 2: 'shadow',
6989
- 3: 'shadow-md',
6990
- 4: 'shadow-lg',
6991
- 5: 'shadow-xl'
9191
+ {
9192
+ variant: 'solid',
9193
+ color: 'secondary',
9194
+ class: 'bg-secondary-500 text-white'
6992
9195
  },
6993
- rounded: {
6994
- none: 'rounded-none',
6995
- sm: 'rounded-sm',
6996
- md: 'rounded-md',
6997
- lg: 'rounded-lg',
6998
- xl: 'rounded-xl',
6999
- '2xl': 'rounded-2xl',
7000
- full: 'rounded-full'
9196
+ {
9197
+ variant: 'solid',
9198
+ color: 'success',
9199
+ class: 'bg-success-500 text-white'
7001
9200
  },
7002
- /*
7003
- * Spacing — six axes (p/px/py/m/mx/my), 11 token steps each.
7004
- * Literals enumerated explicitly so Tailwind's JIT scanner finds
7005
- * every class. Token steps mirror @dashforge/tw-tokens spacing scale.
7006
- */ p: {
7007
- 0: 'p-0',
7008
- '0.5': 'p-0.5',
7009
- 1: 'p-1',
7010
- 2: 'p-2',
7011
- 3: 'p-3',
7012
- 4: 'p-4',
7013
- 6: 'p-6',
7014
- 8: 'p-8',
7015
- 12: 'p-12',
7016
- 16: 'p-16',
7017
- 24: 'p-24'
9201
+ {
9202
+ variant: 'solid',
9203
+ color: 'warning',
9204
+ class: 'bg-warning-500 text-white'
7018
9205
  },
7019
- px: {
7020
- 0: 'px-0',
7021
- '0.5': 'px-0.5',
7022
- 1: 'px-1',
7023
- 2: 'px-2',
7024
- 3: 'px-3',
7025
- 4: 'px-4',
7026
- 6: 'px-6',
7027
- 8: 'px-8',
7028
- 12: 'px-12',
7029
- 16: 'px-16',
7030
- 24: 'px-24'
9206
+ {
9207
+ variant: 'solid',
9208
+ color: 'danger',
9209
+ class: 'bg-danger-500 text-white'
7031
9210
  },
7032
- py: {
7033
- 0: 'py-0',
7034
- '0.5': 'py-0.5',
7035
- 1: 'py-1',
7036
- 2: 'py-2',
7037
- 3: 'py-3',
7038
- 4: 'py-4',
7039
- 6: 'py-6',
7040
- 8: 'py-8',
7041
- 12: 'py-12',
7042
- 16: 'py-16',
7043
- 24: 'py-24'
9211
+ {
9212
+ variant: 'solid',
9213
+ color: 'info',
9214
+ class: 'bg-info-500 text-white'
7044
9215
  },
7045
- m: {
7046
- 0: 'm-0',
7047
- '0.5': 'm-0.5',
7048
- 1: 'm-1',
7049
- 2: 'm-2',
7050
- 3: 'm-3',
7051
- 4: 'm-4',
7052
- 6: 'm-6',
7053
- 8: 'm-8',
7054
- 12: 'm-12',
7055
- 16: 'm-16',
7056
- 24: 'm-24'
9216
+ // ─── outline × color ───────────────────────────────────────────
9217
+ // Border + colored text. Outline neutral: both auto-invert via
9218
+ // CSS-var swap. Color rows: `dark:text-*-300` for legibility in
9219
+ // dark mode on a transparent background.
9220
+ {
9221
+ variant: 'outline',
9222
+ color: 'neutral',
9223
+ class: 'border border-neutral-300 text-neutral-700'
7057
9224
  },
7058
- mx: {
7059
- 0: 'mx-0',
7060
- '0.5': 'mx-0.5',
7061
- 1: 'mx-1',
7062
- 2: 'mx-2',
7063
- 3: 'mx-3',
7064
- 4: 'mx-4',
7065
- 6: 'mx-6',
7066
- 8: 'mx-8',
7067
- 12: 'mx-12',
7068
- 16: 'mx-16',
7069
- 24: 'mx-24'
9225
+ {
9226
+ variant: 'outline',
9227
+ color: 'primary',
9228
+ class: 'border border-primary-500 text-primary-700 dark:text-primary-300'
7070
9229
  },
7071
- my: {
7072
- 0: 'my-0',
7073
- '0.5': 'my-0.5',
7074
- 1: 'my-1',
7075
- 2: 'my-2',
7076
- 3: 'my-3',
7077
- 4: 'my-4',
7078
- 6: 'my-6',
7079
- 8: 'my-8',
7080
- 12: 'my-12',
7081
- 16: 'my-16',
7082
- 24: 'my-24'
9230
+ {
9231
+ variant: 'outline',
9232
+ color: 'secondary',
9233
+ class: 'border border-secondary-500 text-secondary-700 dark:text-secondary-300'
7083
9234
  },
7084
- fullWidth: {
7085
- true: 'w-full'
9235
+ {
9236
+ variant: 'outline',
9237
+ color: 'success',
9238
+ class: 'border border-success-500 text-success-700 dark:text-success-300'
7086
9239
  },
7087
- fullHeight: {
7088
- true: 'h-full'
7089
- }
7090
- },
7091
- /*
7092
- * Compound variants — where surface × color get their actual visual.
7093
- * Twenty-one entries: 7 (outlined) + 7 (soft) + 7 (solid).
7094
- * `plain` and `elevated` don't appear here (no color contribution).
7095
- *
7096
- * Dark-mode pair is baked in: light-mode picks the 50-300 steps,
7097
- * dark-mode picks the 800-950 steps — both reactive to setMode()
7098
- * via the @dashforge/tw-theme CSS variables.
7099
- */ compoundVariants: [
7100
- // ─── outlined × color ─────────────────────────────────────────────
7101
9240
  {
7102
- variant: 'outlined',
9241
+ variant: 'outline',
9242
+ color: 'warning',
9243
+ class: 'border border-warning-500 text-warning-700 dark:text-warning-300'
9244
+ },
9245
+ {
9246
+ variant: 'outline',
9247
+ color: 'danger',
9248
+ class: 'border border-danger-500 text-danger-700 dark:text-danger-300'
9249
+ },
9250
+ {
9251
+ variant: 'outline',
9252
+ color: 'info',
9253
+ class: 'border border-info-500 text-info-700 dark:text-info-300'
9254
+ },
9255
+ // ─── clickable × color hover (subtle bg shift) ─────────────────
9256
+ // Hover state on clickable chips. Each (variant, color) hover
9257
+ // emits a one-tier deeper bg tint, working across both modes via
9258
+ // the same auto-invert logic as the base compound.
9259
+ {
9260
+ clickable: true,
9261
+ variant: 'soft',
9262
+ color: 'neutral',
9263
+ class: 'hover:bg-neutral-200'
9264
+ },
9265
+ {
9266
+ clickable: true,
9267
+ variant: 'soft',
7103
9268
  color: 'primary',
7104
- class: 'border-primary-300 bg-primary-50/40 dark:border-primary-800 dark:bg-primary-950/30'
9269
+ class: 'hover:bg-primary-200 dark:hover:bg-primary-900'
7105
9270
  },
7106
9271
  {
7107
- variant: 'outlined',
9272
+ clickable: true,
9273
+ variant: 'soft',
7108
9274
  color: 'secondary',
7109
- class: 'border-secondary-300 bg-secondary-50/40 dark:border-secondary-800 dark:bg-secondary-950/30'
9275
+ class: 'hover:bg-secondary-200 dark:hover:bg-secondary-900'
7110
9276
  },
7111
9277
  {
7112
- variant: 'outlined',
9278
+ clickable: true,
9279
+ variant: 'soft',
7113
9280
  color: 'success',
7114
- class: 'border-success-300 bg-success-50/40 dark:border-success-800 dark:bg-success-950/30'
9281
+ class: 'hover:bg-success-200 dark:hover:bg-success-900'
7115
9282
  },
7116
9283
  {
7117
- variant: 'outlined',
9284
+ clickable: true,
9285
+ variant: 'soft',
7118
9286
  color: 'warning',
7119
- class: 'border-warning-300 bg-warning-50/40 dark:border-warning-800 dark:bg-warning-950/30'
9287
+ class: 'hover:bg-warning-200 dark:hover:bg-warning-900'
9288
+ },
9289
+ {
9290
+ clickable: true,
9291
+ variant: 'soft',
9292
+ color: 'danger',
9293
+ class: 'hover:bg-danger-200 dark:hover:bg-danger-900'
9294
+ },
9295
+ {
9296
+ clickable: true,
9297
+ variant: 'soft',
9298
+ color: 'info',
9299
+ class: 'hover:bg-info-200 dark:hover:bg-info-900'
9300
+ },
9301
+ // Solid hover → one tone darker
9302
+ {
9303
+ clickable: true,
9304
+ variant: 'solid',
9305
+ color: 'neutral',
9306
+ class: 'hover:bg-neutral-800'
9307
+ },
9308
+ {
9309
+ clickable: true,
9310
+ variant: 'solid',
9311
+ color: 'primary',
9312
+ class: 'hover:bg-primary-600'
9313
+ },
9314
+ {
9315
+ clickable: true,
9316
+ variant: 'solid',
9317
+ color: 'secondary',
9318
+ class: 'hover:bg-secondary-600'
9319
+ },
9320
+ {
9321
+ clickable: true,
9322
+ variant: 'solid',
9323
+ color: 'success',
9324
+ class: 'hover:bg-success-600'
9325
+ },
9326
+ {
9327
+ clickable: true,
9328
+ variant: 'solid',
9329
+ color: 'warning',
9330
+ class: 'hover:bg-warning-600'
9331
+ },
9332
+ {
9333
+ clickable: true,
9334
+ variant: 'solid',
9335
+ color: 'danger',
9336
+ class: 'hover:bg-danger-600'
9337
+ },
9338
+ {
9339
+ clickable: true,
9340
+ variant: 'solid',
9341
+ color: 'info',
9342
+ class: 'hover:bg-info-600'
9343
+ },
9344
+ // Outline hover → tinted bg appears (transparent → light tint)
9345
+ {
9346
+ clickable: true,
9347
+ variant: 'outline',
9348
+ color: 'neutral',
9349
+ class: 'hover:bg-neutral-100'
9350
+ },
9351
+ {
9352
+ clickable: true,
9353
+ variant: 'outline',
9354
+ color: 'primary',
9355
+ class: 'hover:bg-primary-50 dark:hover:bg-primary-950'
9356
+ },
9357
+ {
9358
+ clickable: true,
9359
+ variant: 'outline',
9360
+ color: 'secondary',
9361
+ class: 'hover:bg-secondary-50 dark:hover:bg-secondary-950'
9362
+ },
9363
+ {
9364
+ clickable: true,
9365
+ variant: 'outline',
9366
+ color: 'success',
9367
+ class: 'hover:bg-success-50 dark:hover:bg-success-950'
9368
+ },
9369
+ {
9370
+ clickable: true,
9371
+ variant: 'outline',
9372
+ color: 'warning',
9373
+ class: 'hover:bg-warning-50 dark:hover:bg-warning-950'
7120
9374
  },
7121
9375
  {
7122
- variant: 'outlined',
9376
+ clickable: true,
9377
+ variant: 'outline',
7123
9378
  color: 'danger',
7124
- class: 'border-danger-300 bg-danger-50/40 dark:border-danger-800 dark:bg-danger-950/30'
9379
+ class: 'hover:bg-danger-50 dark:hover:bg-danger-950'
7125
9380
  },
7126
9381
  {
7127
- variant: 'outlined',
9382
+ clickable: true,
9383
+ variant: 'outline',
7128
9384
  color: 'info',
7129
- class: 'border-info-300 bg-info-50/40 dark:border-info-800 dark:bg-info-950/30'
9385
+ class: 'hover:bg-info-50 dark:hover:bg-info-950'
7130
9386
  },
9387
+ // ─── selected × color (focus ring tone) ────────────────────────
9388
+ // When selected, the ring color matches the chip's intent.
7131
9389
  {
7132
- variant: 'outlined',
9390
+ selected: true,
7133
9391
  color: 'neutral',
7134
- // border-neutral-200 auto-inverts via CSS var swap (no `dark:`
7135
- // needed). bg-white is hardcoded — `dark:bg-neutral-100` keeps
7136
- // elevation tier above page surface in dark mode (NOT
7137
- // `dark:bg-neutral-900` which renders near-white).
7138
- class: 'border-neutral-200 bg-white dark:bg-neutral-100'
9392
+ class: 'ring-neutral-400'
7139
9393
  },
7140
- // ─── soft × color ─────────────────────────────────────────────────
7141
9394
  {
7142
- variant: 'soft',
9395
+ selected: true,
7143
9396
  color: 'primary',
7144
- class: 'bg-primary-100 text-primary-900 dark:bg-primary-950/50 dark:text-primary-100'
9397
+ class: 'ring-primary-500'
7145
9398
  },
7146
9399
  {
7147
- variant: 'soft',
9400
+ selected: true,
7148
9401
  color: 'secondary',
7149
- class: 'bg-secondary-100 text-secondary-900 dark:bg-secondary-950/50 dark:text-secondary-100'
9402
+ class: 'ring-secondary-500'
7150
9403
  },
7151
9404
  {
7152
- variant: 'soft',
9405
+ selected: true,
7153
9406
  color: 'success',
7154
- class: 'bg-success-100 text-success-900 dark:bg-success-950/50 dark:text-success-100'
9407
+ class: 'ring-success-500'
7155
9408
  },
7156
9409
  {
7157
- variant: 'soft',
9410
+ selected: true,
7158
9411
  color: 'warning',
7159
- class: 'bg-warning-100 text-warning-900 dark:bg-warning-950/50 dark:text-warning-100'
9412
+ class: 'ring-warning-500'
7160
9413
  },
7161
9414
  {
7162
- variant: 'soft',
9415
+ selected: true,
7163
9416
  color: 'danger',
7164
- class: 'bg-danger-100 text-danger-900 dark:bg-danger-950/50 dark:text-danger-100'
9417
+ class: 'ring-danger-500'
7165
9418
  },
7166
9419
  {
7167
- variant: 'soft',
9420
+ selected: true,
7168
9421
  color: 'info',
7169
- class: 'bg-info-100 text-info-900 dark:bg-info-950/50 dark:text-info-100'
9422
+ class: 'ring-info-500'
7170
9423
  },
9424
+ // ─── clickable × color (focus ring tone) ───────────────────────
9425
+ // Tab-focused ring tone matches the chip color (same intent as
9426
+ // selected but only when keyboard-focused, via focus-visible).
7171
9427
  {
7172
- variant: 'soft',
9428
+ clickable: true,
7173
9429
  color: 'neutral',
7174
- // Both neutral classes auto-invert via the dashforgePreset() CSS
7175
- // var swap — no `dark:` variants needed.
7176
- class: 'bg-neutral-100 text-neutral-900'
9430
+ class: 'focus-visible:ring-neutral-400'
7177
9431
  },
7178
- // ─── solid × color ────────────────────────────────────────────────
7179
9432
  {
7180
- variant: 'solid',
9433
+ clickable: true,
7181
9434
  color: 'primary',
7182
- class: 'bg-primary-600 text-white dark:bg-primary-500'
9435
+ class: 'focus-visible:ring-primary-500'
7183
9436
  },
7184
9437
  {
7185
- variant: 'solid',
9438
+ clickable: true,
7186
9439
  color: 'secondary',
7187
- class: 'bg-secondary-600 text-white dark:bg-secondary-500'
9440
+ class: 'focus-visible:ring-secondary-500'
7188
9441
  },
7189
9442
  {
7190
- variant: 'solid',
9443
+ clickable: true,
7191
9444
  color: 'success',
7192
- class: 'bg-success-600 text-white dark:bg-success-500'
9445
+ class: 'focus-visible:ring-success-500'
7193
9446
  },
7194
9447
  {
7195
- variant: 'solid',
9448
+ clickable: true,
7196
9449
  color: 'warning',
7197
- class: 'bg-warning-500 text-white dark:bg-warning-600'
9450
+ class: 'focus-visible:ring-warning-500'
7198
9451
  },
7199
9452
  {
7200
- variant: 'solid',
9453
+ clickable: true,
7201
9454
  color: 'danger',
7202
- class: 'bg-danger-600 text-white dark:bg-danger-500'
9455
+ class: 'focus-visible:ring-danger-500'
7203
9456
  },
7204
9457
  {
7205
- variant: 'solid',
9458
+ clickable: true,
7206
9459
  color: 'info',
7207
- class: 'bg-info-600 text-white dark:bg-info-500'
7208
- },
7209
- {
7210
- variant: 'solid',
7211
- color: 'neutral',
7212
- // Solid neutral is the high-emphasis "always-foreground accent"
7213
- // variant. Both classes auto-invert: dark surface + light text
7214
- // in light mode (`#171717` / `#fafafa`) flips to light surface +
7215
- // dark text in dark mode (`#f5f5f5` / `#0a0a0a`). The
7216
- // accent inverts WITH the page surface — consistent with the
7217
- // identity rule. Previously used `text-white` (no auto-invert)
7218
- // which forced double-inversion to keep readable.
7219
- class: 'bg-neutral-900 text-neutral-50'
9460
+ class: 'focus-visible:ring-info-500'
7220
9461
  }
7221
9462
  ],
7222
9463
  defaultVariants: {
7223
- variant: 'plain',
7224
9464
  color: 'neutral',
7225
- elevation: 0,
7226
- rounded: 'none'
9465
+ variant: 'soft',
9466
+ size: 'sm'
9467
+ }
9468
+ });
9469
+
9470
+ /**
9471
+ * Default delete-button glyph — inline stroke SVG. Inherits the
9472
+ * chip's text colour via `currentColor`. No icon-library dep.
9473
+ *
9474
+ * @internal
9475
+ */ function DefaultDeleteIcon() {
9476
+ return jsx("svg", {
9477
+ width: "0.875em",
9478
+ height: "0.875em",
9479
+ viewBox: "0 0 14 14",
9480
+ fill: "none",
9481
+ stroke: "currentColor",
9482
+ strokeWidth: 1.75,
9483
+ strokeLinecap: "round",
9484
+ strokeLinejoin: "round",
9485
+ "aria-hidden": "true",
9486
+ children: jsx("path", {
9487
+ d: "m4 4 6 6M10 4l-6 6"
9488
+ })
9489
+ });
9490
+ }
9491
+ /**
9492
+ * Dashforge TW Chip — status / filter / tag pill.
9493
+ *
9494
+ * Promoted from the internal `Table/cells/RenderChip` (Sprint 4.4):
9495
+ * preserves the 3 × 7 variant × color matrix byte-identical and adds
9496
+ * the public API (label, icon, avatar, clickable, onClick, onDelete,
9497
+ * selected, access).
9498
+ *
9499
+ * **Rendering modes** (chosen at runtime from props):
9500
+ * - **Static** (default) → `<span>`. Pure display.
9501
+ * - **Clickable** (`clickable` true OR `onClick` provided) →
9502
+ * `<button type="button">` with focus ring + hover state.
9503
+ * - **Deletable** (`onDelete` provided) → root + trailing `<button>`
9504
+ * for the delete glyph. Static or clickable root depending on
9505
+ * whether `onClick` is also set.
9506
+ *
9507
+ * **Variant axis** is `soft | solid | outline` (Dashforge chip
9508
+ * vocabulary, default `'soft'`). Distinct from Button's
9509
+ * (`solid | outline | ghost | link`) and Alert's
9510
+ * (`standard | filled | outlined`) by design — see CHANGELOG.
9511
+ *
9512
+ * **A11y**:
9513
+ * - Static chip → no role (it's a `<span>`).
9514
+ * - Clickable chip → native `<button>` semantics.
9515
+ * - `selected` → `aria-pressed="true"` (so it announces as a toggle
9516
+ * button in pressed state).
9517
+ * - Delete button → its own `<button>` with `aria-label`
9518
+ * (defaults to `'Remove'`).
9519
+ *
9520
+ * @example
9521
+ * ```tsx
9522
+ * // Static status chip
9523
+ * <Chip label="Active" color="success" />
9524
+ *
9525
+ * // With icon
9526
+ * import { CheckIcon } from 'lucide-react';
9527
+ * <Chip label="Verified" color="success" icon={<CheckIcon size={14} />} />
9528
+ *
9529
+ * // Filter chip (clickable + toggle via selected)
9530
+ * <Chip
9531
+ * label="Last 7 days"
9532
+ * color="primary"
9533
+ * clickable
9534
+ * selected={range === '7d'}
9535
+ * onClick={() => setRange('7d')}
9536
+ * />
9537
+ *
9538
+ * // Removable tag
9539
+ * <Chip
9540
+ * label="design-system"
9541
+ * color="info"
9542
+ * variant="outline"
9543
+ * onDelete={() => removeTag('design-system')}
9544
+ * />
9545
+ *
9546
+ * // RBAC-gated admin chip
9547
+ * <Chip
9548
+ * label="Admin"
9549
+ * color="danger"
9550
+ * variant="solid"
9551
+ * access={{ resource: 'role', action: 'read', onUnauthorized: 'hide' }}
9552
+ * />
9553
+ * ```
9554
+ */ const Chip = /*#__PURE__*/ forwardRef(function Chip(props, ref) {
9555
+ const { label, icon, avatar, variant, color, size, clickable: clickableProp, onClick, onDelete, deleteIcon, deleteLabel = 'Remove', selected, disabled: disabledProp, visibleWhen, access, sx, className } = props;
9556
+ // Bridge — both hooks called unconditionally (rules-of-hooks).
9557
+ // Inside a `<DashForm>`, `useEngineVisibility` subscribes to engine
9558
+ // state and re-evaluates the predicate on changes; outside a form,
9559
+ // the predicate is called with `null` engine (consumer captures
9560
+ // external state in the closure).
9561
+ const bridge = useContext(DashFormContext);
9562
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
9563
+ const accessState = useAccessState(access);
9564
+ if (!isVisible || !accessState.visible) return null;
9565
+ // Effective interactivity / disabled — mirrors Button semantics.
9566
+ const isClickable = Boolean(clickableProp != null ? clickableProp : onClick);
9567
+ const isDisabled = Boolean(disabledProp) || accessState.disabled || accessState.readonly;
9568
+ const classes = cn(chipVariants({
9569
+ color,
9570
+ variant,
9571
+ size,
9572
+ clickable: isClickable || undefined,
9573
+ selected: selected || undefined,
9574
+ disabled: isDisabled || undefined
9575
+ }), sx, className);
9576
+ // ─── Inner content — leading slot + label + delete ────────────
9577
+ // Avatar wins over icon when both provided (MUI parity).
9578
+ // Label is rendered as a direct text node (no wrapping span) so
9579
+ // the chip DOM stays minimal and consumers that target the root
9580
+ // via `getByText(label)` continue working. The root's
9581
+ // `inline-flex items-center` provides vertical alignment with
9582
+ // any leading / trailing slots.
9583
+ const leading = avatar != null ? avatar : icon;
9584
+ const innerContent = jsxs(Fragment$1, {
9585
+ children: [
9586
+ leading != null && jsx("span", {
9587
+ className: "-ml-0.5 inline-flex items-center shrink-0",
9588
+ children: leading
9589
+ }),
9590
+ label,
9591
+ onDelete != null && jsx("button", {
9592
+ type: "button",
9593
+ "aria-label": deleteLabel,
9594
+ onClick: (e)=>{
9595
+ // Stop propagation so the chip's own onClick doesn't fire
9596
+ // when the user clicks the × — MUI matches this behaviour.
9597
+ e.stopPropagation();
9598
+ if (!isDisabled) onDelete(e);
9599
+ },
9600
+ disabled: isDisabled,
9601
+ className: cn('ml-0.5 -mr-1 inline-flex items-center justify-center', 'h-4 w-4 rounded-full opacity-70 hover:opacity-100', 'outline-none focus-visible:ring-2 focus-visible:ring-current', 'transition-opacity'),
9602
+ children: deleteIcon != null ? deleteIcon : jsx(DefaultDeleteIcon, {})
9603
+ })
9604
+ ]
9605
+ });
9606
+ // ─── Interactive root (clickable) ─────────────────────────────
9607
+ if (isClickable) {
9608
+ return jsx("button", {
9609
+ ref: ref,
9610
+ type: "button",
9611
+ className: classes,
9612
+ disabled: isDisabled,
9613
+ "aria-pressed": selected ? true : undefined,
9614
+ onClick: (e)=>{
9615
+ if (isDisabled) return;
9616
+ onClick == null ? void 0 : onClick(e);
9617
+ },
9618
+ onKeyDown: (e)=>{
9619
+ },
9620
+ children: innerContent
9621
+ });
9622
+ }
9623
+ // ─── Static root (display only) ───────────────────────────────
9624
+ return jsx("span", {
9625
+ ref: ref,
9626
+ className: classes,
9627
+ "aria-disabled": isDisabled || undefined,
9628
+ children: innerContent
9629
+ });
9630
+ });
9631
+
9632
+ /**
9633
+ * `typographyVariants` — the full type scale for the @dashforge/tw library.
9634
+ *
9635
+ * Mirrors the MUI Typography variant set (h1–h6 · subtitle1/2 · body1/2 ·
9636
+ * caption · overline) so the mental model carries over for developers
9637
+ * moving between the two ecosystems. Each variant baseline maps to a
9638
+ * Tailwind utility chain that resolves through the @dashforge/tw-tokens
9639
+ * scale (so the visual stays in sync with the rest of the system when the
9640
+ * token theme is patched).
9641
+ *
9642
+ * Variant axes are intentionally ORTHOGONAL — `variant` chooses the type
9643
+ * scale, `weight` overrides the variant's default weight (useful for "h2
9644
+ * but lighter"), `color` picks the intent, `align` picks the axis. They
9645
+ * never collide, so consumers can mix them freely.
9646
+ *
9647
+ * Two boolean flags (`truncate`, `noWrap`) encode the most common one-line
9648
+ * patterns; `gutterBottom` adds the conventional bottom margin used when a
9649
+ * heading precedes a paragraph block (mirror of MUI's same flag).
9650
+ */ const typographyVariants = tv({
9651
+ base: 'text-inherit',
9652
+ variants: {
9653
+ /*
9654
+ * `variant` is the type-scale axis.
9655
+ *
9656
+ * The default font-weight is baked into each variant (headings come
9657
+ * with semibold/bold by default). Consumers override per-instance via
9658
+ * the `weight` axis below — when set, `weight` wins because it's
9659
+ * declared later in the cn() chain and tailwind-merge resolves the
9660
+ * last `font-*` to win.
9661
+ */ variant: {
9662
+ h1: 'text-5xl font-bold leading-[1.05] tracking-[-0.025em]',
9663
+ h2: 'text-4xl font-bold leading-[1.1] tracking-[-0.022em]',
9664
+ h3: 'text-3xl font-semibold leading-[1.15] tracking-[-0.02em]',
9665
+ h4: 'text-2xl font-semibold leading-snug tracking-[-0.015em]',
9666
+ h5: 'text-xl font-semibold leading-snug',
9667
+ h6: 'text-lg font-semibold leading-normal',
9668
+ subtitle1: 'text-base font-medium leading-relaxed',
9669
+ subtitle2: 'text-sm font-medium leading-relaxed',
9670
+ body1: 'text-base font-normal leading-relaxed',
9671
+ body2: 'text-sm font-normal leading-relaxed',
9672
+ caption: 'text-xs font-normal leading-normal',
9673
+ overline: 'text-xs font-semibold uppercase tracking-[0.12em] leading-normal'
9674
+ },
9675
+ /*
9676
+ * `color` is the intent axis. Pairs with the @dashforge/tw-theme
9677
+ * reactive colour vars so the choice survives theme patches and dark
9678
+ * mode flips. `inherit` is the escape hatch — used inside a Box that
9679
+ * has set its own color (e.g. `<Box variant="solid" color="primary">`
9680
+ * paints white text).
9681
+ */ color: {
9682
+ // `inherit` is the explicit escape hatch — use it on a
9683
+ // `<Typography>` inside a coloured surface (e.g.
9684
+ // `<Box variant="solid" color="primary">` paints white text)
9685
+ // so the text picks up the surface colour. It is NO LONGER the
9686
+ // default: a bare `<Typography>` would otherwise inherit the
9687
+ // consuming app's arbitrary root colour (often OS-`prefers-
9688
+ // color-scheme` driven), which diverges from the Dashforge
9689
+ // `data-dash-tw-theme` mode and breaks colour consistency in
9690
+ // dark mode. The default is now `neutral` — see `defaultVariants`.
9691
+ inherit: 'text-inherit',
9692
+ primary: 'text-primary-700 dark:text-primary-400',
9693
+ secondary: 'text-secondary-700 dark:text-secondary-400',
9694
+ success: 'text-success-700 dark:text-success-400',
9695
+ warning: 'text-warning-700 dark:text-warning-400',
9696
+ danger: 'text-danger-700 dark:text-danger-400',
9697
+ info: 'text-info-700 dark:text-info-400',
9698
+ // Neutral palette auto-inverts via the dashforgePreset() CSS-var
9699
+ // swap — no `dark:` variants. `text-neutral-900` resolves to
9700
+ // dark text in light mode and light text in dark mode; same
9701
+ // applies to `text-neutral-600` (muted).
9702
+ neutral: 'text-neutral-900',
9703
+ muted: 'text-neutral-600'
9704
+ },
9705
+ /*
9706
+ * `weight` overrides the variant's default weight. When unset, the
9707
+ * variant's own weight wins. When set, this axis appears LATER in the
9708
+ * cn() chain so tailwind-merge resolves to this value.
9709
+ */ weight: {
9710
+ normal: 'font-normal',
9711
+ medium: 'font-medium',
9712
+ semibold: 'font-semibold',
9713
+ bold: 'font-bold',
9714
+ extrabold: 'font-extrabold'
9715
+ },
9716
+ align: {
9717
+ left: 'text-left',
9718
+ center: 'text-center',
9719
+ right: 'text-right',
9720
+ justify: 'text-justify'
9721
+ },
9722
+ /*
9723
+ * `truncate` collapses to a one-line ellipsis. `noWrap` is the looser
9724
+ * sibling — keeps the text on one line but lets it overflow without
9725
+ * the `…`. Mutually-exclusive intent-wise; if both are passed,
9726
+ * `truncate` wins (later in the cn() chain).
9727
+ */ truncate: {
9728
+ true: 'truncate'
9729
+ },
9730
+ noWrap: {
9731
+ true: 'whitespace-nowrap'
9732
+ },
9733
+ /*
9734
+ * `gutterBottom` adds the conventional bottom margin used when a
9735
+ * heading precedes a paragraph block. Mirror of MUI's same prop —
9736
+ * familiar to developers crossing from the MUI side.
9737
+ */ gutterBottom: {
9738
+ true: 'mb-3'
9739
+ }
9740
+ },
9741
+ defaultVariants: {
9742
+ variant: 'body1',
9743
+ // `neutral` (not `inherit`): a bare `<Typography>` emits
9744
+ // `text-neutral-900`, which auto-inverts via the dashforgePreset()
9745
+ // CSS-var swap and follows the Dashforge mode. Pass
9746
+ // `color="inherit"` explicitly inside a coloured surface.
9747
+ color: 'neutral',
9748
+ align: 'left'
7227
9749
  }
7228
9750
  });
7229
9751
 
7230
9752
  /**
7231
- * `<Box>` — the surface primitive of @dashforge/tw.
7232
- *
7233
- * What it IS:
7234
- * A polymorphic container with typed surface variants (plain /
7235
- * outlined / elevated / soft / solid) × 7 intent colours, plus
7236
- * spacing / sizing / rounded / elevation as enumerated token-scale
7237
- * props. Replaces MUI's Box + Paper + Card + Surface in one component.
7238
- *
7239
- * What it is NOT (deliberate, enforced by the prop set):
7240
- * • Not a flex container → use <Stack>
7241
- * • Not a grid container → use <Grid>
7242
- * • Not a paragraph → use <Typography>
7243
- * • Not a button → use <Button>
9753
+ * Default HTML tag per variant. Headings get their semantic level (h1→h1
9754
+ * etc.); subtitle/body get `<p>` (block, paragraph semantics);
9755
+ * caption/overline get `<span>` (inline, no implicit block break).
9756
+ *
9757
+ * Override per-instance via the `as` prop — useful when the semantic
9758
+ * heading level should differ from the visual scale (e.g. a hero
9759
+ * rendered as `<h2>` but visually styled `h1`).
9760
+ */ const VARIANT_TO_TAG = {
9761
+ h1: 'h1',
9762
+ h2: 'h2',
9763
+ h3: 'h3',
9764
+ h4: 'h4',
9765
+ h5: 'h5',
9766
+ h6: 'h6',
9767
+ subtitle1: 'p',
9768
+ subtitle2: 'p',
9769
+ body1: 'p',
9770
+ body2: 'p',
9771
+ caption: 'span',
9772
+ overline: 'span'
9773
+ };
9774
+ /**
9775
+ * `<Typography>` — semantic typed text, the foundation of every readable
9776
+ * surface in @dashforge/tw.
7244
9777
  *
7245
- * The "Box is not flex" rule is the spine of the layout layer. Without
7246
- * it, every `<div>` in an app gravitates back to Box and the surface
7247
- * vs layout distinction collapses. Box's job is the SURFACE around
7248
- * content; Stack/Grid's job is the ARRANGEMENT of content. Two
7249
- * primitives, two responsibilities.
9778
+ * Why this exists:
9779
+ * Tailwind ships a typographic scale (`text-xl`, `font-bold`,
9780
+ * `leading-relaxed`) but leaves the SEMANTIC HTML tag and the
9781
+ * intent-coloured palette to the consumer. That's fine for one-off
9782
+ * marketing surfaces, but at app scale it means every `<h2>` and every
9783
+ * body paragraph re-derives its own utility chain — and they drift.
7250
9784
  *
7251
- * When BOTH `as` and `asChild` are passed, `asChild` wins. Same rule
7252
- * as Typography — documented to avoid the dimension-bloat of a
7253
- * discriminated union over an 11-axis props type.
9785
+ * Typography moves that decision into a typed prop set: the visual
9786
+ * scale, the intent colour, the alignment, the truncation all live in
9787
+ * `tailwind-variants` and resolve to the same utility chain everywhere
9788
+ * in the app. The default HTML tag is inferred from `variant` so the
9789
+ * semantic layer follows the visual layer; `as` and `asChild` are the
9790
+ * two escape hatches when you need something else.
7254
9791
  *
7255
9792
  * Layering:
7256
- * • Sits BENEATH every visual chrome — every card, every panel,
7257
- * every section background.
7258
- * • Composes ON TOP of @dashforge/tw-tokens (color + spacing + radius
7259
- * + shadow scales) via the dashforgePreset CSS variables.
7260
- * • Pairs with Typography for text content, Stack/Grid for layout
7261
- * children inside it.
7262
- */ const Box = /*#__PURE__*/ forwardRef(function Box(props, ref) {
7263
- const { variant, color, elevation, rounded, p, px, py, m, mx, my, fullWidth, fullHeight, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
9793
+ * • Sits BENEATH every component that renders text — `<Button>`'s
9794
+ * label, `<TextField>`'s helper text, MDX prose in our own docs.
9795
+ * • Composes ON TOP of `@dashforge/tw-tokens` colour scales (so
9796
+ * `color="primary"` paints `text-primary-700` in light, `-400` in dark,
9797
+ * reactive to `setMode()`).
9798
+ * • Polymorphic via Radix Slot — pairs cleanly with router `<Link>`,
9799
+ * `<button>`, or `<a>` without injecting an extra wrapper element.
9800
+ *
9801
+ * Polymorphism rules — `as` vs `asChild`:
9802
+ * • `as` swaps the rendered tag (we still render the element ourselves).
9803
+ * • `asChild` removes our element entirely — the single React child
9804
+ * becomes the rendered tag, with our className/ref merged onto it.
9805
+ * This is the Radix Slot pattern; use when you need a router Link
9806
+ * to style as a heading.
9807
+ * When BOTH are passed, `asChild` wins. We considered making this a
9808
+ * compile-time error via discriminated unions, but the API surface
9809
+ * already has 8 axes and adding one more dimension to the props type
9810
+ * would inflate IntelliSense suggestions for marginal benefit. The
9811
+ * runtime preference is documented; the test asserts it.
9812
+ */ const Typography = /*#__PURE__*/ forwardRef(function Typography(props, ref) {
9813
+ var _ref;
9814
+ const { variant = 'body1', color, weight, align, truncate, noWrap, gutterBottom, as, asChild = false, sx, children } = props, rest = _object_without_properties_loose(props, [
7264
9815
  "variant",
7265
9816
  "color",
7266
- "elevation",
7267
- "rounded",
7268
- "p",
7269
- "px",
7270
- "py",
7271
- "m",
7272
- "mx",
7273
- "my",
7274
- "fullWidth",
7275
- "fullHeight",
9817
+ "weight",
9818
+ "align",
9819
+ "truncate",
9820
+ "noWrap",
9821
+ "gutterBottom",
7276
9822
  "as",
7277
9823
  "asChild",
7278
9824
  "sx",
7279
9825
  "children"
7280
9826
  ]);
7281
- const classes = cn(boxVariants({
9827
+ const classes = cn(typographyVariants({
7282
9828
  variant,
7283
9829
  color,
7284
- elevation,
7285
- rounded,
7286
- p,
7287
- px,
7288
- py,
7289
- m,
7290
- mx,
7291
- my,
7292
- fullWidth,
7293
- fullHeight
9830
+ weight,
9831
+ align,
9832
+ truncate,
9833
+ noWrap,
9834
+ gutterBottom
7294
9835
  }), sx);
9836
+ // asChild wins over `as` when both are passed (see component header).
7295
9837
  if (asChild) {
7296
9838
  return jsx(Slot, _extends({
7297
9839
  ref: ref,
@@ -7300,7 +9842,8 @@ Typography.displayName = 'Typography';
7300
9842
  children: children
7301
9843
  }));
7302
9844
  }
7303
- const Tag = as != null ? as : 'div';
9845
+ // Resolve the rendered tag: explicit `as` > variant default > fallback.
9846
+ const Tag = (_ref = as != null ? as : VARIANT_TO_TAG[variant]) != null ? _ref : 'span';
7304
9847
  return jsx(Tag, _extends({
7305
9848
  ref: ref,
7306
9849
  className: classes
@@ -7308,7 +9851,7 @@ Typography.displayName = 'Typography';
7308
9851
  children: children
7309
9852
  }));
7310
9853
  });
7311
- Box.displayName = 'Box';
9854
+ Typography.displayName = 'Typography';
7312
9855
 
7313
9856
  /**
7314
9857
  * `stackVariants` — flex container 1D, the layout primitive of @dashforge/tw.
@@ -8996,7 +11539,7 @@ const accordionVariants = tv({
8996
11539
  // surface; the border gives the separation). `hover:bg-neutral-100`
8997
11540
  // is the one-tier elevation that works in both modes.
8998
11541
  summary: 'text-neutral-600',
8999
- list: 'inline-flex items-center gap-1',
11542
+ list: 'inline-flex items-center gap-1 list-none pl-0 m-0',
9000
11543
  pageButton: [
9001
11544
  'inline-flex items-center justify-center rounded-md',
9002
11545
  'border border-neutral-300',
@@ -10775,178 +13318,14 @@ function SearchIcon$1(props) {
10775
13318
  });
10776
13319
  }
10777
13320
 
10778
- /**
10779
- * Internal mini-chip used by the cell renderer library. Not exported
10780
- * from `@dashforge/tw` as a standalone component (per Sprint 4.1
10781
- * scope decision — a standalone `<Chip>` may ship in Sprint 5 if
10782
- * demand emerges).
10783
- *
10784
- * Variants:
10785
- * - `color`: 7 intents (neutral / primary / success / warning /
10786
- * danger / info / secondary)
10787
- * - `variant`: `soft` (default) / `solid` / `outline`
10788
- * - `size`: `sm` / `md`
10789
- */ const chipVariants = tv({
10790
- base: [
10791
- 'inline-flex items-center gap-1 rounded-full font-medium',
10792
- 'whitespace-nowrap'
10793
- ],
10794
- variants: {
10795
- color: {
10796
- neutral: '',
10797
- primary: '',
10798
- success: '',
10799
- warning: '',
10800
- danger: '',
10801
- info: '',
10802
- secondary: ''
10803
- },
10804
- variant: {
10805
- soft: '',
10806
- solid: '',
10807
- outline: ''
10808
- },
10809
- size: {
10810
- sm: 'h-5 px-2 text-xs',
10811
- md: 'h-6 px-2.5 text-xs'
10812
- }
10813
- },
10814
- compoundVariants: [
10815
- // soft = subtle bg + dark text.
10816
- // Neutral row uses NO `dark:` variants — the dashforgePreset()
10817
- // CSS-var swap auto-inverts the neutral palette already.
10818
- // Color rows below KEEP `dark:` variants because color palettes
10819
- // don't auto-invert: the `dark:` shift is an intentional design
10820
- // choice (darker bg + lighter text in dark mode for visual cohesion).
10821
- {
10822
- variant: 'soft',
10823
- color: 'neutral',
10824
- class: 'bg-neutral-100 text-neutral-700'
10825
- },
10826
- {
10827
- variant: 'soft',
10828
- color: 'primary',
10829
- class: 'bg-primary-100 dark:bg-primary-950 text-primary-700 dark:text-primary-300'
10830
- },
10831
- {
10832
- variant: 'soft',
10833
- color: 'success',
10834
- class: 'bg-success-100 dark:bg-success-950 text-success-700 dark:text-success-300'
10835
- },
10836
- {
10837
- variant: 'soft',
10838
- color: 'warning',
10839
- class: 'bg-warning-100 dark:bg-warning-950 text-warning-700 dark:text-warning-300'
10840
- },
10841
- {
10842
- variant: 'soft',
10843
- color: 'danger',
10844
- class: 'bg-danger-100 dark:bg-danger-950 text-danger-700 dark:text-danger-300'
10845
- },
10846
- {
10847
- variant: 'soft',
10848
- color: 'info',
10849
- class: 'bg-info-100 dark:bg-info-950 text-info-700 dark:text-info-300'
10850
- },
10851
- {
10852
- variant: 'soft',
10853
- color: 'secondary',
10854
- class: 'bg-secondary-100 dark:bg-secondary-950 text-secondary-700 dark:text-secondary-300'
10855
- },
10856
- // solid = full bg + contrasting text.
10857
- // solid neutral: both `bg-neutral-700` and `text-neutral-50` sit
10858
- // on the neutral palette, so they auto-invert together via the
10859
- // CSS-var swap — bg/text contrast is preserved in BOTH modes.
10860
- // (`text-white` is deliberately NOT used here: white does not
10861
- // auto-invert, which would flip the contrast in dark mode.)
10862
- {
10863
- variant: 'solid',
10864
- color: 'neutral',
10865
- class: 'bg-neutral-700 text-neutral-50'
10866
- },
10867
- {
10868
- variant: 'solid',
10869
- color: 'primary',
10870
- class: 'bg-primary-500 text-white'
10871
- },
10872
- {
10873
- variant: 'solid',
10874
- color: 'success',
10875
- class: 'bg-success-500 text-white'
10876
- },
10877
- {
10878
- variant: 'solid',
10879
- color: 'warning',
10880
- class: 'bg-warning-500 text-white'
10881
- },
10882
- {
10883
- variant: 'solid',
10884
- color: 'danger',
10885
- class: 'bg-danger-500 text-white'
10886
- },
10887
- {
10888
- variant: 'solid',
10889
- color: 'info',
10890
- class: 'bg-info-500 text-white'
10891
- },
10892
- {
10893
- variant: 'solid',
10894
- color: 'secondary',
10895
- class: 'bg-secondary-500 text-white'
10896
- },
10897
- // outline = border + colored text
10898
- // outline neutral: both border + text auto-invert via CSS-var swap.
10899
- {
10900
- variant: 'outline',
10901
- color: 'neutral',
10902
- class: 'border border-neutral-300 text-neutral-700'
10903
- },
10904
- {
10905
- variant: 'outline',
10906
- color: 'primary',
10907
- class: 'border border-primary-500 text-primary-700 dark:text-primary-300'
10908
- },
10909
- {
10910
- variant: 'outline',
10911
- color: 'success',
10912
- class: 'border border-success-500 text-success-700 dark:text-success-300'
10913
- },
10914
- {
10915
- variant: 'outline',
10916
- color: 'warning',
10917
- class: 'border border-warning-500 text-warning-700 dark:text-warning-300'
10918
- },
10919
- {
10920
- variant: 'outline',
10921
- color: 'danger',
10922
- class: 'border border-danger-500 text-danger-700 dark:text-danger-300'
10923
- },
10924
- {
10925
- variant: 'outline',
10926
- color: 'info',
10927
- class: 'border border-info-500 text-info-700 dark:text-info-300'
10928
- },
10929
- {
10930
- variant: 'outline',
10931
- color: 'secondary',
10932
- class: 'border border-secondary-500 text-secondary-700 dark:text-secondary-300'
10933
- }
10934
- ],
10935
- defaultVariants: {
10936
- color: 'neutral',
10937
- variant: 'soft',
10938
- size: 'sm'
10939
- }
10940
- });
10941
13321
  function RenderChip(props) {
10942
13322
  const { children, color, variant, size, className } = props;
10943
- return jsx("span", {
10944
- className: cn(chipVariants({
10945
- color,
10946
- variant,
10947
- size
10948
- }), className),
10949
- children: children
13323
+ return jsx(Chip, {
13324
+ label: children,
13325
+ color: color,
13326
+ variant: variant,
13327
+ size: size,
13328
+ className: className
10950
13329
  });
10951
13330
  }
10952
13331
 
@@ -10992,7 +13371,7 @@ function RenderChip(props) {
10992
13371
  side: "bottom",
10993
13372
  align: "end",
10994
13373
  content: jsx("ul", {
10995
- className: "flex flex-col gap-0.5 min-w-[140px]",
13374
+ className: "flex flex-col gap-0.5 min-w-[140px] list-none pl-0 m-0",
10996
13375
  role: "menu",
10997
13376
  children: visibleActions.map((action, idx)=>jsx(RowActionMenuItem, {
10998
13377
  action: action,
@@ -11812,7 +14191,7 @@ function ColumnVisibilityTrigger(props) {
11812
14191
  ]
11813
14192
  }),
11814
14193
  jsx("ul", {
11815
- className: "flex flex-col gap-0.5 max-h-72 overflow-auto",
14194
+ className: "flex flex-col gap-0.5 max-h-72 overflow-auto list-none pl-0 m-0",
11816
14195
  children: hideableCols.map((col)=>{
11817
14196
  const field = col.field;
11818
14197
  const hidden = hiddenColumns.includes(field);
@@ -12904,6 +15283,6 @@ function SearchIcon(props) {
12904
15283
  */ // ───── Components ─────
12905
15284
  /**
12906
15285
  * Package version (synced with `package.json` at publish time).
12907
- */ const VERSION = '0.11.0-beta';
15286
+ */ const VERSION = '1.1.1';
12908
15287
 
12909
- export { Accordion, AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Calendar, Checkbox, ConfirmDialogProvider, Container, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Grid, LeftNav, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, Skeleton, SnackbarProvider, Stack, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants, leftNavVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, radioGroupVariants, skeletonVariants, snackbarVariants, stackVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };
15288
+ export { AVATAR_SOFT_FALLBACK, AVATAR_TONE_FALLBACK, Accordion, Alert, AlertTitle, AppShell, AspectRatio, Autocomplete, Avatar, AvatarGroup, Badge, Box, Breadcrumbs, Button, Calendar, Card, CardActionArea, CardContent, Checkbox, Chip, ConfirmDialogProvider, Container, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Grid, ICON_BUTTON_BASE, ICON_BUTTON_SIZE_OVERRIDES, IconButton, LeftNav, Menu, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuSkeleton, MenuTrigger, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, SPINNER_COLORS, SPINNER_STROKE_WIDTH, SPINNER_TRACK_OPACITY, Skeleton, SnackbarProvider, Spinner, Stack, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, alertVariants, appShellVariants, autocompleteVariants, avatarVariants, badgeVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, chipVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants, leftNavVariants, menuVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, radioGroupVariants, skeletonVariants, snackbarVariants, spinnerVariants, stackVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };