@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,22 @@
|
|
|
1
|
+
# Popover Implementation Notes
|
|
2
|
+
|
|
3
|
+
- **Built on Mui's Tooltip, in click-controlled mode:** Rather than Mui's `Popover`/`Modal` primitives, this component reuses `@mui/material`'s `Tooltip` — the same base HoverCard and Tooltip already use in this adapter — with `disableHoverListener`/`disableFocusListener`/`disableTouchListener` all set, and `open` fully controlled by this component. This keeps the arrow/placement/token-namespace conventions identical to HoverCard and Tooltip instead of introducing a second, unrelated positioning primitive.
|
|
4
|
+
- **Composable API preserved via child extraction:** Like HoverCard, `Popover.Target`/`Popover.Dropdown` are never rendered themselves — `PopoverBase` walks `children` looking for those `displayName`s and extracts their inner content, then renders the target as Tooltip's single child and the dropdown as Tooltip's `title`.
|
|
5
|
+
- **Diverges from HoverCard on missing Target/Dropdown:** HoverCard silently falls back to an empty `<div />` if the expected child isn't found. Popover throws instead — a silent empty popover is a worse failure mode for a click-triggered disclosure than for a hover card, and the repo convention is to let structural misuse fail loudly rather than mask it.
|
|
6
|
+
- **Click toggle + outside-click close is custom:** Mui's Tooltip has no notion of "click to open" or "click outside to close" once its native hover/focus/touch listeners are disabled — Mantine's Popover provides both natively. This component clones the target element to attach an `onClick` toggle handler and a ref, and closes on outside click via a `mousedown` listener on `document` that ignores clicks inside either the target (`targetRef`) or the dropdown content (`dropdownRef`, a wrapper `<div>` around the dropdown children solely for this hit-test — not a styling root). Escape-to-close comes for free from Tooltip's own internal Escape handling once `onClose` is wired up.
|
|
7
|
+
- **`beak-size` token exemption:** Mui's Tooltip arrow is a fixed CSS shape (`1em`/`0.71em`, relative to the tooltip's own `font-size`) with no JS size prop, unlike Mantine's `arrowSize`. There's no hook to bind `--recursica_ui-kit_components_hover-card-popover_properties_beak-size` to, so it's `recursica-ignore`d here (same conceptual gap as HoverCard/Tooltip's existing exemptions for this token family, just for a different reason — those wrote a comment referencing Mantine's inline-pixel arrow calculations, which doesn't apply in this adapter; this file's comment describes the actual Mui-specific reason).
|
|
8
|
+
- **Arrow fill uses `color`, not `border-color`:** HoverCard's and Tooltip's `.arrow` rules in this adapter set `border-color`, which has no visual effect — Mui's Tooltip arrow is a solid rotated-square filled via `background-color: currentColor` in its `::before`, not a bordered shape. This component's `.arrow` instead sets `color` to the panel's `background-color` token, which actually renders. Intentional deviation from the HoverCard/Tooltip precedent for correctness; not fixed there as it's out of scope for this change.
|
|
9
|
+
- **Shared token namespace:** Same as HoverCard, this component's CSS module exclusively uses `--recursica_ui-kit_components_hover-card-popover_*` tokens (geometry, typography, elevation, layer-aware colors) — no Popover-specific token namespace exists in the schema.
|
|
10
|
+
- **`width` and controlled `opened`/`onChange`:** Not present in the shared `RecursicaPopoverProps` (adapter-common only defines `withBeak`), these are defined locally in `PopoverOwnProps` to match Mantine's own `PopoverProps` surface, since there is no single Mui library type that already provides them.
|
|
11
|
+
|
|
12
|
+
## Gap larger than Mantine's on `withBeak={false}` (Matt Massey, 2026-08-19)
|
|
13
|
+
|
|
14
|
+
**Root cause:** Mui's `Tooltip` styled component ships its own hardcoded per-placement margin (`marginTop`/`marginBottom`: `14px`, or `24px` in touch mode) on the tooltip content div, applied whenever `arrow` is falsy (the `arrow` variant resets it to `margin: 0`). This stacked on top of the `offset` popper modifier already applied in `Popover.tsx` (which alone reproduces Mantine's own gap — Mantine's Popover defaults to an 8px `offset`, the same value used here), so the visible gap was `8 + 14 = 22px` instead of `8px` with `withBeak={false}`.
|
|
15
|
+
|
|
16
|
+
**Fix:** neutralize Mui's built-in margin in `Popover.module.css` via `:global(.MuiTooltip-popper[data-popper-placement]) .dropdown { margin: 0; }`, matching Mui's own rule's specificity (class + attribute + class) so it wins on source order under `injectFirst`. The `offset` modifier is now the single source of truth for the gap, matching Mantine.
|
|
17
|
+
|
|
18
|
+
## Beak had no visible edge against the dropdown body (Matt Massey, 2026-08-19)
|
|
19
|
+
|
|
20
|
+
**Root cause:** Mantine's arrow is a bordered shape — `.arrow { border-color: ... }` — visible because Mantine's own arrow implementation sets `border-width` natively. Mui's arrow `::before` pseudo-element has no border at all by default (just `background-color: currentColor`), so setting only `color` (as this component already did, to fill the triangle) left it with zero visible edge against a similarly-colored panel.
|
|
21
|
+
|
|
22
|
+
**Fix:** added `.arrow::before { border-style: solid; border-width: ...border-size; border-color: ...colors_border-color; }`, the same border tokens the `.dropdown` container itself uses — verified live (Playwright) that the arrow now shows a visible border matching Mantine's.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* HARDCODED VALUES:
|
|
2
|
+
- border-style: solid. Mui's Tooltip content div does not set border-style natively;
|
|
3
|
+
without it, the border-width/border-color tokens below have no visible effect.
|
|
4
|
+
Same pattern as HoverCard / Tooltip / Menu in this adapter.
|
|
5
|
+
- margin: 0 on the dropdown (per placement, see below). Mui's Tooltip content div ships
|
|
6
|
+
its own hardcoded 14px (or 24px on touch) margin toward the target for every placement,
|
|
7
|
+
stacked on top of the `offset` popper modifier Popover.tsx already applies. That modifier
|
|
8
|
+
alone is what matches Mantine's own gap, so Mui's built-in margin must be zeroed out or
|
|
9
|
+
the visible gap ends up far larger than Mantine's.
|
|
10
|
+
- border-style: solid on the arrow's ::before. Mui's arrow pseudo-element has no border by
|
|
11
|
+
default; without it, the border-color/border-width tokens below have no visible effect.
|
|
12
|
+
- All structural layout (display, position, overflow) is deferred to Mui's native
|
|
13
|
+
Popper/Tooltip behavior. We only override visual design tokens (colors, typography,
|
|
14
|
+
spacing, borders).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/* ======================================
|
|
18
|
+
DROPDOWN CONTAINER
|
|
19
|
+
====================================== */
|
|
20
|
+
|
|
21
|
+
.dropdown {
|
|
22
|
+
background-color: var(
|
|
23
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_background-color
|
|
24
|
+
);
|
|
25
|
+
border-style: solid; /* HARDCODE: Mui's Tooltip content div does not set border-style natively */
|
|
26
|
+
border-width: var(
|
|
27
|
+
--recursica_ui-kit_components_hover-card-popover_properties_border-size
|
|
28
|
+
);
|
|
29
|
+
border-color: var(
|
|
30
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_border-color
|
|
31
|
+
);
|
|
32
|
+
border-radius: var(
|
|
33
|
+
--recursica_ui-kit_components_hover-card-popover_properties_border-radius
|
|
34
|
+
);
|
|
35
|
+
box-shadow: var(
|
|
36
|
+
--recursica_ui-kit_components_hover-card-popover_properties_elevation
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
min-width: var(
|
|
40
|
+
--recursica_ui-kit_components_hover-card-popover_properties_min-width
|
|
41
|
+
);
|
|
42
|
+
max-width: var(
|
|
43
|
+
--recursica_ui-kit_components_hover-card-popover_properties_max-width
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
padding: var(
|
|
47
|
+
--recursica_ui-kit_components_hover-card-popover_properties_vertical-padding
|
|
48
|
+
)
|
|
49
|
+
var(
|
|
50
|
+
--recursica_ui-kit_components_hover-card-popover_properties_horizontal-padding
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/* Typography */
|
|
54
|
+
font-family: var(
|
|
55
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_font-family
|
|
56
|
+
);
|
|
57
|
+
font-size: var(
|
|
58
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_font-size
|
|
59
|
+
);
|
|
60
|
+
font-style: var(
|
|
61
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_font-style
|
|
62
|
+
);
|
|
63
|
+
font-weight: var(
|
|
64
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_font-weight
|
|
65
|
+
);
|
|
66
|
+
letter-spacing: var(
|
|
67
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_letter-spacing
|
|
68
|
+
);
|
|
69
|
+
line-height: var(
|
|
70
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_line-height
|
|
71
|
+
);
|
|
72
|
+
text-decoration: var(
|
|
73
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_text-decoration
|
|
74
|
+
);
|
|
75
|
+
text-transform: var(
|
|
76
|
+
--recursica_ui-kit_components_hover-card-popover_properties_content-text_text-transform
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
color: var(
|
|
80
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_content
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Mui's Tooltip content div sets its own margin toward the target per placement
|
|
85
|
+
(marginTop/marginBottom/marginLeft/marginRight), duplicating the gap already produced by
|
|
86
|
+
the `offset` popper modifier in Popover.tsx. Zero it out so the modifier is the single
|
|
87
|
+
source of truth for the gap, matching Mantine's gap. Selector specificity matches Mui's own
|
|
88
|
+
placement rule (class + attribute + class) so it wins on source order (this adapter's
|
|
89
|
+
modules are injected after Mui's via injectFirst). */
|
|
90
|
+
:global(.MuiTooltip-popper[data-popper-placement]) .dropdown {
|
|
91
|
+
margin: 0; /* HARDCODE: cancel Mui's built-in per-placement margin, see file header */
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ======================================
|
|
95
|
+
ARROW / BEAK
|
|
96
|
+
====================================== */
|
|
97
|
+
|
|
98
|
+
/* Mui's arrow is a solid rotated-square shape filled via `currentColor` (no separate
|
|
99
|
+
border), unlike Mantine's bordered-diamond arrow. Fill with the panel's own
|
|
100
|
+
background-color token for the closest visual match Mui's primitive allows. */
|
|
101
|
+
.arrow {
|
|
102
|
+
color: var(
|
|
103
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_background-color
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Mui's arrow ::before has no border by default, so it renders as a solid triangle with no
|
|
108
|
+
visible edge against a similarly-colored dropdown body. Mantine's arrow gets its visibility
|
|
109
|
+
the same way — a border using the popover's own border tokens — so replicate that here. */
|
|
110
|
+
.arrow::before {
|
|
111
|
+
border-style: solid; /* HARDCODE: Mui's arrow ::before does not set border-style natively */
|
|
112
|
+
border-width: var(
|
|
113
|
+
--recursica_ui-kit_components_hover-card-popover_properties_border-size
|
|
114
|
+
);
|
|
115
|
+
border-color: var(
|
|
116
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_border-color
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Popover } from "./Popover";
|
|
3
|
+
import { Button } from "../Button";
|
|
4
|
+
import { Text } from "../Text/Text";
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
type PopoverStoryArgs = Record<string, any>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta = {
|
|
10
|
+
title: "UI-Kit/Popover",
|
|
11
|
+
component: Popover,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
controls: {
|
|
15
|
+
// Explicitly list only the props integrators should configure — Mui's
|
|
16
|
+
// underlying Tooltip props would otherwise leak into Controls.
|
|
17
|
+
include: ["withBeak", "position", "defaultOpened"],
|
|
18
|
+
},
|
|
19
|
+
docs: {
|
|
20
|
+
description: {
|
|
21
|
+
component:
|
|
22
|
+
"The `Popover` component is a composable wrapper around Mui's Tooltip in click-controlled mode. It displays a dropdown panel when the user clicks a target element.",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
argTypes: {
|
|
27
|
+
withBeak: {
|
|
28
|
+
control: "boolean",
|
|
29
|
+
description:
|
|
30
|
+
"Whether to display a beak (arrow) pointing from the dropdown to the target.",
|
|
31
|
+
},
|
|
32
|
+
position: {
|
|
33
|
+
control: "select",
|
|
34
|
+
options: [
|
|
35
|
+
"top",
|
|
36
|
+
"top-start",
|
|
37
|
+
"top-end",
|
|
38
|
+
"bottom",
|
|
39
|
+
"bottom-start",
|
|
40
|
+
"bottom-end",
|
|
41
|
+
"left",
|
|
42
|
+
"left-start",
|
|
43
|
+
"left-end",
|
|
44
|
+
"right",
|
|
45
|
+
"right-start",
|
|
46
|
+
"right-end",
|
|
47
|
+
],
|
|
48
|
+
description: "Dropdown position relative to target",
|
|
49
|
+
},
|
|
50
|
+
defaultOpened: {
|
|
51
|
+
control: "boolean",
|
|
52
|
+
description: "Initial opened state",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default meta;
|
|
58
|
+
type Story = StoryObj<PopoverStoryArgs>;
|
|
59
|
+
|
|
60
|
+
export const Default: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
withBeak: true,
|
|
63
|
+
position: "top",
|
|
64
|
+
},
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
66
|
+
render: ({ withLayer, layer, ...args }: PopoverStoryArgs) => {
|
|
67
|
+
return (
|
|
68
|
+
<Popover width={250} {...args}>
|
|
69
|
+
<Popover.Target>
|
|
70
|
+
<Button variant="solid">Toggle Popover</Button>
|
|
71
|
+
</Popover.Target>
|
|
72
|
+
<Popover.Dropdown>
|
|
73
|
+
<Text>
|
|
74
|
+
This is the popover content. It can contain any elements you want to
|
|
75
|
+
display when the user clicks the target.
|
|
76
|
+
</Text>
|
|
77
|
+
</Popover.Dropdown>
|
|
78
|
+
</Popover>
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const SolidDefault: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
withBeak: true,
|
|
86
|
+
position: "top",
|
|
87
|
+
defaultOpened: true,
|
|
88
|
+
},
|
|
89
|
+
parameters: {
|
|
90
|
+
layout: "centered",
|
|
91
|
+
controls: { disable: true },
|
|
92
|
+
},
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
94
|
+
render: ({ withLayer, layer, ...args }: PopoverStoryArgs) => {
|
|
95
|
+
return (
|
|
96
|
+
<Popover width={200} {...args}>
|
|
97
|
+
<Popover.Target>
|
|
98
|
+
<Button variant="solid">Toggle Popover</Button>
|
|
99
|
+
</Popover.Target>
|
|
100
|
+
<Popover.Dropdown>
|
|
101
|
+
<Text>
|
|
102
|
+
This is a static representation of an opened popover with a beak.
|
|
103
|
+
</Text>
|
|
104
|
+
</Popover.Dropdown>
|
|
105
|
+
</Popover>
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const WithoutBeak: Story = {
|
|
111
|
+
args: {
|
|
112
|
+
withBeak: false,
|
|
113
|
+
position: "bottom",
|
|
114
|
+
defaultOpened: true,
|
|
115
|
+
},
|
|
116
|
+
parameters: {
|
|
117
|
+
layout: "centered",
|
|
118
|
+
controls: { disable: true },
|
|
119
|
+
},
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
121
|
+
render: ({ withLayer, layer, ...args }: PopoverStoryArgs) => {
|
|
122
|
+
return (
|
|
123
|
+
<Popover width={200} {...args}>
|
|
124
|
+
<Popover.Target>
|
|
125
|
+
<Button variant="outline">Bottom Popover</Button>
|
|
126
|
+
</Popover.Target>
|
|
127
|
+
<Popover.Dropdown>
|
|
128
|
+
<Text>This popover is positioned at the bottom and has no beak.</Text>
|
|
129
|
+
</Popover.Dropdown>
|
|
130
|
+
</Popover>
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future Popover component tests. Add tests here if/when needed.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
cloneElement,
|
|
3
|
+
isValidElement,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import {
|
|
9
|
+
Tooltip as MuiTooltip,
|
|
10
|
+
type TooltipProps as MuiTooltipProps,
|
|
11
|
+
} from "@mui/material";
|
|
12
|
+
import {
|
|
13
|
+
filterStylingProps,
|
|
14
|
+
mergeClassNames,
|
|
15
|
+
type RecursicaOverStyled,
|
|
16
|
+
} from "../../utils/filterStylingProps";
|
|
17
|
+
import styles from "./Popover.module.css";
|
|
18
|
+
|
|
19
|
+
// ============================================================
|
|
20
|
+
// POPOVER ROOT
|
|
21
|
+
// ============================================================
|
|
22
|
+
|
|
23
|
+
import { type RecursicaPopoverProps } from "@recursica/adapter-common";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Behavioral props specific to this adapter's click-controlled implementation.
|
|
27
|
+
* `withBeak` comes from the shared `RecursicaPopoverProps` (adapter-common); the
|
|
28
|
+
* rest map to Mantine's own `PopoverProps` surface, reproduced here since Mui has
|
|
29
|
+
* no single library type that already covers them.
|
|
30
|
+
*/
|
|
31
|
+
export interface PopoverOwnProps extends RecursicaPopoverProps {
|
|
32
|
+
/** Dropdown position relative to the target */
|
|
33
|
+
position?: MuiTooltipProps["placement"];
|
|
34
|
+
/** Initial opened state (uncontrolled) */
|
|
35
|
+
defaultOpened?: boolean;
|
|
36
|
+
/** Controlled opened state */
|
|
37
|
+
opened?: boolean;
|
|
38
|
+
/** Called whenever the opened state changes */
|
|
39
|
+
onChange?: (opened: boolean) => void;
|
|
40
|
+
/** Distance in px between the dropdown and the target */
|
|
41
|
+
offset?: number;
|
|
42
|
+
/** Fixed width applied to the dropdown panel */
|
|
43
|
+
width?: number | string;
|
|
44
|
+
children?: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Recursica Popover component wrapping Mui's Tooltip in click-controlled mode.
|
|
49
|
+
*
|
|
50
|
+
* Displays a dropdown panel when the user clicks the target element.
|
|
51
|
+
* Uses the composable dot-notation pattern:
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <Popover withBeak>
|
|
54
|
+
* <Popover.Target>
|
|
55
|
+
* <Button>Click me</Button>
|
|
56
|
+
* </Popover.Target>
|
|
57
|
+
* <Popover.Dropdown>
|
|
58
|
+
* Content displayed in popover
|
|
59
|
+
* </Popover.Dropdown>
|
|
60
|
+
* </Popover>
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export type PopoverProps = RecursicaOverStyled<
|
|
64
|
+
Omit<
|
|
65
|
+
MuiTooltipProps,
|
|
66
|
+
"title" | "children" | "open" | "onClose" | "onOpen" | "placement"
|
|
67
|
+
> &
|
|
68
|
+
PopoverOwnProps
|
|
69
|
+
>;
|
|
70
|
+
|
|
71
|
+
const PopoverBase = function Popover({
|
|
72
|
+
overStyled = false,
|
|
73
|
+
withBeak = true,
|
|
74
|
+
position = "top",
|
|
75
|
+
defaultOpened = false,
|
|
76
|
+
opened,
|
|
77
|
+
onChange,
|
|
78
|
+
offset = 8,
|
|
79
|
+
width,
|
|
80
|
+
children,
|
|
81
|
+
...rest
|
|
82
|
+
}: PopoverProps) {
|
|
83
|
+
const sanitizedProps = filterStylingProps(
|
|
84
|
+
rest as Record<string, unknown>,
|
|
85
|
+
overStyled,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Bind CSS module classes to Mui's internal classNames API
|
|
89
|
+
const mergedClassNames = mergeClassNames(
|
|
90
|
+
{
|
|
91
|
+
tooltip: styles.dropdown,
|
|
92
|
+
arrow: styles.arrow,
|
|
93
|
+
},
|
|
94
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
95
|
+
| Partial<Record<string, string>>
|
|
96
|
+
| undefined,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const [internalOpened, setInternalOpened] = useState(defaultOpened);
|
|
100
|
+
const isControlled = opened !== undefined;
|
|
101
|
+
const currentOpened = isControlled ? (opened as boolean) : internalOpened;
|
|
102
|
+
|
|
103
|
+
const setOpened = (next: boolean) => {
|
|
104
|
+
if (!isControlled) setInternalOpened(next);
|
|
105
|
+
onChange?.(next);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Find Target and Dropdown children
|
|
109
|
+
let targetNode: React.ReactNode = null;
|
|
110
|
+
let dropdownNode: React.ReactNode = null;
|
|
111
|
+
|
|
112
|
+
React.Children.forEach(children, (child) => {
|
|
113
|
+
if (isValidElement(child)) {
|
|
114
|
+
const childElement = child as unknown as {
|
|
115
|
+
type?: { displayName?: string };
|
|
116
|
+
props: { children?: React.ReactNode };
|
|
117
|
+
};
|
|
118
|
+
if (childElement.type?.displayName === "PopoverTarget") {
|
|
119
|
+
targetNode = childElement.props.children;
|
|
120
|
+
} else if (childElement.type?.displayName === "PopoverDropdown") {
|
|
121
|
+
dropdownNode = childElement.props.children;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!targetNode) {
|
|
127
|
+
throw new Error("Popover requires a <Popover.Target> child.");
|
|
128
|
+
}
|
|
129
|
+
if (!dropdownNode) {
|
|
130
|
+
throw new Error("Popover requires a <Popover.Dropdown> child.");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const targetRef = useRef<HTMLElement | null>(null);
|
|
134
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
135
|
+
|
|
136
|
+
// Mui's Tooltip has no native "click outside to close" behavior once its own
|
|
137
|
+
// hover/focus/touch listeners are disabled for click-controlled use, so it's
|
|
138
|
+
// implemented here directly (mirrors Mantine's default closeOnClickOutside).
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!currentOpened) return undefined;
|
|
141
|
+
const handlePointerDown = (event: MouseEvent) => {
|
|
142
|
+
const target = event.target as Node;
|
|
143
|
+
if (targetRef.current?.contains(target)) return;
|
|
144
|
+
if (dropdownRef.current?.contains(target)) return;
|
|
145
|
+
setOpened(false);
|
|
146
|
+
};
|
|
147
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
148
|
+
return () => document.removeEventListener("mousedown", handlePointerDown);
|
|
149
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
150
|
+
}, [currentOpened]);
|
|
151
|
+
|
|
152
|
+
const handleTargetClick = (event: React.MouseEvent) => {
|
|
153
|
+
if (isValidElement(targetNode)) {
|
|
154
|
+
(
|
|
155
|
+
targetNode.props as { onClick?: (e: React.MouseEvent) => void }
|
|
156
|
+
).onClick?.(event);
|
|
157
|
+
}
|
|
158
|
+
setOpened(!currentOpened);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Mui's Tooltip auto-wires `aria-labelledby`/`aria-label` on the target to describe
|
|
162
|
+
// it via the tooltip content once open — correct for an actual tooltip, but wrong here:
|
|
163
|
+
// it would silently replace the target's own accessible name (e.g. a Button's label)
|
|
164
|
+
// with the popover's body text. Explicitly reset both so the target keeps its own name.
|
|
165
|
+
const targetAriaOverrides = {
|
|
166
|
+
"aria-haspopup": "dialog" as const,
|
|
167
|
+
"aria-expanded": currentOpened,
|
|
168
|
+
"aria-labelledby": undefined,
|
|
169
|
+
"aria-label": undefined,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const clonedTarget = isValidElement(targetNode) ? (
|
|
173
|
+
cloneElement(
|
|
174
|
+
targetNode as React.ReactElement,
|
|
175
|
+
{
|
|
176
|
+
onClick: handleTargetClick,
|
|
177
|
+
ref: targetRef,
|
|
178
|
+
...targetAriaOverrides,
|
|
179
|
+
} as Record<string, unknown>,
|
|
180
|
+
)
|
|
181
|
+
) : (
|
|
182
|
+
<span
|
|
183
|
+
onClick={handleTargetClick}
|
|
184
|
+
ref={targetRef as unknown as React.Ref<HTMLSpanElement>}
|
|
185
|
+
{...targetAriaOverrides}
|
|
186
|
+
>
|
|
187
|
+
{targetNode}
|
|
188
|
+
</span>
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<MuiTooltip
|
|
193
|
+
{...(sanitizedProps as unknown as Omit<
|
|
194
|
+
MuiTooltipProps,
|
|
195
|
+
"title" | "children"
|
|
196
|
+
>)}
|
|
197
|
+
title={<div ref={dropdownRef}>{dropdownNode}</div>}
|
|
198
|
+
open={currentOpened}
|
|
199
|
+
onClose={() => setOpened(false)}
|
|
200
|
+
placement={position}
|
|
201
|
+
arrow={withBeak}
|
|
202
|
+
disableHoverListener
|
|
203
|
+
disableFocusListener
|
|
204
|
+
disableTouchListener
|
|
205
|
+
slotProps={{
|
|
206
|
+
popper: {
|
|
207
|
+
modifiers: [
|
|
208
|
+
{
|
|
209
|
+
name: "offset",
|
|
210
|
+
options: {
|
|
211
|
+
offset: [0, offset],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
...(width !== undefined ? { tooltip: { style: { width } } } : {}),
|
|
217
|
+
}}
|
|
218
|
+
classes={mergedClassNames as unknown as MuiTooltipProps["classes"]}
|
|
219
|
+
>
|
|
220
|
+
{clonedTarget}
|
|
221
|
+
</MuiTooltip>
|
|
222
|
+
);
|
|
223
|
+
};
|
|
224
|
+
PopoverBase.displayName = "Popover";
|
|
225
|
+
|
|
226
|
+
// ============================================================
|
|
227
|
+
// POPOVER TARGET
|
|
228
|
+
// ============================================================
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Wrapper for the element that triggers the popover.
|
|
232
|
+
* Requires a single child element; only used as a marker to locate the
|
|
233
|
+
* trigger element, it is never rendered directly (see `PopoverBase`).
|
|
234
|
+
*/
|
|
235
|
+
export type PopoverTargetProps = { children?: React.ReactNode };
|
|
236
|
+
|
|
237
|
+
const PopoverTarget = function PopoverTarget({ children }: PopoverTargetProps) {
|
|
238
|
+
return <>{children}</>;
|
|
239
|
+
};
|
|
240
|
+
PopoverTarget.displayName = "PopoverTarget";
|
|
241
|
+
|
|
242
|
+
// ============================================================
|
|
243
|
+
// POPOVER DROPDOWN
|
|
244
|
+
// ============================================================
|
|
245
|
+
|
|
246
|
+
/** The dropdown panel displayed from the popover. */
|
|
247
|
+
export type PopoverDropdownProps = { children?: React.ReactNode };
|
|
248
|
+
|
|
249
|
+
const PopoverDropdown = function PopoverDropdown({
|
|
250
|
+
children,
|
|
251
|
+
}: PopoverDropdownProps) {
|
|
252
|
+
return <>{children}</>;
|
|
253
|
+
};
|
|
254
|
+
PopoverDropdown.displayName = "PopoverDropdown";
|
|
255
|
+
|
|
256
|
+
// ============================================================
|
|
257
|
+
// DOT NOTATION EXPORT
|
|
258
|
+
// ============================================================
|
|
259
|
+
|
|
260
|
+
type PopoverComponent = typeof PopoverBase & {
|
|
261
|
+
Target: typeof PopoverTarget;
|
|
262
|
+
Dropdown: typeof PopoverDropdown;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export const Popover = PopoverBase as PopoverComponent;
|
|
266
|
+
Popover.Target = PopoverTarget;
|
|
267
|
+
Popover.Dropdown = PopoverDropdown;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Popover - Usage Guide
|
|
2
|
+
|
|
3
|
+
This document describes how to integrate and use the `Popover` component in your projects using `@recursica/adapter-mui-v7`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Import Reference
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Popover } from "@recursica/adapter-mui-v7";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basic Example
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { Popover, Button, Text } from "@recursica/adapter-mui-v7";
|
|
20
|
+
|
|
21
|
+
export default function Demo() {
|
|
22
|
+
return (
|
|
23
|
+
<Popover position="bottom" withBeak>
|
|
24
|
+
<Popover.Target>
|
|
25
|
+
<Button>Open Popover</Button>
|
|
26
|
+
</Popover.Target>
|
|
27
|
+
<Popover.Dropdown>
|
|
28
|
+
<Text size="rec-sm">This is the popover content.</Text>
|
|
29
|
+
</Popover.Dropdown>
|
|
30
|
+
</Popover>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. Design System Integration
|
|
38
|
+
|
|
39
|
+
All Recursica components in the `@recursica/adapter-mui-v7` package adhere strictly to design system spacing, scaling, and behavior patterns.
|
|
40
|
+
|
|
41
|
+
> [!IMPORTANT]
|
|
42
|
+
>
|
|
43
|
+
> - **Anti-override protection**: Rogue style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
|
|
44
|
+
> - **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.
|
|
45
|
+
> - **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.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 4. Key Integration Features & Constraints
|
|
50
|
+
|
|
51
|
+
### Composition
|
|
52
|
+
|
|
53
|
+
`Popover`, `Popover.Target`, and `Popover.Dropdown` are used together: `Popover.Target` wraps the trigger element and applies no styling of its own, while `Popover.Dropdown` renders the styled panel content. Both `Popover.Target` and `Popover.Dropdown` are required — omitting either throws.
|
|
54
|
+
|
|
55
|
+
### Open/close behavior
|
|
56
|
+
|
|
57
|
+
The dropdown opens when the user clicks the target and closes on an outside click, on Escape, or by clicking the target again. Use `opened`/`onChange` for controlled usage, or `defaultOpened` to set the initial uncontrolled state.
|
|
58
|
+
|
|
59
|
+
### Beak (Arrow)
|
|
60
|
+
|
|
61
|
+
The Recursica prop `withBeak` (defaulting to `true`) controls whether the pointer beak is shown.
|
|
62
|
+
|
|
63
|
+
### Position
|
|
64
|
+
|
|
65
|
+
`position` accepts the same 12 placement values as `HoverCard`/`Tooltip` (e.g. `"top"`, `"bottom-start"`, `"right-end"`) and defaults to `"top"`.
|
|
66
|
+
|
|
67
|
+
### Width
|
|
68
|
+
|
|
69
|
+
An optional `width` prop sets a fixed width on the dropdown panel.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Popover";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Radio Implementation Notes
|
|
2
|
+
|
|
3
|
+
### `Radio.Group` `controlMaxWidth` bug (fixed 2026-08-31)
|
|
4
|
+
|
|
5
|
+
`RadioGroup` was passing `--recursica_ui-kit_components_radio-button-item_properties_max-width`
|
|
6
|
+
(400px) as its own `controlMaxWidth`. That token caps a single radio+its own label — it's
|
|
7
|
+
already applied per-item via `.root` in `Radio.module.css`. Passed as the _group's_
|
|
8
|
+
`controlMaxWidth`, `FormControlLayout` applies it to the whole root row, so in side-by-side
|
|
9
|
+
layout the group's own label got squeezed into that same 400px alongside the radios. There is
|
|
10
|
+
no group-level max-width token in the schema — fixed by leaving `controlMaxWidth` `undefined`,
|
|
11
|
+
matching `SwitchGroup`'s existing pattern. Same bug existed in `CheckboxGroup`, fixed the same
|
|
12
|
+
way. Mirrors the identical fix in mantine-adapter (source of truth).
|