@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.
- package/.storybook/commonArgTypes.ts +148 -0
- package/ARCHITECTURE.md +18 -0
- package/CHANGELOG.md +582 -0
- package/LICENSE +21 -0
- package/OVERSTYLING.md +59 -0
- package/README.md +70 -0
- package/SETUP.md +92 -0
- package/USAGE.md +57 -0
- package/dist/adapter-mui-v7.cjs +292 -0
- package/dist/adapter-mui-v7.cjs.map +1 -0
- package/dist/adapter-mui-v7.css +1 -0
- package/dist/adapter-mui-v7.js +40757 -0
- package/dist/adapter-mui-v7.js.map +1 -0
- package/dist/index.d.ts +2350 -0
- package/dist/vite.svg +1 -0
- package/docs/PHILOSOPHY.md +39 -0
- package/llms.txt +76 -0
- package/package.json +151 -0
- package/recursica_brand.json +2677 -0
- package/recursica_tokens.json +799 -0
- package/recursica_ui-kit.json +14981 -0
- package/recursica_variables_scoped.css +30489 -0
- package/src/GlobalExemptions.modules.css +7 -0
- package/src/Introduction.stories.tsx +184 -0
- package/src/OverStyling.tsx +87 -0
- package/src/Version.tsx +37 -0
- package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +107 -0
- package/src/components/Accordion/Accordion.module.css +362 -0
- package/src/components/Accordion/Accordion.stories.tsx +198 -0
- package/src/components/Accordion/Accordion.test.tsx +1 -0
- package/src/components/Accordion/Accordion.tsx +285 -0
- package/src/components/Accordion/USAGE.md +43 -0
- package/src/components/Accordion/index.ts +1 -0
- package/src/components/AssistiveElement/ASSISTIVEELEMENT_IMPLEMENTATION_NOTES.md +59 -0
- package/src/components/AssistiveElement/AssistiveElement.module.css +121 -0
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +68 -0
- package/src/components/AssistiveElement/AssistiveElement.test.tsx +1 -0
- package/src/components/AssistiveElement/AssistiveElement.tsx +94 -0
- package/src/components/AssistiveElement/USAGE.md +36 -0
- package/src/components/AssistiveElement/index.ts +1 -0
- package/src/components/AutoComplete/AutoComplete.module.css +456 -0
- package/src/components/AutoComplete/AutoComplete.stories.tsx +336 -0
- package/src/components/AutoComplete/AutoComplete.test.tsx +1 -0
- package/src/components/AutoComplete/AutoComplete.tsx +266 -0
- package/src/components/AutoComplete/IMPLEMENTATION_NOTES.md +37 -0
- package/src/components/AutoComplete/USAGE.md +68 -0
- package/src/components/AutoComplete/index.ts +1 -0
- package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +36 -0
- package/src/components/Avatar/Avatar.module.css +336 -0
- package/src/components/Avatar/Avatar.stories.tsx +92 -0
- package/src/components/Avatar/Avatar.test.tsx +1 -0
- package/src/components/Avatar/Avatar.tsx +88 -0
- package/src/components/Avatar/USAGE.md +38 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Badge/Badge.module.css +115 -0
- package/src/components/Badge/Badge.stories.tsx +78 -0
- package/src/components/Badge/Badge.test.tsx +1 -0
- package/src/components/Badge/Badge.tsx +61 -0
- package/src/components/Badge/USAGE.md +40 -0
- package/src/components/Badge/index.ts +1 -0
- package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +51 -0
- package/src/components/Breadcrumb/Breadcrumb.module.css +51 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
- package/src/components/Breadcrumb/Breadcrumb.test.tsx +1 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +70 -0
- package/src/components/Breadcrumb/USAGE.md +42 -0
- package/src/components/Breadcrumb/index.ts +1 -0
- package/src/components/Button/BUTTON_IMPLEMENTATION_NOTES.md +123 -0
- package/src/components/Button/Button.module.css +413 -0
- package/src/components/Button/Button.stories.tsx +196 -0
- package/src/components/Button/Button.styleIsolation.dom.test.tsx +98 -0
- package/src/components/Button/Button.tsx +156 -0
- package/src/components/Button/USAGE.md +46 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +15 -0
- package/src/components/Card/Card.module.css +155 -0
- package/src/components/Card/Card.stories.tsx +131 -0
- package/src/components/Card/Card.test.tsx +1 -0
- package/src/components/Card/Card.tsx +202 -0
- package/src/components/Card/USAGE.md +57 -0
- package/src/components/Card/index.ts +1 -0
- package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +14 -0
- package/src/components/Checkbox/Checkbox.module.css +286 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +97 -0
- package/src/components/Checkbox/Checkbox.test.tsx +1 -0
- package/src/components/Checkbox/Checkbox.tsx +259 -0
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +148 -0
- package/src/components/Checkbox/USAGE.md +36 -0
- package/src/components/Checkbox/index.ts +6 -0
- package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +102 -0
- package/src/components/Chip/Chip.module.css +283 -0
- package/src/components/Chip/Chip.stories.tsx +154 -0
- package/src/components/Chip/Chip.test.tsx +1 -0
- package/src/components/Chip/Chip.tsx +168 -0
- package/src/components/Chip/USAGE.md +55 -0
- package/src/components/Chip/index.ts +1 -0
- package/src/components/Container/Container.stories.tsx +118 -0
- package/src/components/Container/Container.test.tsx +1 -0
- package/src/components/Container/Container.tsx +51 -0
- package/src/components/Container/IMPLEMENTATION_NOTES.md +5 -0
- package/src/components/Container/USAGE.md +48 -0
- package/src/components/Container/index.ts +1 -0
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +29 -0
- package/src/components/DatePicker/DatePicker.icons.tsx +35 -0
- package/src/components/DatePicker/DatePicker.module.css +464 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +163 -0
- package/src/components/DatePicker/DatePicker.test.tsx +1 -0
- package/src/components/DatePicker/DatePicker.tsx +229 -0
- package/src/components/DatePicker/USAGE.md +49 -0
- package/src/components/DatePicker/index.ts +1 -0
- package/src/components/Dropdown/BareDropdown.tsx +168 -0
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +45 -0
- package/src/components/Dropdown/Dropdown.icons.tsx +33 -0
- package/src/components/Dropdown/Dropdown.module.css +535 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +252 -0
- package/src/components/Dropdown/Dropdown.test.tsx +1 -0
- package/src/components/Dropdown/Dropdown.tsx +319 -0
- package/src/components/Dropdown/USAGE.md +68 -0
- package/src/components/Dropdown/index.ts +1 -0
- package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +153 -0
- package/src/components/FileInput/FileInput.module.css +268 -0
- package/src/components/FileInput/FileInput.stories.tsx +313 -0
- package/src/components/FileInput/FileInput.test.tsx +1 -0
- package/src/components/FileInput/FileInput.tsx +415 -0
- package/src/components/FileInput/USAGE.md +145 -0
- package/src/components/FileInput/index.ts +1 -0
- package/src/components/FileUpload/FILEUPLOAD_IMPLEMENTATION_NOTES.md +249 -0
- package/src/components/FileUpload/FileUpload.module.css +200 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +362 -0
- package/src/components/FileUpload/FileUpload.test.tsx +1 -0
- package/src/components/FileUpload/FileUpload.tsx +352 -0
- package/src/components/FileUpload/USAGE.md +199 -0
- package/src/components/FileUpload/index.ts +1 -0
- package/src/components/Flex/Flex.stories.tsx +128 -0
- package/src/components/Flex/Flex.test.tsx +1 -0
- package/src/components/Flex/Flex.tsx +89 -0
- package/src/components/Flex/USAGE.md +41 -0
- package/src/components/Flex/index.ts +1 -0
- package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +118 -0
- package/src/components/FormControlLayout/FormControlLayout.test.tsx +1 -0
- package/src/components/FormControlLayout/FormControlLayout.tsx +72 -0
- package/src/components/FormControlLayout/USAGE.md +40 -0
- package/src/components/FormControlLayout/index.ts +1 -0
- package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +35 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +87 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +129 -0
- package/src/components/FormControlWrapper/FormControlWrapper.test.tsx +1 -0
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +149 -0
- package/src/components/FormControlWrapper/USAGE.md +40 -0
- package/src/components/FormControlWrapper/index.ts +1 -0
- package/src/components/Grid/Grid.module.css +72 -0
- package/src/components/Grid/Grid.stories.tsx +161 -0
- package/src/components/Grid/Grid.test.tsx +1 -0
- package/src/components/Grid/Grid.tsx +155 -0
- package/src/components/Grid/IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/Grid/USAGE.md +53 -0
- package/src/components/Grid/index.ts +1 -0
- package/src/components/Group/Group.stories.tsx +120 -0
- package/src/components/Group/Group.test.tsx +1 -0
- package/src/components/Group/Group.tsx +116 -0
- package/src/components/Group/USAGE.md +41 -0
- package/src/components/Group/index.ts +1 -0
- package/src/components/Heading/Heading.stories.tsx +50 -0
- package/src/components/Heading/Heading.test.tsx +1 -0
- package/src/components/Heading/Heading.tsx +28 -0
- package/src/components/Heading/USAGE.md +36 -0
- package/src/components/Heading/index.ts +1 -0
- package/src/components/HoverCard/HoverCard.module.css +107 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +172 -0
- package/src/components/HoverCard/HoverCard.test.tsx +1 -0
- package/src/components/HoverCard/HoverCard.tsx +145 -0
- package/src/components/HoverCard/USAGE.md +45 -0
- package/src/components/HoverCard/index.ts +1 -0
- package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +27 -0
- package/src/components/Label/Label.module.css +169 -0
- package/src/components/Label/Label.stories.tsx +131 -0
- package/src/components/Label/Label.test.tsx +1 -0
- package/src/components/Label/Label.tsx +96 -0
- package/src/components/Label/USAGE.md +36 -0
- package/src/components/Label/index.ts +1 -0
- package/src/components/Layer/USAGE.md +50 -0
- package/src/components/Link/LINK_IMPLEMENTATION_NOTES.md +9 -0
- package/src/components/Link/Link.module.css +105 -0
- package/src/components/Link/Link.stories.tsx +58 -0
- package/src/components/Link/Link.test.tsx +1 -0
- package/src/components/Link/Link.tsx +45 -0
- package/src/components/Link/USAGE.md +40 -0
- package/src/components/Link/index.ts +1 -0
- package/src/components/Loader/LOADER_IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/Loader/Loader.animate.dom.test.tsx +72 -0
- package/src/components/Loader/Loader.module.css +175 -0
- package/src/components/Loader/Loader.stories.tsx +123 -0
- package/src/components/Loader/Loader.test.tsx +1 -0
- package/src/components/Loader/Loader.tsx +72 -0
- package/src/components/Loader/USAGE.md +42 -0
- package/src/components/Loader/index.ts +1 -0
- package/src/components/Menu/IMPLEMENTATION_NOTES.md +5 -0
- package/src/components/Menu/Menu.module.css +283 -0
- package/src/components/Menu/Menu.stories.tsx +405 -0
- package/src/components/Menu/Menu.test.tsx +1 -0
- package/src/components/Menu/Menu.tsx +103 -0
- package/src/components/Menu/USAGE.md +53 -0
- package/src/components/Menu/index.ts +1 -0
- package/src/components/Modal/MODAL_IMPLEMENTATION_NOTES.md +31 -0
- package/src/components/Modal/Modal.module.css +302 -0
- package/src/components/Modal/Modal.stories.tsx +105 -0
- package/src/components/Modal/Modal.test.tsx +1 -0
- package/src/components/Modal/Modal.tsx +231 -0
- package/src/components/Modal/USAGE.md +40 -0
- package/src/components/Modal/index.ts +1 -0
- package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +93 -0
- package/src/components/NumberInput/NumberInput.module.css +303 -0
- package/src/components/NumberInput/NumberInput.stories.tsx +96 -0
- package/src/components/NumberInput/NumberInput.test.tsx +1 -0
- package/src/components/NumberInput/NumberInput.tsx +273 -0
- package/src/components/NumberInput/USAGE.md +36 -0
- package/src/components/NumberInput/index.ts +1 -0
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +40 -0
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +197 -0
- package/src/components/Pagination/Pagination.stories.tsx +48 -0
- package/src/components/Pagination/Pagination.test.tsx +1 -0
- package/src/components/Pagination/Pagination.tsx +123 -0
- package/src/components/Pagination/USAGE.md +36 -0
- package/src/components/Pagination/index.ts +1 -0
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +133 -0
- package/src/components/Panel/Panel.module.css +231 -0
- package/src/components/Panel/Panel.stories.tsx +227 -0
- package/src/components/Panel/Panel.test.tsx +1 -0
- package/src/components/Panel/Panel.tsx +186 -0
- package/src/components/Panel/USAGE.md +46 -0
- package/src/components/Panel/index.ts +1 -0
- package/src/components/Popover/IMPLEMENTATION_NOTES.md +22 -0
- package/src/components/Popover/Popover.module.css +118 -0
- package/src/components/Popover/Popover.stories.tsx +133 -0
- package/src/components/Popover/Popover.test.tsx +1 -0
- package/src/components/Popover/Popover.tsx +267 -0
- package/src/components/Popover/USAGE.md +69 -0
- package/src/components/Popover/index.ts +1 -0
- package/src/components/Radio/RADIO_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Radio/Radio.module.css +247 -0
- package/src/components/Radio/Radio.stories.tsx +93 -0
- package/src/components/Radio/Radio.test.tsx +1 -0
- package/src/components/Radio/Radio.tsx +223 -0
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +122 -0
- package/src/components/Radio/USAGE.md +36 -0
- package/src/components/Radio/index.ts +2 -0
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +163 -0
- package/src/components/ReadOnlyField/ReadOnlyField.test.tsx +1 -0
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +108 -0
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/USAGE.md +36 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +2 -0
- package/src/components/RecursicaThemeProvider/USAGE.md +60 -0
- package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +14 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +201 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +106 -0
- package/src/components/SegmentedControl/SegmentedControl.test.tsx +1 -0
- package/src/components/SegmentedControl/SegmentedControl.tsx +149 -0
- package/src/components/SegmentedControl/USAGE.md +49 -0
- package/src/components/SegmentedControl/index.ts +1 -0
- package/src/components/Slider/IMPLEMENTATION_NOTES.md +55 -0
- package/src/components/Slider/Slider.module.css +635 -0
- package/src/components/Slider/Slider.stories.tsx +282 -0
- package/src/components/Slider/Slider.test.tsx +1 -0
- package/src/components/Slider/Slider.tsx +435 -0
- package/src/components/Slider/USAGE.md +56 -0
- package/src/components/Slider/index.ts +1 -0
- package/src/components/Stack/Stack.stories.tsx +108 -0
- package/src/components/Stack/Stack.test.tsx +1 -0
- package/src/components/Stack/Stack.tsx +43 -0
- package/src/components/Stack/USAGE.md +41 -0
- package/src/components/Stack/index.ts +1 -0
- package/src/components/Stepper/IMPLEMENTATION_NOTES.md +120 -0
- package/src/components/Stepper/Stepper.module.css +474 -0
- package/src/components/Stepper/Stepper.stories.tsx +157 -0
- package/src/components/Stepper/Stepper.test.tsx +1 -0
- package/src/components/Stepper/Stepper.tsx +240 -0
- package/src/components/Stepper/USAGE.md +45 -0
- package/src/components/Stepper/index.ts +1 -0
- package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +147 -0
- package/src/components/Switch/Switch.module.css +346 -0
- package/src/components/Switch/Switch.stories.tsx +114 -0
- package/src/components/Switch/Switch.test.tsx +1 -0
- package/src/components/Switch/Switch.tsx +294 -0
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +156 -0
- package/src/components/Switch/USAGE.md +36 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +75 -0
- package/src/components/Table/Table.module.css +462 -0
- package/src/components/Table/Table.stories.tsx +154 -0
- package/src/components/Table/Table.test.tsx +1 -0
- package/src/components/Table/Table.tsx +250 -0
- package/src/components/Table/USAGE.md +90 -0
- package/src/components/Table/index.ts +1 -0
- package/src/components/Tabs/IMPLEMENTATION_NOTES.md +76 -0
- package/src/components/Tabs/Tabs.module.css +801 -0
- package/src/components/Tabs/Tabs.stories.tsx +174 -0
- package/src/components/Tabs/Tabs.test.tsx +1 -0
- package/src/components/Tabs/Tabs.tsx +103 -0
- package/src/components/Tabs/USAGE.md +61 -0
- package/src/components/Tabs/index.ts +1 -0
- package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +10 -0
- package/src/components/Text/Text.stories.tsx +75 -0
- package/src/components/Text/Text.test.tsx +1 -0
- package/src/components/Text/Text.tsx +19 -0
- package/src/components/Text/USAGE.md +40 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +42 -0
- package/src/components/TextArea/TextArea.module.css +203 -0
- package/src/components/TextArea/TextArea.stories.tsx +94 -0
- package/src/components/TextArea/TextArea.test.tsx +1 -0
- package/src/components/TextArea/TextArea.tsx +162 -0
- package/src/components/TextArea/USAGE.md +38 -0
- package/src/components/TextArea/index.ts +1 -0
- package/src/components/TextField/TextField.module.css +271 -0
- package/src/components/TextField/TextField.stories.tsx +162 -0
- package/src/components/TextField/TextField.test.tsx +1 -0
- package/src/components/TextField/TextField.tsx +182 -0
- package/src/components/TextField/USAGE.md +38 -0
- package/src/components/TextField/index.ts +1 -0
- package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +89 -0
- package/src/components/TimePicker/TimePicker.module.css +262 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +155 -0
- package/src/components/TimePicker/TimePicker.test.tsx +1 -0
- package/src/components/TimePicker/TimePicker.tsx +277 -0
- package/src/components/TimePicker/USAGE.md +61 -0
- package/src/components/TimePicker/index.ts +1 -0
- package/src/components/Timeline/IMPLEMENTATION_NOTES.md +42 -0
- package/src/components/Timeline/Timeline.module.css +363 -0
- package/src/components/Timeline/Timeline.stories.tsx +128 -0
- package/src/components/Timeline/Timeline.test.tsx +1 -0
- package/src/components/Timeline/Timeline.tsx +90 -0
- package/src/components/Timeline/TimelineItem.tsx +95 -0
- package/src/components/Timeline/USAGE.md +41 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Toast/TOAST_IMPLEMENTATION_NOTES.md +37 -0
- package/src/components/Toast/Toast.module.css +193 -0
- package/src/components/Toast/Toast.stories.tsx +67 -0
- package/src/components/Toast/Toast.test.tsx +1 -0
- package/src/components/Toast/Toast.tsx +121 -0
- package/src/components/Toast/USAGE.md +42 -0
- package/src/components/Toast/index.ts +1 -0
- package/src/components/Tooltip/Tooltip.module.css +83 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +165 -0
- package/src/components/Tooltip/Tooltip.test.tsx +1 -0
- package/src/components/Tooltip/Tooltip.tsx +78 -0
- package/src/components/Tooltip/USAGE.md +40 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +140 -0
- package/src/components/TransferList/TransferList.module.css +174 -0
- package/src/components/TransferList/TransferList.stories.tsx +121 -0
- package/src/components/TransferList/TransferList.test.tsx +1 -0
- package/src/components/TransferList/TransferList.tsx +419 -0
- package/src/components/TransferList/USAGE.md +77 -0
- package/src/components/TransferList/index.ts +1 -0
- package/src/components/Tree/IMPLEMENTATION_NOTES.md +53 -0
- package/src/components/Tree/Tree.module.css +272 -0
- package/src/components/Tree/Tree.stories.tsx +104 -0
- package/src/components/Tree/Tree.test.tsx +1 -0
- package/src/components/Tree/Tree.tsx +298 -0
- package/src/components/Tree/USAGE.md +99 -0
- package/src/components/Tree/index.ts +1 -0
- package/src/components/Typography/TYPOGRAPHY_IMPLEMENTATION_NOTES.md +17 -0
- package/src/components/Typography/Typography.module.css +13 -0
- package/src/components/Typography/Typography.test.tsx +1 -0
- package/src/components/Typography/Typography.tsx +43 -0
- package/src/components/Typography/USAGE.md +40 -0
- package/src/components/Typography/index.ts +1 -0
- package/src/components/index.ts +50 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +194 -0
- package/src/types/index.ts +9 -0
- package/src/types/scss-modules.d.ts +9 -0
- package/src/utils/ColorSchemeWrapper.tsx +23 -0
- package/src/utils/RequireAccessibleLabel.ts +1 -0
- package/src/utils/copyToClipboard.ts +36 -0
- package/src/utils/filterStylingProps.test.ts +85 -0
- package/src/utils/filterStylingProps.ts +143 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/renderRichOption.tsx +51 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { AutoComplete } from "./AutoComplete";
|
|
3
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
4
|
+
import { renderRichOptionContent } from "../../utils/renderRichOption";
|
|
5
|
+
import styles from "./AutoComplete.module.css";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof AutoComplete> = {
|
|
8
|
+
title: "UI-Kit/AutoComplete",
|
|
9
|
+
component: AutoComplete,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
The \`AutoComplete\` primitive provides a text input with a dropdown menu for displaying suggestions as the user types.
|
|
16
|
+
|
|
17
|
+
### Architectural Decoupling
|
|
18
|
+
Recursica wraps the internal Mantine \`<Autocomplete>\` component inside the \`WithReadOnlyWrapper\`, ensuring it integrates perfectly with the strict design system form architecture.
|
|
19
|
+
|
|
20
|
+
### Examples
|
|
21
|
+
Always structure horizontal architectures via the generic \`formLayout\` parameter.
|
|
22
|
+
\`\`\`tsx
|
|
23
|
+
<AutoComplete
|
|
24
|
+
label="Country"
|
|
25
|
+
assistiveText="Select your country of residence."
|
|
26
|
+
data={["United States", "Canada", "Mexico", "United Kingdom", "France"]}
|
|
27
|
+
formLayout="stacked"
|
|
28
|
+
/>
|
|
29
|
+
\`\`\`
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
argTypes: {
|
|
35
|
+
...formControlArgTypes,
|
|
36
|
+
disabled: {
|
|
37
|
+
control: "boolean",
|
|
38
|
+
description:
|
|
39
|
+
"Maps the formal disabled variable states structurally to the input core.",
|
|
40
|
+
},
|
|
41
|
+
error: {
|
|
42
|
+
control: "text",
|
|
43
|
+
description:
|
|
44
|
+
"Applies the strict error string boundary rendering invalid structures seamlessly.",
|
|
45
|
+
},
|
|
46
|
+
required: {
|
|
47
|
+
control: "boolean",
|
|
48
|
+
},
|
|
49
|
+
label: {
|
|
50
|
+
control: "text",
|
|
51
|
+
},
|
|
52
|
+
assistiveText: {
|
|
53
|
+
control: "text",
|
|
54
|
+
},
|
|
55
|
+
readOnly: {
|
|
56
|
+
control: "boolean",
|
|
57
|
+
description:
|
|
58
|
+
"Toggles structural read-only data presentation explicitly blocking standard component bindings.",
|
|
59
|
+
},
|
|
60
|
+
wrapItemText: {
|
|
61
|
+
control: "boolean",
|
|
62
|
+
description:
|
|
63
|
+
"Wraps option label/supportingText onto additional lines instead of truncating with an ellipsis.",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default meta;
|
|
69
|
+
|
|
70
|
+
type Story = StoryObj<typeof AutoComplete>;
|
|
71
|
+
|
|
72
|
+
export const Default: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
disabled: false,
|
|
75
|
+
readOnly: false,
|
|
76
|
+
label: "Country Selection",
|
|
77
|
+
placeholder: "Start typing...",
|
|
78
|
+
data: [
|
|
79
|
+
"United States",
|
|
80
|
+
"Canada",
|
|
81
|
+
"Mexico",
|
|
82
|
+
"United Kingdom",
|
|
83
|
+
"France",
|
|
84
|
+
"Germany",
|
|
85
|
+
"Japan",
|
|
86
|
+
"Brazil",
|
|
87
|
+
"India",
|
|
88
|
+
"Australia",
|
|
89
|
+
],
|
|
90
|
+
assistiveText: "Search from a predefined list of countries.",
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const FormsSideBySide: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
label: "Primary Region",
|
|
97
|
+
placeholder: "Select region...",
|
|
98
|
+
data: ["US-East", "US-West", "EU-Central", "AP-South", "SA-East"],
|
|
99
|
+
assistiveText:
|
|
100
|
+
"Select the primary region for the deployment. This violently long string tests native textual wrapping safely mapping alongside inputs.",
|
|
101
|
+
formLayout: "side-by-side",
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const WithLeadingIcon: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
label: "Search Projects",
|
|
108
|
+
placeholder: "Project name...",
|
|
109
|
+
data: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"],
|
|
110
|
+
leftSection: (
|
|
111
|
+
<svg
|
|
112
|
+
width="24"
|
|
113
|
+
height="24"
|
|
114
|
+
viewBox="0 0 24 24"
|
|
115
|
+
fill="none"
|
|
116
|
+
stroke="currentColor"
|
|
117
|
+
strokeWidth="2"
|
|
118
|
+
strokeLinecap="round"
|
|
119
|
+
strokeLinejoin="round"
|
|
120
|
+
>
|
|
121
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
122
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
123
|
+
</svg>
|
|
124
|
+
),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const WithTrailingIcon: Story = {
|
|
129
|
+
args: {
|
|
130
|
+
label: "Validation URL",
|
|
131
|
+
placeholder: "https://recursica.dev",
|
|
132
|
+
data: [
|
|
133
|
+
"https://recursica.dev",
|
|
134
|
+
"https://beta.recursica.dev",
|
|
135
|
+
"https://api.recursica.dev",
|
|
136
|
+
],
|
|
137
|
+
rightSection: (
|
|
138
|
+
<svg
|
|
139
|
+
width="24"
|
|
140
|
+
height="24"
|
|
141
|
+
viewBox="0 0 24 24"
|
|
142
|
+
fill="none"
|
|
143
|
+
stroke="currentColor"
|
|
144
|
+
strokeWidth="2"
|
|
145
|
+
strokeLinecap="round"
|
|
146
|
+
strokeLinejoin="round"
|
|
147
|
+
>
|
|
148
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
149
|
+
</svg>
|
|
150
|
+
),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const UserIcon = (
|
|
155
|
+
<svg
|
|
156
|
+
width="16"
|
|
157
|
+
height="16"
|
|
158
|
+
viewBox="0 0 24 24"
|
|
159
|
+
fill="none"
|
|
160
|
+
stroke="currentColor"
|
|
161
|
+
strokeWidth="2"
|
|
162
|
+
strokeLinecap="round"
|
|
163
|
+
strokeLinejoin="round"
|
|
164
|
+
>
|
|
165
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
166
|
+
<circle cx="12" cy="7" r="4"></circle>
|
|
167
|
+
</svg>
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
export const WithRichOptions: Story = {
|
|
171
|
+
args: {
|
|
172
|
+
label: "Assignee",
|
|
173
|
+
placeholder: "Search team members...",
|
|
174
|
+
data: [
|
|
175
|
+
{
|
|
176
|
+
value: "jdoe",
|
|
177
|
+
label: "Jane Doe",
|
|
178
|
+
leadingIcon: UserIcon,
|
|
179
|
+
supportingText: "jane.doe@example.com",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
value: "asmith",
|
|
183
|
+
label: "Alex Smith",
|
|
184
|
+
leadingIcon: UserIcon,
|
|
185
|
+
supportingText: "alex.smith@example.com",
|
|
186
|
+
},
|
|
187
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
188
|
+
],
|
|
189
|
+
assistiveText:
|
|
190
|
+
"Each option can show a leading icon and supporting text — see MANTINE_ADAPTER_RICH_OPTION_DATA.md.",
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const WithRichOptionsWrapped: Story = {
|
|
195
|
+
args: {
|
|
196
|
+
label: "Assignee",
|
|
197
|
+
placeholder: "Search team members...",
|
|
198
|
+
wrapItemText: true,
|
|
199
|
+
data: [
|
|
200
|
+
{
|
|
201
|
+
value: "jdoe",
|
|
202
|
+
label: "Jane Doe, Senior Staff Engineer, Platform Infrastructure",
|
|
203
|
+
leadingIcon: UserIcon,
|
|
204
|
+
supportingText:
|
|
205
|
+
"jane.doe@example.com — Platform Infrastructure team, on-call rotation lead",
|
|
206
|
+
},
|
|
207
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
208
|
+
],
|
|
209
|
+
assistiveText:
|
|
210
|
+
"wrapItemText=true — long label/supportingText wrap instead of truncating.",
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const optionRowPreviewClassNames = {
|
|
215
|
+
optionContent: styles.optionContent,
|
|
216
|
+
optionIcon: styles.optionIcon,
|
|
217
|
+
optionText: styles.optionText,
|
|
218
|
+
optionTextWrap: styles.optionTextWrap,
|
|
219
|
+
optionSupportingText: styles.optionSupportingText,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const OPTION_ROW_PREVIEW_ITEMS = [
|
|
223
|
+
{
|
|
224
|
+
value: "icon-and-supporting",
|
|
225
|
+
label: "Jane Doe",
|
|
226
|
+
leadingIcon: UserIcon,
|
|
227
|
+
supportingText: "jane.doe@example.com",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
value: "no-icon",
|
|
231
|
+
label: "Alex Smith",
|
|
232
|
+
supportingText:
|
|
233
|
+
"No leadingIcon — label/supportingText shift left, no reserved icon space",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
value: "no-supporting-text",
|
|
237
|
+
label: "Taylor Rivera",
|
|
238
|
+
leadingIcon: UserIcon,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
value: "plain",
|
|
242
|
+
label: "Plain option — no leadingIcon, no supportingText",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
value: "long-text",
|
|
246
|
+
label:
|
|
247
|
+
"A very long option label that, with wrapItemText, wraps onto a second line instead of overflowing the fixed-width dropdown — otherwise it truncates with an ellipsis",
|
|
248
|
+
leadingIcon: UserIcon,
|
|
249
|
+
supportingText:
|
|
250
|
+
"A similarly long supporting text string, to confirm the same wrap-or-truncate behavior applies to it too",
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
// Renders the option row content directly — outside the MUI Popper portal — inside a container
|
|
255
|
+
// sized to Autocomplete's own max-width token. Spacing between rows, icon/supportingText
|
|
256
|
+
// presence-or-absence alignment, and long-text wrapping/truncation are all much easier to inspect
|
|
257
|
+
// this way than by opening the real (portal-rendered) MUI Autocomplete listbox. See
|
|
258
|
+
// MANTINE_ADAPTER_RICH_OPTION_DATA.md.
|
|
259
|
+
const renderOptionRowPreview = (wrapItemText: boolean) => (
|
|
260
|
+
<div
|
|
261
|
+
className={styles.dropdown}
|
|
262
|
+
style={{
|
|
263
|
+
width:
|
|
264
|
+
"var(--recursica_ui-kit_components_autocomplete_variants_layouts_stacked_properties_max-width)",
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
{OPTION_ROW_PREVIEW_ITEMS.map((item) => (
|
|
268
|
+
<div key={item.value} className={styles.option}>
|
|
269
|
+
{renderRichOptionContent(
|
|
270
|
+
item,
|
|
271
|
+
optionRowPreviewClassNames,
|
|
272
|
+
wrapItemText,
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
))}
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// Default: `wrapItemText` is false — label/supportingText truncate to a single line with an
|
|
280
|
+
// ellipsis instead of wrapping.
|
|
281
|
+
export const RichOptionRowPreview: Story = {
|
|
282
|
+
parameters: {
|
|
283
|
+
controls: { disable: true },
|
|
284
|
+
},
|
|
285
|
+
render: () => renderOptionRowPreview(false),
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// `wrapItemText: true` — label/supportingText wrap onto additional lines instead of truncating.
|
|
289
|
+
export const RichOptionRowPreviewWrapped: Story = {
|
|
290
|
+
parameters: {
|
|
291
|
+
controls: { disable: true },
|
|
292
|
+
},
|
|
293
|
+
render: () => renderOptionRowPreview(true),
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
export const Disabled: Story = {
|
|
297
|
+
args: {
|
|
298
|
+
label: "Disabled Deployment Node",
|
|
299
|
+
placeholder: "Disabled primitive map...",
|
|
300
|
+
data: ["Node 1", "Node 2", "Node 3"],
|
|
301
|
+
disabled: true,
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const ErrorState: Story = {
|
|
306
|
+
args: {
|
|
307
|
+
label: "Cluster Failure",
|
|
308
|
+
placeholder: "Failing component instance...",
|
|
309
|
+
data: ["Cluster A", "Cluster B", "Cluster C"],
|
|
310
|
+
defaultValue: "Invalid Cluster",
|
|
311
|
+
error:
|
|
312
|
+
"Critical runtime node disconnect detected traversing DOM architecture.",
|
|
313
|
+
required: true,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export const StaticReadOnly: Story = {
|
|
318
|
+
args: {
|
|
319
|
+
label: "Static ReadOnly Review",
|
|
320
|
+
placeholder: "Ignored...",
|
|
321
|
+
data: ["Option 1", "Option 2"],
|
|
322
|
+
value: "Explicitly Uneditable Bound Output",
|
|
323
|
+
readOnly: true,
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export const EditableReadOnly: Story = {
|
|
328
|
+
args: {
|
|
329
|
+
label: "Editable ReadOnly Review",
|
|
330
|
+
placeholder: "Ignored until active...",
|
|
331
|
+
data: ["Option 1", "Option 2"],
|
|
332
|
+
defaultValue: "Waiting for Edit Execution",
|
|
333
|
+
readOnly: true,
|
|
334
|
+
labelWithEditIcon: true,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future Autocomplete component tests. Add tests here if/when needed.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Autocomplete as MuiAutocomplete,
|
|
4
|
+
type AutocompleteProps as MuiAutocompleteProps,
|
|
5
|
+
TextField as MuiTextField,
|
|
6
|
+
// removed InputWrapperProps
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import {
|
|
9
|
+
type ReadOnlyControlProps,
|
|
10
|
+
type RecursicaComboboxItemWithLabel,
|
|
11
|
+
normalizeComboboxData,
|
|
12
|
+
} from "@recursica/adapter-common";
|
|
13
|
+
import {
|
|
14
|
+
filterStylingProps,
|
|
15
|
+
omitUnsupportedProps,
|
|
16
|
+
mergeClassNames,
|
|
17
|
+
type RecursicaOverStyled,
|
|
18
|
+
} from "../../utils/filterStylingProps";
|
|
19
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
20
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
21
|
+
import { renderRichOptionContent } from "../../utils/renderRichOption";
|
|
22
|
+
import styles from "./AutoComplete.module.css";
|
|
23
|
+
|
|
24
|
+
import { type RecursicaAutocompleteProps as BaseRecursicaAutocompleteProps } from "@recursica/adapter-common";
|
|
25
|
+
|
|
26
|
+
export interface RecursicaAutocompleteProps
|
|
27
|
+
extends Omit<
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
MuiAutocompleteProps<any, any, any, any, "div">,
|
|
30
|
+
| "variant"
|
|
31
|
+
| "size"
|
|
32
|
+
| "wrapperProps"
|
|
33
|
+
| "radius"
|
|
34
|
+
| "renderInput"
|
|
35
|
+
| "classes"
|
|
36
|
+
| "defaultValue"
|
|
37
|
+
>,
|
|
38
|
+
Omit<
|
|
39
|
+
RecursicaFormControlWrapperProps,
|
|
40
|
+
| "controlMaxWidth"
|
|
41
|
+
| "controlMinWidth"
|
|
42
|
+
| "error"
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
| keyof MuiAutocompleteProps<any, any, any, any, "div">
|
|
45
|
+
>,
|
|
46
|
+
ReadOnlyControlProps,
|
|
47
|
+
BaseRecursicaAutocompleteProps {}
|
|
48
|
+
|
|
49
|
+
export type AutoCompleteProps = RecursicaOverStyled<RecursicaAutocompleteProps>;
|
|
50
|
+
|
|
51
|
+
export const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
|
|
52
|
+
function AutoComplete(props, ref) {
|
|
53
|
+
const {
|
|
54
|
+
overStyled = false,
|
|
55
|
+
formLayout = "stacked",
|
|
56
|
+
|
|
57
|
+
// Label & Wrapper Maps
|
|
58
|
+
labelSize,
|
|
59
|
+
labelAlignment,
|
|
60
|
+
labelOptionalText,
|
|
61
|
+
labelWithEditIcon,
|
|
62
|
+
onLabelEditClick,
|
|
63
|
+
|
|
64
|
+
label,
|
|
65
|
+
assistiveText,
|
|
66
|
+
assistiveWithIcon,
|
|
67
|
+
error,
|
|
68
|
+
required,
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
70
|
+
withAsterisk,
|
|
71
|
+
id,
|
|
72
|
+
className,
|
|
73
|
+
style,
|
|
74
|
+
disabled,
|
|
75
|
+
readOnly,
|
|
76
|
+
readOnlyComponent,
|
|
77
|
+
emptyValueComponent,
|
|
78
|
+
value,
|
|
79
|
+
defaultValue,
|
|
80
|
+
data,
|
|
81
|
+
leftSection,
|
|
82
|
+
rightSection,
|
|
83
|
+
placeholder,
|
|
84
|
+
ListboxProps,
|
|
85
|
+
renderOption,
|
|
86
|
+
wrapItemText = false,
|
|
87
|
+
...rest
|
|
88
|
+
} = props;
|
|
89
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
90
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
91
|
+
const UNSUPPORTED_PROPS = [
|
|
92
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
93
|
+
] as const satisfies readonly (keyof MuiAutocompleteProps<
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
any,
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
any,
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
any,
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
any,
|
|
102
|
+
"div"
|
|
103
|
+
>)[];
|
|
104
|
+
|
|
105
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
106
|
+
filterStylingProps(rest, overStyled),
|
|
107
|
+
UNSUPPORTED_PROPS,
|
|
108
|
+
);
|
|
109
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
110
|
+
|
|
111
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely. Note MUI's
|
|
112
|
+
// actual prop is "classes", not "classNames" (that's Mantine's naming) — this used to read
|
|
113
|
+
// the wrong key, silently no-op-ing any caller-supplied classes. The caller-facing slot names
|
|
114
|
+
// below (wrapper/input/dropdown/option) mirror the mantine-adapter's own Autocomplete
|
|
115
|
+
// classNames slots, translated to MUI's real classes slot names (root/inputRoot/listbox/option).
|
|
116
|
+
const callerClasses = restRecord.classes as
|
|
117
|
+
| Partial<Record<string, string>>
|
|
118
|
+
| undefined;
|
|
119
|
+
const mergedClassNames = mergeClassNames(
|
|
120
|
+
{
|
|
121
|
+
root: styles.root,
|
|
122
|
+
inputRoot: styles.input, // Map Mantine .input (wrapper) to MUI's inputRoot
|
|
123
|
+
listbox: styles.dropdown, // Map Mantine .dropdown to MUI's listbox
|
|
124
|
+
option: styles.option,
|
|
125
|
+
},
|
|
126
|
+
callerClasses && {
|
|
127
|
+
root: callerClasses.wrapper,
|
|
128
|
+
inputRoot: callerClasses.input,
|
|
129
|
+
listbox: callerClasses.dropdown,
|
|
130
|
+
option: callerClasses.option,
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const wrapperClass = className
|
|
135
|
+
? `${styles.layoutOverride} ${className}`
|
|
136
|
+
: styles.layoutOverride;
|
|
137
|
+
|
|
138
|
+
const optionClassNames = {
|
|
139
|
+
optionContent: styles.optionContent,
|
|
140
|
+
optionIcon: styles.optionIcon,
|
|
141
|
+
optionText: styles.optionText,
|
|
142
|
+
optionTextWrap: styles.optionTextWrap,
|
|
143
|
+
optionSupportingText: styles.optionSupportingText,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// See Dropdown.tsx's identical use of `normalizeComboboxData` (adapter-common) — items always
|
|
147
|
+
// have a real `label` after this, so `defaultRenderOption` below doesn't need its own fallback.
|
|
148
|
+
const normalizedData = normalizeComboboxData(data);
|
|
149
|
+
|
|
150
|
+
// Default per-option rendering — kept as a fallback so a caller-supplied `renderOption` (an
|
|
151
|
+
// escape hatch, see MANTINE_ADAPTER_RICH_OPTION_DATA.md) still wins.
|
|
152
|
+
const defaultRenderOption = (
|
|
153
|
+
liProps: React.HTMLAttributes<HTMLLIElement> & { key?: React.Key },
|
|
154
|
+
option: unknown,
|
|
155
|
+
) => {
|
|
156
|
+
const { key, ...otherProps } = liProps;
|
|
157
|
+
if (typeof option === "string") {
|
|
158
|
+
return (
|
|
159
|
+
<li key={key} {...otherProps}>
|
|
160
|
+
{option}
|
|
161
|
+
</li>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return (
|
|
165
|
+
<li key={key} {...otherProps}>
|
|
166
|
+
{renderRichOptionContent(
|
|
167
|
+
option as RecursicaComboboxItemWithLabel,
|
|
168
|
+
optionClassNames,
|
|
169
|
+
wrapItemText,
|
|
170
|
+
)}
|
|
171
|
+
</li>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<WithReadOnlyWrapper
|
|
177
|
+
className={wrapperClass}
|
|
178
|
+
style={style as React.CSSProperties}
|
|
179
|
+
controlMaxWidth="var(--autocomplete-control-max-width)"
|
|
180
|
+
controlMinWidth="var(--autocomplete-control-min-width)"
|
|
181
|
+
overStyled={overStyled as true}
|
|
182
|
+
formLayout={formLayout}
|
|
183
|
+
labelSize={labelSize}
|
|
184
|
+
labelAlignment={labelAlignment}
|
|
185
|
+
labelOptionalText={labelOptionalText}
|
|
186
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
187
|
+
onLabelEditClick={onLabelEditClick}
|
|
188
|
+
label={label}
|
|
189
|
+
assistiveText={assistiveText}
|
|
190
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
191
|
+
error={error}
|
|
192
|
+
required={required}
|
|
193
|
+
id={id}
|
|
194
|
+
disabled={disabled}
|
|
195
|
+
readOnly={readOnly}
|
|
196
|
+
readOnlyComponent={readOnlyComponent}
|
|
197
|
+
emptyValueComponent={emptyValueComponent}
|
|
198
|
+
readOnlyType="text"
|
|
199
|
+
readOnlyValue={value !== undefined ? value : defaultValue}
|
|
200
|
+
readOnlyNativeProps={props}
|
|
201
|
+
activeComponent={
|
|
202
|
+
/* Naked Input execution safely decoupled from Mui's macro Input.Wrapper DOM hooks */
|
|
203
|
+
<MuiAutocomplete
|
|
204
|
+
ref={ref}
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
206
|
+
{...(sanitizedProps as any)}
|
|
207
|
+
freeSolo
|
|
208
|
+
disableClearable
|
|
209
|
+
classes={mergedClassNames}
|
|
210
|
+
disabled={disabled}
|
|
211
|
+
value={value}
|
|
212
|
+
defaultValue={defaultValue}
|
|
213
|
+
forcePopupIcon={false}
|
|
214
|
+
data-error={error ? "true" : undefined}
|
|
215
|
+
data-disabled={disabled ? "true" : undefined}
|
|
216
|
+
data-with-left-section={leftSection ? "true" : undefined}
|
|
217
|
+
data-with-right-section={rightSection ? "true" : undefined}
|
|
218
|
+
ListboxProps={{
|
|
219
|
+
...ListboxProps,
|
|
220
|
+
className: mergedClassNames.listbox,
|
|
221
|
+
}}
|
|
222
|
+
slotProps={{
|
|
223
|
+
...(restRecord.slotProps as Record<string, unknown> | undefined),
|
|
224
|
+
popper: {
|
|
225
|
+
...((restRecord.slotProps as { popper?: object } | undefined)
|
|
226
|
+
?.popper ?? {}),
|
|
227
|
+
className: styles.popper,
|
|
228
|
+
},
|
|
229
|
+
}}
|
|
230
|
+
options={normalizedData || []}
|
|
231
|
+
renderOption={renderOption ?? defaultRenderOption}
|
|
232
|
+
renderInput={(params) => {
|
|
233
|
+
const { InputProps, ...restParams } = params;
|
|
234
|
+
return (
|
|
235
|
+
<MuiTextField
|
|
236
|
+
{...restParams}
|
|
237
|
+
placeholder={placeholder}
|
|
238
|
+
variant="standard"
|
|
239
|
+
InputProps={{
|
|
240
|
+
...InputProps,
|
|
241
|
+
startAdornment: leftSection ? (
|
|
242
|
+
<span className={styles.section} data-position="left">
|
|
243
|
+
{leftSection}
|
|
244
|
+
</span>
|
|
245
|
+
) : (
|
|
246
|
+
InputProps.startAdornment
|
|
247
|
+
),
|
|
248
|
+
endAdornment: rightSection ? (
|
|
249
|
+
<span className={styles.section} data-position="right">
|
|
250
|
+
{rightSection}
|
|
251
|
+
</span>
|
|
252
|
+
) : (
|
|
253
|
+
InputProps.endAdornment
|
|
254
|
+
),
|
|
255
|
+
}}
|
|
256
|
+
/>
|
|
257
|
+
);
|
|
258
|
+
}}
|
|
259
|
+
/>
|
|
260
|
+
}
|
|
261
|
+
/>
|
|
262
|
+
);
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
AutoComplete.displayName = "AutoComplete";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Autocomplete Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Rich Option Content (`leadingIcon`/`supportingText`)
|
|
4
|
+
|
|
5
|
+
`data` items accept optional `leadingIcon`/`supportingText` fields, via the shared `RecursicaComboboxItem` type in `@recursica/adapter-common` (see `MANTINE_ADAPTER_RICH_OPTION_DATA.md` at the repo root — proposed against the mantine-adapter, but Forge needs both adapters to accept the same `data` shape, hence the type living in adapter-common rather than being redeclared per adapter). `Autocomplete.tsx` runs `data` through adapter-common's `normalizeComboboxData` first (backfills `label` from `value`, since `label` on the shared type is optional), then installs a default `renderOption` that renders each option's `<li>` via `renderRichOptionContent` (`../../utils/renderRichOption.tsx`, shared with `Dropdown`) — `leadingIcon`+`label`+`supportingText` when either new field is present, or just `label` otherwise. A caller-supplied `renderOption` always wins over the default. The icon is only rendered as a child when `leadingIcon` is set (not a hidden reserved slot), so label/supportingText shift left when there's no icon; the row's `align-items: center` keeps the label vertically centered when there's no `supportingText`.
|
|
6
|
+
|
|
7
|
+
Unlike Dropdown, this doesn't need an explicit `renderValue`-equivalent fix: MUI's `Autocomplete` already separates the closed field's text (driven by `getOptionLabel`, which defaults to `option.label`) from the open dropdown's row rendering (`renderOption`), so enriching `renderOption` alone doesn't touch what the closed field shows.
|
|
8
|
+
|
|
9
|
+
New CSS classes (`.optionContent`/`.optionIcon`/`.optionText`/`.optionSupportingText`) in `Autocomplete.module.css` reuse the menu-item component's icon/supporting-text tokens, matching the mantine-adapter's equivalent addition — no dedicated autocomplete-option tokens exist for either.
|
|
10
|
+
|
|
11
|
+
## `wrapItemText`
|
|
12
|
+
|
|
13
|
+
`label`/`supportingText` default to single-line truncation with an ellipsis (`.optionText > *` — `overflow: clip; overflow-clip-margin: 0.35em; text-overflow: ellipsis; white-space: nowrap`). Passing `wrapItemText` adds `.optionTextWrap` alongside `.optionText`, re-enabling wrapping (`white-space: normal; overflow-wrap: anywhere`) for both children — later-cascade-wins, equal specificity. `renderRichOptionContent` takes `wrapItemText` as a third parameter and combines the two class names when it's true.
|
|
14
|
+
|
|
15
|
+
`.optionText > *` used plain `overflow: hidden` until 2026-08-28 (Matt Massey) — it clipped
|
|
16
|
+
descenders (e.g. "g") whenever `text_line-height` is tighter than the font's natural
|
|
17
|
+
ascent+descent. `overflow-clip-margin` gives ink a small bleed allowance while still clipping
|
|
18
|
+
genuinely overflowing text; same project-wide fix as Chip's `CHIP_IMPLEMENTATION_NOTES.md`.
|
|
19
|
+
|
|
20
|
+
## Missing gap between the input and the open menu (bug fix, Matt Massey, 2026-08-30)
|
|
21
|
+
|
|
22
|
+
MUI's `Autocomplete` sets an inline `margin: 0px` on its own `.MuiAutocomplete-popper`, so the open
|
|
23
|
+
menu rendered flush against the input's bottom border — mantine-adapter's equivalent (`Combobox`,
|
|
24
|
+
built on Mantine's own `Popover`) has an 8px gap there. That gap isn't a Recursica token: it's
|
|
25
|
+
Mantine's own `Popover` untokenized default (`offset: 8` in `@mantine/core`'s `Popover.mjs`), not
|
|
26
|
+
anything mantine-adapter itself wires up — there's no dedicated "menu offset" entry in the token
|
|
27
|
+
schema. Rather than hardcode an unexplained `8px` in adapter-mui-v7, reused
|
|
28
|
+
`--recursica_brand_dimensions_general_default` (also 8px) — the closest real token with a matching
|
|
29
|
+
value — via `slotProps.popper.className` + a new `.popper { margin-top: ...; }` rule (`!important`
|
|
30
|
+
needed: MUI's inline `margin: 0px` otherwise always wins). If the token schema ever adds a real gap
|
|
31
|
+
token for this, prefer it over this one. `Dropdown`'s own MUI `Select`/`MenuProps` popup has the
|
|
32
|
+
identical gap-less behavior, unfixed here — out of scope for this pass, flagged in
|
|
33
|
+
`OPEN_QUESTIONS.txt`.
|
|
34
|
+
|
|
35
|
+
## Selected Option Highlight (bug fix)
|
|
36
|
+
|
|
37
|
+
There was previously no rule at all for "this option matches the current value" — only the shared neutral hover tint, and only while actually hovered. MUI's `Autocomplete` already stamps `aria-selected="true"` onto the matching option (`useAutocomplete.js`'s `getOptionProps`), and `defaultRenderOption` spreads `liProps` (which includes it) straight onto the `<li>`, so the DOM signal was already there — `.option[aria-selected="true"]` now maps it to the same menu-item selected-state background/text-color tokens Dropdown uses. Needs `!important`: MUI's own `.MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected="true"]` rule (`Autocomplete.js`) is a two-class descendant selector — higher specificity than this single-class rule — and paints its own default blue tint otherwise, same reasoning as the padding `!important` above. Verified via Playwright: `aria-selected="true"` plus the token background color both land on the correct option after a plain click-select-reopen.
|