@recursica/adapter-mantine-v8 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 (362) hide show
  1. package/.storybook/commonArgTypes.ts +148 -0
  2. package/ARCHITECTURE.md +18 -0
  3. package/CHANGELOG.md +688 -0
  4. package/LICENSE +21 -0
  5. package/OVERSTYLING.md +56 -0
  6. package/README.md +70 -0
  7. package/SETUP.md +93 -0
  8. package/USAGE.md +57 -0
  9. package/dist/adapter-mantine-v8.cjs +12 -0
  10. package/dist/adapter-mantine-v8.cjs.map +1 -0
  11. package/dist/adapter-mantine-v8.css +1 -0
  12. package/dist/adapter-mantine-v8.js +5759 -0
  13. package/dist/adapter-mantine-v8.js.map +1 -0
  14. package/dist/index.d.ts +2095 -0
  15. package/dist/test.html +1 -0
  16. package/dist/vite.svg +1 -0
  17. package/docs/PHILOSOPHY.md +39 -0
  18. package/llms.txt +74 -0
  19. package/package.json +131 -0
  20. package/recursica_brand.json +2677 -0
  21. package/recursica_tokens.json +799 -0
  22. package/recursica_ui-kit.json +14981 -0
  23. package/recursica_variables_scoped.css +30489 -0
  24. package/src/GlobalExemptions.modules.css +7 -0
  25. package/src/Introduction.stories.tsx +154 -0
  26. package/src/OverStyling.tsx +79 -0
  27. package/src/Version.tsx +44 -0
  28. package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +72 -0
  29. package/src/components/Accordion/Accordion.module.css +367 -0
  30. package/src/components/Accordion/Accordion.stories.tsx +189 -0
  31. package/src/components/Accordion/Accordion.test.tsx +1 -0
  32. package/src/components/Accordion/Accordion.tsx +220 -0
  33. package/src/components/Accordion/USAGE.md +51 -0
  34. package/src/components/Accordion/index.ts +1 -0
  35. package/src/components/AssistiveElement/ASSISTIVEELEMENT_IMPLEMENTATION_NOTES.md +45 -0
  36. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  37. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +68 -0
  38. package/src/components/AssistiveElement/AssistiveElement.test.tsx +1 -0
  39. package/src/components/AssistiveElement/AssistiveElement.tsx +93 -0
  40. package/src/components/AssistiveElement/USAGE.md +36 -0
  41. package/src/components/AutoComplete/AUTOCOMPLETE_IMPLEMENTATION_NOTES.md +42 -0
  42. package/src/components/AutoComplete/AutoComplete.module.css +430 -0
  43. package/src/components/AutoComplete/AutoComplete.stories.tsx +336 -0
  44. package/src/components/AutoComplete/AutoComplete.test.tsx +1 -0
  45. package/src/components/AutoComplete/AutoComplete.tsx +179 -0
  46. package/src/components/AutoComplete/USAGE.md +80 -0
  47. package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +34 -0
  48. package/src/components/Avatar/Avatar.module.css +336 -0
  49. package/src/components/Avatar/Avatar.stories.tsx +92 -0
  50. package/src/components/Avatar/Avatar.test.tsx +1 -0
  51. package/src/components/Avatar/Avatar.tsx +108 -0
  52. package/src/components/Avatar/USAGE.md +38 -0
  53. package/src/components/Avatar/index.ts +1 -0
  54. package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
  55. package/src/components/Badge/Badge.module.css +127 -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 +64 -0
  59. package/src/components/Badge/USAGE.md +56 -0
  60. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +47 -0
  61. package/src/components/Breadcrumb/Breadcrumb.module.css +48 -0
  62. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
  63. package/src/components/Breadcrumb/Breadcrumb.test.tsx +1 -0
  64. package/src/components/Breadcrumb/Breadcrumb.tsx +85 -0
  65. package/src/components/Breadcrumb/USAGE.md +55 -0
  66. package/src/components/Breadcrumb/markCurrentPageItem.test.ts +49 -0
  67. package/src/components/Button/Button.module.css +446 -0
  68. package/src/components/Button/Button.stories.tsx +196 -0
  69. package/src/components/Button/Button.styleIsolation.dom.test.tsx +98 -0
  70. package/src/components/Button/Button.tsx +184 -0
  71. package/src/components/Button/IMPLEMENTATION_NOTES.md +109 -0
  72. package/src/components/Button/USAGE.md +80 -0
  73. package/src/components/Button/index.ts +1 -0
  74. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +17 -0
  75. package/src/components/Card/Card.module.css +119 -0
  76. package/src/components/Card/Card.stories.tsx +131 -0
  77. package/src/components/Card/Card.test.tsx +1 -0
  78. package/src/components/Card/Card.tsx +248 -0
  79. package/src/components/Card/USAGE.md +63 -0
  80. package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
  81. package/src/components/Checkbox/Checkbox.module.css +267 -0
  82. package/src/components/Checkbox/Checkbox.stories.tsx +97 -0
  83. package/src/components/Checkbox/Checkbox.test.tsx +1 -0
  84. package/src/components/Checkbox/Checkbox.tsx +151 -0
  85. package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
  86. package/src/components/Checkbox/CheckboxGroup.tsx +147 -0
  87. package/src/components/Checkbox/USAGE.md +48 -0
  88. package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +110 -0
  89. package/src/components/Chip/Chip.module.css +288 -0
  90. package/src/components/Chip/Chip.stories.tsx +128 -0
  91. package/src/components/Chip/Chip.test.tsx +1 -0
  92. package/src/components/Chip/Chip.tsx +157 -0
  93. package/src/components/Chip/USAGE.md +55 -0
  94. package/src/components/Container/CONTAINER_IMPLEMENTATION_NOTES.md +4 -0
  95. package/src/components/Container/Container.module.css +7 -0
  96. package/src/components/Container/Container.stories.tsx +118 -0
  97. package/src/components/Container/Container.test.tsx +1 -0
  98. package/src/components/Container/Container.tsx +69 -0
  99. package/src/components/Container/USAGE.md +47 -0
  100. package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +31 -0
  101. package/src/components/DatePicker/DatePicker.icons.tsx +27 -0
  102. package/src/components/DatePicker/DatePicker.module.css +566 -0
  103. package/src/components/DatePicker/DatePicker.stories.tsx +158 -0
  104. package/src/components/DatePicker/DatePicker.test.tsx +1 -0
  105. package/src/components/DatePicker/DatePicker.tsx +218 -0
  106. package/src/components/DatePicker/USAGE.md +49 -0
  107. package/src/components/Dropdown/BareDropdown.tsx +97 -0
  108. package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +12 -0
  109. package/src/components/Dropdown/Dropdown.module.css +477 -0
  110. package/src/components/Dropdown/Dropdown.stories.tsx +252 -0
  111. package/src/components/Dropdown/Dropdown.test.tsx +1 -0
  112. package/src/components/Dropdown/Dropdown.tsx +213 -0
  113. package/src/components/Dropdown/USAGE.md +69 -0
  114. package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +153 -0
  115. package/src/components/FileInput/FileInput.module.css +280 -0
  116. package/src/components/FileInput/FileInput.stories.tsx +313 -0
  117. package/src/components/FileInput/FileInput.test.tsx +1 -0
  118. package/src/components/FileInput/FileInput.tsx +417 -0
  119. package/src/components/FileInput/USAGE.md +145 -0
  120. package/src/components/FileUpload/FILEUPLOAD_IMPLEMENTATION_NOTES.md +244 -0
  121. package/src/components/FileUpload/FileUpload.module.css +205 -0
  122. package/src/components/FileUpload/FileUpload.stories.tsx +362 -0
  123. package/src/components/FileUpload/FileUpload.test.tsx +1 -0
  124. package/src/components/FileUpload/FileUpload.tsx +354 -0
  125. package/src/components/FileUpload/USAGE.md +199 -0
  126. package/src/components/Flex/FLEX_IMPLEMENTATION_NOTES.md +4 -0
  127. package/src/components/Flex/Flex.module.css +7 -0
  128. package/src/components/Flex/Flex.stories.tsx +128 -0
  129. package/src/components/Flex/Flex.test.tsx +1 -0
  130. package/src/components/Flex/Flex.tsx +75 -0
  131. package/src/components/Flex/USAGE.md +48 -0
  132. package/src/components/FormControlLayout/FormControlLayout.module.css +90 -0
  133. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +118 -0
  134. package/src/components/FormControlLayout/FormControlLayout.test.tsx +1 -0
  135. package/src/components/FormControlLayout/FormControlLayout.tsx +74 -0
  136. package/src/components/FormControlLayout/USAGE.md +40 -0
  137. package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
  138. package/src/components/FormControlWrapper/FormControlWrapper.module.css +75 -0
  139. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +129 -0
  140. package/src/components/FormControlWrapper/FormControlWrapper.test.tsx +1 -0
  141. package/src/components/FormControlWrapper/FormControlWrapper.tsx +151 -0
  142. package/src/components/FormControlWrapper/USAGE.md +47 -0
  143. package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +18 -0
  144. package/src/components/Grid/Grid.module.css +11 -0
  145. package/src/components/Grid/Grid.stories.tsx +206 -0
  146. package/src/components/Grid/Grid.test.tsx +1 -0
  147. package/src/components/Grid/Grid.tsx +140 -0
  148. package/src/components/Grid/USAGE.md +47 -0
  149. package/src/components/Group/GROUP_IMPLEMENTATION_NOTES.md +4 -0
  150. package/src/components/Group/Group.module.css +7 -0
  151. package/src/components/Group/Group.stories.tsx +120 -0
  152. package/src/components/Group/Group.test.tsx +1 -0
  153. package/src/components/Group/Group.tsx +60 -0
  154. package/src/components/Group/USAGE.md +48 -0
  155. package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +18 -0
  156. package/src/components/Heading/Heading.module.css +13 -0
  157. package/src/components/Heading/Heading.stories.tsx +50 -0
  158. package/src/components/Heading/Heading.test.tsx +1 -0
  159. package/src/components/Heading/Heading.tsx +52 -0
  160. package/src/components/Heading/USAGE.md +36 -0
  161. package/src/components/HoverCard/HOVERCARD_IMPLEMENTATION_NOTES.md +77 -0
  162. package/src/components/HoverCard/HoverCard.module.css +90 -0
  163. package/src/components/HoverCard/HoverCard.stories.tsx +172 -0
  164. package/src/components/HoverCard/HoverCard.test.tsx +1 -0
  165. package/src/components/HoverCard/HoverCard.tsx +133 -0
  166. package/src/components/HoverCard/USAGE.md +52 -0
  167. package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
  168. package/src/components/Label/Label.module.css +141 -0
  169. package/src/components/Label/Label.stories.tsx +131 -0
  170. package/src/components/Label/Label.test.tsx +1 -0
  171. package/src/components/Label/Label.tsx +113 -0
  172. package/src/components/Label/USAGE.md +47 -0
  173. package/src/components/Label/index.ts +1 -0
  174. package/src/components/Layer/USAGE.md +54 -0
  175. package/src/components/Link/IMPLEMENTATION_NOTES.md +19 -0
  176. package/src/components/Link/Link.module.css +110 -0
  177. package/src/components/Link/Link.stories.tsx +58 -0
  178. package/src/components/Link/Link.test.tsx +1 -0
  179. package/src/components/Link/Link.tsx +67 -0
  180. package/src/components/Link/USAGE.md +56 -0
  181. package/src/components/Link/index.ts +1 -0
  182. package/src/components/Loader/LOADER_IMPLEMENTATION_NOTES.md +24 -0
  183. package/src/components/Loader/Loader.animate.dom.test.tsx +82 -0
  184. package/src/components/Loader/Loader.module.css +63 -0
  185. package/src/components/Loader/Loader.stories.tsx +123 -0
  186. package/src/components/Loader/Loader.test.tsx +1 -0
  187. package/src/components/Loader/Loader.tsx +69 -0
  188. package/src/components/Loader/USAGE.md +44 -0
  189. package/src/components/Menu/MENU_IMPLEMENTATION_NOTES.md +88 -0
  190. package/src/components/Menu/Menu.module.css +280 -0
  191. package/src/components/Menu/Menu.stories.tsx +371 -0
  192. package/src/components/Menu/Menu.test.tsx +1 -0
  193. package/src/components/Menu/Menu.tsx +373 -0
  194. package/src/components/Menu/USAGE.md +55 -0
  195. package/src/components/Modal/MODAL_IMPLEMENTATION_NOTES.md +32 -0
  196. package/src/components/Modal/Modal.module.css +302 -0
  197. package/src/components/Modal/Modal.stories.tsx +105 -0
  198. package/src/components/Modal/Modal.test.tsx +1 -0
  199. package/src/components/Modal/Modal.tsx +368 -0
  200. package/src/components/Modal/USAGE.md +58 -0
  201. package/src/components/Modal/index.ts +1 -0
  202. package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +24 -0
  203. package/src/components/NumberInput/NumberInput.module.css +302 -0
  204. package/src/components/NumberInput/NumberInput.stories.tsx +96 -0
  205. package/src/components/NumberInput/NumberInput.test.tsx +1 -0
  206. package/src/components/NumberInput/NumberInput.tsx +157 -0
  207. package/src/components/NumberInput/USAGE.md +52 -0
  208. package/src/components/Pagination/IMPLEMENTATION_NOTES.md +16 -0
  209. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  210. package/src/components/Pagination/Pagination.module.css +188 -0
  211. package/src/components/Pagination/Pagination.stories.tsx +48 -0
  212. package/src/components/Pagination/Pagination.test.tsx +1 -0
  213. package/src/components/Pagination/Pagination.tsx +265 -0
  214. package/src/components/Pagination/USAGE.md +36 -0
  215. package/src/components/Pagination/index.ts +5 -0
  216. package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +116 -0
  217. package/src/components/Panel/Panel.module.css +209 -0
  218. package/src/components/Panel/Panel.stories.tsx +227 -0
  219. package/src/components/Panel/Panel.test.tsx +1 -0
  220. package/src/components/Panel/Panel.tsx +269 -0
  221. package/src/components/Panel/USAGE.md +56 -0
  222. package/src/components/Popover/IMPLEMENTATION_NOTES.md +77 -0
  223. package/src/components/Popover/Popover.module.css +86 -0
  224. package/src/components/Popover/Popover.stories.tsx +133 -0
  225. package/src/components/Popover/Popover.test.tsx +1 -0
  226. package/src/components/Popover/Popover.tsx +142 -0
  227. package/src/components/Popover/USAGE.md +61 -0
  228. package/src/components/Popover/index.ts +1 -0
  229. package/src/components/Radio/Radio.module.css +231 -0
  230. package/src/components/Radio/Radio.stories.tsx +93 -0
  231. package/src/components/Radio/Radio.test.tsx +1 -0
  232. package/src/components/Radio/Radio.tsx +168 -0
  233. package/src/components/Radio/RadioGroup.stories.tsx +117 -0
  234. package/src/components/Radio/RadioGroup.tsx +124 -0
  235. package/src/components/Radio/USAGE.md +36 -0
  236. package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
  237. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  238. package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
  239. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +163 -0
  240. package/src/components/ReadOnlyField/ReadOnlyField.test.tsx +1 -0
  241. package/src/components/ReadOnlyField/ReadOnlyField.tsx +114 -0
  242. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  243. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  244. package/src/components/ReadOnlyField/USAGE.md +44 -0
  245. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  246. package/src/components/ReadOnlyField/index.ts +5 -0
  247. package/src/components/RecursicaThemeProvider/USAGE.md +60 -0
  248. package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +25 -0
  249. package/src/components/SegmentedControl/SegmentedControl.module.css +185 -0
  250. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +106 -0
  251. package/src/components/SegmentedControl/SegmentedControl.test.tsx +1 -0
  252. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -0
  253. package/src/components/SegmentedControl/USAGE.md +55 -0
  254. package/src/components/Slider/IMPLEMENTATION_NOTES.md +92 -0
  255. package/src/components/Slider/Slider.module.css +588 -0
  256. package/src/components/Slider/Slider.stories.tsx +282 -0
  257. package/src/components/Slider/Slider.test.tsx +1 -0
  258. package/src/components/Slider/Slider.tsx +391 -0
  259. package/src/components/Slider/USAGE.md +56 -0
  260. package/src/components/Stack/STACK_IMPLEMENTATION_NOTES.md +4 -0
  261. package/src/components/Stack/Stack.module.css +7 -0
  262. package/src/components/Stack/Stack.stories.tsx +108 -0
  263. package/src/components/Stack/Stack.test.tsx +1 -0
  264. package/src/components/Stack/Stack.tsx +62 -0
  265. package/src/components/Stack/USAGE.md +48 -0
  266. package/src/components/Stepper/IMPLEMENTATION_NOTES.md +28 -0
  267. package/src/components/Stepper/Stepper.module.css +404 -0
  268. package/src/components/Stepper/Stepper.stories.tsx +148 -0
  269. package/src/components/Stepper/Stepper.test.tsx +1 -0
  270. package/src/components/Stepper/Stepper.tsx +93 -0
  271. package/src/components/Stepper/USAGE.md +41 -0
  272. package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +64 -0
  273. package/src/components/Switch/Switch.module.css +279 -0
  274. package/src/components/Switch/Switch.stories.tsx +114 -0
  275. package/src/components/Switch/Switch.test.tsx +1 -0
  276. package/src/components/Switch/Switch.tsx +177 -0
  277. package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
  278. package/src/components/Switch/SwitchGroup.tsx +127 -0
  279. package/src/components/Switch/USAGE.md +42 -0
  280. package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +78 -0
  281. package/src/components/Table/Table.icons.tsx +40 -0
  282. package/src/components/Table/Table.module.css +444 -0
  283. package/src/components/Table/Table.stories.tsx +140 -0
  284. package/src/components/Table/Table.test.tsx +1 -0
  285. package/src/components/Table/Table.tsx +243 -0
  286. package/src/components/Table/USAGE.md +78 -0
  287. package/src/components/Tabs/IMPLEMENTATION_NOTES.md +27 -0
  288. package/src/components/Tabs/Tabs.module.css +591 -0
  289. package/src/components/Tabs/Tabs.stories.tsx +154 -0
  290. package/src/components/Tabs/Tabs.test.tsx +1 -0
  291. package/src/components/Tabs/Tabs.tsx +114 -0
  292. package/src/components/Tabs/USAGE.md +45 -0
  293. package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +12 -0
  294. package/src/components/Text/Text.module.css +13 -0
  295. package/src/components/Text/Text.stories.tsx +75 -0
  296. package/src/components/Text/Text.test.tsx +1 -0
  297. package/src/components/Text/Text.tsx +53 -0
  298. package/src/components/Text/USAGE.md +40 -0
  299. package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +7 -0
  300. package/src/components/TextArea/TextArea.module.css +183 -0
  301. package/src/components/TextArea/TextArea.stories.tsx +94 -0
  302. package/src/components/TextArea/TextArea.test.tsx +1 -0
  303. package/src/components/TextArea/TextArea.tsx +137 -0
  304. package/src/components/TextArea/USAGE.md +48 -0
  305. package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
  306. package/src/components/TextField/TextField.module.css +264 -0
  307. package/src/components/TextField/TextField.stories.tsx +162 -0
  308. package/src/components/TextField/TextField.test.tsx +1 -0
  309. package/src/components/TextField/TextField.tsx +139 -0
  310. package/src/components/TextField/USAGE.md +44 -0
  311. package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +119 -0
  312. package/src/components/TimePicker/TimePicker.module.css +291 -0
  313. package/src/components/TimePicker/TimePicker.stories.tsx +141 -0
  314. package/src/components/TimePicker/TimePicker.test.tsx +1 -0
  315. package/src/components/TimePicker/TimePicker.tsx +304 -0
  316. package/src/components/TimePicker/USAGE.md +57 -0
  317. package/src/components/Timeline/TIMELINE_IMPLEMENTATION_NOTES.md +13 -0
  318. package/src/components/Timeline/Timeline.module.css +373 -0
  319. package/src/components/Timeline/Timeline.stories.tsx +128 -0
  320. package/src/components/Timeline/Timeline.test.tsx +1 -0
  321. package/src/components/Timeline/Timeline.tsx +73 -0
  322. package/src/components/Timeline/TimelineItem.tsx +79 -0
  323. package/src/components/Timeline/USAGE.md +49 -0
  324. package/src/components/Toast/TOAST_IMPLEMENTATION_NOTES.md +39 -0
  325. package/src/components/Toast/Toast.module.css +184 -0
  326. package/src/components/Toast/Toast.stories.tsx +67 -0
  327. package/src/components/Toast/Toast.test.tsx +1 -0
  328. package/src/components/Toast/Toast.tsx +82 -0
  329. package/src/components/Toast/USAGE.md +50 -0
  330. package/src/components/Toast/index.ts +1 -0
  331. package/src/components/Tooltip/TOOLTIP_IMPLEMENTATION_NOTES.md +85 -0
  332. package/src/components/Tooltip/Tooltip.module.css +91 -0
  333. package/src/components/Tooltip/Tooltip.stories.tsx +165 -0
  334. package/src/components/Tooltip/Tooltip.test.tsx +1 -0
  335. package/src/components/Tooltip/Tooltip.tsx +117 -0
  336. package/src/components/Tooltip/USAGE.md +80 -0
  337. package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +111 -0
  338. package/src/components/TransferList/TransferList.module.css +182 -0
  339. package/src/components/TransferList/TransferList.stories.tsx +121 -0
  340. package/src/components/TransferList/TransferList.test.tsx +1 -0
  341. package/src/components/TransferList/TransferList.tsx +419 -0
  342. package/src/components/TransferList/USAGE.md +77 -0
  343. package/src/components/Tree/IMPLEMENTATION_NOTES.md +48 -0
  344. package/src/components/Tree/Tree.module.css +232 -0
  345. package/src/components/Tree/Tree.stories.tsx +104 -0
  346. package/src/components/Tree/Tree.test.tsx +1 -0
  347. package/src/components/Tree/Tree.tsx +220 -0
  348. package/src/components/Tree/USAGE.md +96 -0
  349. package/src/components/Tree/index.ts +1 -0
  350. package/src/components/index.ts +52 -0
  351. package/src/env.d.ts +7 -0
  352. package/src/index.ts +179 -0
  353. package/src/types/index.ts +9 -0
  354. package/src/types/mantine.d.ts +7 -0
  355. package/src/types/scss-modules.d.ts +9 -0
  356. package/src/utils/ColorSchemeWrapper.tsx +27 -0
  357. package/src/utils/RequireAccessibleLabel.ts +1 -0
  358. package/src/utils/copyToClipboard.ts +36 -0
  359. package/src/utils/filterStylingProps.test.ts +85 -0
  360. package/src/utils/filterStylingProps.ts +150 -0
  361. package/src/utils/index.ts +1 -0
  362. package/src/utils/renderRichOption.tsx +65 -0
@@ -0,0 +1,417 @@
1
+ import React, { forwardRef, useEffect, useRef, useState } from "react";
2
+ import { type InputWrapperProps } from "@mantine/core";
3
+ import {
4
+ filterStylingProps,
5
+ type RecursicaOverStyled,
6
+ } from "../../utils/filterStylingProps";
7
+ import { Button } from "../Button/Button";
8
+ import { Chip } from "../Chip/Chip";
9
+ import {
10
+ FormControlWrapper,
11
+ type RecursicaFormControlWrapperProps,
12
+ } from "../FormControlWrapper/FormControlWrapper";
13
+ import styles from "./FileInput.module.css";
14
+
15
+ import {
16
+ fileMatchesAccept,
17
+ type RecursicaFileUploadItem,
18
+ type RecursicaFileInputProps as BaseRecursicaFileInputProps,
19
+ } from "@recursica/adapter-common";
20
+ export type { RecursicaFileUploadItem };
21
+
22
+ export interface RecursicaFileInputProps
23
+ extends Omit<
24
+ React.HTMLAttributes<HTMLDivElement>,
25
+ "children" | "onDrop" | "onChange"
26
+ >,
27
+ Pick<
28
+ InputWrapperProps,
29
+ "label" | "error" | "required" | "withAsterisk" | "id"
30
+ >,
31
+ Omit<
32
+ RecursicaFormControlWrapperProps,
33
+ "controlMaxWidth" | "controlMinWidth"
34
+ >,
35
+ BaseRecursicaFileInputProps {}
36
+
37
+ export type FileInputProps = RecursicaOverStyled<RecursicaFileInputProps>;
38
+
39
+ function UploadIcon() {
40
+ return (
41
+ <svg
42
+ xmlns="http://www.w3.org/2000/svg"
43
+ viewBox="0 0 24 24"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ strokeWidth="2"
47
+ strokeLinecap="round"
48
+ strokeLinejoin="round"
49
+ aria-hidden
50
+ >
51
+ <path d="M12 3v12" />
52
+ <path d="M7 8l5-5 5 5" />
53
+ <path d="M4 21h16" />
54
+ </svg>
55
+ );
56
+ }
57
+
58
+ function ClearIcon() {
59
+ return (
60
+ <svg
61
+ xmlns="http://www.w3.org/2000/svg"
62
+ viewBox="0 0 24 24"
63
+ fill="none"
64
+ stroke="currentColor"
65
+ strokeWidth="2"
66
+ strokeLinecap="round"
67
+ strokeLinejoin="round"
68
+ aria-hidden
69
+ >
70
+ <line x1="18" y1="6" x2="6" y2="18" />
71
+ <line x1="6" y1="6" x2="18" y2="18" />
72
+ </svg>
73
+ );
74
+ }
75
+
76
+ /**
77
+ * A single-line, `TextField`-shaped control for choosing one or more files, sharing
78
+ * `FileUpload`'s selection/validation interface (`accept`/`maxSize`/`maxFiles`, `readOnly`)
79
+ * behind a different presentation.
80
+ *
81
+ * @example
82
+ * <FileInput
83
+ * label="Resume"
84
+ * files={files}
85
+ * onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
86
+ * onFileRemove={() => setFiles([])}
87
+ * />
88
+ */
89
+ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
90
+ function FileInput(props, ref) {
91
+ const {
92
+ overStyled = false,
93
+ formLayout = "stacked",
94
+
95
+ // Label & Wrapper Maps
96
+ labelSize,
97
+ labelAlignment,
98
+ labelOptionalText,
99
+ labelWithEditIcon,
100
+ labelActionArea,
101
+ onLabelEditClick,
102
+
103
+ label,
104
+ assistiveText,
105
+ assistiveWithIcon,
106
+ error,
107
+ required,
108
+ withAsterisk,
109
+ id,
110
+ className,
111
+ style,
112
+ disabled,
113
+ readOnly,
114
+
115
+ files,
116
+ onFilesAdded,
117
+ onFileRemove,
118
+ accept,
119
+ multiple = false,
120
+ maxSize,
121
+ maxFiles,
122
+ onFilesRejected,
123
+ invalidFileTypeMessage = "File type not accepted",
124
+ maxFilesMessage = multiple
125
+ ? `Maximum of ${maxFiles} files allowed`
126
+ : "Only one file is allowed",
127
+ icon,
128
+ clearIcon,
129
+ placeholder = "Select a file...",
130
+ browseLabel = "Choose file",
131
+ removeFileLabel = "Remove",
132
+ clearLabel = "Clear",
133
+ ...rest
134
+ } = props;
135
+
136
+ const sanitizedProps = filterStylingProps(rest, overStyled);
137
+ const restRecord = sanitizedProps as Record<string, unknown>;
138
+
139
+ const interactive = !disabled && !readOnly;
140
+ const hasFiles = !!files && files.length > 0;
141
+
142
+ const inputRef = useRef<HTMLInputElement>(null);
143
+
144
+ // Whether the most recent drop/pick attempt included a file that failed the `accept` check —
145
+ // surfaced as the control's error state (see `effectiveError` below), same as FileUpload.
146
+ const [invalidTypeRejected, setInvalidTypeRejected] = useState(false);
147
+ // Whether the most recent drop/pick attempt included a file past the effective cap — 1 in
148
+ // single-file mode, `maxFiles` in multiple-file mode.
149
+ const [tooManyFilesRejected, setTooManyFilesRejected] = useState(false);
150
+
151
+ const handleFiles = (incoming: FileList | File[]) => {
152
+ if (!interactive) return;
153
+ const list = Array.from(incoming);
154
+ if (list.length === 0) return;
155
+
156
+ // Single-file mode always replaces the current selection rather than adding to it, so it
157
+ // never counts the existing file against the cap — the effective cap is just 1.
158
+ const effectiveMaxFiles = multiple ? maxFiles : 1;
159
+ const currentCount = multiple ? (files?.length ?? 0) : 0;
160
+
161
+ const accepted: File[] = [];
162
+ const rejected: File[] = [];
163
+ let hasInvalidType = false;
164
+ let hasTooMany = false;
165
+ for (const file of list) {
166
+ const isInvalidType = !fileMatchesAccept(file, accept);
167
+ if (isInvalidType) hasInvalidType = true;
168
+ const isTooLarge = maxSize !== undefined && file.size > maxSize;
169
+ const wouldExceedMax =
170
+ effectiveMaxFiles !== undefined &&
171
+ currentCount + accepted.length >= effectiveMaxFiles;
172
+ if (wouldExceedMax) hasTooMany = true;
173
+ const isRejected = isInvalidType || isTooLarge || wouldExceedMax;
174
+ (isRejected ? rejected : accepted).push(file);
175
+ }
176
+ setInvalidTypeRejected(hasInvalidType);
177
+ setTooManyFilesRejected(hasTooMany);
178
+ if (accepted.length > 0) onFilesAdded?.(accepted);
179
+ if (rejected.length > 0) onFilesRejected?.(rejected);
180
+ };
181
+
182
+ // Counts nested dragenter/dragleave pairs (they fire for every child element the pointer
183
+ // crosses, not just the root itself) so the drag-over visual state only clears once the
184
+ // pointer has actually left the control, not just moved between its children. Mirrors
185
+ // FileUpload's dropzone.
186
+ const dragCounterRef = useRef(0);
187
+ const [isDragging, setIsDragging] = useState(false);
188
+
189
+ const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
190
+ event.preventDefault();
191
+ dragCounterRef.current += 1;
192
+ setIsDragging(true);
193
+ };
194
+
195
+ const handleDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
196
+ event.preventDefault();
197
+ dragCounterRef.current -= 1;
198
+ if (dragCounterRef.current <= 0) {
199
+ dragCounterRef.current = 0;
200
+ setIsDragging(false);
201
+ }
202
+ };
203
+
204
+ const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
205
+ // Required so the browser treats this element as a valid drop target.
206
+ event.preventDefault();
207
+ };
208
+
209
+ const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
210
+ event.preventDefault();
211
+ dragCounterRef.current = 0;
212
+ setIsDragging(false);
213
+ handleFiles(event.dataTransfer.files);
214
+ };
215
+
216
+ const openFilePicker = () => {
217
+ if (!interactive) return;
218
+ inputRef.current?.click();
219
+ };
220
+
221
+ const handleRootKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
222
+ if (event.key !== "Enter" && event.key !== " ") return;
223
+ event.preventDefault();
224
+ openFilePicker();
225
+ };
226
+
227
+ const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
228
+ if (event.target.files) handleFiles(event.target.files);
229
+ // Reset so picking the same file again still fires a change event.
230
+ event.target.value = "";
231
+ };
232
+
233
+ const handleClearAll = () => {
234
+ if (!interactive || !files || files.length === 0) return;
235
+ files.forEach((item) => onFileRemove?.(item.id ?? item.file.name));
236
+ };
237
+
238
+ // Roving tabindex across the file chip list (single- or multiple-file mode): only the
239
+ // "active" chip's delete icon is a tab stop, and Left/Right/Up/Down move it — same pattern
240
+ // as FileUpload, see FILEINPUT_IMPLEMENTATION_NOTES.md.
241
+ const [activeChipIndex, setActiveChipIndex] = useState(0);
242
+ const deleteIconRefs = useRef<Array<HTMLSpanElement | null>>([]);
243
+ const prevFileCountRef = useRef(files?.length ?? 0);
244
+
245
+ useEffect(() => {
246
+ const count = files?.length ?? 0;
247
+ if (count > 0 && count < prevFileCountRef.current) {
248
+ const nextIndex = Math.min(activeChipIndex, count - 1);
249
+ setActiveChipIndex(nextIndex);
250
+ deleteIconRefs.current[nextIndex]?.focus();
251
+ }
252
+ prevFileCountRef.current = count;
253
+ // Only react to the file list itself shrinking/growing, not to activeChipIndex changes.
254
+ // eslint-disable-next-line react-hooks/exhaustive-deps
255
+ }, [files]);
256
+
257
+ const handleChipRowKeyDown = (
258
+ event: React.KeyboardEvent<HTMLDivElement>,
259
+ ) => {
260
+ const count = files?.length ?? 0;
261
+ if (count === 0) return;
262
+ let nextIndex: number | undefined;
263
+ if (event.key === "ArrowRight" || event.key === "ArrowDown") {
264
+ nextIndex = (activeChipIndex + 1) % count;
265
+ } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
266
+ nextIndex = (activeChipIndex - 1 + count) % count;
267
+ }
268
+ if (nextIndex === undefined) return;
269
+ event.preventDefault();
270
+ event.stopPropagation();
271
+ setActiveChipIndex(nextIndex);
272
+ deleteIconRefs.current[nextIndex]?.focus();
273
+ };
274
+
275
+ // The built-in `accept`/cap-mismatch message is only shown when the integrator hasn't
276
+ // supplied their own `error` — an explicit error always wins.
277
+ const effectiveError =
278
+ error ??
279
+ (invalidTypeRejected
280
+ ? invalidFileTypeMessage
281
+ : tooManyFilesRejected
282
+ ? maxFilesMessage
283
+ : undefined);
284
+
285
+ const wrapperClass = className
286
+ ? `${styles.layoutOverride} ${className}`
287
+ : styles.layoutOverride;
288
+
289
+ return (
290
+ <FormControlWrapper
291
+ overStyled={overStyled as true}
292
+ className={wrapperClass}
293
+ style={style}
294
+ formLayout={formLayout}
295
+ labelSize={labelSize}
296
+ labelAlignment={labelAlignment}
297
+ labelOptionalText={labelOptionalText}
298
+ labelWithEditIcon={labelWithEditIcon}
299
+ labelActionArea={labelActionArea}
300
+ onLabelEditClick={onLabelEditClick}
301
+ label={label}
302
+ assistiveText={assistiveText}
303
+ assistiveWithIcon={assistiveWithIcon}
304
+ error={effectiveError}
305
+ required={required}
306
+ withAsterisk={withAsterisk}
307
+ id={id}
308
+ controlMaxWidth="var(--file-input-control-max-width)"
309
+ controlMinWidth="var(--file-input-control-min-width)"
310
+ >
311
+ <div
312
+ ref={ref}
313
+ {...restRecord}
314
+ className={styles.root}
315
+ role="button"
316
+ aria-label={browseLabel}
317
+ aria-disabled={disabled ? "true" : undefined}
318
+ tabIndex={interactive ? 0 : -1}
319
+ data-disabled={disabled ? "true" : undefined}
320
+ data-readonly={readOnly ? "true" : undefined}
321
+ data-error={effectiveError ? "true" : undefined}
322
+ data-dragging={isDragging ? "true" : undefined}
323
+ onClick={interactive ? openFilePicker : undefined}
324
+ onKeyDown={interactive ? handleRootKeyDown : undefined}
325
+ onDragEnter={interactive ? handleDragEnter : undefined}
326
+ onDragLeave={interactive ? handleDragLeave : undefined}
327
+ onDragOver={interactive ? handleDragOver : undefined}
328
+ onDrop={interactive ? handleDrop : undefined}
329
+ >
330
+ <span className={styles.leadingIcon} aria-hidden>
331
+ {icon ?? <UploadIcon />}
332
+ </span>
333
+
334
+ <div className={styles.content}>
335
+ {!hasFiles && (
336
+ <span className={styles.value} data-placeholder="true">
337
+ {placeholder}
338
+ </span>
339
+ )}
340
+
341
+ {hasFiles && (
342
+ <div
343
+ className={styles.chipRow}
344
+ onKeyDown={readOnly ? undefined : handleChipRowKeyDown}
345
+ >
346
+ {files!.map((item: RecursicaFileUploadItem, index) => {
347
+ const itemId = item.id ?? item.file.name;
348
+ return (
349
+ <span
350
+ key={itemId}
351
+ className={styles.chipWrapper}
352
+ onClick={(e) => e.stopPropagation()}
353
+ >
354
+ <Chip
355
+ checked={false}
356
+ tabIndex={-1}
357
+ deleteLabel={readOnly ? undefined : removeFileLabel}
358
+ deleteTabIndex={
359
+ !readOnly && index === activeChipIndex ? 0 : -1
360
+ }
361
+ deleteIconRef={(el) => {
362
+ deleteIconRefs.current[index] = el;
363
+ }}
364
+ onDelete={
365
+ readOnly || disabled
366
+ ? undefined
367
+ : () => onFileRemove?.(itemId)
368
+ }
369
+ >
370
+ {item.file.name}
371
+ </Chip>
372
+ </span>
373
+ );
374
+ })}
375
+ </div>
376
+ )}
377
+ </div>
378
+
379
+ {hasFiles && !readOnly && (
380
+ <Button
381
+ overStyled
382
+ variant="text"
383
+ size="small"
384
+ icon={clearIcon ?? <ClearIcon />}
385
+ aria-label={clearLabel}
386
+ className={styles.trailingIcon}
387
+ disabled={disabled}
388
+ onClick={(e) => {
389
+ e.preventDefault();
390
+ e.stopPropagation();
391
+ handleClearAll();
392
+ }}
393
+ onKeyDown={(e) => {
394
+ if (e.key !== "Enter" && e.key !== " ") return;
395
+ e.preventDefault();
396
+ e.stopPropagation();
397
+ handleClearAll();
398
+ }}
399
+ />
400
+ )}
401
+
402
+ <input
403
+ ref={inputRef}
404
+ type="file"
405
+ hidden
406
+ accept={accept}
407
+ multiple={multiple}
408
+ disabled={!interactive}
409
+ onChange={handleInputChange}
410
+ />
411
+ </div>
412
+ </FormControlWrapper>
413
+ );
414
+ },
415
+ );
416
+
417
+ FileInput.displayName = "FileInput";
@@ -0,0 +1,145 @@
1
+ # FileInput - Usage Guide
2
+
3
+ This document describes how to integrate and use the `FileInput` component in your projects using `@recursica/adapter-mantine-v8`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { FileInput } from "@recursica/adapter-mantine-v8";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ `FileInput` is a **controlled** component: it never stores the selected file(s) itself. `onFilesAdded` reports newly picked/dropped files, `onFileRemove` reports which file was removed (or cleared), and you own the `files` array in between.
18
+
19
+ It shares `FileUpload`'s selection/validation interface, but is presented as a single-line, `TextField`-shaped control instead of a dropzone — every selected file renders as a removable chip in a horizontally scrollable row, whether `multiple` is set or not.
20
+
21
+ ```tsx
22
+ import React, { useState } from "react";
23
+ import { FileInput } from "@recursica/adapter-mantine-v8";
24
+ import { type RecursicaFileUploadItem } from "@recursica/adapter-common";
25
+
26
+ export default function Demo() {
27
+ const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
28
+
29
+ return (
30
+ <FileInput
31
+ label="Resume"
32
+ files={files}
33
+ onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
34
+ onFileRemove={() => setFiles([])}
35
+ />
36
+ );
37
+ }
38
+ ```
39
+
40
+ Note that a single-file `onFilesAdded` handler typically **replaces** `files` wholesale (as above)
41
+ rather than appending — picking a new file in single-file mode always replaces the current one.
42
+
43
+ ---
44
+
45
+ ## 3. Props Reference
46
+
47
+ | Prop | Type | Description |
48
+ | ------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
49
+ | `files` | `RecursicaFileUploadItem[]` | Files currently selected. Each item is `{ file: File; id?: string }`. |
50
+ | `onFilesAdded` | `(files: File[]) => void` | Called with newly picked/dropped files. Only the new files — merge them into `files` yourself. |
51
+ | `onFileRemove` | `(id: string) => void` | Called with a file's `id` (or `file.name` if no `id` was given) when it's removed via a chip's remove icon or the trailing clear button. |
52
+ | `accept` | `string` | Native `accept` attribute (e.g. `".pdf,.png"` or `"image/*"`) — constrains the picker dialog, and is also enforced against dropped files (via `onFilesRejected`), since the browser never applies `accept` to a `drop` event itself. |
53
+ | `multiple` | `boolean` | Whether more than one file can be selected/dropped at once. Defaults to `false`, unlike `FileUpload` (defaults to `true`). |
54
+ | `maxSize` | `number` | Maximum size per file, in bytes. Oversized files go to `onFilesRejected` instead of `onFilesAdded`. |
55
+ | `maxFiles` | `number` | Maximum total number of files allowed. Only meaningful when `multiple` is `true` — single-file mode always caps at 1 regardless of this prop. |
56
+ | `onFilesRejected` | `(files: File[]) => void` | Called with files rejected for exceeding `maxSize`/`maxFiles` (or the single-file cap) or not matching `accept`. |
57
+ | `invalidFileTypeMessage` | `React.ReactNode` | Error message shown when a file is rejected for not matching `accept`. Defaults to `"File type not accepted"`. An explicit `error` prop always takes priority over this. |
58
+ | `maxFilesMessage` | `React.ReactNode` | Error message shown when a file is rejected for exceeding the cap. Defaults to `"Maximum of {maxFiles} files allowed"` when `multiple`, or `"Only one file is allowed"` otherwise. An explicit `error` prop always wins. |
59
+ | `icon` | `React.ReactNode` | Leading icon shown inside the control. Defaults to the built-in upload icon. |
60
+ | `clearIcon` | `React.ReactNode` | Trailing clear-all icon shown once a file is selected. Defaults to the built-in X icon. |
61
+ | `placeholder` | `React.ReactNode` | Text shown when no file is selected. Defaults to `"Select a file..."`. |
62
+ | `browseLabel` | `string` | Screen-reader label for the control itself (it's the sole interactive/focusable surface, there being no separate "Browse" button). Defaults to `"Choose file"`. |
63
+ | `removeFileLabel` | `string` | Screen-reader label for a file chip's remove button. Defaults to `"Remove"`. |
64
+ | `clearLabel` | `string` | Screen-reader label (`aria-label`) for the trailing clear-all `Button`. Defaults to `"Clear"`. |
65
+ | `disabled` | `boolean` | Disables the control and its clear/remove icons. |
66
+ | `readOnly` | `boolean` | Renders `files` as a static, non-interactive display with no clear/remove icons, and disables picking or dropping new files. |
67
+
68
+ `FileInput` also accepts the standard Recursica form-control props (`label`, `assistiveText`, `error`, `required`, `withAsterisk`, `formLayout`, `labelSize`, `labelAlignment`, `labelOptionalText`, `labelWithEditIcon`, `onLabelEditClick`) — see [FormControlWrapper's USAGE.md](../FormControlWrapper/USAGE.md) for how these behave.
69
+
70
+ ---
71
+
72
+ ## 4. Multiple Files
73
+
74
+ ```tsx
75
+ <FileInput
76
+ label="Attachments"
77
+ assistiveText="Up to 5 files"
78
+ multiple
79
+ files={files}
80
+ onFilesAdded={(added) =>
81
+ setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
82
+ }
83
+ onFileRemove={(id) =>
84
+ setFiles((prev) =>
85
+ prev.filter((item) => (item.id ?? item.file.name) !== id),
86
+ )
87
+ }
88
+ />
89
+ ```
90
+
91
+ With `multiple`, the same horizontally scrollable row of removable chips (the same `Chip`
92
+ component `FileUpload` uses) can hold more than one file, and the trailing `Button` clears the
93
+ entire selection at once rather than removing a single file.
94
+
95
+ ---
96
+
97
+ ## 5. Rejecting Oversized or Wrong-Type Files
98
+
99
+ Works exactly like `FileUpload`:
100
+
101
+ ```tsx
102
+ <FileInput
103
+ label="Resume"
104
+ assistiveText="PDF only, max 5MB"
105
+ accept=".pdf"
106
+ maxSize={5 * 1024 * 1024}
107
+ files={files}
108
+ onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
109
+ onFileRemove={() => setFiles([])}
110
+ />
111
+ ```
112
+
113
+ A mismatched or oversized file puts the control into its error state automatically — no need to
114
+ wire `onFilesRejected` into your own `error` prop just to show something. Override the message
115
+ with `invalidFileTypeMessage`/`maxFilesMessage`, or pass your own `error` prop to take over the
116
+ error state entirely.
117
+
118
+ ---
119
+
120
+ ## 6. Read-Only Display
121
+
122
+ ```tsx
123
+ <FileInput
124
+ label="Submitted Files"
125
+ assistiveText="Submitted files cannot be changed"
126
+ multiple
127
+ readOnly
128
+ files={files}
129
+ />
130
+ ```
131
+
132
+ Renders `files` as a static display with no clear/remove icons, and the control is no longer
133
+ focusable or clickable.
134
+
135
+ ---
136
+
137
+ ## 7. Design System Integration
138
+
139
+ All Recursica components in the `@recursica/adapter-mantine-v8` package adhere strictly to design system spacing, scaling, and behavior patterns.
140
+
141
+ > [!IMPORTANT]
142
+ >
143
+ > - **Anti-override protection**: Rogue style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
144
+ > - **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.
145
+ > - **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.