@recursica/adapter-mui-v7 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/.storybook/commonArgTypes.ts +148 -0
  2. package/ARCHITECTURE.md +18 -0
  3. package/CHANGELOG.md +582 -0
  4. package/LICENSE +21 -0
  5. package/OVERSTYLING.md +59 -0
  6. package/README.md +70 -0
  7. package/SETUP.md +92 -0
  8. package/USAGE.md +57 -0
  9. package/dist/adapter-mui-v7.cjs +292 -0
  10. package/dist/adapter-mui-v7.cjs.map +1 -0
  11. package/dist/adapter-mui-v7.css +1 -0
  12. package/dist/adapter-mui-v7.js +40757 -0
  13. package/dist/adapter-mui-v7.js.map +1 -0
  14. package/dist/index.d.ts +2350 -0
  15. package/dist/vite.svg +1 -0
  16. package/docs/PHILOSOPHY.md +39 -0
  17. package/llms.txt +76 -0
  18. package/package.json +151 -0
  19. package/recursica_brand.json +2677 -0
  20. package/recursica_tokens.json +799 -0
  21. package/recursica_ui-kit.json +14981 -0
  22. package/recursica_variables_scoped.css +30489 -0
  23. package/src/GlobalExemptions.modules.css +7 -0
  24. package/src/Introduction.stories.tsx +184 -0
  25. package/src/OverStyling.tsx +87 -0
  26. package/src/Version.tsx +37 -0
  27. package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +107 -0
  28. package/src/components/Accordion/Accordion.module.css +362 -0
  29. package/src/components/Accordion/Accordion.stories.tsx +198 -0
  30. package/src/components/Accordion/Accordion.test.tsx +1 -0
  31. package/src/components/Accordion/Accordion.tsx +285 -0
  32. package/src/components/Accordion/USAGE.md +43 -0
  33. package/src/components/Accordion/index.ts +1 -0
  34. package/src/components/AssistiveElement/ASSISTIVEELEMENT_IMPLEMENTATION_NOTES.md +59 -0
  35. package/src/components/AssistiveElement/AssistiveElement.module.css +121 -0
  36. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +68 -0
  37. package/src/components/AssistiveElement/AssistiveElement.test.tsx +1 -0
  38. package/src/components/AssistiveElement/AssistiveElement.tsx +94 -0
  39. package/src/components/AssistiveElement/USAGE.md +36 -0
  40. package/src/components/AssistiveElement/index.ts +1 -0
  41. package/src/components/AutoComplete/AutoComplete.module.css +456 -0
  42. package/src/components/AutoComplete/AutoComplete.stories.tsx +336 -0
  43. package/src/components/AutoComplete/AutoComplete.test.tsx +1 -0
  44. package/src/components/AutoComplete/AutoComplete.tsx +266 -0
  45. package/src/components/AutoComplete/IMPLEMENTATION_NOTES.md +37 -0
  46. package/src/components/AutoComplete/USAGE.md +68 -0
  47. package/src/components/AutoComplete/index.ts +1 -0
  48. package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +36 -0
  49. package/src/components/Avatar/Avatar.module.css +336 -0
  50. package/src/components/Avatar/Avatar.stories.tsx +92 -0
  51. package/src/components/Avatar/Avatar.test.tsx +1 -0
  52. package/src/components/Avatar/Avatar.tsx +88 -0
  53. package/src/components/Avatar/USAGE.md +38 -0
  54. package/src/components/Avatar/index.ts +1 -0
  55. package/src/components/Badge/Badge.module.css +115 -0
  56. package/src/components/Badge/Badge.stories.tsx +78 -0
  57. package/src/components/Badge/Badge.test.tsx +1 -0
  58. package/src/components/Badge/Badge.tsx +61 -0
  59. package/src/components/Badge/USAGE.md +40 -0
  60. package/src/components/Badge/index.ts +1 -0
  61. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +51 -0
  62. package/src/components/Breadcrumb/Breadcrumb.module.css +51 -0
  63. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
  64. package/src/components/Breadcrumb/Breadcrumb.test.tsx +1 -0
  65. package/src/components/Breadcrumb/Breadcrumb.tsx +70 -0
  66. package/src/components/Breadcrumb/USAGE.md +42 -0
  67. package/src/components/Breadcrumb/index.ts +1 -0
  68. package/src/components/Button/BUTTON_IMPLEMENTATION_NOTES.md +123 -0
  69. package/src/components/Button/Button.module.css +413 -0
  70. package/src/components/Button/Button.stories.tsx +196 -0
  71. package/src/components/Button/Button.styleIsolation.dom.test.tsx +98 -0
  72. package/src/components/Button/Button.tsx +156 -0
  73. package/src/components/Button/USAGE.md +46 -0
  74. package/src/components/Button/index.ts +1 -0
  75. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +15 -0
  76. package/src/components/Card/Card.module.css +155 -0
  77. package/src/components/Card/Card.stories.tsx +131 -0
  78. package/src/components/Card/Card.test.tsx +1 -0
  79. package/src/components/Card/Card.tsx +202 -0
  80. package/src/components/Card/USAGE.md +57 -0
  81. package/src/components/Card/index.ts +1 -0
  82. package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +14 -0
  83. package/src/components/Checkbox/Checkbox.module.css +286 -0
  84. package/src/components/Checkbox/Checkbox.stories.tsx +97 -0
  85. package/src/components/Checkbox/Checkbox.test.tsx +1 -0
  86. package/src/components/Checkbox/Checkbox.tsx +259 -0
  87. package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
  88. package/src/components/Checkbox/CheckboxGroup.tsx +148 -0
  89. package/src/components/Checkbox/USAGE.md +36 -0
  90. package/src/components/Checkbox/index.ts +6 -0
  91. package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +102 -0
  92. package/src/components/Chip/Chip.module.css +283 -0
  93. package/src/components/Chip/Chip.stories.tsx +154 -0
  94. package/src/components/Chip/Chip.test.tsx +1 -0
  95. package/src/components/Chip/Chip.tsx +168 -0
  96. package/src/components/Chip/USAGE.md +55 -0
  97. package/src/components/Chip/index.ts +1 -0
  98. package/src/components/Container/Container.stories.tsx +118 -0
  99. package/src/components/Container/Container.test.tsx +1 -0
  100. package/src/components/Container/Container.tsx +51 -0
  101. package/src/components/Container/IMPLEMENTATION_NOTES.md +5 -0
  102. package/src/components/Container/USAGE.md +48 -0
  103. package/src/components/Container/index.ts +1 -0
  104. package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +29 -0
  105. package/src/components/DatePicker/DatePicker.icons.tsx +35 -0
  106. package/src/components/DatePicker/DatePicker.module.css +464 -0
  107. package/src/components/DatePicker/DatePicker.stories.tsx +163 -0
  108. package/src/components/DatePicker/DatePicker.test.tsx +1 -0
  109. package/src/components/DatePicker/DatePicker.tsx +229 -0
  110. package/src/components/DatePicker/USAGE.md +49 -0
  111. package/src/components/DatePicker/index.ts +1 -0
  112. package/src/components/Dropdown/BareDropdown.tsx +168 -0
  113. package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +45 -0
  114. package/src/components/Dropdown/Dropdown.icons.tsx +33 -0
  115. package/src/components/Dropdown/Dropdown.module.css +535 -0
  116. package/src/components/Dropdown/Dropdown.stories.tsx +252 -0
  117. package/src/components/Dropdown/Dropdown.test.tsx +1 -0
  118. package/src/components/Dropdown/Dropdown.tsx +319 -0
  119. package/src/components/Dropdown/USAGE.md +68 -0
  120. package/src/components/Dropdown/index.ts +1 -0
  121. package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +153 -0
  122. package/src/components/FileInput/FileInput.module.css +268 -0
  123. package/src/components/FileInput/FileInput.stories.tsx +313 -0
  124. package/src/components/FileInput/FileInput.test.tsx +1 -0
  125. package/src/components/FileInput/FileInput.tsx +415 -0
  126. package/src/components/FileInput/USAGE.md +145 -0
  127. package/src/components/FileInput/index.ts +1 -0
  128. package/src/components/FileUpload/FILEUPLOAD_IMPLEMENTATION_NOTES.md +249 -0
  129. package/src/components/FileUpload/FileUpload.module.css +200 -0
  130. package/src/components/FileUpload/FileUpload.stories.tsx +362 -0
  131. package/src/components/FileUpload/FileUpload.test.tsx +1 -0
  132. package/src/components/FileUpload/FileUpload.tsx +352 -0
  133. package/src/components/FileUpload/USAGE.md +199 -0
  134. package/src/components/FileUpload/index.ts +1 -0
  135. package/src/components/Flex/Flex.stories.tsx +128 -0
  136. package/src/components/Flex/Flex.test.tsx +1 -0
  137. package/src/components/Flex/Flex.tsx +89 -0
  138. package/src/components/Flex/USAGE.md +41 -0
  139. package/src/components/Flex/index.ts +1 -0
  140. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  141. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +118 -0
  142. package/src/components/FormControlLayout/FormControlLayout.test.tsx +1 -0
  143. package/src/components/FormControlLayout/FormControlLayout.tsx +72 -0
  144. package/src/components/FormControlLayout/USAGE.md +40 -0
  145. package/src/components/FormControlLayout/index.ts +1 -0
  146. package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +35 -0
  147. package/src/components/FormControlWrapper/FormControlWrapper.module.css +87 -0
  148. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +129 -0
  149. package/src/components/FormControlWrapper/FormControlWrapper.test.tsx +1 -0
  150. package/src/components/FormControlWrapper/FormControlWrapper.tsx +149 -0
  151. package/src/components/FormControlWrapper/USAGE.md +40 -0
  152. package/src/components/FormControlWrapper/index.ts +1 -0
  153. package/src/components/Grid/Grid.module.css +72 -0
  154. package/src/components/Grid/Grid.stories.tsx +161 -0
  155. package/src/components/Grid/Grid.test.tsx +1 -0
  156. package/src/components/Grid/Grid.tsx +155 -0
  157. package/src/components/Grid/IMPLEMENTATION_NOTES.md +24 -0
  158. package/src/components/Grid/USAGE.md +53 -0
  159. package/src/components/Grid/index.ts +1 -0
  160. package/src/components/Group/Group.stories.tsx +120 -0
  161. package/src/components/Group/Group.test.tsx +1 -0
  162. package/src/components/Group/Group.tsx +116 -0
  163. package/src/components/Group/USAGE.md +41 -0
  164. package/src/components/Group/index.ts +1 -0
  165. package/src/components/Heading/Heading.stories.tsx +50 -0
  166. package/src/components/Heading/Heading.test.tsx +1 -0
  167. package/src/components/Heading/Heading.tsx +28 -0
  168. package/src/components/Heading/USAGE.md +36 -0
  169. package/src/components/Heading/index.ts +1 -0
  170. package/src/components/HoverCard/HoverCard.module.css +107 -0
  171. package/src/components/HoverCard/HoverCard.stories.tsx +172 -0
  172. package/src/components/HoverCard/HoverCard.test.tsx +1 -0
  173. package/src/components/HoverCard/HoverCard.tsx +145 -0
  174. package/src/components/HoverCard/USAGE.md +45 -0
  175. package/src/components/HoverCard/index.ts +1 -0
  176. package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +27 -0
  177. package/src/components/Label/Label.module.css +169 -0
  178. package/src/components/Label/Label.stories.tsx +131 -0
  179. package/src/components/Label/Label.test.tsx +1 -0
  180. package/src/components/Label/Label.tsx +96 -0
  181. package/src/components/Label/USAGE.md +36 -0
  182. package/src/components/Label/index.ts +1 -0
  183. package/src/components/Layer/USAGE.md +50 -0
  184. package/src/components/Link/LINK_IMPLEMENTATION_NOTES.md +9 -0
  185. package/src/components/Link/Link.module.css +105 -0
  186. package/src/components/Link/Link.stories.tsx +58 -0
  187. package/src/components/Link/Link.test.tsx +1 -0
  188. package/src/components/Link/Link.tsx +45 -0
  189. package/src/components/Link/USAGE.md +40 -0
  190. package/src/components/Link/index.ts +1 -0
  191. package/src/components/Loader/LOADER_IMPLEMENTATION_NOTES.md +25 -0
  192. package/src/components/Loader/Loader.animate.dom.test.tsx +72 -0
  193. package/src/components/Loader/Loader.module.css +175 -0
  194. package/src/components/Loader/Loader.stories.tsx +123 -0
  195. package/src/components/Loader/Loader.test.tsx +1 -0
  196. package/src/components/Loader/Loader.tsx +72 -0
  197. package/src/components/Loader/USAGE.md +42 -0
  198. package/src/components/Loader/index.ts +1 -0
  199. package/src/components/Menu/IMPLEMENTATION_NOTES.md +5 -0
  200. package/src/components/Menu/Menu.module.css +283 -0
  201. package/src/components/Menu/Menu.stories.tsx +405 -0
  202. package/src/components/Menu/Menu.test.tsx +1 -0
  203. package/src/components/Menu/Menu.tsx +103 -0
  204. package/src/components/Menu/USAGE.md +53 -0
  205. package/src/components/Menu/index.ts +1 -0
  206. package/src/components/Modal/MODAL_IMPLEMENTATION_NOTES.md +31 -0
  207. package/src/components/Modal/Modal.module.css +302 -0
  208. package/src/components/Modal/Modal.stories.tsx +105 -0
  209. package/src/components/Modal/Modal.test.tsx +1 -0
  210. package/src/components/Modal/Modal.tsx +231 -0
  211. package/src/components/Modal/USAGE.md +40 -0
  212. package/src/components/Modal/index.ts +1 -0
  213. package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +93 -0
  214. package/src/components/NumberInput/NumberInput.module.css +303 -0
  215. package/src/components/NumberInput/NumberInput.stories.tsx +96 -0
  216. package/src/components/NumberInput/NumberInput.test.tsx +1 -0
  217. package/src/components/NumberInput/NumberInput.tsx +273 -0
  218. package/src/components/NumberInput/USAGE.md +36 -0
  219. package/src/components/NumberInput/index.ts +1 -0
  220. package/src/components/Pagination/IMPLEMENTATION_NOTES.md +40 -0
  221. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  222. package/src/components/Pagination/Pagination.module.css +197 -0
  223. package/src/components/Pagination/Pagination.stories.tsx +48 -0
  224. package/src/components/Pagination/Pagination.test.tsx +1 -0
  225. package/src/components/Pagination/Pagination.tsx +123 -0
  226. package/src/components/Pagination/USAGE.md +36 -0
  227. package/src/components/Pagination/index.ts +1 -0
  228. package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +133 -0
  229. package/src/components/Panel/Panel.module.css +231 -0
  230. package/src/components/Panel/Panel.stories.tsx +227 -0
  231. package/src/components/Panel/Panel.test.tsx +1 -0
  232. package/src/components/Panel/Panel.tsx +186 -0
  233. package/src/components/Panel/USAGE.md +46 -0
  234. package/src/components/Panel/index.ts +1 -0
  235. package/src/components/Popover/IMPLEMENTATION_NOTES.md +22 -0
  236. package/src/components/Popover/Popover.module.css +118 -0
  237. package/src/components/Popover/Popover.stories.tsx +133 -0
  238. package/src/components/Popover/Popover.test.tsx +1 -0
  239. package/src/components/Popover/Popover.tsx +267 -0
  240. package/src/components/Popover/USAGE.md +69 -0
  241. package/src/components/Popover/index.ts +1 -0
  242. package/src/components/Radio/RADIO_IMPLEMENTATION_NOTES.md +12 -0
  243. package/src/components/Radio/Radio.module.css +247 -0
  244. package/src/components/Radio/Radio.stories.tsx +93 -0
  245. package/src/components/Radio/Radio.test.tsx +1 -0
  246. package/src/components/Radio/Radio.tsx +223 -0
  247. package/src/components/Radio/RadioGroup.stories.tsx +117 -0
  248. package/src/components/Radio/RadioGroup.tsx +122 -0
  249. package/src/components/Radio/USAGE.md +36 -0
  250. package/src/components/Radio/index.ts +2 -0
  251. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  252. package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
  253. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +163 -0
  254. package/src/components/ReadOnlyField/ReadOnlyField.test.tsx +1 -0
  255. package/src/components/ReadOnlyField/ReadOnlyField.tsx +108 -0
  256. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  257. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  258. package/src/components/ReadOnlyField/USAGE.md +36 -0
  259. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  260. package/src/components/ReadOnlyField/index.ts +2 -0
  261. package/src/components/RecursicaThemeProvider/USAGE.md +60 -0
  262. package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +14 -0
  263. package/src/components/SegmentedControl/SegmentedControl.module.css +201 -0
  264. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +106 -0
  265. package/src/components/SegmentedControl/SegmentedControl.test.tsx +1 -0
  266. package/src/components/SegmentedControl/SegmentedControl.tsx +149 -0
  267. package/src/components/SegmentedControl/USAGE.md +49 -0
  268. package/src/components/SegmentedControl/index.ts +1 -0
  269. package/src/components/Slider/IMPLEMENTATION_NOTES.md +55 -0
  270. package/src/components/Slider/Slider.module.css +635 -0
  271. package/src/components/Slider/Slider.stories.tsx +282 -0
  272. package/src/components/Slider/Slider.test.tsx +1 -0
  273. package/src/components/Slider/Slider.tsx +435 -0
  274. package/src/components/Slider/USAGE.md +56 -0
  275. package/src/components/Slider/index.ts +1 -0
  276. package/src/components/Stack/Stack.stories.tsx +108 -0
  277. package/src/components/Stack/Stack.test.tsx +1 -0
  278. package/src/components/Stack/Stack.tsx +43 -0
  279. package/src/components/Stack/USAGE.md +41 -0
  280. package/src/components/Stack/index.ts +1 -0
  281. package/src/components/Stepper/IMPLEMENTATION_NOTES.md +120 -0
  282. package/src/components/Stepper/Stepper.module.css +474 -0
  283. package/src/components/Stepper/Stepper.stories.tsx +157 -0
  284. package/src/components/Stepper/Stepper.test.tsx +1 -0
  285. package/src/components/Stepper/Stepper.tsx +240 -0
  286. package/src/components/Stepper/USAGE.md +45 -0
  287. package/src/components/Stepper/index.ts +1 -0
  288. package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +147 -0
  289. package/src/components/Switch/Switch.module.css +346 -0
  290. package/src/components/Switch/Switch.stories.tsx +114 -0
  291. package/src/components/Switch/Switch.test.tsx +1 -0
  292. package/src/components/Switch/Switch.tsx +294 -0
  293. package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
  294. package/src/components/Switch/SwitchGroup.tsx +156 -0
  295. package/src/components/Switch/USAGE.md +36 -0
  296. package/src/components/Switch/index.ts +2 -0
  297. package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +75 -0
  298. package/src/components/Table/Table.module.css +462 -0
  299. package/src/components/Table/Table.stories.tsx +154 -0
  300. package/src/components/Table/Table.test.tsx +1 -0
  301. package/src/components/Table/Table.tsx +250 -0
  302. package/src/components/Table/USAGE.md +90 -0
  303. package/src/components/Table/index.ts +1 -0
  304. package/src/components/Tabs/IMPLEMENTATION_NOTES.md +76 -0
  305. package/src/components/Tabs/Tabs.module.css +801 -0
  306. package/src/components/Tabs/Tabs.stories.tsx +174 -0
  307. package/src/components/Tabs/Tabs.test.tsx +1 -0
  308. package/src/components/Tabs/Tabs.tsx +103 -0
  309. package/src/components/Tabs/USAGE.md +61 -0
  310. package/src/components/Tabs/index.ts +1 -0
  311. package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +10 -0
  312. package/src/components/Text/Text.stories.tsx +75 -0
  313. package/src/components/Text/Text.test.tsx +1 -0
  314. package/src/components/Text/Text.tsx +19 -0
  315. package/src/components/Text/USAGE.md +40 -0
  316. package/src/components/Text/index.ts +1 -0
  317. package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +42 -0
  318. package/src/components/TextArea/TextArea.module.css +203 -0
  319. package/src/components/TextArea/TextArea.stories.tsx +94 -0
  320. package/src/components/TextArea/TextArea.test.tsx +1 -0
  321. package/src/components/TextArea/TextArea.tsx +162 -0
  322. package/src/components/TextArea/USAGE.md +38 -0
  323. package/src/components/TextArea/index.ts +1 -0
  324. package/src/components/TextField/TextField.module.css +271 -0
  325. package/src/components/TextField/TextField.stories.tsx +162 -0
  326. package/src/components/TextField/TextField.test.tsx +1 -0
  327. package/src/components/TextField/TextField.tsx +182 -0
  328. package/src/components/TextField/USAGE.md +38 -0
  329. package/src/components/TextField/index.ts +1 -0
  330. package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +89 -0
  331. package/src/components/TimePicker/TimePicker.module.css +262 -0
  332. package/src/components/TimePicker/TimePicker.stories.tsx +155 -0
  333. package/src/components/TimePicker/TimePicker.test.tsx +1 -0
  334. package/src/components/TimePicker/TimePicker.tsx +277 -0
  335. package/src/components/TimePicker/USAGE.md +61 -0
  336. package/src/components/TimePicker/index.ts +1 -0
  337. package/src/components/Timeline/IMPLEMENTATION_NOTES.md +42 -0
  338. package/src/components/Timeline/Timeline.module.css +363 -0
  339. package/src/components/Timeline/Timeline.stories.tsx +128 -0
  340. package/src/components/Timeline/Timeline.test.tsx +1 -0
  341. package/src/components/Timeline/Timeline.tsx +90 -0
  342. package/src/components/Timeline/TimelineItem.tsx +95 -0
  343. package/src/components/Timeline/USAGE.md +41 -0
  344. package/src/components/Timeline/index.ts +2 -0
  345. package/src/components/Toast/TOAST_IMPLEMENTATION_NOTES.md +37 -0
  346. package/src/components/Toast/Toast.module.css +193 -0
  347. package/src/components/Toast/Toast.stories.tsx +67 -0
  348. package/src/components/Toast/Toast.test.tsx +1 -0
  349. package/src/components/Toast/Toast.tsx +121 -0
  350. package/src/components/Toast/USAGE.md +42 -0
  351. package/src/components/Toast/index.ts +1 -0
  352. package/src/components/Tooltip/Tooltip.module.css +83 -0
  353. package/src/components/Tooltip/Tooltip.stories.tsx +165 -0
  354. package/src/components/Tooltip/Tooltip.test.tsx +1 -0
  355. package/src/components/Tooltip/Tooltip.tsx +78 -0
  356. package/src/components/Tooltip/USAGE.md +40 -0
  357. package/src/components/Tooltip/index.ts +1 -0
  358. package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +140 -0
  359. package/src/components/TransferList/TransferList.module.css +174 -0
  360. package/src/components/TransferList/TransferList.stories.tsx +121 -0
  361. package/src/components/TransferList/TransferList.test.tsx +1 -0
  362. package/src/components/TransferList/TransferList.tsx +419 -0
  363. package/src/components/TransferList/USAGE.md +77 -0
  364. package/src/components/TransferList/index.ts +1 -0
  365. package/src/components/Tree/IMPLEMENTATION_NOTES.md +53 -0
  366. package/src/components/Tree/Tree.module.css +272 -0
  367. package/src/components/Tree/Tree.stories.tsx +104 -0
  368. package/src/components/Tree/Tree.test.tsx +1 -0
  369. package/src/components/Tree/Tree.tsx +298 -0
  370. package/src/components/Tree/USAGE.md +99 -0
  371. package/src/components/Tree/index.ts +1 -0
  372. package/src/components/Typography/TYPOGRAPHY_IMPLEMENTATION_NOTES.md +17 -0
  373. package/src/components/Typography/Typography.module.css +13 -0
  374. package/src/components/Typography/Typography.test.tsx +1 -0
  375. package/src/components/Typography/Typography.tsx +43 -0
  376. package/src/components/Typography/USAGE.md +40 -0
  377. package/src/components/Typography/index.ts +1 -0
  378. package/src/components/index.ts +50 -0
  379. package/src/env.d.ts +7 -0
  380. package/src/index.ts +194 -0
  381. package/src/types/index.ts +9 -0
  382. package/src/types/scss-modules.d.ts +9 -0
  383. package/src/utils/ColorSchemeWrapper.tsx +23 -0
  384. package/src/utils/RequireAccessibleLabel.ts +1 -0
  385. package/src/utils/copyToClipboard.ts +36 -0
  386. package/src/utils/filterStylingProps.test.ts +85 -0
  387. package/src/utils/filterStylingProps.ts +143 -0
  388. package/src/utils/index.ts +1 -0
  389. package/src/utils/renderRichOption.tsx +51 -0
@@ -0,0 +1,53 @@
1
+ # Tree Implementation Notes
2
+
3
+ - **New dependency: `@mui/x-tree-view`.** Neither `@mui/material` nor `@mui/lab` (already a peer dep, used for Timeline) ships a tree component. `@mui/x-tree-view`'s free/MIT community edition (`RichTreeView` + `TreeItem` building blocks) was added as an optional peer + dev dependency (mirroring how `@mui/lab` is declared), confirmed with the user before adding since it's a new external dependency, not a mechanical port.
4
+
5
+ - **`RichTreeView` over `SimpleTreeView`.** `SimpleTreeView` is composable/children-based (you hand-write nested `<TreeItem>` JSX); `RichTreeView` is data-driven (`items` prop), matching Mantine's `data`-prop shape and our `RecursicaTreeProps.data` API far more directly. No conversion needed for `id`/`children` (bridged via `getItemId`), only `getItemLabel`.
6
+
7
+ - **Fully custom `slots.item` (`CustomTreeItem`), not the default `TreeItem`.** Every visual property comes from `Tree.module.css` rather than `@mui/x-tree-view`'s own default styles (which apply real theme palette colors for hover/selected/focused states that would otherwise fight our tokens). `CustomTreeItem` is built from the officially-documented low-level building blocks (`useTreeItem`, `TreeItemProvider`, `TreeItemRoot`, `TreeItemContent`, `TreeItemIconContainer`, `TreeItemGroupTransition`, `TreeItemLabel`, `TreeItemIcon`) — the same pattern MUI X's own docs use for "rich"/custom tree items — rather than rebuilding keyboard/focus/ARIA handling from scratch.
8
+
9
+ - **`getItemLabel` only ever sees a string; the real `React.ReactNode` label comes from `useTreeItemModel`.** `RichTreeView`'s internal model requires `getItemLabel(item): string` (used for its own a11y/typeahead search), so a non-string `RecursicaTreeNode.label` is coerced to `item.value` there — this string is _never actually rendered_. Inside `CustomTreeItem`, `useTreeItemModel<RecursicaTreeNode>(itemId)` retrieves the **original** untransformed node (real `label: ReactNode` included), which is what actually gets rendered in `<TreeItemLabel>`. This is the documented MUI X mechanism for rich/custom tree item content — not a workaround.
10
+
11
+ - **`TreeItemContent` requires a `status` prop.** Its styled-component variant system reads `status: UseTreeItemStatus` directly (not just via `getContentProps()`'s spread) — omitting it is a compile error, not just a missing visual detail.
12
+
13
+ - **Indentation: preserve MUI's own depth formula, override only the base.** MUI computes each row's `padding-left` as `calc(<base> + --TreeView-itemChildrenIndentation * --TreeView-itemDepth)`, with `--TreeView-itemChildrenIndentation` set as an **inline style** on the root `<ul>` (see `useTreeViewRootProps` in `@mui/x-tree-view/internals`). `Tree.module.css` overrides that CSS variable with `!important` (same category of override as the mantine-adapter's `--level-offset`, itself modeled on this adapter's own Timeline `--mantine-spacing-xl` / `--tl-line-width` precedent — inline styles beat plain stylesheet rules regardless of specificity, so `!important` is required, not optional). `.row`'s own `padding-left` then reuses the same `calc(... + --TreeView-itemChildrenIndentation * --TreeView-itemDepth)` formula, just substituting our `horizontal-padding` token as the base instead of MUI's theme spacing — so per-level indentation keeps working, only its amount changes.
14
+
15
+ - **No `!important` needed for color/hover/selected/disabled overrides.** Unlike the CSS-variable overrides above, these come from `@mui/x-tree-view`'s own Emotion-generated _stylesheet_ rules (not inline styles). Because this app's `<StyledEngineProvider injectFirst>` setup (required per `SETUP.md`, already configured in `.storybook/preview.tsx`) injects MUI's baseline styles before our CSS Modules, our classes win via normal DOM source order — matching `COMPONENT_DEV_GUIDE.md`'s explicit "no `!important`" rule for this category, and the existing `Button`/`Menu` precedent in this adapter.
16
+
17
+ - **`expandOnClick`/`selectOnClick` are approximated, not exact.** Mantine's `Tree` has two independent boolean toggles for these. `@mui/x-tree-view` only has `expansionTrigger: 'content' | 'iconContainer'` (does clicking the row content expand it, or only the icon container?) and `disableSelection` (turns off selection everywhere, not just on a content click). Mapping used: `expansionTrigger = expandOnClick ? "content" : "iconContainer"`, `disableSelection = !selectOnClick`. The specific combination "clicking anywhere expands, but nothing is ever selectable via click" has no exact MUI equivalent beyond `disableSelection` — documented here rather than silently diverging without explanation.
18
+
19
+ - **Deliberately not implemented (no tokens back them):** checkbox/indeterminate node state (`@mui/x-tree-view` supports `checkboxSelection`, but the Figma UI Kit's `tree` tokens only define `selected`/`unselected`, not a checked state) and per-node `disabled` (no token, and no `disabled` field in `RecursicaTreeNode`). Matches the mantine-adapter implementation's same decision.
20
+
21
+ - **RESOLVED (2026-08-08) — KNOWN GAP (2026-08-06): Forge's own preview shows a red chevron with more chevron-to-label spacing than what's currently implemented here.** Explained by the Button swap below: Forge's chevron is Button's own "text" variant color (Recursica's red/alert-adjacent brand color), not a `tree`-namespace icon-color token — there never was a missing token to find. Re-verified against both the compiled `recursica_variables_scoped.css` and the raw `recursica_ui-kit.json` at the time: the `tree` schema has no `icon-color` property at all, and `button-node-gap` (used as `.row`'s `gap`) resolves to exactly 4px — confirmed not a mapping bug on our side, just the wrong mental model (a hand-drawn glyph instead of an actual Button).
22
+
23
+ - **Keyboard nav (Matt Massey, 2026-08-08): arrow up/down already worked — only the focus ring was missing.** `@mui/x-tree-view`'s own `useTreeItem` already implements full WAI-ARIA roving-tabindex keyboard navigation — confirmed via Playwright that focus genuinely moves between rows on `ArrowDown`. The reason it looked broken: DOM focus lands on `.node` (the same `<li role="treeitem">` `TreeItemRoot` renders as), and `.row`'s only focus-related rule reset MUI's own focused-state _background color_ back to the unselected default — no replacement focus indicator was ever added. Fixed by adding the same `:focus-visible` box-shadow ring every other focusable component uses, keyed off `.node:focus-visible` since that's where DOM focus actually lands. Originally drawn on `.row` (the whole button+label box); see the next entry for why it now targets `.label` only.
24
+
25
+ - **Chevron → `Button` (Matt Massey, 2026-08-08).** Forge's real design uses an actual `Button` (`variant="text"` `size="small"`, icon-only) for the expand/collapse chevron, not a hand-drawn glyph — see the resolved "KNOWN GAP" above. `ExpandGlyph`/`CollapseGlyph`/`EndGlyph` (the three `slots.expandIcon`/`collapseIcon`/`endIcon` components MUI's `TreeItemIcon` picks between based on row state) now all delegate to a shared `ExpandToggleButton`, which renders a real `<Button icon={<ChevronGlyph/>} tabIndex={-1} aria-hidden="true" .../>`:
26
+
27
+ - **Doesn't reopen any nested-focusable-element concern**: the embedded Button is never independently focusable or tab-stoppable (`tabIndex={-1}`) and is hidden from assistive tech (`aria-hidden`) — the row (`.node`) stays the single focusable/interactive element. Confirmed MUI's `ButtonBase` doesn't call `stopPropagation()` on click, so clicking the chevron still bubbles up to `TreeItemContent`'s existing click handling and triggers expand/collapse exactly as before — no `onClick` needed on the embedded Button itself.
28
+ - **Rendered for every row, including leaves** (`EndGlyph` → `ExpandToggleButton hidden`), so every row reserves identical layout space; a `.expandButtonHidden { visibility: hidden; }` class hides it on leaf rows without needing to duplicate Button's own size tokens for a placeholder. `.iconContainer`'s old hardcoded `width: 1em` (sized for the previous plain-SVG glyph) was removed so the real Button can size itself from its own tokens.
29
+ - **Rotation targets the glyph directly, not Button's internals**: `ChevronGlyph`'s `<svg>` carries its own `styles.chevron`/`styles.chevronExpanded` classes directly, not reached through Button's DOM at all — `Button`'s own `.iconWrapper` isn't reachable from `Tree.module.css` in the first place (CSS Modules don't expose a stable cross-file class name for it, unlike MUI's own stable `Mui*` global classes used elsewhere in this file).
30
+ - **Focus ring narrowed to `.label` only, excluding the Button** (Matt: "the focus ring for an item should be around just the node, not including the chevron button" / "the chevron button should not have a focus state"). `.label` was given `flex: 1 1 auto; height: 100%` so the ring still reads as a clean box covering the row's remaining width, not a tight text-only outline.
31
+ - **Found and fixed a real, separate bug while embedding this**: `Button.tsx` (both adapters) explicitly sets `className={finalClass}` (merging `styles.root` with any caller className) but then spreads `{...sanitizedProps}` _after_ it — and `sanitizedProps` still contains the original, unmodified `className` key, since it was only _read_, never deleted. The later spread silently overwrote `finalClass` with just the caller's own class whenever one was passed (e.g. `styles.expandButton` here) — exact same bug class as this adapter's own `Dropdown.tsx`/`BareDropdown.tsx` had. This was fully visible here: the chevron rendered in MUI's own default blue (`MuiButton-colorPrimary`), since `Button-module__root` — and therefore every `[data-variant]` color rule — was missing from the DOM entirely. Confirmed mantine-adapter had the identical bug in its own `Button.tsx`, just masked there by a separate `classNames={{root: ...}}` object prop unaffected by the overwrite — fixed in both regardless.
32
+
33
+ - **Expand/collapse and select made fully independent (Matt Massey, 2026-08-10), superseding the "approximated, not exact" `expandOnClick`/`selectOnClick` mapping described above.** Matt clarified the real requirement: the button must be the _only_ way to expand/collapse on click, a row click must _only_ select, `Enter`/`Space` must _only_ select (never expand, even on a node with children), and `ArrowLeft`/`ArrowRight` must _only_ expand/collapse (never select) — letting a user toggle a subtree open without ever changing selection, and vice versa. Removed `expandOnClick`/`selectOnClick` entirely (per point 5 of that request), since the old approximation problem disappears once the pattern is fixed rather than configurable.
34
+
35
+ - **Click**: `expansionTrigger` is now unconditionally `"iconContainer"` (previously `expandOnClick ? "content" : "iconContainer"`). That alone wasn't enough: `@mui/x-tree-view`'s `createContentHandleClick` (wired onto `TreeItemContent`/`.row`) calls `interactions.handleSelection` on _any_ click that reaches it — including a click on the icon container, since it's a DOM descendant of the content and the icon container's own click handler never stops propagation. `CustomTreeItem` now wraps `getIconContainerProps()`'s `onClick` with `event.stopPropagation()` before delegating to it, so an icon-container click still expands/collapses (via the library's own `createIconContainerHandleClick`) but never bubbles up to also select.
36
+ - **Enter**: `@mui/x-tree-view`'s own keyboard plugin expands an expandable node on `Enter` and only falls back to selecting when the node has no children — not what we need (`Enter` must always select). `Space` already selects unconditionally by default, so it needed no change. Overrode just `Enter` via `getRootProps({..., onKeyDown: handleRootKeyDown})`: `useTreeItem`'s `createRootHandleKeyDown` runs any externally-supplied `onKeyDown` _before_ its own internal handling and skips that internal handling entirely if the external handler sets `event.defaultMuiPrevented = true` — the library's documented extension point for exactly this kind of override, not an undocumented hack. `handleRootKeyDown` calls `interactions.handleSelection` (from `useTreeItemUtils`, the same hook `useTreeItem` uses internally) directly. `ArrowLeft`/`ArrowRight` needed no changes — the built-in handling already only calls `store.expansion.setItemExpansion`, never touches selection.
37
+ - **Background/focus scoped to the label, not the whole node** (Matt: "The focus/background color should be on the node's label, not the entire node"): moved every visual property (padding, border, font, unselected/selected colors, hover overlay) off `.row` and onto `.label` — `.row` is now a plain flex layout container (indentation + `button-node-gap` only). `data-selected`/`data-focused` are still only set on `.row` (via `getContentProps()`), so the selected-state rule now reads `.row[data-selected] .label` (a descendant selector) instead of styling `.row` directly; same for the hover overlay (`.row:hover .label::after`). The focus ring (added in the entry above) already targeted `.label` only, so it needed no further change.
38
+
39
+ - **Three follow-on fixes to the label-scoping work above (Matt Massey, 2026-08-10):**
40
+
41
+ - **Selected/hover chip was full-row width, not label width, in both adapters.** `.label` had `flex: 1 1 auto` — the `1` flex-grow stretched it to fill `.row`'s entire remaining width (icon container aside), so the highlighted chip visually covered the whole row even though the _properties_ were correctly scoped to `.label`. Changed to `flex: 0 1 auto`, but that alone wasn't enough here (unlike mantine-adapter): `@mui/x-tree-view`'s own `.MuiTreeItem-label` sets a literal `width: 100%`, which a flex-basis change can't beat since it's a direct `width` declaration, not a sizing default. Added an explicit `width: auto` to `.label` to override it.
42
+ - **Focusing an expanded parent drew the ring on every descendant label too, in both adapters.** `.node:focus-visible .label` is a plain descendant selector — every child node's `.label` (inside the nested `.subtree` `<ul>`) is _also_ a descendant of the focused parent's `.node` `<li>`, at any depth, so the ring matched all of them at once. Fixed by scoping to `.node:focus-visible > .row .label`: `.row` is always a direct child of its own `.node`, but never of an ancestor `.node` (those reach it through the intervening `.subtree` `<ul>` and nested `<li>` instead) — the `>` combinator excludes every one of them.
43
+ - **MUI-only: a leftover default selected background was still showing through.** `@mui/x-tree-view`'s own `TreeItemContent` ships a built-in selected tint (`rgba(25, 118, 210, 0.08)`, and a separate, higher-specificity `rgba(25, 118, 210, 0.2)` for the `[data-selected][data-focused]` combination specifically) that the earlier `.row:hover, .row[data-focused] { background-color: transparent; }` neutralizer never covered. Added `.row[data-selected]` to that neutralizer — but the `[data-selected][data-focused]` compound needed its _own_ explicit rule too: MUI's equivalent compound selector has higher specificity (two attribute selectors plus its own class) than a single-attribute `.row[data-selected]` rule, so it kept winning regardless of source order until matched with an equally-specific `.row[data-selected][data-focused]` rule on our side.
44
+
45
+ - **Whole-tree `disabled` (Matt Massey, 2026-08-10), added to support a `Disabled` story.** Unlike mantine-adapter (which has zero `disabled` concept anywhere in its `Tree` API — see its own note on this), `@mui/x-tree-view` already had per-item `disabled` plumbing sitting mostly dormant here: `CustomTreeItem` already destructured `disabled` off `UseTreeItemParameters`, and `.row[data-disabled] { opacity: ...; cursor: auto; }` already existed in `Tree.module.css` — just with no way for a caller to actually set it, since `RecursicaTreeNode` has no `disabled` field (and still doesn't; per-node disabling remains unexposed, same reasoning as the existing "Deliberately not implemented" entry above).
46
+
47
+ - **Implementation**: `isItemDisabled={disabled ? () => true : undefined}` passed to `<RichTreeView>` — marking every item disabled at once, reusing the library's own per-item mechanism rather than inventing a parallel one. `interactions.handleExpansion`/`handleSelection` (used by both the icon container's click handling and our own `Enter`-key override) already check `status.disabled` internally and no-op, so nothing in `CustomTreeItem` needed an extra guard.
48
+ - **Visual**: the pre-existing `.row[data-disabled]` rule now actually activates. Verified a pre-selected node's chip stays visible underneath the dimming (`.row[data-selected] .label` and `.row[data-disabled]` are independent, non-conflicting selectors — one styles `.label`, the other dims the whole `.row` via `opacity`), per Matt's ask to check that combination.
49
+ - **Found while verifying**: disabled rows still showed the hover tint (`.label::after` opacity) on mouse-over — MUI's own disabled checks block the actual select/expand _action_ on click, but `:hover` is pure CSS with nothing stopping it. Added `pointer-events: none` to `.row[data-disabled]`, matching mantine-adapter's `.root[data-disabled]` (tree-wide there; per-row here, since MUI's disabled state is inherently per-item even when every item is disabled at once by the same whole-tree prop).
50
+
51
+ - **`ExpandToggleButton` could still steal DOM focus despite `tabIndex={-1}` (Matt Massey, 2026-08-24), caught via a live browser console warning, in both adapters.** `tabIndex={-1}`/`aria-hidden` only removes the embedded chevron `Button` from the sequential tab order; it doesn't stop a native `<button>` from receiving DOM focus on a direct mouse click on the chevron, which browsers still do regardless of `tabIndex`. Since the button also has `aria-hidden="true"`, clicking it left an aria-hidden element holding focus — flagged by Chrome as "Blocked aria-hidden on an element because its descendant retained focus." Fixed with `onMouseDown={(event) => event.preventDefault()}` on the `Button` inside `ExpandToggleButton`: this suppresses the browser's default click-focuses-the-target behavior without affecting expand/collapse (still driven entirely by `TreeItemIconContainer`'s own click handling, untouched by this). Same fix applied to mantine-adapter's embedded chevron `Button`.
52
+
53
+ - **Chevron/label gap was actually negative (source-of-truth audit, 2026-08-30).** `.row`'s flex `gap` (`button-node-gap` token) measured correctly as 4px between `.iconContainer` and `.label` via `getComputedStyle`, but the _rendered_ gap between the visible chevron button and the label text was -4px (an 8px overlap) per live `getBoundingClientRect()` — mantine's equivalent measured the correct +4px. Root cause: MUI's own `.MuiTreeItem-iconContainer` hardcodes `width: 16px` (sized for a plain icon glyph); our expand `Button` is 32px and doesn't shrink to fit, so it overflowed its 16px flex-box footprint by 8px on each side while the _container's_ box (the actual flex gap participant) stayed narrow. Fixed with `width: auto` on `.iconContainer` so it sizes to its real (button) content instead of MUI's icon-sized default — verified gap is now +4px on every row, matching mantine exactly.
@@ -0,0 +1,272 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_disabled
4
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
5
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
6
+ * recursica-allow-brand: --recursica_brand_states_focus_color
7
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
8
+ * recursica-allow-brand: --recursica_brand_states_hover_color
9
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
10
+ */
11
+
12
+ /* HARDCODED VALUES:
13
+ * - list-style/margin/padding resets on .root/.subtree/.node: layout resets, no corresponding
14
+ * design tokens (structural, not visual design values).
15
+ * - .chevron rotation timing (150ms ease): no dedicated timing token exists for Tree; the glyph
16
+ * itself is sized/colored entirely by Button's own "text"/"small" tokens now, not a hand-drawn
17
+ * size/color like the previous plain-SVG implementation.
18
+ * - Hover overlay uses the generic --recursica_brand_states_hover_* tokens (same technique as
19
+ * Menu/Accordion/Button): Tree has no component-specific hover tokens of its own.
20
+ * - .row's padding-left keeps MUI's own depth-based indentation formula (indent * depth), just
21
+ * without the horizontal-padding base — that base now lives on .label instead (see below).
22
+ *
23
+ * NOTE: the row's box model (padding/border/font/color/background) lives entirely on `.label`,
24
+ * not `.row` — the highlighted "chip" (hover/selected/focus) covers only the label, never the
25
+ * expand button. `.row` itself is a plain flex layout container (indentation + button-node-gap).
26
+ */
27
+
28
+ .root {
29
+ margin: 0;
30
+ padding: 0;
31
+ list-style: none;
32
+ outline: 0;
33
+ max-width: var(--recursica_ui-kit_components_tree_properties_max-width);
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--recursica_ui-kit_components_tree_properties_item-gap);
37
+
38
+ /* MUI sets this as an inline style on the root <ul> (see useTreeViewRootProps in
39
+ @mui/x-tree-view). Override with !important to reliably beat it regardless of stylesheet
40
+ injection order (same category of override as mantine-adapter's --level-offset, itself
41
+ modeled on Timeline's --mantine-spacing-xl / --tl-line-width). */
42
+ --TreeView-itemChildrenIndentation: var(
43
+ --recursica_ui-kit_components_tree_properties_indent
44
+ ) !important;
45
+ }
46
+
47
+ .subtree {
48
+ margin: 0;
49
+ padding: 0;
50
+ list-style: none;
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: var(--recursica_ui-kit_components_tree_properties_item-gap);
54
+ }
55
+
56
+ .node {
57
+ list-style: none;
58
+ }
59
+
60
+ /* @mui/x-tree-view's roving-tabindex keyboard navigation (arrow up/down moves focus between
61
+ rows, left/right expand/collapse, matching the WAI-ARIA treeitem pattern) already works out of
62
+ the box — but with no visible indicator, it looked broken. DOM focus lands on this <li> (.node),
63
+ not the visible row box, so the ring is drawn on .label instead — the "node" content area,
64
+ deliberately excluding the expand Button, which never shows a focus state of its own (it's not
65
+ independently focusable at all: tabIndex={-1}). */
66
+ .node:focus-visible {
67
+ outline: none;
68
+ }
69
+ /* `> .row` (child combinator), not a plain descendant selector: a node's own `.row` is always a
70
+ direct child of its `.node` <li>, while every descendant node's `.row` (inside the nested
71
+ `.subtree` <ul>) is not — without the `>`, focusing a parent node drew the ring on every
72
+ expanded descendant's label too, since they're all still descendants of the focused `.node`. */
73
+ .node:focus-visible > .row .label {
74
+ box-shadow:
75
+ 0 0 0 var(--recursica_brand_states_focus_border-size)
76
+ var(--recursica_brand_states_focus_color),
77
+ 0 0 var(--recursica_brand_states_focus_blur)
78
+ var(--recursica_brand_states_focus_margin)
79
+ var(--recursica_brand_states_focus_color);
80
+ }
81
+
82
+ .row {
83
+ position: relative;
84
+ box-sizing: border-box;
85
+ display: flex;
86
+ align-items: center;
87
+ width: 100%;
88
+ cursor: pointer;
89
+ gap: var(--recursica_ui-kit_components_tree_properties_button-node-gap);
90
+ padding-left: calc(
91
+ var(--TreeView-itemChildrenIndentation, 0px) * var(--TreeView-itemDepth, 0)
92
+ );
93
+ }
94
+
95
+ /* Neutralize MUI's own hover/focused/selected background-color changes (see TreeItemContent's
96
+ defaults in @mui/x-tree-view, e.g. its built-in `rgba(25, 118, 210, 0.08)` selected tint) so
97
+ only the Recursica hover overlay and selected styling on .label below control the look; .row
98
+ itself carries no background-color of its own to fall back to now. The `[data-selected]
99
+ [data-focused]` compound needs its own rule: MUI's own equivalent compound selector has higher
100
+ specificity (2 attributes + its class) than a single-attribute `.row[data-selected]` rule
101
+ would, so it would otherwise still win regardless of source order. */
102
+ .row:hover,
103
+ .row[data-focused],
104
+ .row[data-selected],
105
+ .row[data-selected][data-focused] {
106
+ background-color: transparent;
107
+ }
108
+
109
+ .row[data-disabled] {
110
+ opacity: var(--recursica_brand_states_disabled);
111
+ cursor: auto;
112
+ /* MUI's own disabled checks already block select/expand on click, but hover is pure CSS —
113
+ without this, a disabled row still showed the hover tint (.label::after opacity) on mouse-
114
+ over, misleadingly suggesting it's interactive. Matches mantine-adapter's `.root[data-
115
+ disabled] { pointer-events: none; }`, just scoped per-row instead of tree-wide, since MUI's
116
+ disabled state is a per-item concept even when driven by our own whole-tree prop. */
117
+ pointer-events: none;
118
+ }
119
+
120
+ .iconContainer {
121
+ position: relative;
122
+ z-index: 1;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ flex-shrink: 0;
127
+ color: inherit;
128
+ /* MUI's own `.MuiTreeItem-iconContainer` hardcodes `width: 16px` (sized for a
129
+ plain icon glyph), narrower than our 32px expand `Button`. The button
130
+ doesn't shrink to fit, so it overflowed this container by 8px on each
131
+ side — meaning `.row`'s `gap` (button-node-gap) measured a 4px space
132
+ between the *container* and `.label`, while the actual button visually
133
+ overlapped the label by 4px. `width: auto` lets the container size to its
134
+ real (button) content instead of MUI's icon-sized default. */
135
+ width: auto;
136
+ }
137
+
138
+ /* The expand Button is always rendered (even for leaf nodes, which have nothing to toggle) so
139
+ every row reserves identical layout space — hidden via visibility (not display) on leaf rows so
140
+ the label stays aligned across the whole tree without duplicating Button's own size tokens. */
141
+ .expandButton {
142
+ flex-shrink: 0;
143
+ }
144
+ .expandButtonHidden {
145
+ visibility: hidden;
146
+ }
147
+
148
+ .chevron {
149
+ transition: transform 150ms ease;
150
+ }
151
+ .chevronExpanded {
152
+ transform: rotate(90deg);
153
+ }
154
+
155
+ .label {
156
+ position: relative;
157
+ z-index: 1;
158
+ /* Sized to its own content, not stretched to fill the row's remaining width — the
159
+ hover/selected/focus "chip" should cover just the label, not the full row width.
160
+ `width: auto` is required in addition to `flex: 0 1 auto`: MuiTreeItem's own default
161
+ `.MuiTreeItem-label` sets a literal `width: 100%`, which a flex-basis alone can't beat. */
162
+ flex: 0 1 auto;
163
+ width: auto;
164
+ box-sizing: border-box;
165
+ display: flex;
166
+ align-items: center;
167
+ padding: var(--recursica_ui-kit_components_tree_properties_vertical-padding)
168
+ var(--recursica_ui-kit_components_tree_properties_horizontal-padding);
169
+ border-style: solid; /* HARDCODE: structural border rule; thickness/color are tokened */
170
+ border-width: var(--recursica_ui-kit_components_tree_properties_border-size);
171
+ border-radius: var(
172
+ --recursica_ui-kit_components_tree_properties_border-radius
173
+ );
174
+
175
+ font-family: var(
176
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_font-family
177
+ );
178
+ font-size: var(
179
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_font-size
180
+ );
181
+ font-style: var(
182
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_font-style
183
+ );
184
+ font-weight: var(
185
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_font-weight
186
+ );
187
+ letter-spacing: var(
188
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_letter-spacing
189
+ );
190
+ line-height: var(
191
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_line-height
192
+ );
193
+ text-decoration: var(
194
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_text-decoration
195
+ );
196
+ text-transform: var(
197
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_text_text-transform
198
+ );
199
+
200
+ color: var(
201
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_colors_text-color
202
+ );
203
+ background-color: var(
204
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_colors_background-color
205
+ );
206
+ border-color: var(
207
+ --recursica_ui-kit_components_tree_variants_selection-states_unselected_properties_colors_border-color
208
+ );
209
+ }
210
+
211
+ /* Hover overlay via ::after pseudo-element (same technique as Menu/Accordion), scoped to the
212
+ label's own box so hover never visually covers the expand button. `.label`'s `position:
213
+ relative` + `z-index: 1` above already forms a stacking context, so `z-index: -1` here paints
214
+ the overlay behind the label's real text content within that context, not behind `.label`
215
+ itself. */
216
+ .label::after {
217
+ content: "";
218
+ position: absolute;
219
+ inset: 0;
220
+ border-radius: inherit;
221
+ z-index: -1;
222
+ pointer-events: none;
223
+ background-color: var(--recursica_brand_states_hover_color);
224
+ opacity: 0;
225
+ transition: opacity 150ms ease;
226
+ }
227
+ .row:hover .label::after {
228
+ opacity: var(--recursica_brand_states_hover_opacity);
229
+ }
230
+
231
+ /* Selected state — read off `.row`'s own `data-selected`/`data-focused` (set by
232
+ `getContentProps()`), applied to `.label`. Written after the hover/focus reset above so it
233
+ wins the equal-specificity tie via source order, including when hovered/focused while
234
+ selected. */
235
+ .row[data-selected] .label,
236
+ .row[data-selected]:hover .label,
237
+ .row[data-selected][data-focused] .label {
238
+ font-family: var(
239
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_font-family
240
+ );
241
+ font-size: var(
242
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_font-size
243
+ );
244
+ font-style: var(
245
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_font-style
246
+ );
247
+ font-weight: var(
248
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_font-weight
249
+ );
250
+ letter-spacing: var(
251
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_letter-spacing
252
+ );
253
+ line-height: var(
254
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_line-height
255
+ );
256
+ text-decoration: var(
257
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_text-decoration
258
+ );
259
+ text-transform: var(
260
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_text_text-transform
261
+ );
262
+
263
+ color: var(
264
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_colors_text-color
265
+ );
266
+ background-color: var(
267
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_colors_background-color
268
+ );
269
+ border-color: var(
270
+ --recursica_ui-kit_components_tree_variants_selection-states_selected_properties_colors_border-color
271
+ );
272
+ }
@@ -0,0 +1,104 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Tree } from "./Tree";
3
+ import { Layer, type RecursicaTreeNode } from "@recursica/adapter-common";
4
+
5
+ const sampleData: RecursicaTreeNode[] = [
6
+ {
7
+ value: "documents",
8
+ label: "Documents",
9
+ children: [
10
+ { value: "documents/resume.pdf", label: "resume.pdf" },
11
+ { value: "documents/cover-letter.docx", label: "cover-letter.docx" },
12
+ {
13
+ value: "documents/taxes",
14
+ label: "Taxes",
15
+ children: [
16
+ { value: "documents/taxes/2023.pdf", label: "2023.pdf" },
17
+ { value: "documents/taxes/2024.pdf", label: "2024.pdf" },
18
+ ],
19
+ },
20
+ ],
21
+ },
22
+ {
23
+ value: "photos",
24
+ label: "Photos",
25
+ children: [
26
+ { value: "photos/vacation.jpg", label: "vacation.jpg" },
27
+ { value: "photos/family.jpg", label: "family.jpg" },
28
+ ],
29
+ },
30
+ { value: "readme.md", label: "readme.md" },
31
+ ];
32
+
33
+ const meta: Meta<typeof Tree> = {
34
+ title: "UI-Kit/Tree",
35
+ component: Tree,
36
+ tags: ["autodocs"],
37
+ parameters: {
38
+ docs: {
39
+ description: {
40
+ component: `
41
+ The \`Tree\` component renders hierarchical \`data\` as an expandable/selectable tree, wrapping \`@mui/x-tree-view\`'s \`RichTreeView\` with a fully custom item renderer so every visual property (row box model, selected/unselected colors and typography, indent, item spacing) comes from Recursica's \`tree\` design tokens.
42
+
43
+ Each node needs a unique \`value\` and a \`label\`; a \`children\` array (even empty) makes a node expandable.
44
+ `,
45
+ },
46
+ },
47
+ },
48
+ };
49
+
50
+ export default meta;
51
+
52
+ export const Default: StoryObj<typeof Tree> = {
53
+ render: () => (
54
+ <Tree data={sampleData} initialExpandedValues={["documents"]} />
55
+ ),
56
+ };
57
+
58
+ export const AllExpanded: StoryObj<typeof Tree> = {
59
+ render: () => <Tree data={sampleData} initialExpandedValues="*" />,
60
+ };
61
+
62
+ export const PreSelected: StoryObj<typeof Tree> = {
63
+ render: () => (
64
+ <Tree
65
+ data={sampleData}
66
+ initialExpandedValues={["documents"]}
67
+ initialSelectedValues={["documents/resume.pdf"]}
68
+ />
69
+ ),
70
+ };
71
+
72
+ export const MultipleSelection: StoryObj<typeof Tree> = {
73
+ render: () => (
74
+ <Tree
75
+ data={sampleData}
76
+ initialExpandedValues="*"
77
+ initialSelectedValues={["documents/resume.pdf", "photos/vacation.jpg"]}
78
+ multiple
79
+ />
80
+ ),
81
+ };
82
+
83
+ /** Whole tree disabled, with a node pre-selected so the selected chip's styling under the
84
+ * disabled dimming can be checked alongside unselected rows. */
85
+ export const Disabled: StoryObj<typeof Tree> = {
86
+ render: () => (
87
+ <Tree
88
+ data={sampleData}
89
+ initialExpandedValues={["documents"]}
90
+ initialSelectedValues={["documents/resume.pdf"]}
91
+ disabled
92
+ />
93
+ ),
94
+ };
95
+
96
+ /** Demonstrates the component nested inside a non-default layer — the one case where an
97
+ * explicit `<Layer>` wrap belongs in a story (see COMPONENT_STORYBOOK_GUIDE.md §9). */
98
+ export const LayerOne: StoryObj<typeof Tree> = {
99
+ render: () => (
100
+ <Layer layer={1} style={{ padding: "24px" }}>
101
+ <Tree data={sampleData} initialExpandedValues={["documents"]} />
102
+ </Layer>
103
+ ),
104
+ };
@@ -0,0 +1 @@
1
+ // Placeholder for future Tree component tests. Add tests here if/when needed.