@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,285 @@
1
+ import React, { forwardRef, createContext, useContext, useState } from "react";
2
+ import {
3
+ Accordion as MuiAccordion,
4
+ AccordionSummary as MuiAccordionSummary,
5
+ AccordionDetails as MuiAccordionDetails,
6
+ type AccordionProps as MuiAccordionProps,
7
+ type AccordionSummaryProps as MuiAccordionSummaryProps,
8
+ type AccordionDetailsProps as MuiAccordionDetailsProps,
9
+ } from "@mui/material";
10
+ import {
11
+ filterStylingProps,
12
+ type RecursicaOverStyled,
13
+ } from "../../utils/filterStylingProps";
14
+ import styles from "./Accordion.module.css";
15
+
16
+ import {
17
+ type RecursicaAccordionProps,
18
+ type RecursicaAccordionItemProps,
19
+ type RecursicaAccordionControlProps,
20
+ type RecursicaAccordionPanelProps,
21
+ } from "@recursica/adapter-common";
22
+
23
+ const ChevronIcon = () => (
24
+ <svg
25
+ viewBox="0 0 24 24"
26
+ fill="none"
27
+ xmlns="http://www.w3.org/2000/svg"
28
+ stroke="currentColor"
29
+ strokeWidth="2"
30
+ strokeLinecap="round"
31
+ strokeLinejoin="round"
32
+ style={{ width: "100%", height: "100%" }}
33
+ >
34
+ <polyline points="6 9 12 15 18 9"></polyline>
35
+ </svg>
36
+ );
37
+
38
+ const AccordionContext = createContext<{
39
+ value: string | string[] | null;
40
+ onChange: (val: string) => void;
41
+ chevron?: React.ReactNode;
42
+ } | null>(null);
43
+
44
+ // ==== ACCORDION CONTAINER ====
45
+ export type AccordionProps = RecursicaOverStyled<
46
+ React.HTMLAttributes<HTMLDivElement> &
47
+ RecursicaAccordionProps & {
48
+ // MUI has no native multi-panel accordion group to match (its own `Accordion` is a
49
+ // single controlled item) — this signature is Recursica's own, same as TransferList.
50
+ onChange?: (value: string | string[] | null) => void;
51
+ }
52
+ >;
53
+
54
+ const AccordionBase = forwardRef<HTMLDivElement, AccordionProps>(
55
+ function Accordion(
56
+ {
57
+ variant = "default",
58
+ overStyled = false,
59
+ value,
60
+ defaultValue,
61
+ onChange,
62
+ multiple = false,
63
+ chevron,
64
+ children,
65
+ ...rest
66
+ },
67
+ ref,
68
+ ) {
69
+ const sanitizedProps = filterStylingProps(rest, overStyled);
70
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
71
+ .className as string | undefined;
72
+
73
+ const finalClass =
74
+ [styles.root, classNameProp].filter(Boolean).join(" ") || undefined;
75
+
76
+ const [uncontrolledValue, setUncontrolledValue] = useState<
77
+ string | string[] | null
78
+ >(defaultValue !== undefined ? defaultValue : multiple ? [] : null);
79
+ const activeValue = value !== undefined ? value : uncontrolledValue;
80
+
81
+ const handleItemChange = (itemValue: string) => {
82
+ let newValue: string | string[] | null;
83
+ if (multiple) {
84
+ const arr = Array.isArray(activeValue)
85
+ ? activeValue
86
+ : activeValue
87
+ ? [activeValue as string]
88
+ : [];
89
+ newValue = arr.includes(itemValue)
90
+ ? arr.filter((v) => v !== itemValue)
91
+ : [...arr, itemValue];
92
+ } else {
93
+ newValue = activeValue === itemValue ? null : itemValue;
94
+ }
95
+ setUncontrolledValue(newValue);
96
+ onChange?.(newValue);
97
+ };
98
+
99
+ return (
100
+ <AccordionContext.Provider
101
+ value={{ value: activeValue, onChange: handleItemChange, chevron }}
102
+ >
103
+ <div
104
+ ref={ref}
105
+ {...(sanitizedProps as React.HTMLAttributes<HTMLDivElement>)}
106
+ className={finalClass}
107
+ data-variant={variant}
108
+ >
109
+ {children}
110
+ </div>
111
+ </AccordionContext.Provider>
112
+ );
113
+ },
114
+ );
115
+ AccordionBase.displayName = "Accordion";
116
+
117
+ export type AccordionItemWrapperProps = RecursicaOverStyled<
118
+ // `expanded`/`onChange` are computed internally from the group context — omitted so a
119
+ // caller can't silently detach this item from the controlled state. `defaultExpanded` has
120
+ // no meaning here since expansion is always driven by the group's `value`. `disabled` is
121
+ // shared as-is with `RecursicaAccordionItemProps.disabled` — same name, same shape, no
122
+ // reshaping needed; see ACCORDION_IMPLEMENTATION_NOTES.md.
123
+ Omit<
124
+ MuiAccordionProps,
125
+ "children" | "value" | "expanded" | "onChange" | "defaultExpanded"
126
+ > & {
127
+ children?: React.ReactNode;
128
+ value: string;
129
+ } & RecursicaAccordionItemProps
130
+ >;
131
+
132
+ export const AccordionItem = forwardRef<
133
+ HTMLDivElement,
134
+ AccordionItemWrapperProps
135
+ >(function AccordionItem(
136
+ {
137
+ title,
138
+ leftIcon,
139
+ divider = true,
140
+ children,
141
+ value,
142
+ disabled = false,
143
+ overStyled = false,
144
+ style,
145
+ ...rest
146
+ },
147
+ ref,
148
+ ) {
149
+ const sanitizedProps = filterStylingProps(rest, overStyled);
150
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
151
+ .className as string | undefined;
152
+
153
+ const finalClass =
154
+ [styles.item, divider ? undefined : styles.noDivider, classNameProp]
155
+ .filter(Boolean)
156
+ .join(" ") || undefined;
157
+
158
+ const ctx = useContext(AccordionContext);
159
+ const isExpanded = ctx
160
+ ? Array.isArray(ctx.value)
161
+ ? ctx.value.includes(value)
162
+ : ctx.value === value
163
+ : undefined;
164
+
165
+ const handleChange = () => {
166
+ ctx?.onChange(value);
167
+ };
168
+
169
+ // background-color: transparent allows Layer overrides to naturally penetrate
170
+ const mergedStyle = { backgroundColor: "transparent", ...style };
171
+
172
+ return (
173
+ <MuiAccordion
174
+ ref={ref}
175
+ {...(sanitizedProps as unknown as Omit<
176
+ MuiAccordionProps,
177
+ "expanded" | "onChange" | "defaultExpanded" | "disabled"
178
+ >)}
179
+ className={finalClass}
180
+ disableGutters
181
+ elevation={0}
182
+ square
183
+ expanded={isExpanded}
184
+ onChange={handleChange}
185
+ disabled={disabled}
186
+ data-disabled={disabled || undefined}
187
+ style={mergedStyle}
188
+ >
189
+ {
190
+ (title ? (
191
+ <>
192
+ <AccordionControl leftIcon={leftIcon}>{title}</AccordionControl>
193
+ <AccordionPanel>{children}</AccordionPanel>
194
+ </>
195
+ ) : (
196
+ children
197
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
+ )) as any
199
+ }
200
+ </MuiAccordion>
201
+ );
202
+ });
203
+ AccordionItem.displayName = "AccordionItem";
204
+
205
+ // ==== ACCORDION CONTROL ====
206
+ export type AccordionControlWrapperProps = RecursicaOverStyled<
207
+ // `expandIcon` is resolved internally from the container's `chevron` (via context) —
208
+ // omitted so a caller can't silently override it with MUI's native chevron slot.
209
+ Omit<MuiAccordionSummaryProps, "expandIcon"> & RecursicaAccordionControlProps
210
+ >;
211
+
212
+ export const AccordionControl = forwardRef<
213
+ HTMLDivElement,
214
+ AccordionControlWrapperProps
215
+ >(function AccordionControl(
216
+ { leftIcon, children, overStyled = false, ...rest },
217
+ ref,
218
+ ) {
219
+ const sanitizedProps = filterStylingProps(rest, overStyled);
220
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
221
+ .className as string | undefined;
222
+
223
+ const finalClass =
224
+ [styles.control, classNameProp].filter(Boolean).join(" ") || undefined;
225
+
226
+ const ctx = useContext(AccordionContext);
227
+ const resolvedChevron = ctx?.chevron ?? <ChevronIcon />;
228
+
229
+ return (
230
+ <MuiAccordionSummary
231
+ ref={ref}
232
+ {...(sanitizedProps as unknown as MuiAccordionSummaryProps)}
233
+ className={finalClass}
234
+ expandIcon={<span className={styles.chevron}>{resolvedChevron}</span>}
235
+ >
236
+ {leftIcon && (
237
+ <span className={styles.iconLeftWrapper} aria-hidden>
238
+ {leftIcon}
239
+ </span>
240
+ )}
241
+ <div className={styles.label}>{children}</div>
242
+ </MuiAccordionSummary>
243
+ );
244
+ });
245
+ AccordionControl.displayName = "AccordionControl";
246
+
247
+ // ==== ACCORDION PANEL ====
248
+ export type AccordionPanelWrapperProps = RecursicaOverStyled<
249
+ MuiAccordionDetailsProps & RecursicaAccordionPanelProps
250
+ >;
251
+
252
+ export const AccordionPanel = forwardRef<
253
+ HTMLDivElement,
254
+ AccordionPanelWrapperProps
255
+ >(function AccordionPanel({ overStyled = false, children, ...rest }, ref) {
256
+ const sanitizedProps = filterStylingProps(rest, overStyled);
257
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
258
+ .className as string | undefined;
259
+
260
+ const finalClass =
261
+ [styles.panel, classNameProp].filter(Boolean).join(" ") || undefined;
262
+
263
+ return (
264
+ <MuiAccordionDetails
265
+ ref={ref}
266
+ {...(sanitizedProps as unknown as MuiAccordionDetailsProps)}
267
+ className={finalClass}
268
+ >
269
+ <div className={styles.content}>{children}</div>
270
+ </MuiAccordionDetails>
271
+ );
272
+ });
273
+ AccordionPanel.displayName = "AccordionPanel";
274
+
275
+ // ==== DOT NOTATION EXPORT ====
276
+ type AccordionComponent = typeof AccordionBase & {
277
+ Item: typeof AccordionItem;
278
+ Control: typeof AccordionControl;
279
+ Panel: typeof AccordionPanel;
280
+ };
281
+
282
+ export const Accordion = AccordionBase as AccordionComponent;
283
+ Accordion.Item = AccordionItem;
284
+ Accordion.Control = AccordionControl;
285
+ Accordion.Panel = AccordionPanel;
@@ -0,0 +1,43 @@
1
+ # Accordion - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Accordion` component in your projects using `@recursica/adapter-mui-v7`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Accordion } from "@recursica/adapter-mui-v7";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Accordion } from "@recursica/adapter-mui-v7";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Accordion>
24
+ <Accordion.Item value="item-1">
25
+ <Accordion.Control>Section 1</Accordion.Control>
26
+ <Accordion.Panel>Section 1 content</Accordion.Panel>
27
+ </Accordion.Item>
28
+ </Accordion>
29
+ );
30
+ }
31
+ ```
32
+
33
+ ---
34
+
35
+ ## 3. Design System Integration
36
+
37
+ All Recursica components in the `@recursica/adapter-mui-v7` package adhere strictly to design system spacing, scaling, and behavior patterns.
38
+
39
+ > [!IMPORTANT]
40
+ >
41
+ > - **Anti-override protection**: Rogues style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
42
+ > - **No Direct Layers**: Do not pass a `layer` prop to this component. To place it on a specific visual layer, wrap it in a `<Layer layer={0|1|2|3}>` component natively.
43
+ > - **Variables and Theming**: Styling is entirely determined by local CSS variables defined in `recursica_variables_scoped.css` and mapped in the component's CSS module.
@@ -0,0 +1 @@
1
+ export * from "./Accordion";
@@ -0,0 +1,59 @@
1
+ # AssistiveElement – implementation notes
2
+
3
+ Decisions and design tweaks specific to the UI Kit's AssistiveElement wrapped against
4
+ `@mui/material`.
5
+
6
+ ---
7
+
8
+ ## 1. Forced `component="div"` trades away `FormHelperText`'s default semantics
9
+
10
+ **Decision:** `FormHelperText` renders a `<p>` by default — a reasonable semantic choice for
11
+ standalone helper/error text. This adapter forces `component="div"` instead, purely so the
12
+ icon + text can sit side by side in a flex row; `<p>` can't contain the flex layout the same
13
+ way without other tradeoffs.
14
+ **Implementation:** `component="div"` and `error={isError}` (derived from `assistiveVariant`)
15
+ are both computed internally and omitted from `AssistiveElementProps` (`Omit<FormHelperTextProps,
16
+ "error" | "component">`) so a caller can't silently override either via spread.
17
+ `aria-describedby`/`aria-errormessage` association with a field is a different story: this
18
+ component has no reference to any sibling field, so it can't wire that itself. It's already
19
+ handled one layer up, in `FormControlWrapper` — which generates the ids, passes them down via
20
+ this component's own `id` prop, and clones `aria-describedby`/`aria-errormessage` onto the
21
+ field. Only a fully standalone `<AssistiveElement>` used outside `FormControlWrapper` still
22
+ needs that wiring done by hand. `role="alert"` for the error variant is handled here instead
23
+ (see §4) — that piece genuinely belongs at this component's own level.
24
+
25
+ ---
26
+
27
+ ## 2. CSS specificity tie against MUI's own `.Mui-error` color
28
+
29
+ **Decision:** MUI's `FormHelperText` sets its own error color via `.MuiFormHelperText-root.Mui-error`
30
+ (two classes) whenever `error` is set. Our own `.root[data-variant="error"]` (one class + one
31
+ attribute) lands at the _same_ specificity — without help, whichever rule loads later in the
32
+ merged stylesheet wins, which is import-order luck rather than a deterministic override.
33
+ **Implementation:** `!important` on that one rule's `color` guarantees ours wins regardless of
34
+ load order (same reasoning as the focus-ring override in the Accordion adapter). Not needed for
35
+ the `help` variant — MUI's unconditional base `color` on `.root` alone already has lower
36
+ specificity than our attribute-qualified rule there.
37
+
38
+ ---
39
+
40
+ ## 3. Native passthrough capabilities, left unblocked and undocumented
41
+
42
+ **Decision:** `FormHelperText`'s own `disabled`/`filled`/`focused`/`margin`/`required`/`variant`/
43
+ `classes`/`sx` all pass straight through today (none of them are omitted, and none are
44
+ computed internally so there's nothing for them to silently override). They're also not added
45
+ to `RecursicaAssistiveElementProps` — same "accidental passthrough" class as Accordion's
46
+ `disabled` was before it got formalized. Known asymmetry: MUI callers can reach for these
47
+ today, Mantine callers can't (Mantine's `AssistiveElement` wraps no native component at all).
48
+ Revisit if a real use case shows up for any of them.
49
+
50
+ ---
51
+
52
+ ## 4. `role="alert"` defaults on for the error variant
53
+
54
+ **Decision:** Error text needs to be announced by assistive tech as it appears or changes;
55
+ static help text doesn't. Rather than requiring every integrator to remember `role="alert"`,
56
+ `assistiveVariant="error"` defaults it automatically.
57
+ **Implementation:** `role` is destructured out and resolved as `role ?? (isError ? "alert" :
58
+ undefined)` before rendering — an explicit caller-supplied `role` always wins over the default,
59
+ and `"help"` gets no default at all.
@@ -0,0 +1,121 @@
1
+ /*
2
+ ASSISTIVE ELEMENT ARCHITECTURE
3
+ This component strictly standardizes FormHelperText logic dynamically natively decoupled from the overarching input blocks.
4
+ */
5
+
6
+ .root {
7
+ /* Override MUI FormHelperText native resets structurally safely */
8
+ display: flex !important;
9
+ align-items: flex-start !important; /* Forces the icon to align with the first line of text */
10
+ margin: 0 !important; /* HARDCODE: structural reset */
11
+ padding: 0 !important; /* HARDCODE: structural reset */
12
+ width: 100%;
13
+
14
+ gap: var(
15
+ --recursica_ui-kit_components_assistive-element_properties_icon-text-gap
16
+ );
17
+ margin-top: var(
18
+ --recursica_ui-kit_components_assistive-element_properties_top-margin
19
+ ) !important;
20
+ max-width: var(
21
+ --recursica_ui-kit_components_assistive-element_properties_max-width
22
+ );
23
+
24
+ font-family: var(
25
+ --recursica_ui-kit_components_assistive-element_properties_text_font-family
26
+ );
27
+ font-size: var(
28
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
29
+ );
30
+ font-style: var(
31
+ --recursica_ui-kit_components_assistive-element_properties_text_font-style
32
+ );
33
+ font-weight: var(
34
+ --recursica_ui-kit_components_assistive-element_properties_text_font-weight
35
+ );
36
+ letter-spacing: var(
37
+ --recursica_ui-kit_components_assistive-element_properties_text_letter-spacing
38
+ );
39
+ line-height: var(
40
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
41
+ );
42
+ text-decoration: var(
43
+ --recursica_ui-kit_components_assistive-element_properties_text_text-decoration
44
+ );
45
+ text-transform: var(
46
+ --recursica_ui-kit_components_assistive-element_properties_text_text-transform
47
+ );
48
+ }
49
+
50
+ .text {
51
+ overflow-wrap: break-word;
52
+ white-space: normal;
53
+ }
54
+
55
+ /* Base Icon Architecture */
56
+ .icon {
57
+ flex-shrink: 0 !important; /* Prevent squishing */
58
+ display: flex !important;
59
+ align-items: center !important;
60
+ justify-content: center !important;
61
+
62
+ width: var(
63
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
64
+ ) !important;
65
+ height: calc(
66
+ var(
67
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
68
+ ) *
69
+ var(
70
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
71
+ )
72
+ ) !important; /* Forces exact centering bounds within the first text line vertically */
73
+ }
74
+
75
+ .icon :global(svg) {
76
+ width: var(
77
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
78
+ ) !important;
79
+ height: var(
80
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
81
+ ) !important;
82
+ }
83
+
84
+ /* =========================================
85
+ VARIANTS: Default / Help
86
+ ========================================= */
87
+
88
+ .root[data-variant="help"] {
89
+ color: var(
90
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_text-color
91
+ );
92
+ }
93
+
94
+ .root[data-variant="help"] .icon {
95
+ color: var(
96
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_icon-color
97
+ );
98
+ }
99
+
100
+ /* =========================================
101
+ VARIANTS: Error
102
+ ========================================= */
103
+
104
+ /* MUI's own `FormHelperText` sets its error color via `.MuiFormHelperText-root.Mui-error`
105
+ (two classes) whenever `error` is set — the exact same specificity as our own
106
+ `.root[data-variant="error"]` (one class + one attribute), so whichever rule loads later
107
+ in the merged stylesheet would otherwise win. `!important` makes ours win deterministically
108
+ regardless of CSS import order (same reasoning as the focus-ring override elsewhere in this
109
+ codebase). Not needed for the `help` variant above — MUI's unconditional base `color` on
110
+ `.root` alone has lower specificity than ours there already. */
111
+ .root[data-variant="error"] {
112
+ color: var(
113
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_text-color
114
+ ) !important;
115
+ }
116
+
117
+ .root[data-variant="error"] .icon {
118
+ color: var(
119
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_icon-color
120
+ );
121
+ }
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AssistiveElement } from "./AssistiveElement";
3
+
4
+ const meta: Meta<typeof AssistiveElement> = {
5
+ title: "UI-Kit/AssistiveElement",
6
+ component: AssistiveElement,
7
+ tags: ["autodocs"],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component:
12
+ "The `AssistiveElement` is a semantic structural primitive designed to standardize Helper and Error descriptive blocks natively beneath form components globally. By explicitly wiring to the `--recursica_ui-kit_components_assistive-element` layout tokens, this component safely injects custom SVGs (Alerts vs Info circles) alongside constrained flex-wrapping typography strings, preserving flawless line-height and alignment logic entirely decoupled from underlying input engine frameworks.",
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ assistiveVariant: {
18
+ control: "radio",
19
+ options: ["help", "error"],
20
+ },
21
+ assistiveWithIcon: {
22
+ control: "boolean",
23
+ },
24
+ },
25
+ };
26
+
27
+ export default meta;
28
+
29
+ type Story = StoryObj<typeof AssistiveElement>;
30
+
31
+ export const DefaultHelp: Story = {
32
+ args: {
33
+ children:
34
+ "This is a standard assistive layout explaining specific configurations.",
35
+ assistiveVariant: "help",
36
+ assistiveWithIcon: true,
37
+ },
38
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
39
+ render: ({ withLayer, layer, ...args }: any) => (
40
+ <AssistiveElement {...args} />
41
+ ),
42
+ };
43
+
44
+ export const ErrorState: Story = {
45
+ args: {
46
+ children:
47
+ "Invalid property. You must satisfy the constraints outlined above.",
48
+ assistiveVariant: "error",
49
+ assistiveWithIcon: true,
50
+ },
51
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
52
+ render: ({ withLayer, layer, ...args }: any) => (
53
+ <AssistiveElement {...args} />
54
+ ),
55
+ };
56
+
57
+ export const NoIconHelp: Story = {
58
+ args: {
59
+ children:
60
+ "Fallback textual representation without visual injection targets.",
61
+ assistiveVariant: "help",
62
+ assistiveWithIcon: false,
63
+ },
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
65
+ render: ({ withLayer, layer, ...args }: any) => (
66
+ <AssistiveElement {...args} />
67
+ ),
68
+ };
@@ -0,0 +1 @@
1
+ // Placeholder for future AssistiveElement component tests. Add tests here if/when needed.
@@ -0,0 +1,94 @@
1
+ import React from "react";
2
+ import { FormHelperText, type FormHelperTextProps } from "@mui/material";
3
+ import {
4
+ filterStylingProps,
5
+ type RecursicaOverStyled,
6
+ } from "../../utils/filterStylingProps";
7
+ import styles from "./AssistiveElement.module.css";
8
+
9
+ import { type RecursicaAssistiveElementProps } from "@recursica/adapter-common";
10
+
11
+ // `error`/`component` are computed internally (from `assistiveVariant`, and forced to "div"
12
+ // for layout) — omitted so a caller can't silently override either via spread.
13
+ export type AssistiveElementProps = RecursicaOverStyled<
14
+ Omit<FormHelperTextProps, "error" | "component"> &
15
+ RecursicaAssistiveElementProps
16
+ >;
17
+
18
+ export const AssistiveElement = React.forwardRef<
19
+ HTMLParagraphElement,
20
+ AssistiveElementProps
21
+ >(function AssistiveElement(props, ref) {
22
+ const {
23
+ assistiveVariant = "help",
24
+ assistiveWithIcon = true,
25
+ overStyled = false,
26
+ className,
27
+ children,
28
+ role,
29
+ ...rest
30
+ } = props;
31
+
32
+ const sanitizedProps = filterStylingProps(rest, overStyled);
33
+
34
+ const HelpIcon = () => (
35
+ <svg
36
+ xmlns="http://www.w3.org/2000/svg"
37
+ viewBox="0 0 24 24"
38
+ fill="none"
39
+ stroke="currentColor"
40
+ strokeWidth="2"
41
+ strokeLinecap="round"
42
+ strokeLinejoin="round"
43
+ >
44
+ <circle cx="12" cy="12" r="10" />
45
+ <path d="M12 16v-4" />
46
+ <path d="M12 8h.01" />
47
+ </svg>
48
+ );
49
+
50
+ const ErrorIcon = () => (
51
+ <svg
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke="currentColor"
56
+ strokeWidth="2"
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ >
60
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
61
+ <path d="M12 9v4" />
62
+ <path d="M12 17h.01" />
63
+ </svg>
64
+ );
65
+
66
+ // Map to MUI's native error prop if it's explicitly an error state
67
+ const isError = assistiveVariant === "error";
68
+
69
+ // Announce error text as it appears/changes; a caller-supplied `role` always wins. No
70
+ // default for `help` — static descriptive text doesn't need a live region.
71
+ const resolvedRole = role ?? (isError ? "alert" : undefined);
72
+
73
+ // Force component = "div" so flex layouts execute correctly inside HelperText
74
+ return (
75
+ <FormHelperText
76
+ ref={ref}
77
+ {...(sanitizedProps as Omit<FormHelperTextProps, "error" | "component">)}
78
+ component="div"
79
+ error={isError}
80
+ role={resolvedRole}
81
+ data-variant={assistiveVariant}
82
+ className={className ? `${styles.root} ${className}` : styles.root}
83
+ >
84
+ {assistiveWithIcon && (
85
+ <div className={styles.icon}>
86
+ {assistiveVariant === "error" ? <ErrorIcon /> : <HelpIcon />}
87
+ </div>
88
+ )}
89
+ <span className={styles.text}>{children}</span>
90
+ </FormHelperText>
91
+ );
92
+ });
93
+
94
+ AssistiveElement.displayName = "AssistiveElement";