@recursica/mui-adapter 0.6.0 → 0.7.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/ARCHITECTURE.md +15 -0
- package/CHANGELOG.md +6 -0
- package/SETUP.md +77 -0
- package/USAGE.md +55 -0
- package/dist/mui-adapter.cjs +26 -26
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +3721 -3511
- package/dist/mui-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +9 -29
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +2 -5
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +4 -8
- package/dist/src/components/Avatar/Avatar.d.ts +1 -6
- package/dist/src/components/Badge/Badge.d.ts +1 -4
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +2 -3
- package/dist/src/components/Button/Button.d.ts +2 -17
- package/dist/src/components/Card/Card.d.ts +2 -4
- package/dist/src/components/Checkbox/Checkbox.d.ts +4 -6
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +8 -6
- package/dist/src/components/Chip/Chip.d.ts +2 -11
- package/dist/src/components/Container/Container.d.ts +1 -5
- package/dist/src/components/DatePicker/DatePicker.d.ts +2 -1
- package/dist/src/components/Dropdown/Dropdown.d.ts +4 -14
- package/dist/src/components/Flex/Flex.d.ts +2 -22
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +2 -7
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +3 -18
- package/dist/src/components/Group/Group.d.ts +2 -7
- package/dist/src/components/HoverCard/HoverCard.d.ts +13 -39
- package/dist/src/components/Label/Label.d.ts +2 -9
- package/dist/src/components/Link/Link.d.ts +2 -6
- package/dist/src/components/Loader/Loader.d.ts +2 -7
- package/dist/src/components/Menu/Menu.d.ts +24 -98
- package/dist/src/components/Modal/Modal.d.ts +32 -31
- package/dist/src/components/NumberInput/NumberInput.d.ts +3 -5
- package/dist/src/components/Pagination/Pagination.d.ts +8 -41
- package/dist/src/components/Panel/Panel.d.ts +5 -18
- package/dist/src/components/Radio/Radio.d.ts +4 -4
- package/dist/src/components/Radio/RadioGroup.d.ts +3 -7
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +2 -5
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +5 -15
- package/dist/src/components/Slider/Slider.d.ts +4 -14
- package/dist/src/components/Stack/Stack.d.ts +2 -5
- package/dist/src/components/Stepper/Stepper.d.ts +36 -16
- package/dist/src/components/Switch/Switch.d.ts +4 -4
- package/dist/src/components/Switch/SwitchGroup.d.ts +4 -4
- package/dist/src/components/Table/Table.d.ts +2 -1
- package/dist/src/components/Tabs/Tabs.d.ts +11 -26
- package/dist/src/components/Text/Text.d.ts +2 -16
- package/dist/src/components/TextArea/TextArea.d.ts +3 -9
- package/dist/src/components/TextField/TextField.d.ts +3 -7
- package/dist/src/components/TimePicker/TimePicker.d.ts +2 -1
- package/dist/src/components/Timeline/Timeline.d.ts +2 -1
- package/dist/src/components/Timeline/TimelineItem.d.ts +2 -13
- package/dist/src/components/Title/Title.d.ts +2 -15
- package/dist/src/components/Toast/Toast.d.ts +11 -16
- package/dist/src/components/Tooltip/Tooltip.d.ts +7 -21
- package/dist/src/components/TransferList/TransferList.d.ts +2 -1
- package/dist/src/components/Typography/Typography.d.ts +1 -1
- package/dist/src/utils/RequireAccessibleLabel.d.ts +1 -18
- package/dist/src/utils/filterStylingProps.d.ts +1 -11
- package/llms.txt +16 -0
- package/package.json +6 -2
- package/src/components/Accordion/Accordion.module.css +1 -2
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.tsx +19 -23
- package/src/components/AssistiveElement/AssistiveElement.module.css +32 -29
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +10 -27
- package/src/components/AssistiveElement/AssistiveElement.tsx +11 -15
- package/src/components/Autocomplete/Autocomplete.module.css +17 -18
- package/src/components/Autocomplete/Autocomplete.stories.tsx +7 -16
- package/src/components/Autocomplete/Autocomplete.tsx +27 -45
- package/src/components/Avatar/Avatar.tsx +2 -13
- package/src/components/Badge/Badge.tsx +2 -5
- package/src/components/Breadcrumb/Breadcrumb.tsx +1 -4
- package/src/components/Button/Button.module.css +19 -61
- package/src/components/Button/Button.stories.tsx +24 -23
- package/src/components/Button/Button.tsx +7 -18
- package/src/components/Card/Card.module.css +36 -0
- package/src/components/Card/Card.tsx +6 -4
- package/src/components/Checkbox/Checkbox.module.css +27 -10
- package/src/components/Checkbox/Checkbox.tsx +124 -24
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +43 -23
- package/src/components/Chip/Chip.module.css +52 -34
- package/src/components/Chip/Chip.tsx +45 -47
- package/src/components/Container/Container.stories.tsx +103 -20
- package/src/components/Container/Container.tsx +1 -13
- package/src/components/DatePicker/DatePicker.stories.tsx +125 -19
- package/src/components/DatePicker/DatePicker.tsx +3 -1
- package/src/components/Dropdown/Dropdown.tsx +7 -9
- package/src/components/FileInput/FileInput.stories.tsx +2 -19
- package/src/components/FileUpload/FileUpload.stories.tsx +2 -19
- package/src/components/Flex/Flex.stories.tsx +113 -20
- package/src/components/Flex/Flex.tsx +16 -26
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +0 -8
- package/src/components/FormControlLayout/FormControlLayout.tsx +18 -7
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +1 -10
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +12 -21
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +11 -30
- package/src/components/Group/Group.stories.tsx +6 -30
- package/src/components/Group/Group.tsx +14 -9
- package/src/components/HoverCard/HoverCard.tsx +72 -75
- package/src/components/Label/Label.module.css +4 -0
- package/src/components/Label/Label.stories.tsx +18 -12
- package/src/components/Label/Label.tsx +3 -9
- package/src/components/Link/Link.module.css +4 -3
- package/src/components/Link/Link.stories.tsx +2 -8
- package/src/components/Link/Link.tsx +1 -5
- package/src/components/Loader/Loader.module.css +1 -2
- package/src/components/Loader/Loader.tsx +1 -7
- package/src/components/Menu/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Menu/Menu.stories.tsx +106 -188
- package/src/components/Menu/Menu.tsx +34 -280
- package/src/components/Modal/Modal.tsx +158 -160
- package/src/components/NumberInput/NumberInput.tsx +6 -18
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +3 -0
- package/src/components/Pagination/Pagination.module.css +12 -24
- package/src/components/Pagination/Pagination.stories.tsx +0 -8
- package/src/components/Pagination/Pagination.tsx +20 -140
- package/src/components/Panel/Panel.tsx +7 -30
- package/src/components/Radio/Radio.module.css +25 -10
- package/src/components/Radio/Radio.tsx +51 -19
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +26 -12
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +6 -6
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +5 -36
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +9 -13
- package/src/components/SegmentedControl/SegmentedControl.module.css +23 -47
- package/src/components/SegmentedControl/SegmentedControl.tsx +44 -14
- package/src/components/Slider/Slider.module.css +1 -4
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +26 -25
- package/src/components/Stack/Stack.stories.tsx +4 -25
- package/src/components/Stack/Stack.tsx +1 -5
- package/src/components/Stepper/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Stepper/Stepper.stories.tsx +56 -43
- package/src/components/Stepper/Stepper.tsx +89 -87
- package/src/components/Switch/Switch.module.css +21 -0
- package/src/components/Switch/Switch.tsx +63 -16
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +27 -18
- package/src/components/Table/Table.stories.tsx +1 -19
- package/src/components/Table/Table.tsx +3 -1
- package/src/components/Tabs/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Tabs/Tabs.stories.tsx +41 -24
- package/src/components/Tabs/Tabs.tsx +49 -66
- package/src/components/Text/Text.stories.tsx +5 -17
- package/src/components/Text/Text.tsx +1 -15
- package/src/components/TextArea/TextArea.tsx +7 -17
- package/src/components/TextField/TextField.stories.tsx +1 -27
- package/src/components/TextField/TextField.tsx +4 -6
- package/src/components/TimePicker/TimePicker.stories.tsx +2 -19
- package/src/components/TimePicker/TimePicker.tsx +3 -1
- package/src/components/Timeline/Timeline.tsx +4 -1
- package/src/components/Timeline/TimelineItem.tsx +1 -12
- package/src/components/Title/Title.stories.tsx +0 -18
- package/src/components/Title/Title.tsx +1 -7
- package/src/components/Toast/Toast.stories.tsx +1 -1
- package/src/components/Toast/Toast.tsx +62 -28
- package/src/components/Tooltip/Tooltip.tsx +11 -37
- package/src/components/TransferList/TransferList.stories.tsx +1 -19
- package/src/components/TransferList/TransferList.tsx +3 -1
- package/src/utils/RequireAccessibleLabel.ts +1 -12
- package/src/utils/filterStylingProps.ts +7 -20
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
-
import { Stepper } from "./Stepper";
|
|
3
|
+
import { Stepper, Step, StepLabel } from "./Stepper";
|
|
4
4
|
import { Button } from "../Button/Button";
|
|
5
5
|
import { Group } from "../Group/Group";
|
|
6
6
|
import { Flex } from "../Flex/Flex";
|
|
@@ -11,19 +11,6 @@ const meta: Meta<typeof Stepper> = {
|
|
|
11
11
|
tags: ["autodocs"],
|
|
12
12
|
parameters: {
|
|
13
13
|
layout: "centered",
|
|
14
|
-
docs: {
|
|
15
|
-
description: {
|
|
16
|
-
component: `
|
|
17
|
-
The \`Stepper\` component provides a visual progression interface strictly mapped to Recursica's UI-Kit.
|
|
18
|
-
This component wraps Mantine's \`Stepper\` and natively enforces Figma tokens for sizes, colors, gaps, and states (\`completed\`, \`current\`, \`upcoming\`).
|
|
19
|
-
|
|
20
|
-
### Layout & Orientation
|
|
21
|
-
Orientation dictates how the steps flow:
|
|
22
|
-
- **horizontal**: Steps render side-by-side.
|
|
23
|
-
- **vertical**: Steps stack vertically, mapping seamlessly to the vertical gap tokens.
|
|
24
|
-
`,
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
14
|
},
|
|
28
15
|
argTypes: {
|
|
29
16
|
size: {
|
|
@@ -34,9 +21,6 @@ Orientation dictates how the steps flow:
|
|
|
34
21
|
control: "radio",
|
|
35
22
|
options: ["horizontal", "vertical"],
|
|
36
23
|
},
|
|
37
|
-
defaultChecked: {
|
|
38
|
-
table: { disable: true },
|
|
39
|
-
},
|
|
40
24
|
},
|
|
41
25
|
};
|
|
42
26
|
|
|
@@ -50,23 +34,39 @@ const InteractiveStepper = (args: React.ComponentProps<typeof Stepper>) => {
|
|
|
50
34
|
const prevStep = () =>
|
|
51
35
|
setActive((current) => (current > 0 ? current - 1 : current));
|
|
52
36
|
|
|
37
|
+
const steps = [
|
|
38
|
+
{
|
|
39
|
+
label: "First step",
|
|
40
|
+
description: "Create an account and set up your billing profile",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: "Second step",
|
|
44
|
+
description:
|
|
45
|
+
"Verify email and ensure all notification preferences are correct",
|
|
46
|
+
},
|
|
47
|
+
{ label: "Final step", description: "Get full access" },
|
|
48
|
+
];
|
|
49
|
+
|
|
53
50
|
return (
|
|
54
51
|
<Flex direction="column" w={600}>
|
|
55
|
-
<Stepper {...args}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
label="Second step"
|
|
62
|
-
description="Verify email and ensure all notification preferences are correct"
|
|
63
|
-
/>
|
|
64
|
-
<Stepper.Step label="Final step" description="Get full access" />
|
|
65
|
-
<Stepper.Completed>
|
|
66
|
-
Completed, click back button to get to previous step
|
|
67
|
-
</Stepper.Completed>
|
|
52
|
+
<Stepper {...args} activeStep={active}>
|
|
53
|
+
{steps.map((step, index) => (
|
|
54
|
+
<Step key={index} completed={active > index}>
|
|
55
|
+
<StepLabel description={step.description}>{step.label}</StepLabel>
|
|
56
|
+
</Step>
|
|
57
|
+
))}
|
|
68
58
|
</Stepper>
|
|
69
59
|
|
|
60
|
+
{active === steps.length ? (
|
|
61
|
+
<div style={{ marginTop: 24, textAlign: "center" }}>
|
|
62
|
+
Completed, click back button to get to previous step
|
|
63
|
+
</div>
|
|
64
|
+
) : (
|
|
65
|
+
<div style={{ marginTop: 24, textAlign: "center" }}>
|
|
66
|
+
Step {active + 1} content
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
70
|
<Group mt={24} justify="center" gap={8}>
|
|
71
71
|
<Button variant="outline" onClick={prevStep} disabled={active === 0}>
|
|
72
72
|
Previous step
|
|
@@ -110,23 +110,36 @@ const StressTestStepper = (args: React.ComponentProps<typeof Stepper>) => {
|
|
|
110
110
|
const prevStep = () =>
|
|
111
111
|
setActive((current) => (current > 0 ? current - 1 : current));
|
|
112
112
|
|
|
113
|
+
const steps = [
|
|
114
|
+
{
|
|
115
|
+
label:
|
|
116
|
+
"This is an extremely long step title designed to test how the layout handles multiline text wrapping and constraints",
|
|
117
|
+
description: "Create an account and set up your billing profile",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
label: "Second step",
|
|
121
|
+
description:
|
|
122
|
+
"Verify email and ensure all notification preferences are correct",
|
|
123
|
+
},
|
|
124
|
+
{ label: "Final step", description: undefined },
|
|
125
|
+
];
|
|
126
|
+
|
|
113
127
|
return (
|
|
114
128
|
<Flex direction="column" w={600}>
|
|
115
|
-
<Stepper {...args}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
label="Second step"
|
|
122
|
-
description="Verify email and ensure all notification preferences are correct"
|
|
123
|
-
/>
|
|
124
|
-
<Stepper.Step label="Final step" />
|
|
125
|
-
<Stepper.Completed>
|
|
126
|
-
Completed, click back button to get to previous step
|
|
127
|
-
</Stepper.Completed>
|
|
129
|
+
<Stepper {...args} activeStep={active}>
|
|
130
|
+
{steps.map((step, index) => (
|
|
131
|
+
<Step key={index} completed={active > index}>
|
|
132
|
+
<StepLabel description={step.description}>{step.label}</StepLabel>
|
|
133
|
+
</Step>
|
|
134
|
+
))}
|
|
128
135
|
</Stepper>
|
|
129
136
|
|
|
137
|
+
{active === steps.length && (
|
|
138
|
+
<div style={{ marginTop: 24, textAlign: "center" }}>
|
|
139
|
+
Completed, click back button to get to previous step
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
|
|
130
143
|
<Group mt={24} justify="center" gap={8}>
|
|
131
144
|
<Button variant="outline" onClick={prevStep} disabled={active === 0}>
|
|
132
145
|
Previous step
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Stepper as MuiStepper,
|
|
4
4
|
Step as MuiStep,
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
StepButton as MuiStepButton,
|
|
7
7
|
StepConnector as MuiStepConnector,
|
|
8
8
|
type StepperProps as MuiStepperProps,
|
|
9
|
+
type StepProps as MuiStepProps,
|
|
10
|
+
type StepLabelProps as MuiStepLabelProps,
|
|
11
|
+
type StepButtonProps as MuiStepButtonProps,
|
|
12
|
+
type StepConnectorProps as MuiStepConnectorProps,
|
|
9
13
|
} from "@mui/material";
|
|
10
14
|
import {
|
|
11
15
|
filterStylingProps,
|
|
@@ -13,23 +17,11 @@ import {
|
|
|
13
17
|
} from "../../utils/filterStylingProps";
|
|
14
18
|
import styles from "./Stepper.module.css";
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
extends Omit<
|
|
18
|
-
MuiStepperProps,
|
|
19
|
-
"size" | "color" | "radius" | "iconSize" | "contentPadding" | "activeStep"
|
|
20
|
-
> {
|
|
21
|
-
size?: "small" | "large";
|
|
22
|
-
active?: number;
|
|
23
|
-
onStepClick?: (stepIndex: number) => void;
|
|
24
|
-
orientation?: "horizontal" | "vertical";
|
|
25
|
-
}
|
|
20
|
+
import { type RecursicaStepperProps } from "@recursica/adapter-common";
|
|
26
21
|
|
|
27
22
|
export type StepperProps = RecursicaOverStyled<RecursicaStepperProps>;
|
|
28
23
|
|
|
29
|
-
export const
|
|
30
|
-
export const StepperCompleted: React.FC<any> = () => null;
|
|
31
|
-
|
|
32
|
-
const _Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
24
|
+
export const Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
33
25
|
function Stepper(props, ref) {
|
|
34
26
|
const {
|
|
35
27
|
overStyled = false,
|
|
@@ -37,27 +29,11 @@ const _Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
|
37
29
|
orientation = "horizontal",
|
|
38
30
|
className,
|
|
39
31
|
style,
|
|
40
|
-
active = 0,
|
|
41
|
-
onStepClick,
|
|
42
|
-
children,
|
|
43
32
|
...rest
|
|
44
33
|
} = props;
|
|
45
34
|
|
|
46
35
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
47
36
|
|
|
48
|
-
const steps = React.Children.toArray(children).filter(
|
|
49
|
-
(child: any) =>
|
|
50
|
-
isValidElement(child) &&
|
|
51
|
-
(child.type === StepperStep ||
|
|
52
|
-
(child.type as any).displayName === "StepperStep"),
|
|
53
|
-
);
|
|
54
|
-
const completed = React.Children.toArray(children).find(
|
|
55
|
-
(child: any) =>
|
|
56
|
-
isValidElement(child) &&
|
|
57
|
-
(child.type === StepperCompleted ||
|
|
58
|
-
(child.type as any).displayName === "StepperCompleted"),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
37
|
return (
|
|
62
38
|
<div
|
|
63
39
|
className={`${styles.root} ${orientation === "horizontal" ? styles.horizontal : styles.vertical} ${size === "large" ? styles.large : styles.small} ${className || ""}`}
|
|
@@ -68,7 +44,6 @@ const _Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
|
68
44
|
<MuiStepper
|
|
69
45
|
ref={ref as any}
|
|
70
46
|
orientation={orientation}
|
|
71
|
-
activeStep={active}
|
|
72
47
|
connector={
|
|
73
48
|
<MuiStepConnector
|
|
74
49
|
classes={{
|
|
@@ -83,65 +58,92 @@ const _Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
|
83
58
|
root: styles.steps,
|
|
84
59
|
}}
|
|
85
60
|
{...(sanitizedProps as MuiStepperProps)}
|
|
86
|
-
|
|
87
|
-
{steps.map((step: any, index: number) => {
|
|
88
|
-
const stepProps = step.props;
|
|
89
|
-
const label = stepProps.label;
|
|
90
|
-
const description = stepProps.description;
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<MuiStep
|
|
94
|
-
key={index}
|
|
95
|
-
classes={{ root: styles.step }}
|
|
96
|
-
completed={active > index}
|
|
97
|
-
>
|
|
98
|
-
<MuiStepButton onClick={() => onStepClick?.(index)}>
|
|
99
|
-
<MuiStepLabel
|
|
100
|
-
optional={
|
|
101
|
-
description ? (
|
|
102
|
-
<div className={styles.stepDescription}>
|
|
103
|
-
{description}
|
|
104
|
-
</div>
|
|
105
|
-
) : null
|
|
106
|
-
}
|
|
107
|
-
classes={{
|
|
108
|
-
label: styles.stepLabel,
|
|
109
|
-
iconContainer: styles.stepIcon,
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
{label}
|
|
113
|
-
</MuiStepLabel>
|
|
114
|
-
</MuiStepButton>
|
|
115
|
-
</MuiStep>
|
|
116
|
-
);
|
|
117
|
-
})}
|
|
118
|
-
</MuiStepper>
|
|
119
|
-
{active >= steps.length && completed && (
|
|
120
|
-
<div className={styles.content}>
|
|
121
|
-
{(completed as React.ReactElement).props.children}
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
{active < steps.length &&
|
|
125
|
-
steps[active] &&
|
|
126
|
-
(steps[active] as React.ReactElement).props.children && (
|
|
127
|
-
<div className={styles.content}>
|
|
128
|
-
{(steps[active] as React.ReactElement).props.children}
|
|
129
|
-
</div>
|
|
130
|
-
)}
|
|
61
|
+
/>
|
|
131
62
|
</div>
|
|
132
63
|
);
|
|
133
64
|
},
|
|
134
65
|
);
|
|
135
66
|
|
|
136
|
-
|
|
137
|
-
|
|
67
|
+
Stepper.displayName = "Stepper";
|
|
68
|
+
|
|
69
|
+
export type StepProps = RecursicaOverStyled<MuiStepProps>;
|
|
70
|
+
|
|
71
|
+
export const Step = forwardRef<HTMLDivElement, StepProps>(
|
|
72
|
+
function Step(props, ref) {
|
|
73
|
+
const { overStyled = false, className, ...rest } = props;
|
|
74
|
+
return (
|
|
75
|
+
<MuiStep
|
|
76
|
+
ref={ref}
|
|
77
|
+
classes={{ root: styles.step }}
|
|
78
|
+
className={className || ""}
|
|
79
|
+
{...(filterStylingProps(rest, overStyled) as MuiStepProps)}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
Step.displayName = "Step";
|
|
86
|
+
|
|
87
|
+
export type StepButtonProps = RecursicaOverStyled<MuiStepButtonProps>;
|
|
88
|
+
|
|
89
|
+
export const StepButton = forwardRef<HTMLButtonElement, StepButtonProps>(
|
|
90
|
+
function StepButton(props, ref) {
|
|
91
|
+
const { overStyled = false, className, ...rest } = props;
|
|
92
|
+
return (
|
|
93
|
+
<MuiStepButton
|
|
94
|
+
ref={ref}
|
|
95
|
+
className={className || ""}
|
|
96
|
+
{...(filterStylingProps(rest, overStyled) as MuiStepButtonProps)}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
StepButton.displayName = "StepButton";
|
|
103
|
+
|
|
104
|
+
export type StepLabelProps = RecursicaOverStyled<
|
|
105
|
+
MuiStepLabelProps & { description?: React.ReactNode }
|
|
106
|
+
>;
|
|
138
107
|
|
|
139
|
-
export const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
108
|
+
export const StepLabel = forwardRef<HTMLDivElement, StepLabelProps>(
|
|
109
|
+
function StepLabel(props, ref) {
|
|
110
|
+
const { overStyled = false, className, description, ...rest } = props;
|
|
111
|
+
return (
|
|
112
|
+
<MuiStepLabel
|
|
113
|
+
ref={ref}
|
|
114
|
+
className={className || ""}
|
|
115
|
+
classes={{
|
|
116
|
+
label: styles.stepLabel,
|
|
117
|
+
iconContainer: styles.stepIcon,
|
|
118
|
+
}}
|
|
119
|
+
optional={
|
|
120
|
+
description ? (
|
|
121
|
+
<div className={styles.stepDescription}>{description}</div>
|
|
122
|
+
) : (
|
|
123
|
+
rest.optional
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
{...(filterStylingProps(rest, overStyled) as MuiStepLabelProps)}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
StepLabel.displayName = "StepLabel";
|
|
133
|
+
|
|
134
|
+
export type StepConnectorProps = RecursicaOverStyled<MuiStepConnectorProps>;
|
|
135
|
+
|
|
136
|
+
export const StepConnector = forwardRef<HTMLDivElement, StepConnectorProps>(
|
|
137
|
+
function StepConnector(props, ref) {
|
|
138
|
+
const { overStyled = false, className, ...rest } = props;
|
|
139
|
+
return (
|
|
140
|
+
<MuiStepConnector
|
|
141
|
+
ref={ref}
|
|
142
|
+
className={className || ""}
|
|
143
|
+
{...(filterStylingProps(rest, overStyled) as MuiStepConnectorProps)}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
},
|
|
147
|
+
);
|
|
146
148
|
|
|
147
|
-
|
|
149
|
+
StepConnector.displayName = "StepConnector";
|
|
@@ -150,6 +150,27 @@
|
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
.description {
|
|
154
|
+
margin-top: 5px; /* HARDCODE: mantine default */
|
|
155
|
+
color: #868e96; /* HARDCODE: mantine default dimmed */
|
|
156
|
+
font-size: 12px; /* HARDCODE: mantine default xs */
|
|
157
|
+
line-height: 1.2;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.error {
|
|
161
|
+
margin-top: 5px; /* HARDCODE: mantine default */
|
|
162
|
+
color: #fa5252; /* HARDCODE: mantine default error */
|
|
163
|
+
font-size: 12px; /* HARDCODE: mantine default xs */
|
|
164
|
+
line-height: 1.2;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.description[data-disabled="true"],
|
|
168
|
+
.error[data-disabled="true"] {
|
|
169
|
+
opacity: var(
|
|
170
|
+
--recursica_ui-kit_components_switch-item_properties_disabled-opacity
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
.thumbIconWrapper {
|
|
154
175
|
display: flex;
|
|
155
176
|
align-items: center;
|
|
@@ -9,25 +9,33 @@ import {
|
|
|
9
9
|
filterStylingProps,
|
|
10
10
|
type RecursicaOverStyled,
|
|
11
11
|
} from "../../utils/filterStylingProps";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type RequireAccessibleLabel,
|
|
14
|
+
type RecursicaSwitchProps,
|
|
15
|
+
} from "@recursica/adapter-common";
|
|
13
16
|
import {
|
|
14
17
|
FormControlLayout,
|
|
15
18
|
type FormControlLayoutProps,
|
|
16
19
|
} from "../FormControlLayout/FormControlLayout";
|
|
17
20
|
import styles from "./Switch.module.css";
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
// Removed unused SwitchGroup import
|
|
23
|
+
|
|
24
|
+
export type SwitchWrapperProps = Omit<
|
|
25
|
+
MuiSwitchProps,
|
|
26
|
+
"size" | "color" | "radius" | "variant"
|
|
27
|
+
> &
|
|
28
|
+
RecursicaSwitchProps &
|
|
29
|
+
ReadOnlyControlProps &
|
|
30
|
+
Pick<
|
|
31
|
+
FormControlLayoutProps,
|
|
32
|
+
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
33
|
+
>;
|
|
20
34
|
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
ReadOnlyControlProps &
|
|
24
|
-
Pick<
|
|
25
|
-
FormControlLayoutProps,
|
|
26
|
-
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
27
|
-
>
|
|
28
|
-
>;
|
|
35
|
+
export type RecursicaSwitchPropsAlias =
|
|
36
|
+
RequireAccessibleLabel<SwitchWrapperProps>;
|
|
29
37
|
|
|
30
|
-
export type SwitchProps = RecursicaOverStyled<
|
|
38
|
+
export type SwitchProps = RecursicaOverStyled<RecursicaSwitchPropsAlias>;
|
|
31
39
|
|
|
32
40
|
type SwitchComponent = any;
|
|
33
41
|
|
|
@@ -38,11 +46,15 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
38
46
|
readOnly,
|
|
39
47
|
readOnlyComponent,
|
|
40
48
|
disabled,
|
|
41
|
-
thumbIcon
|
|
49
|
+
// Removed thumbIcon from destructured props since it's not supported
|
|
42
50
|
formLayout,
|
|
43
51
|
labelSize,
|
|
44
52
|
controlMaxWidth,
|
|
45
53
|
controlMinWidth,
|
|
54
|
+
label,
|
|
55
|
+
description,
|
|
56
|
+
error,
|
|
57
|
+
style,
|
|
46
58
|
...rest
|
|
47
59
|
} = props;
|
|
48
60
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
@@ -125,8 +137,8 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
125
137
|
// We omit Mui's sizing/coloring so we rely strictly on variables from Switch.module.css
|
|
126
138
|
const switchNode = (
|
|
127
139
|
<MuiSwitch
|
|
128
|
-
ref={ref}
|
|
129
|
-
className={finalClass}
|
|
140
|
+
ref={ref as any}
|
|
141
|
+
className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
|
|
130
142
|
classes={mergedClassNames}
|
|
131
143
|
disabled={readOnly || disabled}
|
|
132
144
|
data-disabled={readOnly || disabled || undefined}
|
|
@@ -135,6 +147,41 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
135
147
|
/>
|
|
136
148
|
);
|
|
137
149
|
|
|
150
|
+
const finalNode = label ? (
|
|
151
|
+
<div className={finalClass} style={style as React.CSSProperties}>
|
|
152
|
+
<div className={styles.body}>
|
|
153
|
+
{switchNode}
|
|
154
|
+
<div className={styles.labelWrapper}>
|
|
155
|
+
<label
|
|
156
|
+
className={styles.label}
|
|
157
|
+
htmlFor={restRecord.id as string}
|
|
158
|
+
data-disabled={readOnly || disabled ? true : undefined}
|
|
159
|
+
>
|
|
160
|
+
{label as React.ReactNode}
|
|
161
|
+
</label>
|
|
162
|
+
{description && (
|
|
163
|
+
<div
|
|
164
|
+
className={styles.description}
|
|
165
|
+
data-disabled={readOnly || disabled ? true : undefined}
|
|
166
|
+
>
|
|
167
|
+
{description}
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
{error && (
|
|
171
|
+
<div
|
|
172
|
+
className={styles.error}
|
|
173
|
+
data-disabled={readOnly || disabled ? true : undefined}
|
|
174
|
+
>
|
|
175
|
+
{error}
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
) : (
|
|
182
|
+
switchNode
|
|
183
|
+
);
|
|
184
|
+
|
|
138
185
|
if (formLayout) {
|
|
139
186
|
return (
|
|
140
187
|
<FormControlLayout
|
|
@@ -143,12 +190,12 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
143
190
|
controlMaxWidth={controlMaxWidth}
|
|
144
191
|
controlMinWidth={controlMinWidth}
|
|
145
192
|
>
|
|
146
|
-
{
|
|
193
|
+
{finalNode}
|
|
147
194
|
</FormControlLayout>
|
|
148
195
|
);
|
|
149
196
|
}
|
|
150
197
|
|
|
151
|
-
return
|
|
198
|
+
return finalNode;
|
|
152
199
|
},
|
|
153
200
|
) as SwitchComponent;
|
|
154
201
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Switch } from "./Switch";
|
|
3
|
+
import { SwitchGroup } from "./SwitchGroup";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SwitchGroup> = {
|
|
8
|
+
title: "UI-Kit/SwitchGroup",
|
|
9
|
+
component: SwitchGroup,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
The \`SwitchGroup\` component is the mandatory organizational wrapper for orchestrating \`Switch\` primitives into macro form layouts. It inherently leverages the \`FormControlWrapper\`, granting native access to structural alignments, assistive descriptions, and strict flex arrays.
|
|
16
|
+
|
|
17
|
+
### Supported Layout Vectors
|
|
18
|
+
- **\`formLayout="stacked"\`**: Top-to-bottom layout cascading the Label bounding box down vertically into a standard stacked switch array.
|
|
19
|
+
- **\`formLayout="side-by-side"\`**: Flow architecture pulling the grouping Label dynamically to the left while structurally organizing the internal switches cleanly alongside it horizontally.
|
|
20
|
+
|
|
21
|
+
> [!IMPORTANT]
|
|
22
|
+
> If you require a solitary \`Switch\` field to natively accept an overarching form label, assistive text, or align with other side-by-side components in a form column, you must wrap it inside a \`Switch.Group\` to trigger the \`FormControlWrapper\` mapping.
|
|
23
|
+
`,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
...formControlArgTypes,
|
|
29
|
+
readOnly: {
|
|
30
|
+
control: "boolean",
|
|
31
|
+
description:
|
|
32
|
+
"Passes read-only lock natively down mapping inner switches into explicitly disabled states.",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
|
|
39
|
+
type Story = StoryObj<typeof SwitchGroup>;
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
formLayout: "stacked",
|
|
44
|
+
label: "Standard Group",
|
|
45
|
+
},
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
47
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
48
|
+
const [value, setValue] = useState<string[]>([]);
|
|
49
|
+
return (
|
|
50
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
51
|
+
<Switch value="1" label="Option 1" />
|
|
52
|
+
<Switch value="2" label="Option 2" />
|
|
53
|
+
</SwitchGroup>
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const StackedLayout: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
formLayout: "stacked",
|
|
61
|
+
label: "Notification Settings",
|
|
62
|
+
description: "Manage your preferences.",
|
|
63
|
+
assistiveText: "We recommend turning these on.",
|
|
64
|
+
error: "",
|
|
65
|
+
required: true,
|
|
66
|
+
},
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
68
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
69
|
+
const [value, setValue] = useState<string[]>(["email"]);
|
|
70
|
+
return (
|
|
71
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
72
|
+
<Switch value="email" label="Email Alerts" />
|
|
73
|
+
<Switch value="push" label="Push Notifications" />
|
|
74
|
+
<Switch value="sms" label="SMS Messages" />
|
|
75
|
+
</SwitchGroup>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const SideBySideLayout: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
...StackedLayout.args,
|
|
83
|
+
formLayout: "side-by-side",
|
|
84
|
+
labelSize: "default",
|
|
85
|
+
labelAlignment: "left",
|
|
86
|
+
},
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
88
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
89
|
+
const [value, setValue] = useState<string[]>([]);
|
|
90
|
+
return (
|
|
91
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
92
|
+
<Switch value="weekly" label="Weekly Digest" />
|
|
93
|
+
<Switch value="marketing" label="Marketing Emails" />
|
|
94
|
+
</SwitchGroup>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const SolitaryFormControl: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
formLayout: "side-by-side",
|
|
102
|
+
label: "Enable Backups",
|
|
103
|
+
description: "Automatically back up your data nightly.",
|
|
104
|
+
},
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
106
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
107
|
+
const [value, setValue] = useState<string[]>(["auto"]);
|
|
108
|
+
return (
|
|
109
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
110
|
+
<Switch value="auto" label="Auto-backup" />
|
|
111
|
+
</SwitchGroup>
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const ReadOnly: Story = {
|
|
117
|
+
args: {
|
|
118
|
+
...StackedLayout.args,
|
|
119
|
+
readOnly: true,
|
|
120
|
+
},
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
122
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
123
|
+
const [value, setValue] = useState<string[]>(["email", "sms"]);
|
|
124
|
+
return (
|
|
125
|
+
<SwitchGroup {...args} value={value} onChange={setValue}>
|
|
126
|
+
<Switch value="email" label="Email Alerts" />
|
|
127
|
+
<Switch value="push" label="Push Notifications" />
|
|
128
|
+
<Switch value="sms" label="SMS Messages" />
|
|
129
|
+
</SwitchGroup>
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
};
|