@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,252 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Dropdown } from "./Dropdown";
|
|
4
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
5
|
+
import { renderRichOptionContent } from "../../utils/renderRichOption";
|
|
6
|
+
import styles from "./Dropdown.module.css";
|
|
7
|
+
|
|
8
|
+
type DropdownStoryProps = React.ComponentProps<typeof Dropdown>;
|
|
9
|
+
|
|
10
|
+
const meta: Meta<DropdownStoryProps> = {
|
|
11
|
+
title: "UI-Kit/Dropdown",
|
|
12
|
+
component: Dropdown,
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
"Dropdown provides a selectable list of options, mapping natively over Mantine's Select component encapsulated within the standardized FormControlWrapper.",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
label: "Country Selection",
|
|
24
|
+
assistiveText: "Select your country of origin.",
|
|
25
|
+
placeholder: "Pick value",
|
|
26
|
+
data: ["United States", "Canada", "Mexico", "United Kingdom", "France"],
|
|
27
|
+
disabled: false,
|
|
28
|
+
required: false,
|
|
29
|
+
readOnly: false,
|
|
30
|
+
clearable: false,
|
|
31
|
+
},
|
|
32
|
+
argTypes: {
|
|
33
|
+
disabled: {
|
|
34
|
+
control: "boolean",
|
|
35
|
+
},
|
|
36
|
+
...formControlArgTypes,
|
|
37
|
+
readOnly: {
|
|
38
|
+
control: "boolean",
|
|
39
|
+
},
|
|
40
|
+
clearable: {
|
|
41
|
+
control: "boolean",
|
|
42
|
+
},
|
|
43
|
+
wrapItemText: {
|
|
44
|
+
control: "boolean",
|
|
45
|
+
description:
|
|
46
|
+
"Wraps option label/supportingText onto additional lines instead of truncating with an ellipsis.",
|
|
47
|
+
},
|
|
48
|
+
containerWidth: {
|
|
49
|
+
table: { disable: true },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
|
|
56
|
+
type Story = StoryObj<DropdownStoryProps>;
|
|
57
|
+
|
|
58
|
+
export const Default: Story = {
|
|
59
|
+
args: {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Clearable: Story = {
|
|
63
|
+
args: {
|
|
64
|
+
label: "Clearable Options",
|
|
65
|
+
clearable: true,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const WithLeadingIcon: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
label: "Destination",
|
|
72
|
+
startAdornment: (
|
|
73
|
+
<svg
|
|
74
|
+
width="24"
|
|
75
|
+
height="24"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke="currentColor"
|
|
79
|
+
strokeWidth="2"
|
|
80
|
+
strokeLinecap="round"
|
|
81
|
+
strokeLinejoin="round"
|
|
82
|
+
>
|
|
83
|
+
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
|
|
84
|
+
<circle cx="12" cy="10" r="3"></circle>
|
|
85
|
+
</svg>
|
|
86
|
+
),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const UserIcon = (
|
|
91
|
+
<svg
|
|
92
|
+
width="16"
|
|
93
|
+
height="16"
|
|
94
|
+
viewBox="0 0 24 24"
|
|
95
|
+
fill="none"
|
|
96
|
+
stroke="currentColor"
|
|
97
|
+
strokeWidth="2"
|
|
98
|
+
strokeLinecap="round"
|
|
99
|
+
strokeLinejoin="round"
|
|
100
|
+
>
|
|
101
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
102
|
+
<circle cx="12" cy="7" r="4"></circle>
|
|
103
|
+
</svg>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
export const WithRichOptions: Story = {
|
|
107
|
+
args: {
|
|
108
|
+
label: "Assignee",
|
|
109
|
+
placeholder: "Pick a team member",
|
|
110
|
+
assistiveText:
|
|
111
|
+
"Each option can show a leading icon and supporting text — see MANTINE_ADAPTER_RICH_OPTION_DATA.md.",
|
|
112
|
+
data: [
|
|
113
|
+
{
|
|
114
|
+
value: "jdoe",
|
|
115
|
+
label: "Jane Doe",
|
|
116
|
+
leadingIcon: UserIcon,
|
|
117
|
+
supportingText: "jane.doe@example.com",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
value: "asmith",
|
|
121
|
+
label: "Alex Smith",
|
|
122
|
+
leadingIcon: UserIcon,
|
|
123
|
+
supportingText: "alex.smith@example.com",
|
|
124
|
+
},
|
|
125
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const WithRichOptionsWrapped: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
label: "Assignee",
|
|
133
|
+
placeholder: "Pick a team member",
|
|
134
|
+
wrapItemText: true,
|
|
135
|
+
data: [
|
|
136
|
+
{
|
|
137
|
+
value: "jdoe",
|
|
138
|
+
label: "Jane Doe, Senior Staff Engineer, Platform Infrastructure",
|
|
139
|
+
leadingIcon: UserIcon,
|
|
140
|
+
supportingText:
|
|
141
|
+
"jane.doe@example.com — Platform Infrastructure team, on-call rotation lead",
|
|
142
|
+
},
|
|
143
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
144
|
+
],
|
|
145
|
+
assistiveText:
|
|
146
|
+
"wrapItemText=true — long label/supportingText wrap instead of truncating.",
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const optionRowPreviewClassNames = {
|
|
151
|
+
optionContent: styles.optionContent,
|
|
152
|
+
optionIcon: styles.optionIcon,
|
|
153
|
+
optionText: styles.optionText,
|
|
154
|
+
optionTextWrap: styles.optionTextWrap,
|
|
155
|
+
optionSupportingText: styles.optionSupportingText,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const OPTION_ROW_PREVIEW_ITEMS = [
|
|
159
|
+
{
|
|
160
|
+
value: "icon-and-supporting",
|
|
161
|
+
label: "Jane Doe",
|
|
162
|
+
leadingIcon: UserIcon,
|
|
163
|
+
supportingText: "jane.doe@example.com",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
value: "no-icon",
|
|
167
|
+
label: "Alex Smith",
|
|
168
|
+
supportingText:
|
|
169
|
+
"No leadingIcon — label/supportingText shift left, no reserved icon space",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
value: "no-supporting-text",
|
|
173
|
+
label: "Taylor Rivera",
|
|
174
|
+
leadingIcon: UserIcon,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
value: "plain",
|
|
178
|
+
label: "Plain option — no leadingIcon, no supportingText",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
value: "long-text",
|
|
182
|
+
label:
|
|
183
|
+
"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",
|
|
184
|
+
leadingIcon: UserIcon,
|
|
185
|
+
supportingText:
|
|
186
|
+
"A similarly long supporting text string, to confirm the same wrap-or-truncate behavior applies to it too",
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
// Renders the option row content directly — outside the MUI Menu portal — inside a container
|
|
191
|
+
// sized to Dropdown's own max-width token. Spacing between rows, icon/supportingText presence-
|
|
192
|
+
// or-absence alignment, and long-text wrapping/truncation are all much easier to inspect this way
|
|
193
|
+
// than by opening the real (portal-rendered) MUI Select menu. See
|
|
194
|
+
// MANTINE_ADAPTER_RICH_OPTION_DATA.md.
|
|
195
|
+
const renderOptionRowPreview = (wrapItemText: boolean) => (
|
|
196
|
+
<div
|
|
197
|
+
className={styles.dropdown}
|
|
198
|
+
style={{
|
|
199
|
+
width:
|
|
200
|
+
"var(--recursica_ui-kit_components_dropdown_variants_layouts_stacked_properties_max-width)",
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
{OPTION_ROW_PREVIEW_ITEMS.map((item) => (
|
|
204
|
+
<div key={item.value} className={styles.option}>
|
|
205
|
+
{renderRichOptionContent(
|
|
206
|
+
item,
|
|
207
|
+
optionRowPreviewClassNames,
|
|
208
|
+
wrapItemText,
|
|
209
|
+
)}
|
|
210
|
+
</div>
|
|
211
|
+
))}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Default: `wrapItemText` is false — label/supportingText truncate to a single line with an
|
|
216
|
+
// ellipsis instead of wrapping.
|
|
217
|
+
export const RichOptionRowPreview: Story = {
|
|
218
|
+
parameters: {
|
|
219
|
+
controls: { disable: true },
|
|
220
|
+
},
|
|
221
|
+
render: () => renderOptionRowPreview(false),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// `wrapItemText: true` — label/supportingText wrap onto additional lines instead of truncating.
|
|
225
|
+
export const RichOptionRowPreviewWrapped: Story = {
|
|
226
|
+
parameters: {
|
|
227
|
+
controls: { disable: true },
|
|
228
|
+
},
|
|
229
|
+
render: () => renderOptionRowPreview(true),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const StaticError: Story = {
|
|
233
|
+
args: {
|
|
234
|
+
error: "You must choose a valid destination.",
|
|
235
|
+
value: "Invalid Island",
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const StaticDisabled: Story = {
|
|
240
|
+
args: {
|
|
241
|
+
disabled: true,
|
|
242
|
+
value: "United States",
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const StaticReadOnly: Story = {
|
|
247
|
+
args: {
|
|
248
|
+
label: "Read Only View",
|
|
249
|
+
readOnly: true,
|
|
250
|
+
value: "Canada",
|
|
251
|
+
},
|
|
252
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future Dropdown component tests. Add tests here if/when needed.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import React, { forwardRef, ReactNode, useEffect, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Select as MuiSelect,
|
|
4
|
+
SelectProps as MuiSelectProps,
|
|
5
|
+
SelectChangeEvent,
|
|
6
|
+
MenuItem,
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import {
|
|
9
|
+
type ReadOnlyControlProps,
|
|
10
|
+
normalizeComboboxData,
|
|
11
|
+
} from "@recursica/adapter-common";
|
|
12
|
+
import {
|
|
13
|
+
filterStylingProps,
|
|
14
|
+
omitUnsupportedProps,
|
|
15
|
+
type RecursicaOverStyled,
|
|
16
|
+
} from "../../utils/filterStylingProps";
|
|
17
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
18
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
19
|
+
import { renderRichOptionContent } from "../../utils/renderRichOption";
|
|
20
|
+
import { ChevronIcon, ClearIcon } from "./Dropdown.icons";
|
|
21
|
+
import styles from "./Dropdown.module.css";
|
|
22
|
+
|
|
23
|
+
import { type RecursicaDropdownProps as BaseRecursicaDropdownProps } from "@recursica/adapter-common";
|
|
24
|
+
|
|
25
|
+
// Swapped in for MUI's default `IconComponent` only when we're rendering our own combined
|
|
26
|
+
// clear+chevron `endAdornment` (see `showClear` below) — a stable reference so it isn't recreated
|
|
27
|
+
// every render.
|
|
28
|
+
function HiddenIcon() {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RecursicaDropdownProps
|
|
33
|
+
extends Omit<
|
|
34
|
+
MuiSelectProps,
|
|
35
|
+
| keyof React.HTMLAttributes<HTMLDivElement>
|
|
36
|
+
| "size"
|
|
37
|
+
| "variant"
|
|
38
|
+
| "classes"
|
|
39
|
+
| "inputProps"
|
|
40
|
+
| "SelectDisplayProps"
|
|
41
|
+
| "ref"
|
|
42
|
+
| "error"
|
|
43
|
+
>,
|
|
44
|
+
// `onChange` is swept up by the `keyof React.HTMLAttributes<HTMLDivElement>` omission above
|
|
45
|
+
// (it's a standard DOM attribute name too) even though MUI's own `Select#onChange` has a
|
|
46
|
+
// completely different, non-DOM signature — add it back explicitly with MUI's real signature.
|
|
47
|
+
Pick<MuiSelectProps, "onChange">,
|
|
48
|
+
Omit<
|
|
49
|
+
RecursicaFormControlWrapperProps,
|
|
50
|
+
"controlMaxWidth" | "controlMinWidth" | "onChange"
|
|
51
|
+
>,
|
|
52
|
+
ReadOnlyControlProps,
|
|
53
|
+
BaseRecursicaDropdownProps {}
|
|
54
|
+
|
|
55
|
+
export type DropdownProps = RecursicaOverStyled<RecursicaDropdownProps>;
|
|
56
|
+
|
|
57
|
+
export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
58
|
+
function Dropdown(props, ref) {
|
|
59
|
+
const {
|
|
60
|
+
overStyled = false,
|
|
61
|
+
formLayout = "stacked",
|
|
62
|
+
containerWidth,
|
|
63
|
+
|
|
64
|
+
// Label & Wrapper Maps
|
|
65
|
+
labelSize,
|
|
66
|
+
labelAlignment,
|
|
67
|
+
labelOptionalText,
|
|
68
|
+
labelWithEditIcon,
|
|
69
|
+
onLabelEditClick,
|
|
70
|
+
|
|
71
|
+
label,
|
|
72
|
+
assistiveText,
|
|
73
|
+
assistiveWithIcon,
|
|
74
|
+
error,
|
|
75
|
+
required,
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
77
|
+
withAsterisk,
|
|
78
|
+
id,
|
|
79
|
+
className,
|
|
80
|
+
style,
|
|
81
|
+
disabled,
|
|
82
|
+
readOnly,
|
|
83
|
+
readOnlyComponent,
|
|
84
|
+
emptyValueComponent,
|
|
85
|
+
value,
|
|
86
|
+
defaultValue,
|
|
87
|
+
onChange,
|
|
88
|
+
data,
|
|
89
|
+
startAdornment,
|
|
90
|
+
clearable,
|
|
91
|
+
wrapItemText = false,
|
|
92
|
+
placeholder,
|
|
93
|
+
...rest
|
|
94
|
+
} = props;
|
|
95
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
96
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
97
|
+
const UNSUPPORTED_PROPS = [
|
|
98
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
99
|
+
"variant", // Recursica styles the naked select directly; MUI's standard/filled/outlined unused
|
|
100
|
+
] as const satisfies readonly (keyof MuiSelectProps)[];
|
|
101
|
+
|
|
102
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
103
|
+
filterStylingProps(rest, overStyled),
|
|
104
|
+
UNSUPPORTED_PROPS,
|
|
105
|
+
);
|
|
106
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
107
|
+
|
|
108
|
+
// MUI's basic `Select` has no built-in "clearable" concept (unlike Mantine's `Select`, which
|
|
109
|
+
// manages its own internal value so it can reset itself to `null` on demand) — the clear
|
|
110
|
+
// button needs somewhere to write an empty value to, so the value is lifted here rather than
|
|
111
|
+
// left for MUI's own internal uncontrolled state.
|
|
112
|
+
const [internalValue, setInternalValue] = useState<unknown>(
|
|
113
|
+
() => value ?? defaultValue ?? "",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (value !== undefined) setInternalValue(value);
|
|
118
|
+
}, [value]);
|
|
119
|
+
|
|
120
|
+
const handleChange = (
|
|
121
|
+
event: SelectChangeEvent<unknown>,
|
|
122
|
+
child: ReactNode,
|
|
123
|
+
) => {
|
|
124
|
+
setInternalValue(event.target.value);
|
|
125
|
+
(
|
|
126
|
+
onChange as
|
|
127
|
+
| ((event: SelectChangeEvent<unknown>, child: ReactNode) => void)
|
|
128
|
+
| undefined
|
|
129
|
+
)?.(event, child);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const hasValue =
|
|
133
|
+
internalValue !== "" &&
|
|
134
|
+
internalValue !== null &&
|
|
135
|
+
internalValue !== undefined;
|
|
136
|
+
const showClear = !!clearable && hasValue && !disabled && !readOnly;
|
|
137
|
+
|
|
138
|
+
const handleClear = (event: React.MouseEvent) => {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
event.stopPropagation();
|
|
141
|
+
setInternalValue("");
|
|
142
|
+
(
|
|
143
|
+
onChange as
|
|
144
|
+
| ((event: SelectChangeEvent<unknown>, child: ReactNode) => void)
|
|
145
|
+
| undefined
|
|
146
|
+
)?.({ target: { value: "" } } as SelectChangeEvent<unknown>, null);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const injectedStyles = {
|
|
150
|
+
...((style as React.CSSProperties) || {}),
|
|
151
|
+
width: containerWidth || "100%",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const wrapperClass = className
|
|
155
|
+
? `${styles.layoutOverride} ${className}`
|
|
156
|
+
: styles.layoutOverride;
|
|
157
|
+
|
|
158
|
+
const optionClassNames = {
|
|
159
|
+
optionContent: styles.optionContent,
|
|
160
|
+
optionIcon: styles.optionIcon,
|
|
161
|
+
optionText: styles.optionText,
|
|
162
|
+
optionTextWrap: styles.optionTextWrap,
|
|
163
|
+
optionSupportingText: styles.optionSupportingText,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// See AutoComplete.tsx's identical use of `normalizeComboboxData` (adapter-common) — items
|
|
167
|
+
// always have a real `label` after this, so downstream code doesn't need its own `?? value`
|
|
168
|
+
// fallback at every read.
|
|
169
|
+
const normalizedData = normalizeComboboxData(data);
|
|
170
|
+
|
|
171
|
+
const renderOptions = () => {
|
|
172
|
+
if (!normalizedData) return null;
|
|
173
|
+
return normalizedData.map((item, index) => {
|
|
174
|
+
if (typeof item === "string") {
|
|
175
|
+
return (
|
|
176
|
+
<MenuItem
|
|
177
|
+
key={`${item}-${index}`}
|
|
178
|
+
value={item}
|
|
179
|
+
className={styles.option}
|
|
180
|
+
disableRipple
|
|
181
|
+
// Dropdown.module.css's own selected-state tint (`.option[data-selected="true"]`)
|
|
182
|
+
// needs this explicitly — MUI's own `Mui-selected` class carries its default primary-
|
|
183
|
+
// color tint instead, which is what shows through without it.
|
|
184
|
+
data-selected={item === internalValue ? "true" : undefined}
|
|
185
|
+
>
|
|
186
|
+
{item}
|
|
187
|
+
</MenuItem>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return (
|
|
191
|
+
<MenuItem
|
|
192
|
+
key={`${item.value}-${index}`}
|
|
193
|
+
value={item.value}
|
|
194
|
+
disabled={item.disabled}
|
|
195
|
+
className={styles.option}
|
|
196
|
+
disableRipple
|
|
197
|
+
data-selected={item.value === internalValue ? "true" : undefined}
|
|
198
|
+
>
|
|
199
|
+
{renderRichOptionContent(item, optionClassNames, wrapItemText)}
|
|
200
|
+
</MenuItem>
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// MUI's closed-field display otherwise mirrors the selected `MenuItem`'s children directly —
|
|
206
|
+
// now that those can be a rich icon+label+supportingText row, `renderValue` keeps the closed
|
|
207
|
+
// field showing just the plain label, matching the field's own single-line text control.
|
|
208
|
+
const renderValue = (selected: unknown) => {
|
|
209
|
+
if (selected === "" || selected === undefined || selected === null) {
|
|
210
|
+
return placeholder ? (
|
|
211
|
+
<span className={styles.placeholder}>{placeholder}</span>
|
|
212
|
+
) : (
|
|
213
|
+
""
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const match = normalizedData?.find((item) =>
|
|
217
|
+
typeof item === "string" ? item === selected : item.value === selected,
|
|
218
|
+
);
|
|
219
|
+
if (!match) return "";
|
|
220
|
+
return typeof match === "string" ? match : match.label;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const wrappedStartAdornment = startAdornment ? (
|
|
224
|
+
<span className={styles.section} data-position="left">
|
|
225
|
+
{startAdornment}
|
|
226
|
+
</span>
|
|
227
|
+
) : undefined;
|
|
228
|
+
|
|
229
|
+
const wrappedEndAdornment = showClear ? (
|
|
230
|
+
<span className={styles.section} data-position="right">
|
|
231
|
+
<button
|
|
232
|
+
type="button"
|
|
233
|
+
className={styles.clearButton}
|
|
234
|
+
aria-label="Clear"
|
|
235
|
+
onClick={handleClear}
|
|
236
|
+
>
|
|
237
|
+
<ClearIcon />
|
|
238
|
+
</button>
|
|
239
|
+
<ChevronIcon />
|
|
240
|
+
</span>
|
|
241
|
+
) : undefined;
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<WithReadOnlyWrapper
|
|
245
|
+
className={wrapperClass}
|
|
246
|
+
style={injectedStyles}
|
|
247
|
+
controlMaxWidth="var(--dropdown-control-max-width)"
|
|
248
|
+
controlMinWidth="var(--dropdown-control-min-width)"
|
|
249
|
+
overStyled={overStyled as true}
|
|
250
|
+
formLayout={formLayout}
|
|
251
|
+
labelSize={labelSize}
|
|
252
|
+
labelAlignment={labelAlignment}
|
|
253
|
+
labelOptionalText={labelOptionalText}
|
|
254
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
255
|
+
onLabelEditClick={onLabelEditClick}
|
|
256
|
+
label={label as ReactNode}
|
|
257
|
+
assistiveText={assistiveText}
|
|
258
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
259
|
+
error={!!error}
|
|
260
|
+
required={required}
|
|
261
|
+
id={id}
|
|
262
|
+
readOnly={readOnly}
|
|
263
|
+
readOnlyComponent={readOnlyComponent}
|
|
264
|
+
emptyValueComponent={emptyValueComponent}
|
|
265
|
+
readOnlyType="text"
|
|
266
|
+
readOnlyValue={
|
|
267
|
+
value !== undefined
|
|
268
|
+
? String(value)
|
|
269
|
+
: defaultValue
|
|
270
|
+
? String(defaultValue)
|
|
271
|
+
: undefined
|
|
272
|
+
}
|
|
273
|
+
readOnlyNativeProps={props}
|
|
274
|
+
activeComponent={
|
|
275
|
+
<MuiSelect
|
|
276
|
+
ref={ref}
|
|
277
|
+
{...(sanitizedProps as unknown as MuiSelectProps)}
|
|
278
|
+
disabled={disabled}
|
|
279
|
+
value={internalValue}
|
|
280
|
+
onChange={handleChange}
|
|
281
|
+
error={!!error}
|
|
282
|
+
required={required}
|
|
283
|
+
displayEmpty
|
|
284
|
+
renderValue={renderValue}
|
|
285
|
+
className={styles.root}
|
|
286
|
+
classes={{
|
|
287
|
+
select: styles.input,
|
|
288
|
+
icon: styles.icon,
|
|
289
|
+
}}
|
|
290
|
+
IconComponent={showClear ? HiddenIcon : undefined}
|
|
291
|
+
startAdornment={wrappedStartAdornment}
|
|
292
|
+
endAdornment={wrappedEndAdornment}
|
|
293
|
+
MenuProps={{
|
|
294
|
+
classes: { paper: styles.dropdown },
|
|
295
|
+
}}
|
|
296
|
+
// Dropdown.module.css's error/disabled state rules key off `.root[data-error]`/
|
|
297
|
+
// `[data-disabled]` (the outer Select element, matching the `className={styles.root}`
|
|
298
|
+
// above) — `inputProps` below only reaches the nested accessibility <input>, which that
|
|
299
|
+
// selector never matches, so these need to be set here too. (Previously only set via
|
|
300
|
+
// inputProps, which meant the error border never actually appeared on the Dropdown.)
|
|
301
|
+
data-disabled={disabled ? "true" : undefined}
|
|
302
|
+
data-error={error ? "true" : undefined}
|
|
303
|
+
data-with-left-section={startAdornment ? "true" : undefined}
|
|
304
|
+
data-with-right-section={showClear ? "true" : undefined}
|
|
305
|
+
inputProps={{
|
|
306
|
+
"data-disabled": disabled ? "true" : undefined,
|
|
307
|
+
"data-error": error ? "true" : undefined,
|
|
308
|
+
...(restRecord.inputProps as Record<string, unknown>),
|
|
309
|
+
}}
|
|
310
|
+
>
|
|
311
|
+
{renderOptions()}
|
|
312
|
+
</MuiSelect>
|
|
313
|
+
}
|
|
314
|
+
/>
|
|
315
|
+
);
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
Dropdown.displayName = "Dropdown";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Dropdown - Usage Guide
|
|
2
|
+
|
|
3
|
+
This document describes how to integrate and use the `Dropdown` component in your projects using `@recursica/adapter-mui-v7`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Import Reference
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Dropdown } from "@recursica/adapter-mui-v7";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basic Example
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { Dropdown } from "@recursica/adapter-mui-v7";
|
|
20
|
+
|
|
21
|
+
export default function Demo() {
|
|
22
|
+
return (
|
|
23
|
+
<Dropdown placeholder="Select an option">
|
|
24
|
+
<Dropdown.Item value="1">Option 1</Dropdown.Item>
|
|
25
|
+
<Dropdown.Item value="2">Option 2</Dropdown.Item>
|
|
26
|
+
</Dropdown>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Design System Integration
|
|
34
|
+
|
|
35
|
+
All Recursica components in the `@recursica/adapter-mui-v7` package adhere strictly to design system spacing, scaling, and behavior patterns.
|
|
36
|
+
|
|
37
|
+
> [!IMPORTANT]
|
|
38
|
+
>
|
|
39
|
+
> - **Anti-override protection**: Rogues style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
|
|
40
|
+
> - **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.
|
|
41
|
+
> - **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.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 4. Notes
|
|
46
|
+
|
|
47
|
+
- Pass `startAdornment` for a leading icon, and `clearable` (with a value present) to show a clear button — both render using the dropdown's own icon-color tokens, matching the mantine-adapter's `leftSection`/`clearable` behavior.
|
|
48
|
+
- `onChange` follows MUI's native `Select` signature: `(event: SelectChangeEvent, child: ReactNode) => void`.
|
|
49
|
+
- `data` items can carry a `leadingIcon` and `supportingText`, rendered inside each option row
|
|
50
|
+
(the closed field always shows the plain `label`, matching the mantine-adapter):
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Dropdown
|
|
54
|
+
label="Assignee"
|
|
55
|
+
data={[
|
|
56
|
+
{
|
|
57
|
+
value: "jdoe",
|
|
58
|
+
label: "Jane Doe",
|
|
59
|
+
leadingIcon: <UserIcon />,
|
|
60
|
+
supportingText: "jane.doe@example.com",
|
|
61
|
+
},
|
|
62
|
+
{ value: "asmith", label: "Alex Smith" },
|
|
63
|
+
]}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
By default `label`/`supportingText` truncate to a single line with an ellipsis. Set
|
|
68
|
+
`wrapItemText` to wrap them onto additional lines instead: `<Dropdown data={data} wrapItemText />`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Dropdown";
|