@recursica/adapter-mantine-v8 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/commonArgTypes.ts +148 -0
- package/ARCHITECTURE.md +18 -0
- package/CHANGELOG.md +688 -0
- package/LICENSE +21 -0
- package/OVERSTYLING.md +56 -0
- package/README.md +70 -0
- package/SETUP.md +93 -0
- package/USAGE.md +57 -0
- package/dist/adapter-mantine-v8.cjs +12 -0
- package/dist/adapter-mantine-v8.cjs.map +1 -0
- package/dist/adapter-mantine-v8.css +1 -0
- package/dist/adapter-mantine-v8.js +5759 -0
- package/dist/adapter-mantine-v8.js.map +1 -0
- package/dist/index.d.ts +2095 -0
- package/dist/test.html +1 -0
- package/dist/vite.svg +1 -0
- package/docs/PHILOSOPHY.md +39 -0
- package/llms.txt +74 -0
- package/package.json +131 -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 +154 -0
- package/src/OverStyling.tsx +79 -0
- package/src/Version.tsx +44 -0
- package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +72 -0
- package/src/components/Accordion/Accordion.module.css +367 -0
- package/src/components/Accordion/Accordion.stories.tsx +189 -0
- package/src/components/Accordion/Accordion.test.tsx +1 -0
- package/src/components/Accordion/Accordion.tsx +220 -0
- package/src/components/Accordion/USAGE.md +51 -0
- package/src/components/Accordion/index.ts +1 -0
- package/src/components/AssistiveElement/ASSISTIVEELEMENT_IMPLEMENTATION_NOTES.md +45 -0
- package/src/components/AssistiveElement/AssistiveElement.module.css +111 -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 +93 -0
- package/src/components/AssistiveElement/USAGE.md +36 -0
- package/src/components/AutoComplete/AUTOCOMPLETE_IMPLEMENTATION_NOTES.md +42 -0
- package/src/components/AutoComplete/AutoComplete.module.css +430 -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 +179 -0
- package/src/components/AutoComplete/USAGE.md +80 -0
- package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +34 -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 +108 -0
- package/src/components/Avatar/USAGE.md +38 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Badge/Badge.module.css +127 -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 +64 -0
- package/src/components/Badge/USAGE.md +56 -0
- package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +47 -0
- package/src/components/Breadcrumb/Breadcrumb.module.css +48 -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 +85 -0
- package/src/components/Breadcrumb/USAGE.md +55 -0
- package/src/components/Breadcrumb/markCurrentPageItem.test.ts +49 -0
- package/src/components/Button/Button.module.css +446 -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 +184 -0
- package/src/components/Button/IMPLEMENTATION_NOTES.md +109 -0
- package/src/components/Button/USAGE.md +80 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +17 -0
- package/src/components/Card/Card.module.css +119 -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 +248 -0
- package/src/components/Card/USAGE.md +63 -0
- package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/Checkbox/Checkbox.module.css +267 -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 +151 -0
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +147 -0
- package/src/components/Checkbox/USAGE.md +48 -0
- package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +110 -0
- package/src/components/Chip/Chip.module.css +288 -0
- package/src/components/Chip/Chip.stories.tsx +128 -0
- package/src/components/Chip/Chip.test.tsx +1 -0
- package/src/components/Chip/Chip.tsx +157 -0
- package/src/components/Chip/USAGE.md +55 -0
- package/src/components/Container/CONTAINER_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Container/Container.module.css +7 -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 +69 -0
- package/src/components/Container/USAGE.md +47 -0
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +31 -0
- package/src/components/DatePicker/DatePicker.icons.tsx +27 -0
- package/src/components/DatePicker/DatePicker.module.css +566 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +158 -0
- package/src/components/DatePicker/DatePicker.test.tsx +1 -0
- package/src/components/DatePicker/DatePicker.tsx +218 -0
- package/src/components/DatePicker/USAGE.md +49 -0
- package/src/components/Dropdown/BareDropdown.tsx +97 -0
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Dropdown/Dropdown.module.css +477 -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 +213 -0
- package/src/components/Dropdown/USAGE.md +69 -0
- package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +153 -0
- package/src/components/FileInput/FileInput.module.css +280 -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 +417 -0
- package/src/components/FileInput/USAGE.md +145 -0
- package/src/components/FileUpload/FILEUPLOAD_IMPLEMENTATION_NOTES.md +244 -0
- package/src/components/FileUpload/FileUpload.module.css +205 -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 +354 -0
- package/src/components/FileUpload/USAGE.md +199 -0
- package/src/components/Flex/FLEX_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Flex/Flex.module.css +7 -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 +75 -0
- package/src/components/Flex/USAGE.md +48 -0
- package/src/components/FormControlLayout/FormControlLayout.module.css +90 -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 +74 -0
- package/src/components/FormControlLayout/USAGE.md +40 -0
- package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +75 -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 +151 -0
- package/src/components/FormControlWrapper/USAGE.md +47 -0
- package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Grid/Grid.module.css +11 -0
- package/src/components/Grid/Grid.stories.tsx +206 -0
- package/src/components/Grid/Grid.test.tsx +1 -0
- package/src/components/Grid/Grid.tsx +140 -0
- package/src/components/Grid/USAGE.md +47 -0
- package/src/components/Group/GROUP_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Group/Group.module.css +7 -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 +60 -0
- package/src/components/Group/USAGE.md +48 -0
- package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Heading/Heading.module.css +13 -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 +52 -0
- package/src/components/Heading/USAGE.md +36 -0
- package/src/components/HoverCard/HOVERCARD_IMPLEMENTATION_NOTES.md +77 -0
- package/src/components/HoverCard/HoverCard.module.css +90 -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 +133 -0
- package/src/components/HoverCard/USAGE.md +52 -0
- package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Label/Label.module.css +141 -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 +113 -0
- package/src/components/Label/USAGE.md +47 -0
- package/src/components/Label/index.ts +1 -0
- package/src/components/Layer/USAGE.md +54 -0
- package/src/components/Link/IMPLEMENTATION_NOTES.md +19 -0
- package/src/components/Link/Link.module.css +110 -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 +67 -0
- package/src/components/Link/USAGE.md +56 -0
- package/src/components/Link/index.ts +1 -0
- package/src/components/Loader/LOADER_IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/Loader/Loader.animate.dom.test.tsx +82 -0
- package/src/components/Loader/Loader.module.css +63 -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 +69 -0
- package/src/components/Loader/USAGE.md +44 -0
- package/src/components/Menu/MENU_IMPLEMENTATION_NOTES.md +88 -0
- package/src/components/Menu/Menu.module.css +280 -0
- package/src/components/Menu/Menu.stories.tsx +371 -0
- package/src/components/Menu/Menu.test.tsx +1 -0
- package/src/components/Menu/Menu.tsx +373 -0
- package/src/components/Menu/USAGE.md +55 -0
- package/src/components/Modal/MODAL_IMPLEMENTATION_NOTES.md +32 -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 +368 -0
- package/src/components/Modal/USAGE.md +58 -0
- package/src/components/Modal/index.ts +1 -0
- package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/NumberInput/NumberInput.module.css +302 -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 +157 -0
- package/src/components/NumberInput/USAGE.md +52 -0
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +16 -0
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +188 -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 +265 -0
- package/src/components/Pagination/USAGE.md +36 -0
- package/src/components/Pagination/index.ts +5 -0
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +116 -0
- package/src/components/Panel/Panel.module.css +209 -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 +269 -0
- package/src/components/Panel/USAGE.md +56 -0
- package/src/components/Popover/IMPLEMENTATION_NOTES.md +77 -0
- package/src/components/Popover/Popover.module.css +86 -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 +142 -0
- package/src/components/Popover/USAGE.md +61 -0
- package/src/components/Popover/index.ts +1 -0
- package/src/components/Radio/Radio.module.css +231 -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 +168 -0
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +124 -0
- package/src/components/Radio/USAGE.md +36 -0
- package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -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 +114 -0
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/USAGE.md +44 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +5 -0
- package/src/components/RecursicaThemeProvider/USAGE.md +60 -0
- package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +185 -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 +110 -0
- package/src/components/SegmentedControl/USAGE.md +55 -0
- package/src/components/Slider/IMPLEMENTATION_NOTES.md +92 -0
- package/src/components/Slider/Slider.module.css +588 -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 +391 -0
- package/src/components/Slider/USAGE.md +56 -0
- package/src/components/Stack/STACK_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Stack/Stack.module.css +7 -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 +62 -0
- package/src/components/Stack/USAGE.md +48 -0
- package/src/components/Stepper/IMPLEMENTATION_NOTES.md +28 -0
- package/src/components/Stepper/Stepper.module.css +404 -0
- package/src/components/Stepper/Stepper.stories.tsx +148 -0
- package/src/components/Stepper/Stepper.test.tsx +1 -0
- package/src/components/Stepper/Stepper.tsx +93 -0
- package/src/components/Stepper/USAGE.md +41 -0
- package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +64 -0
- package/src/components/Switch/Switch.module.css +279 -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 +177 -0
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +127 -0
- package/src/components/Switch/USAGE.md +42 -0
- package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +78 -0
- package/src/components/Table/Table.icons.tsx +40 -0
- package/src/components/Table/Table.module.css +444 -0
- package/src/components/Table/Table.stories.tsx +140 -0
- package/src/components/Table/Table.test.tsx +1 -0
- package/src/components/Table/Table.tsx +243 -0
- package/src/components/Table/USAGE.md +78 -0
- package/src/components/Tabs/IMPLEMENTATION_NOTES.md +27 -0
- package/src/components/Tabs/Tabs.module.css +591 -0
- package/src/components/Tabs/Tabs.stories.tsx +154 -0
- package/src/components/Tabs/Tabs.test.tsx +1 -0
- package/src/components/Tabs/Tabs.tsx +114 -0
- package/src/components/Tabs/USAGE.md +45 -0
- package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Text/Text.module.css +13 -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 +53 -0
- package/src/components/Text/USAGE.md +40 -0
- package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +7 -0
- package/src/components/TextArea/TextArea.module.css +183 -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 +137 -0
- package/src/components/TextArea/USAGE.md +48 -0
- package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
- package/src/components/TextField/TextField.module.css +264 -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 +139 -0
- package/src/components/TextField/USAGE.md +44 -0
- package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +119 -0
- package/src/components/TimePicker/TimePicker.module.css +291 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +141 -0
- package/src/components/TimePicker/TimePicker.test.tsx +1 -0
- package/src/components/TimePicker/TimePicker.tsx +304 -0
- package/src/components/TimePicker/USAGE.md +57 -0
- package/src/components/Timeline/TIMELINE_IMPLEMENTATION_NOTES.md +13 -0
- package/src/components/Timeline/Timeline.module.css +373 -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 +73 -0
- package/src/components/Timeline/TimelineItem.tsx +79 -0
- package/src/components/Timeline/USAGE.md +49 -0
- package/src/components/Toast/TOAST_IMPLEMENTATION_NOTES.md +39 -0
- package/src/components/Toast/Toast.module.css +184 -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 +82 -0
- package/src/components/Toast/USAGE.md +50 -0
- package/src/components/Toast/index.ts +1 -0
- package/src/components/Tooltip/TOOLTIP_IMPLEMENTATION_NOTES.md +85 -0
- package/src/components/Tooltip/Tooltip.module.css +91 -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 +117 -0
- package/src/components/Tooltip/USAGE.md +80 -0
- package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +111 -0
- package/src/components/TransferList/TransferList.module.css +182 -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/Tree/IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Tree/Tree.module.css +232 -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 +220 -0
- package/src/components/Tree/USAGE.md +96 -0
- package/src/components/Tree/index.ts +1 -0
- package/src/components/index.ts +52 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +179 -0
- package/src/types/index.ts +9 -0
- package/src/types/mantine.d.ts +7 -0
- package/src/types/scss-modules.d.ts +9 -0
- package/src/utils/ColorSchemeWrapper.tsx +27 -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 +150 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/renderRichOption.tsx +65 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Switch Implementation Notes
|
|
2
|
+
|
|
3
|
+
This document acts as a living record tracking the layout decisions, architectural tradeoffs, and structural hacks explicitly applied to the `Switch` component wrapper to bridge Mantine's defaults with the Recursica UI Kit's rigid requirements.
|
|
4
|
+
|
|
5
|
+
## 1. Stripping Mantine's Size Engine
|
|
6
|
+
|
|
7
|
+
Mantine uses properties like `size`, `color`, and `radius` to dynamically map CSS layout values across its `.track` and `.thumb` nodes. We proactively strip and delete these properties using `filterStylingProps` to entirely neutralize this native behavior.
|
|
8
|
+
|
|
9
|
+
## 2. Hardcoded Values & Transitions
|
|
10
|
+
|
|
11
|
+
Mantine injects dynamic width/height attributes into its switch through inline CSS variables (e.g. `--switch-height`). To strictly enforce the UI Kit tokens without breaking Mantine's internal math, our `Switch.module.css` structurally remaps Mantine's internal variables explicitly:
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
--switch-width: var(--switch-track-width);
|
|
15
|
+
--switch-height: calc(
|
|
16
|
+
var(--switch-thumb-height) + (var(--switch-track-padding) * 2)
|
|
17
|
+
);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
We also hardcode `border: none` since the UI kit designs rely purely on box-shadow elevations and background color tracking. Mantine’s default border logic is entirely disabled.
|
|
21
|
+
|
|
22
|
+
## 3. ReadOnly Behavior
|
|
23
|
+
|
|
24
|
+
Similar to `Checkbox`, the `Switch` component handles `readOnly` presentation by dropping the entire underlying node tree and falling back structurally onto `<FormControlWrapper>` when `readOnly: true`. This strictly preserves exact baseline alignment across all primitives without trying to hack disabled CSS to look like read-only text.
|
|
25
|
+
|
|
26
|
+
## 4. Hover State Reset
|
|
27
|
+
|
|
28
|
+
Mantine forcefully triggers track hover color states globally. Since Recursica currently does not map specific hover states to switch backgrounds across themes (falling back to standard unselected tokens or simply providing a cursor), we structurally wipe out Mantine's `.track:hover` class block inside `Switch.module.css`.
|
|
29
|
+
|
|
30
|
+
## 5. `RecursicaSwitchGroupProps.value`/`onChange` shape
|
|
31
|
+
|
|
32
|
+
Tightened to `string[]`/`(value: string[]) => void` to match Mantine's real `Switch.Group` value
|
|
33
|
+
type exactly (previously `unknown[]`, which was accurate but forced `as any` casts everywhere
|
|
34
|
+
this component's `value`/`defaultValue` were handed to `<MantineSwitch.Group>`). Now a genuine
|
|
35
|
+
"Shared" prop — same name and shape as native, no reshaping needed.
|
|
36
|
+
|
|
37
|
+
## 6. `onChange` shape mismatch (Forge's report) doesn't apply
|
|
38
|
+
|
|
39
|
+
Neither `Switch` nor `SwitchGroup` reshapes native `onChange` — it flows straight through as
|
|
40
|
+
Mantine's real event-based signature (`(event: ChangeEvent<HTMLInputElement>) => void` for
|
|
41
|
+
`Switch`, `(value: string[]) => void` for `SwitchGroup`). Forge's report flagged an
|
|
42
|
+
event-vs-boolean mismatch, but that's Forge's own dispatcher typing against the old deleted
|
|
43
|
+
local components — our contract never claimed a boolean shape to begin with.
|
|
44
|
+
|
|
45
|
+
## 7. Focus ring was Mantine's own default blue, not a Recursica color
|
|
46
|
+
|
|
47
|
+
**Found 2026-08-14:** the hidden `<input>` was left with Mantine's own default focus outline
|
|
48
|
+
(no override existed despite this file previously claiming "focus rings handled internally by
|
|
49
|
+
Mantine" as an intentional decision — verified live, it renders Mantine's theme blue, not a
|
|
50
|
+
Recursica token). Fixed: `.root input:focus-visible { outline: none }` suppresses the native
|
|
51
|
+
outline on the input, and `.root input:focus-visible + .track { ... }` draws the real ring
|
|
52
|
+
(same `--recursica_brand_states_focus_*` tokens used elsewhere) on the visible track instead —
|
|
53
|
+
matching the fix made to `mui-adapter`'s Switch for parity.
|
|
54
|
+
|
|
55
|
+
## `SwitchGroup` side-by-side layout always rendered as if stacked
|
|
56
|
+
|
|
57
|
+
**Found 2026-08-14, reported by Matt (against mui-adapter, reproduced here too):** `SwitchGroup`
|
|
58
|
+
passed the switch-item's own inline label max-width token (200px) as the group's
|
|
59
|
+
`controlMaxWidth` — but the mandatory side-by-side label column is a fixed 224px, wider than
|
|
60
|
+
that cap, so the label always overflowed onto its own line regardless of layout mode. Not a
|
|
61
|
+
mui-only bug: same code pattern, same result, here. Fixed by not capping the group's control
|
|
62
|
+
width at all — each switch's own label already wraps at 200px via `.labelWrapper`, so no
|
|
63
|
+
group-level cap is needed. Verified live in both adapters: side-by-side now shows the label
|
|
64
|
+
column at left with the switches beside it; stacked layout unaffected.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* HARDCODED VALUES
|
|
10
|
+
*
|
|
11
|
+
* 1. track border: none; (we do not use border for the switch track)
|
|
12
|
+
* 2. thumb border: none; (we do not use border for the thumb)
|
|
13
|
+
* 3. input outline: none; (the real ring is drawn on .track instead, below, from Recursica
|
|
14
|
+
* focus tokens — previously left to Mantine's own internal default outline, which renders
|
|
15
|
+
* Mantine's theme blue, not a Recursica color)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
.root {
|
|
19
|
+
/* Provide a stable cursor when not disabled */
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
|
|
22
|
+
--switch-track-padding: var(
|
|
23
|
+
--recursica_ui-kit_components_switch_properties_track-inner-padding
|
|
24
|
+
);
|
|
25
|
+
--switch-thumb-width: var(
|
|
26
|
+
--recursica_ui-kit_components_switch_properties_thumb-width
|
|
27
|
+
);
|
|
28
|
+
--switch-thumb-height: var(
|
|
29
|
+
--recursica_ui-kit_components_switch_properties_thumb-height
|
|
30
|
+
);
|
|
31
|
+
--switch-track-width: var(
|
|
32
|
+
--recursica_ui-kit_components_switch_properties_track-width
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.root:has(input:disabled) {
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Track/thumb dim per selection state; label dims via its own (single, state-agnostic) token */
|
|
41
|
+
.root:has(input:checked:disabled) .track {
|
|
42
|
+
opacity: var(
|
|
43
|
+
--recursica_ui-kit_components_switch_variants_selection-states_selected_variants_states_disabled_properties_opacity
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
.root:has(input:disabled):not(:has(input:checked)) .track {
|
|
47
|
+
opacity: var(
|
|
48
|
+
--recursica_ui-kit_components_switch_variants_selection-states_unselected_variants_states_disabled_properties_opacity
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.body {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: var(--recursica_ui-kit_components_switch-item_properties_label-gap);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* HARDCODE: disable native hover color transitions applied by Mantine */
|
|
59
|
+
.track:hover {
|
|
60
|
+
background-color: var(
|
|
61
|
+
--recursica_ui-kit_components_switch_variants_selection-states_unselected_properties_colors_track-color
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
.root input:checked + .track:hover {
|
|
65
|
+
background-color: var(
|
|
66
|
+
--recursica_ui-kit_components_switch_variants_selection-states_selected_properties_colors_track-color
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.track {
|
|
71
|
+
width: var(--switch-track-width);
|
|
72
|
+
height: calc(var(--switch-thumb-height) + (var(--switch-track-padding) * 2));
|
|
73
|
+
border-radius: var(
|
|
74
|
+
--recursica_ui-kit_components_switch_properties_track-border-radius
|
|
75
|
+
);
|
|
76
|
+
background-color: var(
|
|
77
|
+
--recursica_ui-kit_components_switch_variants_selection-states_unselected_properties_colors_track-color
|
|
78
|
+
);
|
|
79
|
+
box-shadow: var(
|
|
80
|
+
--recursica_ui-kit_components_switch_properties_track-elevation
|
|
81
|
+
);
|
|
82
|
+
padding: var(--switch-track-padding);
|
|
83
|
+
border: none; /* HARDCODE */
|
|
84
|
+
|
|
85
|
+
/* Mantine uses variables internally for these, but we explicitly override the CSS to ensure our dimensions */
|
|
86
|
+
--switch-width: var(--switch-track-width);
|
|
87
|
+
--switch-height: calc(
|
|
88
|
+
var(--switch-thumb-height) + (var(--switch-track-padding) * 2)
|
|
89
|
+
);
|
|
90
|
+
--switch-thumb-size: var(--switch-thumb-width);
|
|
91
|
+
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
box-sizing: border-box;
|
|
95
|
+
transition: background-color 150ms ease;
|
|
96
|
+
cursor: inherit;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.root input:checked + .track {
|
|
100
|
+
background-color: var(
|
|
101
|
+
--recursica_ui-kit_components_switch_variants_selection-states_selected_properties_colors_track-color
|
|
102
|
+
);
|
|
103
|
+
border-color: transparent;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* HARDCODE #3: the input itself is visually hidden (Mantine's usual pattern) but still
|
|
107
|
+
receives real keyboard focus and Mantine's own default focus outline — suppressed here so
|
|
108
|
+
the ring below (drawn on .track, the visible pill) is the only one that shows. */
|
|
109
|
+
.root input:focus-visible {
|
|
110
|
+
outline: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.root input:focus-visible + .track {
|
|
114
|
+
outline: var(--recursica_brand_states_focus_border-size) solid
|
|
115
|
+
var(--recursica_brand_states_focus_color);
|
|
116
|
+
outline-offset: var(--recursica_brand_states_focus_margin);
|
|
117
|
+
box-shadow: 0 0 var(--recursica_brand_states_focus_blur)
|
|
118
|
+
var(--recursica_brand_states_focus_color);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.thumb {
|
|
122
|
+
width: var(--switch-thumb-width);
|
|
123
|
+
height: var(--switch-thumb-height);
|
|
124
|
+
border-radius: var(
|
|
125
|
+
--recursica_ui-kit_components_switch_properties_thumb-border-radius
|
|
126
|
+
);
|
|
127
|
+
background-color: var(
|
|
128
|
+
--recursica_ui-kit_components_switch_variants_selection-states_unselected_properties_colors_thumb-color
|
|
129
|
+
);
|
|
130
|
+
box-shadow: var(
|
|
131
|
+
--recursica_ui-kit_components_switch_properties_thumb-elevation
|
|
132
|
+
);
|
|
133
|
+
border: none; /* HARDCODE */
|
|
134
|
+
|
|
135
|
+
transition: background-color 150ms ease;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.root input:checked + .track .thumb {
|
|
139
|
+
background-color: var(
|
|
140
|
+
--recursica_ui-kit_components_switch_variants_selection-states_selected_properties_colors_thumb-color
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.labelWrapper {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
max-width: var(
|
|
148
|
+
--recursica_ui-kit_components_switch-item_properties_label-max-width
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
.root:has(input:disabled) .labelWrapper {
|
|
152
|
+
opacity: var(
|
|
153
|
+
--recursica_ui-kit_components_switch-item_variants_states_disabled_properties_opacity
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.label {
|
|
158
|
+
font-family: var(
|
|
159
|
+
--recursica_ui-kit_components_switch-item_properties_text_font-family
|
|
160
|
+
);
|
|
161
|
+
font-size: var(
|
|
162
|
+
--recursica_ui-kit_components_switch-item_properties_text_font-size
|
|
163
|
+
);
|
|
164
|
+
font-weight: var(
|
|
165
|
+
--recursica_ui-kit_components_switch-item_properties_text_font-weight
|
|
166
|
+
);
|
|
167
|
+
font-style: var(
|
|
168
|
+
--recursica_ui-kit_components_switch-item_properties_text_font-style
|
|
169
|
+
);
|
|
170
|
+
line-height: var(
|
|
171
|
+
--recursica_ui-kit_components_switch-item_properties_text_line-height
|
|
172
|
+
);
|
|
173
|
+
letter-spacing: var(
|
|
174
|
+
--recursica_ui-kit_components_switch-item_properties_text_letter-spacing
|
|
175
|
+
);
|
|
176
|
+
text-transform: var(
|
|
177
|
+
--recursica_ui-kit_components_switch-item_properties_text_text-transform
|
|
178
|
+
);
|
|
179
|
+
text-decoration: var(
|
|
180
|
+
--recursica_ui-kit_components_switch-item_properties_text_text-decoration
|
|
181
|
+
);
|
|
182
|
+
color: var(
|
|
183
|
+
--recursica_ui-kit_components_switch-item_properties_colors_text-color
|
|
184
|
+
);
|
|
185
|
+
cursor: inherit;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.root:has(input:disabled) .label {
|
|
189
|
+
color: var(
|
|
190
|
+
--recursica_ui-kit_components_switch-item_properties_colors_disabled-text
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.thumbIconWrapper {
|
|
195
|
+
display: flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
width: 100%;
|
|
199
|
+
height: 100%;
|
|
200
|
+
position: relative;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.checkIcon,
|
|
204
|
+
.closeIcon {
|
|
205
|
+
width: var(--recursica_ui-kit_components_switch_properties_thumb-icon-size);
|
|
206
|
+
height: var(--recursica_ui-kit_components_switch_properties_thumb-icon-size);
|
|
207
|
+
position: absolute;
|
|
208
|
+
transition:
|
|
209
|
+
opacity 150ms ease,
|
|
210
|
+
transform 150ms ease;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Unchecked state */
|
|
214
|
+
.checkIcon {
|
|
215
|
+
opacity: 0;
|
|
216
|
+
transform: scale(0.5);
|
|
217
|
+
color: var(
|
|
218
|
+
--recursica_ui-kit_components_switch_variants_selection-states_selected_properties_colors_icon-color
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.closeIcon {
|
|
223
|
+
opacity: 1;
|
|
224
|
+
transform: scale(1);
|
|
225
|
+
color: var(
|
|
226
|
+
--recursica_ui-kit_components_switch_variants_selection-states_unselected_properties_colors_icon-color
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Checked state */
|
|
231
|
+
.root input:checked + .track .thumb .checkIcon {
|
|
232
|
+
opacity: 1;
|
|
233
|
+
transform: scale(1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.root input:checked + .track .thumb .closeIcon {
|
|
237
|
+
opacity: 0;
|
|
238
|
+
transform: scale(0.5);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* --- GROUP STYLES --- */
|
|
242
|
+
.groupRoot {
|
|
243
|
+
display: flex;
|
|
244
|
+
flex-direction: column;
|
|
245
|
+
align-items: flex-start;
|
|
246
|
+
padding: var(--recursica_ui-kit_components_switch-group_properties_padding);
|
|
247
|
+
gap: var(--recursica_ui-kit_components_switch-group_properties_item-gap);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* SideBySide Variant */
|
|
251
|
+
.groupRoot[data-layout="side-by-side"] {
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
gap: var(
|
|
254
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_side-by-side_properties_gutter
|
|
255
|
+
);
|
|
256
|
+
padding-top: var(
|
|
257
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_side-by-side_properties_vertical-padding
|
|
258
|
+
);
|
|
259
|
+
padding-bottom: var(
|
|
260
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_side-by-side_properties_vertical-padding
|
|
261
|
+
);
|
|
262
|
+
margin-top: var(
|
|
263
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
264
|
+
);
|
|
265
|
+
margin-bottom: var(
|
|
266
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* Stacked Variant */
|
|
271
|
+
.groupRoot[data-layout="stacked"] {
|
|
272
|
+
flex-direction: column;
|
|
273
|
+
margin-top: var(
|
|
274
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_stacked_properties_label-field-gap
|
|
275
|
+
);
|
|
276
|
+
margin-bottom: var(
|
|
277
|
+
--recursica_ui-kit_components_switch-group_variants_layouts_stacked_properties_top-bottom-margin
|
|
278
|
+
);
|
|
279
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Switch } from "./Switch";
|
|
3
|
+
import { Stack } from "../Stack/Stack";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Switch> = {
|
|
6
|
+
title: "UI-Kit/Switch",
|
|
7
|
+
component: Switch,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
The \`Switch\` component is an atomic form primitive representing boolean states, natively aligned to the Recursica design system.
|
|
14
|
+
|
|
15
|
+
> [!IMPORTANT]
|
|
16
|
+
> Unlike \`Checkbox\`, the \`Switch\` is typically used for standalone settings toggles. It fully supports the universal \`ReadOnlyField\` boundaries when passed the \`readOnly\` attribute.
|
|
17
|
+
|
|
18
|
+
### Usage
|
|
19
|
+
To render a standard switch:
|
|
20
|
+
\`\`\`tsx
|
|
21
|
+
<Switch label="Enable Notifications" defaultChecked />
|
|
22
|
+
\`\`\`
|
|
23
|
+
`,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
checked: {
|
|
29
|
+
control: "boolean",
|
|
30
|
+
},
|
|
31
|
+
disabled: {
|
|
32
|
+
control: "boolean",
|
|
33
|
+
},
|
|
34
|
+
required: {
|
|
35
|
+
control: "boolean",
|
|
36
|
+
},
|
|
37
|
+
error: {
|
|
38
|
+
control: "boolean",
|
|
39
|
+
},
|
|
40
|
+
defaultChecked: {
|
|
41
|
+
control: "boolean",
|
|
42
|
+
},
|
|
43
|
+
readOnly: {
|
|
44
|
+
control: "boolean",
|
|
45
|
+
description:
|
|
46
|
+
"Toggles structural read-only data presentation bypassing interaction boundaries completely.",
|
|
47
|
+
},
|
|
48
|
+
controlMaxWidth: { table: { disable: true } },
|
|
49
|
+
controlMinWidth: { table: { disable: true } },
|
|
50
|
+
emptyValueComponent: { table: { disable: true } },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
|
|
56
|
+
type Story = StoryObj<typeof Switch>;
|
|
57
|
+
|
|
58
|
+
export const Default: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
disabled: false,
|
|
61
|
+
label: "Standard Switch",
|
|
62
|
+
},
|
|
63
|
+
// We explicitly destructure global Storybook injected arguments so they do not leak into the DOM natively.
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
65
|
+
render: ({ withLayer, layer, ...args }: any) => <Switch {...args} />,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const SideBySideLayout: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
label: "Opt-in form alignment",
|
|
71
|
+
formLayout: "side-by-side",
|
|
72
|
+
},
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
74
|
+
render: ({ withLayer, layer, ...args }: any) => <Switch {...args} />,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const StaticVariations: Story = {
|
|
78
|
+
args: {},
|
|
79
|
+
render: () => (
|
|
80
|
+
<Stack gap="xl">
|
|
81
|
+
<Switch label="Default Unchecked State" />
|
|
82
|
+
<Switch label="Checked State" defaultChecked />
|
|
83
|
+
<Switch label="Disabled Unchecked" disabled />
|
|
84
|
+
<Switch label="Disabled Checked" defaultChecked disabled />
|
|
85
|
+
</Stack>
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const ReadOnly: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
...Default.args,
|
|
92
|
+
readOnly: true,
|
|
93
|
+
},
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
95
|
+
render: ({ withLayer, layer, ...args }: any) => <Switch {...args} />,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const CustomReadOnly: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
...Default.args,
|
|
101
|
+
readOnly: true,
|
|
102
|
+
},
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
104
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
105
|
+
<Switch
|
|
106
|
+
{...args}
|
|
107
|
+
readOnlyComponent={({ checked, label }) => (
|
|
108
|
+
<span style={{ fontWeight: "bold", color: checked ? "green" : "red" }}>
|
|
109
|
+
{label} {checked ? "ENABLED" : "DISABLED"}
|
|
110
|
+
</span>
|
|
111
|
+
)}
|
|
112
|
+
/>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future Switch component tests. Add tests here if/when needed.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { forwardRef, type CSSProperties } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Switch as MantineSwitch,
|
|
4
|
+
type SwitchProps as MantineSwitchProps,
|
|
5
|
+
CheckIcon,
|
|
6
|
+
CloseIcon,
|
|
7
|
+
} from "@mantine/core";
|
|
8
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
filterStylingProps,
|
|
12
|
+
omitUnsupportedProps,
|
|
13
|
+
withCallerOverride,
|
|
14
|
+
mergeClassNames,
|
|
15
|
+
mergeStyles,
|
|
16
|
+
type RecursicaOverStyled,
|
|
17
|
+
} from "../../utils/filterStylingProps";
|
|
18
|
+
import {
|
|
19
|
+
type RequireAccessibleLabel,
|
|
20
|
+
type RecursicaSwitchProps,
|
|
21
|
+
} from "@recursica/adapter-common";
|
|
22
|
+
import {
|
|
23
|
+
FormControlLayout,
|
|
24
|
+
type FormControlLayoutProps,
|
|
25
|
+
} from "../FormControlLayout/FormControlLayout";
|
|
26
|
+
import styles from "./Switch.module.css";
|
|
27
|
+
|
|
28
|
+
import { SwitchGroup } from "./SwitchGroup";
|
|
29
|
+
|
|
30
|
+
export type SwitchWrapperProps = Omit<
|
|
31
|
+
MantineSwitchProps,
|
|
32
|
+
"size" | "color" | "radius" | "variant"
|
|
33
|
+
> &
|
|
34
|
+
RecursicaSwitchProps &
|
|
35
|
+
ReadOnlyControlProps &
|
|
36
|
+
Pick<
|
|
37
|
+
FormControlLayoutProps,
|
|
38
|
+
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
export type RecursicaSwitchPropsAlias =
|
|
42
|
+
RequireAccessibleLabel<SwitchWrapperProps>;
|
|
43
|
+
|
|
44
|
+
export type SwitchProps = RecursicaOverStyled<RecursicaSwitchPropsAlias>;
|
|
45
|
+
|
|
46
|
+
type SwitchComponent = React.ForwardRefExoticComponent<
|
|
47
|
+
SwitchProps & React.RefAttributes<HTMLInputElement>
|
|
48
|
+
> & {
|
|
49
|
+
Group: typeof SwitchGroup;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
53
|
+
function Switch(props, ref) {
|
|
54
|
+
const {
|
|
55
|
+
overStyled = false,
|
|
56
|
+
readOnly,
|
|
57
|
+
readOnlyComponent,
|
|
58
|
+
disabled,
|
|
59
|
+
thumbIcon,
|
|
60
|
+
formLayout,
|
|
61
|
+
labelSize,
|
|
62
|
+
controlMaxWidth,
|
|
63
|
+
controlMinWidth,
|
|
64
|
+
...rest
|
|
65
|
+
} = props;
|
|
66
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
67
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
68
|
+
const UNSUPPORTED_PROPS = [
|
|
69
|
+
"size", // Recursica controls sizing via Switch.module.css variables, not Mantine's native size scale.
|
|
70
|
+
"color", // Colors are token-driven via Switch.module.css; Mantine's native palette isn't exposed.
|
|
71
|
+
"radius", // Corner radius is fixed by Recursica tokens in Switch.module.css, not caller-configurable.
|
|
72
|
+
"variant", // Switch has a single Recursica-defined visual treatment; Mantine's variant isn't exposed.
|
|
73
|
+
] as const satisfies readonly (keyof MantineSwitchProps)[];
|
|
74
|
+
|
|
75
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
76
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
77
|
+
UNSUPPORTED_PROPS,
|
|
78
|
+
) as Partial<typeof rest>;
|
|
79
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
80
|
+
|
|
81
|
+
const mergedClassNames = mergeClassNames(
|
|
82
|
+
{
|
|
83
|
+
root: styles.root,
|
|
84
|
+
body: styles.body,
|
|
85
|
+
track: styles.track,
|
|
86
|
+
thumb: styles.thumb,
|
|
87
|
+
trackLabel: styles.trackLabel,
|
|
88
|
+
labelWrapper: styles.labelWrapper,
|
|
89
|
+
label: styles.label,
|
|
90
|
+
},
|
|
91
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Recursica has no default per-slot inline styles here (all default styling comes from
|
|
95
|
+
// Switch.module.css above) — this only exists so a caller-supplied `styles` prop (permitted
|
|
96
|
+
// once overStyled=true clears filterStylingProps) merges in per-slot instead of needing its
|
|
97
|
+
// own passthrough path.
|
|
98
|
+
const mergedStyles = mergeStyles(
|
|
99
|
+
{} as Record<string, CSSProperties | undefined>,
|
|
100
|
+
restRecord.styles as Partial<Record<string, CSSProperties>> | undefined,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
104
|
+
const finalClass = classNameProp
|
|
105
|
+
? `${styles.root} ${classNameProp}`
|
|
106
|
+
: styles.root;
|
|
107
|
+
|
|
108
|
+
if (readOnly && !!readOnlyComponent) {
|
|
109
|
+
const isChecked = !!(restRecord.checked ?? restRecord.defaultChecked);
|
|
110
|
+
const ReadOnlyComp = readOnlyComponent;
|
|
111
|
+
const roNode = (
|
|
112
|
+
<ReadOnlyComp
|
|
113
|
+
{...props}
|
|
114
|
+
checked={isChecked}
|
|
115
|
+
label={restRecord.label as React.ReactNode}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (formLayout) {
|
|
120
|
+
return (
|
|
121
|
+
<FormControlLayout
|
|
122
|
+
formLayout={formLayout}
|
|
123
|
+
labelSize={labelSize}
|
|
124
|
+
controlMaxWidth={controlMaxWidth}
|
|
125
|
+
controlMinWidth={controlMinWidth}
|
|
126
|
+
>
|
|
127
|
+
{roNode}
|
|
128
|
+
</FormControlLayout>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return <>{roNode}</>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const FinalThumbIcon = (
|
|
136
|
+
<div className={styles.thumbIconWrapper}>
|
|
137
|
+
<CheckIcon className={styles.checkIcon} />
|
|
138
|
+
<CloseIcon className={styles.closeIcon} />
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// We omit Mantine's sizing/coloring so we rely strictly on variables from Switch.module.css
|
|
143
|
+
const switchNode = (
|
|
144
|
+
<MantineSwitch
|
|
145
|
+
ref={ref}
|
|
146
|
+
{...(sanitizedProps as unknown as MantineSwitchProps)}
|
|
147
|
+
className={finalClass}
|
|
148
|
+
classNames={mergedClassNames}
|
|
149
|
+
styles={mergedStyles}
|
|
150
|
+
disabled={readOnly || disabled}
|
|
151
|
+
data-disabled={readOnly || disabled || undefined}
|
|
152
|
+
thumbIcon={withCallerOverride<React.ReactNode>(
|
|
153
|
+
FinalThumbIcon,
|
|
154
|
+
thumbIcon,
|
|
155
|
+
)}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (formLayout) {
|
|
160
|
+
return (
|
|
161
|
+
<FormControlLayout
|
|
162
|
+
formLayout={formLayout}
|
|
163
|
+
labelSize={labelSize}
|
|
164
|
+
controlMaxWidth={controlMaxWidth}
|
|
165
|
+
controlMinWidth={controlMinWidth}
|
|
166
|
+
>
|
|
167
|
+
{switchNode}
|
|
168
|
+
</FormControlLayout>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return switchNode;
|
|
173
|
+
},
|
|
174
|
+
) as SwitchComponent;
|
|
175
|
+
|
|
176
|
+
Switch.displayName = "Switch";
|
|
177
|
+
Switch.Group = SwitchGroup;
|