@recursica/mui-adapter 0.26.0 → 0.27.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/CHANGELOG.md +11 -0
- package/dist/index.d.ts +82 -0
- package/dist/mui-adapter.cjs +73 -73
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.js +9164 -9078
- package/dist/mui-adapter.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Autocomplete/Autocomplete.tsx +46 -34
- package/src/components/Avatar/Avatar.tsx +11 -21
- package/src/components/Badge/Badge.tsx +9 -13
- package/src/components/Breadcrumb/Breadcrumb.tsx +12 -18
- package/src/components/Button/Button.tsx +17 -11
- package/src/components/Card/Card.tsx +9 -19
- package/src/components/Checkbox/Checkbox.tsx +22 -26
- package/src/components/Checkbox/CheckboxGroup.tsx +3 -3
- package/src/components/Chip/Chip.tsx +12 -19
- package/src/components/Dropdown/BareDropdown.tsx +14 -8
- package/src/components/Dropdown/Dropdown.tsx +13 -6
- package/src/components/FileInput/FileInput.tsx +1 -1
- package/src/components/FileUpload/FileUpload.tsx +1 -1
- package/src/components/FormControlLayout/FormControlLayout.tsx +1 -1
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +1 -1
- package/src/components/HoverCard/HoverCard.tsx +13 -21
- package/src/components/Menu/Menu.tsx +14 -6
- package/src/components/Modal/Modal.tsx +12 -6
- package/src/components/NumberInput/NumberInput.tsx +30 -15
- package/src/components/Pagination/Pagination.tsx +5 -15
- package/src/components/Panel/Panel.tsx +17 -25
- package/src/components/Popover/Popover.tsx +10 -18
- package/src/components/Radio/Radio.tsx +21 -26
- package/src/components/Radio/RadioGroup.tsx +3 -4
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +1 -1
- package/src/components/SegmentedControl/SegmentedControl.tsx +20 -25
- package/src/components/Slider/Slider.tsx +40 -43
- package/src/components/Stepper/Stepper.tsx +34 -14
- package/src/components/Switch/Switch.tsx +33 -49
- package/src/components/Switch/SwitchGroup.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +12 -4
- package/src/components/TextArea/TextArea.tsx +30 -7
- package/src/components/TextField/TextField.tsx +30 -26
- package/src/components/Timeline/Timeline.tsx +9 -1
- package/src/components/Timeline/TimelineItem.tsx +2 -2
- package/src/components/Toast/Toast.tsx +14 -23
- package/src/components/Tooltip/Tooltip.tsx +14 -27
- package/src/components/TransferList/TransferList.tsx +1 -1
- package/src/utils/filterStylingProps.ts +4 -0
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "@mui/material";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
8
9
|
type RecursicaOverStyled,
|
|
9
10
|
} from "../../utils/filterStylingProps";
|
|
10
11
|
import styles from "./HoverCard.module.css";
|
|
@@ -35,27 +36,18 @@ const HoverCardBase = function HoverCard({
|
|
|
35
36
|
overStyled,
|
|
36
37
|
);
|
|
37
38
|
|
|
38
|
-
// Bind CSS module classes to Mui's internal classNames API
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
const o = classNamesProp as Record<string, string>;
|
|
51
|
-
Object.keys(o).forEach((key) => {
|
|
52
|
-
if (mergedClassNames[key]) {
|
|
53
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
54
|
-
} else {
|
|
55
|
-
mergedClassNames[key] = o[key];
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
39
|
+
// Bind CSS module classes to Mui's internal classNames API. Note MUI's actual prop is
|
|
40
|
+
// "classes", not "classNames" (that's Mantine's naming) — this used to read the wrong key,
|
|
41
|
+
// silently no-op-ing any caller-supplied classes.
|
|
42
|
+
const mergedClassNames = mergeClassNames(
|
|
43
|
+
{
|
|
44
|
+
tooltip: styles.dropdown,
|
|
45
|
+
arrow: styles.arrow,
|
|
46
|
+
},
|
|
47
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
48
|
+
| Partial<Record<string, string>>
|
|
49
|
+
| undefined,
|
|
50
|
+
);
|
|
59
51
|
|
|
60
52
|
// Find Target and Dropdown children
|
|
61
53
|
let targetNode: React.ReactNode = null;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@mui/material";
|
|
10
10
|
import {
|
|
11
11
|
filterStylingProps,
|
|
12
|
+
mergeClassNames,
|
|
12
13
|
type RecursicaOverStyled,
|
|
13
14
|
} from "../../utils/filterStylingProps";
|
|
14
15
|
import styles from "./Menu.module.css";
|
|
@@ -23,15 +24,22 @@ export const Menu = forwardRef<HTMLDivElement, MenuProps>(function Menu(
|
|
|
23
24
|
) {
|
|
24
25
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
25
26
|
|
|
27
|
+
const mergedClassNames = mergeClassNames(
|
|
28
|
+
{
|
|
29
|
+
paper: styles.dropdown,
|
|
30
|
+
list: styles.dropdown,
|
|
31
|
+
},
|
|
32
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
33
|
+
| Partial<Record<string, string>>
|
|
34
|
+
| undefined,
|
|
35
|
+
);
|
|
36
|
+
|
|
26
37
|
return (
|
|
27
38
|
<MuiMenu
|
|
28
39
|
ref={ref}
|
|
29
40
|
{...(sanitizedProps as MuiMenuProps)}
|
|
30
41
|
className={`${styles.dropdown} ${className || ""}`}
|
|
31
|
-
classes={
|
|
32
|
-
paper: styles.dropdown,
|
|
33
|
-
list: styles.dropdown,
|
|
34
|
-
}}
|
|
42
|
+
classes={mergedClassNames}
|
|
35
43
|
/>
|
|
36
44
|
);
|
|
37
45
|
});
|
|
@@ -47,8 +55,8 @@ export const MenuItem = forwardRef<HTMLLIElement, MenuItemProps>(
|
|
|
47
55
|
return (
|
|
48
56
|
<MuiMenuItem
|
|
49
57
|
ref={ref}
|
|
50
|
-
className={`${styles.item} ${className || ""}`}
|
|
51
58
|
{...(sanitizedProps as MuiMenuItemProps)}
|
|
59
|
+
className={`${styles.item} ${className || ""}`}
|
|
52
60
|
/>
|
|
53
61
|
);
|
|
54
62
|
},
|
|
@@ -65,8 +73,8 @@ export const MenuDivider = forwardRef<HTMLHRElement, MenuDividerProps>(
|
|
|
65
73
|
return (
|
|
66
74
|
<MuiDivider
|
|
67
75
|
ref={ref}
|
|
68
|
-
className={`${styles.divider} ${className || ""}`}
|
|
69
76
|
{...(sanitizedProps as MuiDividerProps)}
|
|
77
|
+
className={`${styles.divider} ${className || ""}`}
|
|
70
78
|
/>
|
|
71
79
|
);
|
|
72
80
|
},
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@mui/material";
|
|
13
13
|
import {
|
|
14
14
|
filterStylingProps,
|
|
15
|
+
mergeClassNames,
|
|
15
16
|
type RecursicaOverStyled,
|
|
16
17
|
} from "../../utils/filterStylingProps";
|
|
17
18
|
import styles from "./Modal.module.css";
|
|
@@ -58,6 +59,13 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(
|
|
|
58
59
|
) {
|
|
59
60
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
60
61
|
|
|
62
|
+
const mergedClassNames = mergeClassNames(
|
|
63
|
+
{ paper: styles.inner },
|
|
64
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
65
|
+
| Partial<Record<string, string>>
|
|
66
|
+
| undefined,
|
|
67
|
+
);
|
|
68
|
+
|
|
61
69
|
let footer: React.ReactNode = null;
|
|
62
70
|
const bodyChildren: React.ReactNode[] = [];
|
|
63
71
|
|
|
@@ -79,9 +87,7 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(
|
|
|
79
87
|
ref={ref}
|
|
80
88
|
{...(sanitizedProps as MuiDialogProps)}
|
|
81
89
|
className={`${styles.root} ${className || ""}`}
|
|
82
|
-
classes={
|
|
83
|
-
paper: styles.inner,
|
|
84
|
-
}}
|
|
90
|
+
classes={mergedClassNames}
|
|
85
91
|
open={
|
|
86
92
|
opened ||
|
|
87
93
|
((sanitizedProps as Record<string, unknown>).open as boolean) ||
|
|
@@ -124,8 +130,8 @@ const ModalTitle = forwardRef<HTMLDivElement, ModalTitleProps>(
|
|
|
124
130
|
<MuiDialogTitle
|
|
125
131
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
132
|
ref={ref as any}
|
|
127
|
-
className={`${styles.title} ${className || ""}`}
|
|
128
133
|
{...(sanitizedProps as MuiDialogTitleProps)}
|
|
134
|
+
className={`${styles.title} ${className || ""}`}
|
|
129
135
|
/>
|
|
130
136
|
);
|
|
131
137
|
},
|
|
@@ -174,11 +180,11 @@ const ModalBody = forwardRef<HTMLDivElement, ModalBodyProps>(function ModalBody(
|
|
|
174
180
|
internalRef as React.MutableRefObject<HTMLDivElement | null>
|
|
175
181
|
).current = node as HTMLDivElement;
|
|
176
182
|
}}
|
|
183
|
+
{...(sanitizedProps as MuiDialogContentProps)}
|
|
177
184
|
onScroll={() => checkScroll()}
|
|
178
185
|
data-scrolled-top={scrolledTop || undefined}
|
|
179
186
|
data-scrolled-bottom={scrolledBottom || undefined}
|
|
180
187
|
className={`${styles.content} ${styles.scrollArea} ${className || ""}`}
|
|
181
|
-
{...(sanitizedProps as MuiDialogContentProps)}
|
|
182
188
|
>
|
|
183
189
|
{children}
|
|
184
190
|
</MuiDialogContent>
|
|
@@ -201,8 +207,8 @@ const ModalFooter = forwardRef<HTMLDivElement, ModalFooterProps>(
|
|
|
201
207
|
<MuiDialogActions
|
|
202
208
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
209
|
ref={ref as any}
|
|
204
|
-
className={`${styles.footer} ${className || ""}`}
|
|
205
210
|
{...(sanitizedProps as MuiDialogActionsProps)}
|
|
211
|
+
className={`${styles.footer} ${className || ""}`}
|
|
206
212
|
/>
|
|
207
213
|
);
|
|
208
214
|
},
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
10
|
+
mergeClassNames,
|
|
9
11
|
type RecursicaOverStyled,
|
|
10
12
|
} from "../../utils/filterStylingProps";
|
|
11
13
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -73,22 +75,33 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
73
75
|
...rest
|
|
74
76
|
} = props;
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
79
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
80
|
+
const UNSUPPORTED_PROPS = [
|
|
81
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
82
|
+
"variant", // Recursica styles the naked input directly; MUI's standard/filled/outlined unused
|
|
83
|
+
] as const satisfies readonly (keyof MuiNumberInputProps)[];
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
86
|
+
filterStylingProps(rest, overStyled),
|
|
87
|
+
UNSUPPORTED_PROPS,
|
|
88
|
+
);
|
|
89
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
83
90
|
|
|
84
|
-
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely. This
|
|
92
|
+
// previously always overwrote the caller's `classes` prop wholesale (no merge at all) —
|
|
93
|
+
// now merged per-slot via mergeClassNames so a caller-supplied slot value extends rather
|
|
94
|
+
// than replaces ours.
|
|
95
|
+
const mergedClassNames = mergeClassNames(
|
|
96
|
+
{
|
|
97
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
98
|
+
input: styles.input,
|
|
99
|
+
section: styles.section,
|
|
100
|
+
controls: styles.controls,
|
|
101
|
+
control: styles.control,
|
|
102
|
+
},
|
|
103
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
104
|
+
);
|
|
92
105
|
|
|
93
106
|
const wrapperClass = className
|
|
94
107
|
? `${styles.layoutOverride} ${className}`
|
|
@@ -138,7 +151,9 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
138
151
|
<MuiNumberInput
|
|
139
152
|
ref={ref}
|
|
140
153
|
{...(sanitizedProps as unknown as MuiNumberInputProps)}
|
|
141
|
-
classes={
|
|
154
|
+
classes={
|
|
155
|
+
mergedClassNames as unknown as MuiNumberInputProps["classes"]
|
|
156
|
+
}
|
|
142
157
|
disabled={disabled}
|
|
143
158
|
value={value}
|
|
144
159
|
defaultValue={defaultValue}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "@mui/material";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
8
9
|
type RecursicaOverStyled,
|
|
9
10
|
} from "../../utils/filterStylingProps";
|
|
10
11
|
import styles from "./Pagination.module.css";
|
|
@@ -19,21 +20,10 @@ function usePaginationClassNames(restRecord: Record<string, unknown>): {
|
|
|
19
20
|
className: string;
|
|
20
21
|
classNames: Partial<Record<string, string>>;
|
|
21
22
|
} {
|
|
22
|
-
const mergedClassNames
|
|
23
|
-
root: styles.root,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const classNamesProp = restRecord.classNames;
|
|
28
|
-
if (
|
|
29
|
-
classNamesProp &&
|
|
30
|
-
typeof classNamesProp === "object" &&
|
|
31
|
-
!Array.isArray(classNamesProp)
|
|
32
|
-
) {
|
|
33
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
34
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
35
|
-
mergedClassNames.ul = o.ul ? `${styles.ul} ${o.ul}` : styles.ul;
|
|
36
|
-
}
|
|
23
|
+
const mergedClassNames = mergeClassNames(
|
|
24
|
+
{ root: styles.root, ul: styles.ul },
|
|
25
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
26
|
+
);
|
|
37
27
|
|
|
38
28
|
const classNameProp = restRecord.className as string | undefined;
|
|
39
29
|
const finalClass = classNameProp
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "@mui/material";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
8
9
|
type RecursicaOverStyled,
|
|
9
10
|
} from "../../utils/filterStylingProps";
|
|
10
11
|
import styles from "./Panel.module.css";
|
|
@@ -63,30 +64,21 @@ const PanelBase = function Panel({
|
|
|
63
64
|
}: PanelProps) {
|
|
64
65
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
65
66
|
|
|
66
|
-
// Bind CSS module classes to Mui's internal classNames API
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
const o = classNamesProp as Record<string, string>;
|
|
82
|
-
Object.keys(o).forEach((key) => {
|
|
83
|
-
if (mergedClassNames[key]) {
|
|
84
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
85
|
-
} else {
|
|
86
|
-
mergedClassNames[key] = o[key];
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
67
|
+
// Bind CSS module classes to Mui's internal classNames API. Note MUI's actual prop is
|
|
68
|
+
// "classes", not "classNames" (that's Mantine's naming) — this used to read the wrong key,
|
|
69
|
+
// silently no-op-ing any caller-supplied classes.
|
|
70
|
+
const mergedClassNames = mergeClassNames(
|
|
71
|
+
{
|
|
72
|
+
content: styles.content,
|
|
73
|
+
header: styles.header,
|
|
74
|
+
title: wrapHeaderText ? styles.titleTruncate : styles.title,
|
|
75
|
+
body: styles.body,
|
|
76
|
+
inner: styles.inner,
|
|
77
|
+
},
|
|
78
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
79
|
+
| Partial<Record<string, string>>
|
|
80
|
+
| undefined,
|
|
81
|
+
);
|
|
90
82
|
|
|
91
83
|
return (
|
|
92
84
|
<MuiDrawer
|
|
@@ -94,7 +86,7 @@ const PanelBase = function Panel({
|
|
|
94
86
|
keepMounted={keepMounted}
|
|
95
87
|
open={Boolean(opened)}
|
|
96
88
|
{...(sanitizedProps as unknown as MuiDrawerProps)}
|
|
97
|
-
classes={mergedClassNames}
|
|
89
|
+
classes={mergedClassNames as unknown as MuiDrawerProps["classes"]}
|
|
98
90
|
/>
|
|
99
91
|
);
|
|
100
92
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "@mui/material";
|
|
12
12
|
import {
|
|
13
13
|
filterStylingProps,
|
|
14
|
+
mergeClassNames,
|
|
14
15
|
type RecursicaOverStyled,
|
|
15
16
|
} from "../../utils/filterStylingProps";
|
|
16
17
|
import styles from "./Popover.module.css";
|
|
@@ -85,24 +86,15 @@ const PopoverBase = function Popover({
|
|
|
85
86
|
);
|
|
86
87
|
|
|
87
88
|
// Bind CSS module classes to Mui's internal classNames API
|
|
88
|
-
const mergedClassNames
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
!Array.isArray(classesProp)
|
|
98
|
-
) {
|
|
99
|
-
const o = classesProp as Record<string, string>;
|
|
100
|
-
Object.keys(o).forEach((key) => {
|
|
101
|
-
mergedClassNames[key] = mergedClassNames[key]
|
|
102
|
-
? `${mergedClassNames[key]} ${o[key]}`
|
|
103
|
-
: o[key];
|
|
104
|
-
});
|
|
105
|
-
}
|
|
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
|
+
);
|
|
106
98
|
|
|
107
99
|
const [internalOpened, setInternalOpened] = useState(defaultOpened);
|
|
108
100
|
const isControlled = opened !== undefined;
|
|
@@ -7,6 +7,8 @@ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
|
7
7
|
import { RadioGroup } from "./RadioGroup";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import {
|
|
@@ -72,13 +74,19 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
|
|
72
74
|
style,
|
|
73
75
|
...rest
|
|
74
76
|
} = props;
|
|
75
|
-
|
|
77
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
78
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
79
|
+
const UNSUPPORTED_PROPS = [
|
|
80
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
81
|
+
"color", // Colors are token-driven; MUI's native palette isn't exposed
|
|
82
|
+
] as const satisfies readonly (keyof MuiRadioProps)[];
|
|
83
|
+
|
|
84
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
85
|
+
filterStylingProps(rest, overStyled),
|
|
86
|
+
UNSUPPORTED_PROPS,
|
|
87
|
+
);
|
|
76
88
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
77
89
|
|
|
78
|
-
// Actively delete dimension bindings that bypass the abstraction
|
|
79
|
-
delete restRecord["size"];
|
|
80
|
-
delete restRecord["color"];
|
|
81
|
-
|
|
82
90
|
// NOTE: MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
83
91
|
// was reading the wrong key and silently doing nothing. Fixed. Also "body"/"inner"/"radio"/
|
|
84
92
|
// "icon"/"labelWrapper"/"label" below were never real MUI `classes` slots (MUI's Radio only
|
|
@@ -89,27 +97,14 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
|
|
89
97
|
// only the label showed. Fixed by keeping only the real slots here and drawing the circle as
|
|
90
98
|
// our own combined icon/checkedIcon node instead (see radioNode below), same pattern already
|
|
91
99
|
// used by Checkbox.
|
|
92
|
-
const mergedClassNames
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
classesProp &&
|
|
101
|
-
typeof classesProp === "object" &&
|
|
102
|
-
!Array.isArray(classesProp)
|
|
103
|
-
) {
|
|
104
|
-
const o = classesProp as Partial<Record<string, string>>;
|
|
105
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
106
|
-
mergedClassNames.checked = o.checked
|
|
107
|
-
? `${styles.checked} ${o.checked}`
|
|
108
|
-
: styles.checked;
|
|
109
|
-
mergedClassNames.disabled = o.disabled
|
|
110
|
-
? `${styles.disabled} ${o.disabled}`
|
|
111
|
-
: styles.disabled;
|
|
112
|
-
}
|
|
100
|
+
const mergedClassNames = mergeClassNames(
|
|
101
|
+
{
|
|
102
|
+
root: styles.root,
|
|
103
|
+
checked: styles.checked,
|
|
104
|
+
disabled: styles.disabled,
|
|
105
|
+
},
|
|
106
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
107
|
+
);
|
|
113
108
|
|
|
114
109
|
const classNameProp = restRecord.className as string | undefined;
|
|
115
110
|
const finalClass = classNameProp
|
|
@@ -73,11 +73,10 @@ export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
|
|
|
73
73
|
onChange,
|
|
74
74
|
...rest
|
|
75
75
|
} = props;
|
|
76
|
+
// NOTE: MuiRadioGroupProps (via FormGroupProps) has no native "size" (or other
|
|
77
|
+
// Recursica-unsupported) prop to leak through here — nothing to add to an UNSUPPORTED_PROPS
|
|
78
|
+
// list beyond what filterStylingProps already blocks.
|
|
76
79
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
77
|
-
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
78
|
-
|
|
79
|
-
// Delete prohibited sizing hooks from bypassing the variables
|
|
80
|
-
delete restRecord["size"];
|
|
81
80
|
|
|
82
81
|
return (
|
|
83
82
|
<WithReadOnlyWrapper
|
|
@@ -27,12 +27,12 @@ export const ReadOnlyTextField: React.FC<ReadOnlyTextFieldProps> = ({
|
|
|
27
27
|
return (
|
|
28
28
|
<Box
|
|
29
29
|
component="p"
|
|
30
|
+
{...sanitizedProps}
|
|
30
31
|
className={
|
|
31
32
|
classNameProp
|
|
32
33
|
? `${styles.textField} ${classNameProp}`
|
|
33
34
|
: styles.textField
|
|
34
35
|
}
|
|
35
|
-
{...sanitizedProps}
|
|
36
36
|
>
|
|
37
37
|
{value != null
|
|
38
38
|
? React.isValidElement(value)
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
} from "@mui/material";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
10
|
+
mergeClassNames,
|
|
9
11
|
type RecursicaOverStyled,
|
|
10
12
|
} from "../../utils/filterStylingProps";
|
|
11
13
|
import styles from "./SegmentedControl.module.css";
|
|
@@ -34,27 +36,14 @@ function useSegmentedControlClassNames(restRecord: Record<string, unknown>): {
|
|
|
34
36
|
className: string;
|
|
35
37
|
classNames: Partial<Record<string, string>>;
|
|
36
38
|
} {
|
|
37
|
-
const mergedClassNames
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
classNamesProp &&
|
|
46
|
-
typeof classNamesProp === "object" &&
|
|
47
|
-
!Array.isArray(classNamesProp)
|
|
48
|
-
) {
|
|
49
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
50
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
51
|
-
mergedClassNames.control = o.control
|
|
52
|
-
? `${styles.control} ${o.control}`
|
|
53
|
-
: styles.control;
|
|
54
|
-
mergedClassNames.label = o.label
|
|
55
|
-
? `${styles.label} ${o.label}`
|
|
56
|
-
: styles.label;
|
|
57
|
-
}
|
|
39
|
+
const mergedClassNames = mergeClassNames(
|
|
40
|
+
{
|
|
41
|
+
root: styles.root,
|
|
42
|
+
control: styles.control,
|
|
43
|
+
label: styles.label,
|
|
44
|
+
},
|
|
45
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
46
|
+
);
|
|
58
47
|
|
|
59
48
|
const classNameProp = restRecord.className as string | undefined;
|
|
60
49
|
const finalClass = classNameProp
|
|
@@ -77,11 +66,17 @@ const _SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
|
|
|
77
66
|
},
|
|
78
67
|
ref,
|
|
79
68
|
) {
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
70
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
71
|
+
const UNSUPPORTED_PROPS = [
|
|
72
|
+
"disabled", // Recursica controls per-item disabled state via `data.disabled`, not the group
|
|
73
|
+
] as const satisfies readonly (keyof MuiSegmentedControlProps)[];
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
76
|
+
filterStylingProps(rest, overStyled),
|
|
77
|
+
UNSUPPORTED_PROPS,
|
|
78
|
+
);
|
|
79
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
85
80
|
|
|
86
81
|
const stylingParams = useSegmentedControlClassNames(restRecord);
|
|
87
82
|
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
10
|
+
mergeClassNames,
|
|
9
11
|
type RecursicaOverStyled,
|
|
10
12
|
} from "../../utils/filterStylingProps";
|
|
11
13
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -137,13 +139,18 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
|
|
|
137
139
|
setInputValue(resolvedValue.toString());
|
|
138
140
|
};
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
143
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
144
|
+
const UNSUPPORTED_PROPS = [
|
|
145
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
146
|
+
"color", // Colors are token-driven; MUI's native palette isn't exposed
|
|
147
|
+
] as const satisfies readonly (keyof MuiSliderProps)[];
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
150
|
+
filterStylingProps(rest, overStyled),
|
|
151
|
+
UNSUPPORTED_PROPS,
|
|
152
|
+
);
|
|
153
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
147
154
|
|
|
148
155
|
// MUI always programmatically re-focuses its hidden native input on pointer interaction,
|
|
149
156
|
// which browsers' `:focus-visible` heuristic treats as keyboard-visible — unlike Mantine's
|
|
@@ -175,43 +182,33 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
|
|
|
175
182
|
externalOnKeyDown?.(e);
|
|
176
183
|
};
|
|
177
184
|
|
|
178
|
-
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
mergedClassNames.thumb = o.thumb
|
|
206
|
-
? `${styles.sliderThumb} ${o.thumb}`
|
|
207
|
-
: styles.sliderThumb;
|
|
208
|
-
mergedClassNames.mark = o.mark
|
|
209
|
-
? `${styles.sliderMark} ${o.mark}`
|
|
210
|
-
: styles.sliderMark;
|
|
211
|
-
mergedClassNames.markLabel = o.markLabel
|
|
212
|
-
? `${styles.sliderMarkLabel} ${o.markLabel}`
|
|
213
|
-
: styles.sliderMarkLabel;
|
|
214
|
-
}
|
|
185
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely. Note MUI's
|
|
186
|
+
// actual prop is "classes", not "classNames" (that's Mantine's naming) — this used to read
|
|
187
|
+
// the wrong key, silently no-op-ing any caller-supplied classes. The caller-facing slot names
|
|
188
|
+
// below (root/track/bar/thumb/mark/markLabel) mirror the mantine-adapter's own Slider
|
|
189
|
+
// classNames slots, translated to MUI's real classes slot names (root/rail/track/thumb/...).
|
|
190
|
+
const callerClasses = restRecord.classes as
|
|
191
|
+
| Partial<Record<string, string>>
|
|
192
|
+
| undefined;
|
|
193
|
+
const mergedClassNames = mergeClassNames(
|
|
194
|
+
{
|
|
195
|
+
root: styles.sliderRoot,
|
|
196
|
+
rail: styles.sliderTrack,
|
|
197
|
+
track: styles.sliderBar,
|
|
198
|
+
thumb: styles.sliderThumb,
|
|
199
|
+
mark: styles.sliderMark,
|
|
200
|
+
markLabel: styles.sliderMarkLabel,
|
|
201
|
+
valueLabel: styles.sliderTooltip,
|
|
202
|
+
},
|
|
203
|
+
callerClasses && {
|
|
204
|
+
root: callerClasses.root,
|
|
205
|
+
rail: callerClasses.track,
|
|
206
|
+
track: callerClasses.bar,
|
|
207
|
+
thumb: callerClasses.thumb,
|
|
208
|
+
mark: callerClasses.mark,
|
|
209
|
+
markLabel: callerClasses.markLabel,
|
|
210
|
+
},
|
|
211
|
+
);
|
|
215
212
|
|
|
216
213
|
const wrapperClass = className
|
|
217
214
|
? `${styles.layoutOverride} ${className}`
|