@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,294 @@
|
|
|
1
|
+
import { forwardRef, useContext } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Switch as MuiSwitch,
|
|
4
|
+
type SwitchProps as MuiSwitchProps,
|
|
5
|
+
} from "@mui/material";
|
|
6
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
12
|
+
type RecursicaOverStyled,
|
|
13
|
+
} from "../../utils/filterStylingProps";
|
|
14
|
+
import {
|
|
15
|
+
type RequireAccessibleLabel,
|
|
16
|
+
type RecursicaSwitchProps,
|
|
17
|
+
} from "@recursica/adapter-common";
|
|
18
|
+
import {
|
|
19
|
+
FormControlLayout,
|
|
20
|
+
type FormControlLayoutProps,
|
|
21
|
+
} from "../FormControlLayout/FormControlLayout";
|
|
22
|
+
import { AssistiveElement } from "../AssistiveElement/AssistiveElement";
|
|
23
|
+
import styles from "./Switch.module.css";
|
|
24
|
+
|
|
25
|
+
import { SwitchGroup, SwitchGroupContext } from "./SwitchGroup";
|
|
26
|
+
|
|
27
|
+
// Same glyphs as mantine-adapter's Switch (re-exported from @mantine/core's CheckIcon/CloseIcon,
|
|
28
|
+
// not importable here since adapter-mui-v7 doesn't depend on @mantine/core) — kept pixel-identical
|
|
29
|
+
// so adapter-tester's cross-adapter diff stays clean. Same precedent as Checkbox's inline icons.
|
|
30
|
+
function CheckIcon(props: React.ComponentProps<"svg">) {
|
|
31
|
+
return (
|
|
32
|
+
<svg
|
|
33
|
+
viewBox="0 0 10 7"
|
|
34
|
+
fill="none"
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
<path
|
|
39
|
+
d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z"
|
|
40
|
+
fill="currentColor"
|
|
41
|
+
fillRule="evenodd"
|
|
42
|
+
clipRule="evenodd"
|
|
43
|
+
/>
|
|
44
|
+
</svg>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function CloseIcon(props: React.ComponentProps<"svg">) {
|
|
49
|
+
return (
|
|
50
|
+
<svg
|
|
51
|
+
viewBox="0 0 15 15"
|
|
52
|
+
fill="none"
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<path
|
|
57
|
+
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
|
|
58
|
+
fill="currentColor"
|
|
59
|
+
fillRule="evenodd"
|
|
60
|
+
clipRule="evenodd"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type SwitchWrapperProps = Omit<
|
|
67
|
+
MuiSwitchProps,
|
|
68
|
+
"size" | "color" | "radius" | "variant"
|
|
69
|
+
> &
|
|
70
|
+
RecursicaSwitchProps &
|
|
71
|
+
ReadOnlyControlProps &
|
|
72
|
+
Pick<
|
|
73
|
+
FormControlLayoutProps,
|
|
74
|
+
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
75
|
+
>;
|
|
76
|
+
|
|
77
|
+
export type RecursicaSwitchPropsAlias =
|
|
78
|
+
RequireAccessibleLabel<SwitchWrapperProps>;
|
|
79
|
+
|
|
80
|
+
export type SwitchProps = RecursicaOverStyled<RecursicaSwitchPropsAlias>;
|
|
81
|
+
|
|
82
|
+
type SwitchComponent = React.ForwardRefExoticComponent<
|
|
83
|
+
SwitchProps & React.RefAttributes<HTMLInputElement>
|
|
84
|
+
> & {
|
|
85
|
+
Group: typeof SwitchGroup;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
89
|
+
function Switch(props, ref) {
|
|
90
|
+
const {
|
|
91
|
+
overStyled = false,
|
|
92
|
+
readOnly,
|
|
93
|
+
readOnlyComponent,
|
|
94
|
+
disabled,
|
|
95
|
+
formLayout,
|
|
96
|
+
labelSize,
|
|
97
|
+
controlMaxWidth,
|
|
98
|
+
controlMinWidth,
|
|
99
|
+
label,
|
|
100
|
+
description,
|
|
101
|
+
error,
|
|
102
|
+
style,
|
|
103
|
+
...rest
|
|
104
|
+
} = props;
|
|
105
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
106
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above. We
|
|
107
|
+
// rely strictly on variables from Switch.module.css instead of MUI's own dimension/palette props.
|
|
108
|
+
const UNSUPPORTED_PROPS = [
|
|
109
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
110
|
+
"color", // Recursica hardcodes color="default" below; MUI's palette-driven colors are unused
|
|
111
|
+
] as const satisfies readonly (keyof MuiSwitchProps)[];
|
|
112
|
+
|
|
113
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
114
|
+
filterStylingProps(rest, overStyled),
|
|
115
|
+
UNSUPPORTED_PROPS,
|
|
116
|
+
);
|
|
117
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
118
|
+
|
|
119
|
+
// NOTE: classes.root must stay keyed "root" (MUI's own slot name), but it targets a
|
|
120
|
+
// dedicated .switchRoot CSS class, not .root — .root is this component's own outer
|
|
121
|
+
// wrapper class (applied separately, see finalClass below) and sizing the actual MUI
|
|
122
|
+
// switch span the same way would squeeze that wrapper down to the track's own width.
|
|
123
|
+
// See SWITCH_IMPLEMENTATION_NOTES.md #5.
|
|
124
|
+
// NOTE: no "thumb" key here — the default MuiSwitch-thumb slot classes.thumb targets is
|
|
125
|
+
// never rendered. We supply our own icon/checkedIcon node below (see thumbNode), which
|
|
126
|
+
// replaces that slot outright (confirmed against MUI's SwitchBase/Switch source: icon and
|
|
127
|
+
// checkedIcon substitute the whole default thumb, they don't add to it), so the .thumb
|
|
128
|
+
// class is applied directly to our own node instead.
|
|
129
|
+
// NOTE: MUI's actual prop is "classes", not "classNames" (that's Mantine's naming). Also
|
|
130
|
+
// note "body"/"trackLabel"/"labelWrapper"/"label" below aren't real MUI `classes` slots at
|
|
131
|
+
// all (MUI only recognizes root/switchBase/track/thumb/input/checked/disabled/colorX/sizeX/
|
|
132
|
+
// edgeX) — those four are this component's own hardcoded elements, styled directly in the
|
|
133
|
+
// JSX below, not through MUI's classes mechanism. Only root/track/switchBase actually take
|
|
134
|
+
// effect via this merge; the rest are pre-existing dead weight, unrelated to the merge itself.
|
|
135
|
+
const mergedClassNames = mergeClassNames(
|
|
136
|
+
{
|
|
137
|
+
root: styles.switchRoot,
|
|
138
|
+
body: styles.body,
|
|
139
|
+
track: styles.track,
|
|
140
|
+
switchBase: styles.switchBase,
|
|
141
|
+
trackLabel: styles.trackLabel,
|
|
142
|
+
labelWrapper: styles.labelWrapper,
|
|
143
|
+
label: styles.label,
|
|
144
|
+
},
|
|
145
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
149
|
+
const finalClass = classNameProp
|
|
150
|
+
? `${styles.root} ${classNameProp}`
|
|
151
|
+
: styles.root;
|
|
152
|
+
|
|
153
|
+
// Threads a grouped Switch's checked state through SwitchGroupContext (mirrors Checkbox's
|
|
154
|
+
// identical CheckboxGroupContext pattern) — FormGroup itself has no controlled-value
|
|
155
|
+
// concept, so this is the only place group membership is actually computed. Standalone
|
|
156
|
+
// (ungrouped) Switch usage is untouched: isChecked/isGroupReadOnly are no-ops outside a
|
|
157
|
+
// <Switch.Group>. See SWITCH_IMPLEMENTATION_NOTES.md.
|
|
158
|
+
const groupContext = useContext(SwitchGroupContext);
|
|
159
|
+
const isGrouped = groupContext !== null;
|
|
160
|
+
const isGroupReadOnly = isGrouped ? !!groupContext.readOnly : false;
|
|
161
|
+
const isChecked = isGrouped
|
|
162
|
+
? (groupContext.value || []).includes(restRecord.value as string)
|
|
163
|
+
: !!(restRecord.checked ?? restRecord.defaultChecked);
|
|
164
|
+
|
|
165
|
+
const handleGroupChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
166
|
+
if (!isGrouped) return;
|
|
167
|
+
const currentValue = groupContext.value || [];
|
|
168
|
+
const thisValue = restRecord.value as string;
|
|
169
|
+
const newValue = event.target.checked
|
|
170
|
+
? [...currentValue, thisValue]
|
|
171
|
+
: currentValue.filter((v) => v !== thisValue);
|
|
172
|
+
groupContext.onChange?.(event, newValue);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
if (readOnly && !!readOnlyComponent) {
|
|
176
|
+
const ReadOnlyComp = readOnlyComponent;
|
|
177
|
+
const roNode = (
|
|
178
|
+
<ReadOnlyComp
|
|
179
|
+
{...props}
|
|
180
|
+
checked={isChecked}
|
|
181
|
+
label={label as React.ReactNode}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
if (formLayout) {
|
|
186
|
+
return (
|
|
187
|
+
<FormControlLayout
|
|
188
|
+
formLayout={formLayout}
|
|
189
|
+
labelSize={labelSize}
|
|
190
|
+
controlMaxWidth={controlMaxWidth}
|
|
191
|
+
controlMinWidth={controlMinWidth}
|
|
192
|
+
>
|
|
193
|
+
{roNode}
|
|
194
|
+
</FormControlLayout>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return <>{roNode}</>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Same node passed as both icon and checkedIcon (mirrors MUI's own Switch.js, which does
|
|
202
|
+
// the same: `icon={icon} checkedIcon={icon}`) so it never unmounts/remounts on toggle —
|
|
203
|
+
// that's what lets the checkIcon/closeIcon crossfade via CSS opacity instead of a hard swap,
|
|
204
|
+
// matching mantine-adapter's thumbIcon (single node, both glyphs always mounted).
|
|
205
|
+
const thumbNode = (
|
|
206
|
+
<span className={styles.thumb}>
|
|
207
|
+
<span className={styles.thumbIconWrapper}>
|
|
208
|
+
<CheckIcon className={styles.checkIcon} />
|
|
209
|
+
<CloseIcon className={styles.closeIcon} />
|
|
210
|
+
</span>
|
|
211
|
+
</span>
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// We omit Mui's sizing/coloring so we rely strictly on variables from Switch.module.css.
|
|
215
|
+
// color is hardcoded to "default" (never left to MUI's "primary" fallback) so MUI's own
|
|
216
|
+
// palette-driven blue checked/hover/focus styling never activates in the first place —
|
|
217
|
+
// see SWITCH_IMPLEMENTATION_NOTES.md #5.
|
|
218
|
+
const isSwitchDisabled = readOnly || disabled || isGroupReadOnly;
|
|
219
|
+
|
|
220
|
+
const switchNode = (
|
|
221
|
+
<MuiSwitch
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
|
+
ref={ref as any}
|
|
224
|
+
{...(sanitizedProps as unknown as MuiSwitchProps)}
|
|
225
|
+
className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
|
|
226
|
+
classes={mergedClassNames}
|
|
227
|
+
color="default"
|
|
228
|
+
disabled={isSwitchDisabled}
|
|
229
|
+
data-disabled={isSwitchDisabled || undefined}
|
|
230
|
+
icon={thumbNode}
|
|
231
|
+
checkedIcon={thumbNode}
|
|
232
|
+
{...(isGrouped
|
|
233
|
+
? { checked: isChecked, onChange: handleGroupChange }
|
|
234
|
+
: {})}
|
|
235
|
+
disableRipple
|
|
236
|
+
/>
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const finalNode = label ? (
|
|
240
|
+
<div className={finalClass} style={style as React.CSSProperties}>
|
|
241
|
+
<div className={styles.body}>
|
|
242
|
+
{switchNode}
|
|
243
|
+
<div className={styles.labelWrapper}>
|
|
244
|
+
<label
|
|
245
|
+
className={styles.label}
|
|
246
|
+
htmlFor={restRecord.id as string}
|
|
247
|
+
data-disabled={readOnly || disabled ? true : undefined}
|
|
248
|
+
>
|
|
249
|
+
{label as React.ReactNode}
|
|
250
|
+
</label>
|
|
251
|
+
{/* description/error are mutually exclusive — error takes precedence */}
|
|
252
|
+
{error ? (
|
|
253
|
+
<AssistiveElement
|
|
254
|
+
assistiveVariant="error"
|
|
255
|
+
assistiveWithIcon={false}
|
|
256
|
+
>
|
|
257
|
+
{error}
|
|
258
|
+
</AssistiveElement>
|
|
259
|
+
) : (
|
|
260
|
+
description && (
|
|
261
|
+
<AssistiveElement
|
|
262
|
+
assistiveVariant="help"
|
|
263
|
+
assistiveWithIcon={false}
|
|
264
|
+
>
|
|
265
|
+
{description}
|
|
266
|
+
</AssistiveElement>
|
|
267
|
+
)
|
|
268
|
+
)}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
) : (
|
|
273
|
+
switchNode
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (formLayout) {
|
|
277
|
+
return (
|
|
278
|
+
<FormControlLayout
|
|
279
|
+
formLayout={formLayout}
|
|
280
|
+
labelSize={labelSize}
|
|
281
|
+
controlMaxWidth={controlMaxWidth}
|
|
282
|
+
controlMinWidth={controlMinWidth}
|
|
283
|
+
>
|
|
284
|
+
{finalNode}
|
|
285
|
+
</FormControlLayout>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return finalNode;
|
|
290
|
+
},
|
|
291
|
+
) as SwitchComponent;
|
|
292
|
+
|
|
293
|
+
Switch.displayName = "Switch";
|
|
294
|
+
Switch.Group = SwitchGroup;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Switch } from "./Switch";
|
|
3
|
+
import { SwitchGroup } from "./SwitchGroup";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SwitchGroup> = {
|
|
8
|
+
title: "UI-Kit/SwitchGroup",
|
|
9
|
+
component: SwitchGroup,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
The \`SwitchGroup\` component is the mandatory organizational wrapper for orchestrating \`Switch\` primitives into macro form layouts. It inherently leverages the \`FormControlWrapper\`, granting native access to structural alignments, assistive descriptions, and strict flex arrays.
|
|
16
|
+
|
|
17
|
+
### Supported Layout Vectors
|
|
18
|
+
- **\`formLayout="stacked"\`**: Top-to-bottom layout cascading the Label bounding box down vertically into a standard stacked switch array.
|
|
19
|
+
- **\`formLayout="side-by-side"\`**: Flow architecture pulling the grouping Label dynamically to the left while structurally organizing the internal switches cleanly alongside it horizontally.
|
|
20
|
+
|
|
21
|
+
> [!IMPORTANT]
|
|
22
|
+
> If you require a solitary \`Switch\` field to natively accept an overarching form label, assistive text, or align with other side-by-side components in a form column, you must wrap it inside a \`Switch.Group\` to trigger the \`FormControlWrapper\` mapping.
|
|
23
|
+
`,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
...formControlArgTypes,
|
|
29
|
+
readOnly: {
|
|
30
|
+
control: "boolean",
|
|
31
|
+
description:
|
|
32
|
+
"Passes read-only lock natively down mapping inner switches into explicitly disabled states.",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
|
|
39
|
+
type Story = StoryObj<typeof SwitchGroup>;
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
formLayout: "stacked",
|
|
44
|
+
label: "Standard Group",
|
|
45
|
+
},
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
47
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
48
|
+
const [value, setValue] = useState<string[]>([]);
|
|
49
|
+
return (
|
|
50
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
51
|
+
<Switch value="1" label="Option 1" />
|
|
52
|
+
<Switch value="2" label="Option 2" />
|
|
53
|
+
</SwitchGroup>
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const StackedLayout: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
formLayout: "stacked",
|
|
61
|
+
label: "Notification Settings",
|
|
62
|
+
description: "Manage your preferences.",
|
|
63
|
+
assistiveText: "We recommend turning these on.",
|
|
64
|
+
error: "",
|
|
65
|
+
required: true,
|
|
66
|
+
},
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
68
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
69
|
+
const [value, setValue] = useState<string[]>(["email"]);
|
|
70
|
+
return (
|
|
71
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
72
|
+
<Switch value="email" label="Email Alerts" />
|
|
73
|
+
<Switch value="push" label="Push Notifications" />
|
|
74
|
+
<Switch value="sms" label="SMS Messages" />
|
|
75
|
+
</SwitchGroup>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const SideBySideLayout: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
...StackedLayout.args,
|
|
83
|
+
formLayout: "side-by-side",
|
|
84
|
+
labelSize: "default",
|
|
85
|
+
labelAlignment: "left",
|
|
86
|
+
},
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
88
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
89
|
+
const [value, setValue] = useState<string[]>([]);
|
|
90
|
+
return (
|
|
91
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
92
|
+
<Switch value="weekly" label="Weekly Digest" />
|
|
93
|
+
<Switch value="marketing" label="Marketing Emails" />
|
|
94
|
+
</SwitchGroup>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const SolitaryFormControl: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
formLayout: "side-by-side",
|
|
102
|
+
label: "Enable Backups",
|
|
103
|
+
description: "Automatically back up your data nightly.",
|
|
104
|
+
},
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
106
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
107
|
+
const [value, setValue] = useState<string[]>(["auto"]);
|
|
108
|
+
return (
|
|
109
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
110
|
+
<Switch value="auto" label="Auto-backup" />
|
|
111
|
+
</SwitchGroup>
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const ReadOnly: Story = {
|
|
117
|
+
args: {
|
|
118
|
+
...StackedLayout.args,
|
|
119
|
+
readOnly: true,
|
|
120
|
+
},
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
122
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
123
|
+
const [value, setValue] = useState<string[]>(["email", "sms"]);
|
|
124
|
+
return (
|
|
125
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
126
|
+
<Switch value="email" label="Email Alerts" />
|
|
127
|
+
<Switch value="push" label="Push Notifications" />
|
|
128
|
+
<Switch value="sms" label="SMS Messages" />
|
|
129
|
+
</SwitchGroup>
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import React, { forwardRef } from "react";
|
|
3
|
+
import {
|
|
4
|
+
FormGroup as MuiFormGroup,
|
|
5
|
+
type FormGroupProps as MuiFormGroupProps,
|
|
6
|
+
} from "@mui/material";
|
|
7
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
8
|
+
import {
|
|
9
|
+
filterStylingProps,
|
|
10
|
+
type RecursicaOverStyled,
|
|
11
|
+
} from "../../utils/filterStylingProps";
|
|
12
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
13
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
14
|
+
import styles from "./Switch.module.css";
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line react-refresh/only-export-components
|
|
17
|
+
export const SwitchGroupContext = React.createContext<{
|
|
18
|
+
value?: string[];
|
|
19
|
+
onChange?: (event: React.SyntheticEvent, value: string[]) => void;
|
|
20
|
+
name?: string;
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
} | null>(null);
|
|
23
|
+
|
|
24
|
+
import { type RecursicaSwitchGroupProps as BaseRecursicaSwitchGroupProps } from "@recursica/adapter-common";
|
|
25
|
+
|
|
26
|
+
export interface RecursicaSwitchGroupProps
|
|
27
|
+
extends Omit<
|
|
28
|
+
MuiFormGroupProps,
|
|
29
|
+
| "size"
|
|
30
|
+
| "labelProps"
|
|
31
|
+
| "onChange"
|
|
32
|
+
| "classes"
|
|
33
|
+
| "ref"
|
|
34
|
+
| "value"
|
|
35
|
+
| "defaultValue"
|
|
36
|
+
>,
|
|
37
|
+
Omit<
|
|
38
|
+
RecursicaFormControlWrapperProps,
|
|
39
|
+
| "controlMaxWidth"
|
|
40
|
+
| "controlMinWidth"
|
|
41
|
+
| "classes"
|
|
42
|
+
| "onChange"
|
|
43
|
+
| "defaultValue"
|
|
44
|
+
>,
|
|
45
|
+
ReadOnlyControlProps,
|
|
46
|
+
BaseRecursicaSwitchGroupProps {
|
|
47
|
+
// MUI has no native switch-group concept to match (its own `FormGroup` is layout-only, no
|
|
48
|
+
// value/onChange) — this signature is Recursica's own, same as TransferList/Accordion/CheckboxGroup.
|
|
49
|
+
onChange?: (value: string[]) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type SwitchGroupProps = RecursicaOverStyled<RecursicaSwitchGroupProps>;
|
|
53
|
+
|
|
54
|
+
export const SwitchGroup = forwardRef<HTMLDivElement, SwitchGroupProps>(
|
|
55
|
+
function SwitchGroup(props, ref) {
|
|
56
|
+
const {
|
|
57
|
+
overStyled = false,
|
|
58
|
+
formLayout = "stacked",
|
|
59
|
+
|
|
60
|
+
// Label Wrappers
|
|
61
|
+
labelSize,
|
|
62
|
+
labelAlignment,
|
|
63
|
+
labelOptionalText,
|
|
64
|
+
labelWithEditIcon,
|
|
65
|
+
onLabelEditClick,
|
|
66
|
+
|
|
67
|
+
// Base Mui Extracted Attributes
|
|
68
|
+
label,
|
|
69
|
+
description,
|
|
70
|
+
assistiveText,
|
|
71
|
+
assistiveWithIcon,
|
|
72
|
+
error,
|
|
73
|
+
required,
|
|
74
|
+
// removed withAsterisk
|
|
75
|
+
id,
|
|
76
|
+
className,
|
|
77
|
+
style,
|
|
78
|
+
children,
|
|
79
|
+
readOnly,
|
|
80
|
+
readOnlyComponent,
|
|
81
|
+
emptyValueComponent,
|
|
82
|
+
value,
|
|
83
|
+
defaultValue,
|
|
84
|
+
onChange,
|
|
85
|
+
...rest
|
|
86
|
+
} = props;
|
|
87
|
+
// NOTE: MuiFormGroupProps has no native "size" (or other Recursica-unsupported) prop to
|
|
88
|
+
// leak through here — nothing to add to an UNSUPPORTED_PROPS list beyond what
|
|
89
|
+
// filterStylingProps already blocks.
|
|
90
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
91
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
92
|
+
|
|
93
|
+
const handleChange = (
|
|
94
|
+
_event: React.SyntheticEvent,
|
|
95
|
+
childValue: string[],
|
|
96
|
+
) => {
|
|
97
|
+
if (onChange) {
|
|
98
|
+
onChange(childValue);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<WithReadOnlyWrapper
|
|
104
|
+
className={className}
|
|
105
|
+
style={style as React.CSSProperties}
|
|
106
|
+
// No group-level max-width: switch-item label-max-width (200px) caps a single switch's
|
|
107
|
+
// own label text, not the whole group — reusing it here made the side-by-side label
|
|
108
|
+
// column (fixed 224px) wider than its container, so it always wrapped onto its own line.
|
|
109
|
+
controlMaxWidth={undefined}
|
|
110
|
+
controlMinWidth={undefined}
|
|
111
|
+
overStyled={overStyled as true}
|
|
112
|
+
// Strictly override. ARIA grouping prohibits interactive switches nested natively inside <label>.
|
|
113
|
+
formLayout={formLayout}
|
|
114
|
+
labelSize={labelSize}
|
|
115
|
+
labelAlignment={labelAlignment}
|
|
116
|
+
labelOptionalText={labelOptionalText}
|
|
117
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
118
|
+
onLabelEditClick={onLabelEditClick}
|
|
119
|
+
label={label}
|
|
120
|
+
description={description}
|
|
121
|
+
assistiveText={assistiveText}
|
|
122
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
123
|
+
error={error}
|
|
124
|
+
required={required}
|
|
125
|
+
id={id}
|
|
126
|
+
readOnly={readOnly && !!readOnlyComponent}
|
|
127
|
+
readOnlyComponent={readOnlyComponent}
|
|
128
|
+
emptyValueComponent={emptyValueComponent}
|
|
129
|
+
readOnlyType="text"
|
|
130
|
+
readOnlyValue={value !== undefined ? value : defaultValue}
|
|
131
|
+
readOnlyNativeProps={props}
|
|
132
|
+
activeComponent={
|
|
133
|
+
<SwitchGroupContext.Provider
|
|
134
|
+
value={{
|
|
135
|
+
value: value !== undefined ? value : defaultValue,
|
|
136
|
+
onChange: handleChange,
|
|
137
|
+
name: restRecord.name as string | undefined,
|
|
138
|
+
readOnly: readOnly || !!(restRecord as any).disabled,
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<MuiFormGroup
|
|
142
|
+
ref={ref}
|
|
143
|
+
{...(sanitizedProps as unknown as MuiFormGroupProps)}
|
|
144
|
+
className={`${styles.groupRoot} ${(sanitizedProps as any).className || ""}`.trim()}
|
|
145
|
+
data-layout={formLayout}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</MuiFormGroup>
|
|
149
|
+
</SwitchGroupContext.Provider>
|
|
150
|
+
}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
SwitchGroup.displayName = "SwitchGroup";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Switch - Usage Guide
|
|
2
|
+
|
|
3
|
+
This document describes how to integrate and use the `Switch` component in your projects using `@recursica/adapter-mui-v7`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Import Reference
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Switch } from "@recursica/adapter-mui-v7";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basic Example
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { Switch } from "@recursica/adapter-mui-v7";
|
|
20
|
+
|
|
21
|
+
export default function Demo() {
|
|
22
|
+
return <Switch label="Enable notifications" defaultChecked />;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 3. Design System Integration
|
|
29
|
+
|
|
30
|
+
All Recursica components in the `@recursica/adapter-mui-v7` package adhere strictly to design system spacing, scaling, and behavior patterns.
|
|
31
|
+
|
|
32
|
+
> [!IMPORTANT]
|
|
33
|
+
>
|
|
34
|
+
> - **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.
|
|
35
|
+
> - **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.
|
|
36
|
+
> - **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.
|