@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
package/package.json
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
9
9
|
import {
|
|
10
10
|
filterStylingProps,
|
|
11
|
+
omitUnsupportedProps,
|
|
12
|
+
mergeClassNames,
|
|
11
13
|
type RecursicaOverStyled,
|
|
12
14
|
} from "../../utils/filterStylingProps";
|
|
13
15
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -77,42 +79,50 @@ export const Autocomplete = forwardRef<HTMLInputElement, AutocompleteProps>(
|
|
|
77
79
|
ListboxProps,
|
|
78
80
|
...rest
|
|
79
81
|
} = props;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
83
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
84
|
+
const UNSUPPORTED_PROPS = [
|
|
85
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
86
|
+
] as const satisfies readonly (keyof MuiAutocompleteProps<
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
any,
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
any,
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
any,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
any,
|
|
95
|
+
"div"
|
|
96
|
+
>)[];
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
option: styles.option,
|
|
94
|
-
};
|
|
98
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
99
|
+
filterStylingProps(rest, overStyled),
|
|
100
|
+
UNSUPPORTED_PROPS,
|
|
101
|
+
);
|
|
102
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
: styles.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
:
|
|
115
|
-
|
|
104
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely. Note MUI's
|
|
105
|
+
// actual prop is "classes", not "classNames" (that's Mantine's naming) — this used to read
|
|
106
|
+
// the wrong key, silently no-op-ing any caller-supplied classes. The caller-facing slot names
|
|
107
|
+
// below (wrapper/input/dropdown/option) mirror the mantine-adapter's own Autocomplete
|
|
108
|
+
// classNames slots, translated to MUI's real classes slot names (root/inputRoot/listbox/option).
|
|
109
|
+
const callerClasses = restRecord.classes as
|
|
110
|
+
| Partial<Record<string, string>>
|
|
111
|
+
| undefined;
|
|
112
|
+
const mergedClassNames = mergeClassNames(
|
|
113
|
+
{
|
|
114
|
+
root: styles.root,
|
|
115
|
+
inputRoot: styles.input, // Map Mantine .input (wrapper) to MUI's inputRoot
|
|
116
|
+
listbox: styles.dropdown, // Map Mantine .dropdown to MUI's listbox
|
|
117
|
+
option: styles.option,
|
|
118
|
+
},
|
|
119
|
+
callerClasses && {
|
|
120
|
+
root: callerClasses.wrapper,
|
|
121
|
+
inputRoot: callerClasses.input,
|
|
122
|
+
listbox: callerClasses.dropdown,
|
|
123
|
+
option: callerClasses.option,
|
|
124
|
+
},
|
|
125
|
+
);
|
|
116
126
|
|
|
117
127
|
const wrapperClass = className
|
|
118
128
|
? `${styles.layoutOverride} ${className}`
|
|
@@ -148,6 +158,8 @@ export const Autocomplete = forwardRef<HTMLInputElement, AutocompleteProps>(
|
|
|
148
158
|
/* Naked Input execution safely decoupled from Mui's macro Input.Wrapper DOM hooks */
|
|
149
159
|
<MuiAutocomplete
|
|
150
160
|
ref={ref}
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
|
+
{...(sanitizedProps as any)}
|
|
151
163
|
freeSolo
|
|
152
164
|
disableClearable
|
|
153
165
|
classes={mergedClassNames}
|
|
@@ -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 "./Avatar.module.css";
|
|
@@ -42,27 +43,16 @@ const _Avatar = forwardRef<HTMLDivElement, AvatarProps>(function Avatar(
|
|
|
42
43
|
computedStyle = "icon";
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
!Array.isArray(classNamesProp)
|
|
56
|
-
) {
|
|
57
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
58
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
59
|
-
mergedClassNames.image = o.image
|
|
60
|
-
? `${styles.image} ${o.image}`
|
|
61
|
-
: styles.image;
|
|
62
|
-
mergedClassNames.placeholder = o.placeholder
|
|
63
|
-
? `${styles.placeholder} ${o.placeholder}`
|
|
64
|
-
: styles.placeholder;
|
|
65
|
-
}
|
|
46
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
47
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
48
|
+
const mergedClassNames = mergeClassNames(
|
|
49
|
+
{
|
|
50
|
+
root: styles.root,
|
|
51
|
+
image: styles.image,
|
|
52
|
+
placeholder: styles.placeholder,
|
|
53
|
+
},
|
|
54
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
55
|
+
);
|
|
66
56
|
|
|
67
57
|
const classNameProp = restRecord.className as string | undefined;
|
|
68
58
|
|
|
@@ -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 "./Badge.module.css";
|
|
@@ -24,19 +25,14 @@ const _Badge = forwardRef<HTMLDivElement, BadgeProps>(function Badge(
|
|
|
24
25
|
// External layout props like margins are safely preserved.
|
|
25
26
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
!Array.isArray(classNamesProp)
|
|
36
|
-
) {
|
|
37
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
38
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
39
|
-
}
|
|
28
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
29
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
30
|
+
const mergedClassNames = mergeClassNames(
|
|
31
|
+
{ root: styles.root },
|
|
32
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
33
|
+
| Partial<Record<string, string>>
|
|
34
|
+
| undefined,
|
|
35
|
+
);
|
|
40
36
|
|
|
41
37
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
42
38
|
.className as string | undefined;
|
|
@@ -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 "./Breadcrumb.module.css";
|
|
@@ -22,24 +23,17 @@ export const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>(
|
|
|
22
23
|
overStyled,
|
|
23
24
|
);
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
) {
|
|
37
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
38
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
39
|
-
mergedClassNames.separator = o.separator
|
|
40
|
-
? `${styles.separator} ${o.separator}`
|
|
41
|
-
: styles.separator;
|
|
42
|
-
}
|
|
26
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
27
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
28
|
+
const mergedClassNames = mergeClassNames(
|
|
29
|
+
{
|
|
30
|
+
root: styles.root,
|
|
31
|
+
separator: styles.separator,
|
|
32
|
+
},
|
|
33
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
34
|
+
| Partial<Record<string, string>>
|
|
35
|
+
| undefined,
|
|
36
|
+
);
|
|
43
37
|
|
|
44
38
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
45
39
|
.className as string | undefined;
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
} from "@mui/material";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
10
|
+
withCallerOverride,
|
|
9
11
|
type RecursicaOverStyled,
|
|
10
12
|
} from "../../utils/filterStylingProps";
|
|
11
13
|
import styles from "./Button.module.css";
|
|
@@ -54,11 +56,17 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
54
56
|
},
|
|
55
57
|
ref,
|
|
56
58
|
) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
60
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
61
|
+
const UNSUPPORTED_PROPS = [
|
|
62
|
+
"color", // Recursica handles colors internally via tokens
|
|
63
|
+
] as const satisfies readonly (keyof MuiButtonProps)[];
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
66
|
+
filterStylingProps(rest, overStyled),
|
|
67
|
+
UNSUPPORTED_PROPS,
|
|
68
|
+
);
|
|
69
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
62
70
|
|
|
63
71
|
const hasStartIcon = !!icon || !!restRecord["startIcon"];
|
|
64
72
|
const hasEndIcon = !!restRecord["endIcon"];
|
|
@@ -87,18 +95,16 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
87
95
|
const finalClass = classNameProp
|
|
88
96
|
? `${styles.root} ${classNameProp}`
|
|
89
97
|
: styles.root;
|
|
90
|
-
// className is merged explicitly above — don't let the {...sanitizedProps} spread below
|
|
91
|
-
// silently overwrite finalClass with just the caller's own class (same bug class as
|
|
92
|
-
// mui-adapter's Dropdown/BareDropdown.tsx had).
|
|
93
|
-
delete restRecord["className"];
|
|
94
98
|
|
|
95
99
|
// We don't map Recursica variant/size to MUI's because we want to completely disable MUI's native
|
|
96
100
|
// variant logic (e.g., elevation, shadows) and style everything strictly through our CSS Modules.
|
|
97
101
|
// However, Mui Button requires some string, but we can just leave it as standard or ignore since
|
|
98
102
|
// our CSS resets its properties anyway, but to be clean we just don't pass variant to MUI.
|
|
99
103
|
|
|
100
|
-
const resolvedLoaderSize =
|
|
101
|
-
|
|
104
|
+
const resolvedLoaderSize = withCallerOverride(
|
|
105
|
+
size === "small" ? "small" : "default",
|
|
106
|
+
loaderSize,
|
|
107
|
+
);
|
|
102
108
|
|
|
103
109
|
let loadingIndicator = restRecord.loadingIndicator as React.ReactNode;
|
|
104
110
|
if (useRecursicaLoader) {
|
|
@@ -110,6 +116,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
110
116
|
return (
|
|
111
117
|
<MuiButton
|
|
112
118
|
ref={ref}
|
|
119
|
+
{...sanitizedProps}
|
|
113
120
|
disableRipple
|
|
114
121
|
disableElevation
|
|
115
122
|
className={finalClass}
|
|
@@ -131,7 +138,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
131
138
|
data-size={size}
|
|
132
139
|
data-content={contentType}
|
|
133
140
|
{...(restRecord.loading ? { "data-loading": "true" } : {})}
|
|
134
|
-
{...sanitizedProps}
|
|
135
141
|
disabled={!!restRecord.disabled || !!restRecord.loading}
|
|
136
142
|
>
|
|
137
143
|
<span className={styles.labelText}>{children}</span>
|
|
@@ -2,6 +2,7 @@ import { forwardRef } from "react";
|
|
|
2
2
|
import { Card as MuiCard, type CardProps as MuiCardProps } from "@mui/material";
|
|
3
3
|
import {
|
|
4
4
|
filterStylingProps,
|
|
5
|
+
mergeClassNames,
|
|
5
6
|
type RecursicaOverStyled,
|
|
6
7
|
} from "../../utils/filterStylingProps";
|
|
7
8
|
import styles from "./Card.module.css";
|
|
@@ -51,25 +52,14 @@ const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
|
51
52
|
}
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
!Array.isArray(classNamesProp)
|
|
63
|
-
) {
|
|
64
|
-
const o = classNamesProp as Record<string, string>;
|
|
65
|
-
Object.keys(o).forEach((key) => {
|
|
66
|
-
if (mergedClassNames[key]) {
|
|
67
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
68
|
-
} else {
|
|
69
|
-
mergedClassNames[key] = o[key];
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
|
55
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
56
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
57
|
+
const mergedClassNames = mergeClassNames(
|
|
58
|
+
{ root: styles.root },
|
|
59
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
60
|
+
| Partial<Record<string, string>>
|
|
61
|
+
| undefined,
|
|
62
|
+
);
|
|
73
63
|
|
|
74
64
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
75
65
|
.className as string | undefined;
|
|
@@ -7,6 +7,8 @@ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
|
7
7
|
import { CheckboxGroup, CheckboxGroupContext } from "./CheckboxGroup";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import {
|
|
@@ -58,33 +60,27 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
58
60
|
...rest
|
|
59
61
|
} = props;
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
64
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
65
|
+
const UNSUPPORTED_PROPS = [
|
|
66
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
67
|
+
"color", // Colors are token-driven; MUI's native palette isn't exposed
|
|
68
|
+
] as const satisfies readonly (keyof MuiCheckboxProps)[];
|
|
66
69
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
71
|
+
filterStylingProps(rest, overStyled),
|
|
72
|
+
UNSUPPORTED_PROPS,
|
|
73
|
+
);
|
|
74
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
72
75
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
mergedClassNames.checked = o.checked
|
|
82
|
-
? `${styles.checked} ${o.checked}`
|
|
83
|
-
: styles.checked;
|
|
84
|
-
mergedClassNames.disabled = o.disabled
|
|
85
|
-
? `${styles.disabled} ${o.disabled}`
|
|
86
|
-
: styles.disabled;
|
|
87
|
-
}
|
|
76
|
+
const mergedClassNames = mergeClassNames(
|
|
77
|
+
{
|
|
78
|
+
root: styles.root,
|
|
79
|
+
checked: styles.checked,
|
|
80
|
+
disabled: styles.disabled,
|
|
81
|
+
},
|
|
82
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
83
|
+
);
|
|
88
84
|
|
|
89
85
|
const classNameProp = restRecord.className as string | undefined;
|
|
90
86
|
const finalClass = classNameProp
|
|
@@ -172,10 +168,10 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
172
168
|
const checkboxNode = (
|
|
173
169
|
<MuiCheckbox
|
|
174
170
|
ref={ref}
|
|
171
|
+
{...(sanitizedProps as unknown as MuiCheckboxProps)}
|
|
175
172
|
className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
|
|
176
173
|
classes={mergedClassNames}
|
|
177
174
|
disabled={readOnly || disabled || isGroupReadOnly}
|
|
178
|
-
{...(sanitizedProps as unknown as MuiCheckboxProps)}
|
|
179
175
|
{...(isControlled ? { checked: isChecked as boolean } : {})}
|
|
180
176
|
onChange={handleChange}
|
|
181
177
|
disableRipple
|
|
@@ -75,12 +75,12 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
75
75
|
row,
|
|
76
76
|
...rest
|
|
77
77
|
} = props;
|
|
78
|
+
// NOTE: this component's props surface (HTMLAttributes<HTMLDivElement>-based) has no native
|
|
79
|
+
// "size" (or other Recursica-unsupported) prop to leak through here — nothing to add to an
|
|
80
|
+
// UNSUPPORTED_PROPS list beyond what filterStylingProps already blocks.
|
|
78
81
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
79
82
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
80
83
|
|
|
81
|
-
// Delete prohibited sizing hooks
|
|
82
|
-
delete restRecord["size"];
|
|
83
|
-
|
|
84
84
|
const handleChange = (_event: React.SyntheticEvent, childValue: any) => {
|
|
85
85
|
if (onChange) {
|
|
86
86
|
onChange(childValue);
|
|
@@ -2,6 +2,7 @@ import React, { forwardRef } from "react";
|
|
|
2
2
|
import { Chip as MuiChip, type ChipProps as MuiChipProps } from "@mui/material";
|
|
3
3
|
import {
|
|
4
4
|
filterStylingProps,
|
|
5
|
+
mergeClassNames,
|
|
5
6
|
type RecursicaOverStyled,
|
|
6
7
|
} from "../../utils/filterStylingProps";
|
|
7
8
|
import styles from "./Chip.module.css";
|
|
@@ -75,25 +76,17 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(function Chip(
|
|
|
75
76
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
76
77
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
!Array.isArray(classNamesProp)
|
|
90
|
-
) {
|
|
91
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
92
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
93
|
-
mergedClassNames.label = o.label
|
|
94
|
-
? `${styles.label} ${o.label}`
|
|
95
|
-
: styles.label;
|
|
96
|
-
}
|
|
79
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
80
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
81
|
+
const mergedClassNames = mergeClassNames(
|
|
82
|
+
{
|
|
83
|
+
root: styles.root,
|
|
84
|
+
label: styles.label,
|
|
85
|
+
icon: styles.leadingIcon,
|
|
86
|
+
deleteIcon: styles.removeIconWrapper,
|
|
87
|
+
},
|
|
88
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
89
|
+
);
|
|
97
90
|
|
|
98
91
|
const classNameProp = restRecord.className as string | undefined;
|
|
99
92
|
const finalClass = classNameProp
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@mui/material";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
9
10
|
type RecursicaOverStyled,
|
|
10
11
|
} from "../../utils/filterStylingProps";
|
|
11
12
|
import styles from "./Dropdown.module.css";
|
|
@@ -52,14 +53,19 @@ export const BareDropdown = forwardRef<
|
|
|
52
53
|
error,
|
|
53
54
|
...rest
|
|
54
55
|
} = props;
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
57
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
58
|
+
const UNSUPPORTED_PROPS = [
|
|
59
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
60
|
+
"variant", // Recursica styles the naked select directly; MUI's standard/filled/outlined unused
|
|
61
|
+
"classes", // Recursica computes its own classes object below; caller's would silently clobber it
|
|
62
|
+
] as const satisfies readonly (keyof MuiSelectProps)[];
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
65
|
+
filterStylingProps(rest, overStyled),
|
|
66
|
+
UNSUPPORTED_PROPS,
|
|
67
|
+
);
|
|
68
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
63
69
|
|
|
64
70
|
const mergedClassName = className
|
|
65
71
|
? `${styles.root} ${className}`
|
|
@@ -100,6 +106,7 @@ export const BareDropdown = forwardRef<
|
|
|
100
106
|
return (
|
|
101
107
|
<MuiSelect
|
|
102
108
|
ref={ref}
|
|
109
|
+
{...(sanitizedProps as unknown as MuiSelectProps)}
|
|
103
110
|
disabled={disabled}
|
|
104
111
|
value={value}
|
|
105
112
|
defaultValue={defaultValue}
|
|
@@ -125,7 +132,6 @@ export const BareDropdown = forwardRef<
|
|
|
125
132
|
"data-error": error ? "true" : undefined,
|
|
126
133
|
...(restRecord.inputProps as Record<string, unknown>),
|
|
127
134
|
}}
|
|
128
|
-
{...(sanitizedProps as unknown as MuiSelectProps)}
|
|
129
135
|
>
|
|
130
136
|
{renderOptions()}
|
|
131
137
|
</MuiSelect>
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
10
11
|
type RecursicaOverStyled,
|
|
11
12
|
} from "../../utils/filterStylingProps";
|
|
12
13
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -73,12 +74,18 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
73
74
|
clearable, // Not natively supported by basic MUI Select, stubbed
|
|
74
75
|
...rest
|
|
75
76
|
} = props;
|
|
76
|
-
|
|
77
|
-
|
|
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
|
+
"variant", // Recursica styles the naked select directly; MUI's standard/filled/outlined unused
|
|
82
|
+
] as const satisfies readonly (keyof MuiSelectProps)[];
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
85
|
+
filterStylingProps(rest, overStyled),
|
|
86
|
+
UNSUPPORTED_PROPS,
|
|
87
|
+
);
|
|
88
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
82
89
|
|
|
83
90
|
const injectedStyles = {
|
|
84
91
|
...((style as React.CSSProperties) || {}),
|
|
@@ -159,6 +166,7 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
159
166
|
activeComponent={
|
|
160
167
|
<MuiSelect
|
|
161
168
|
ref={ref}
|
|
169
|
+
{...(sanitizedProps as unknown as MuiSelectProps)}
|
|
162
170
|
disabled={disabled}
|
|
163
171
|
value={value}
|
|
164
172
|
defaultValue={defaultValue}
|
|
@@ -185,7 +193,6 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
185
193
|
"data-error": error ? "true" : undefined,
|
|
186
194
|
...(restRecord.inputProps as Record<string, unknown>),
|
|
187
195
|
}}
|
|
188
|
-
{...(sanitizedProps as unknown as MuiSelectProps)}
|
|
189
196
|
>
|
|
190
197
|
{renderOptions()}
|
|
191
198
|
</MuiSelect>
|
|
@@ -308,6 +308,7 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
|
|
|
308
308
|
>
|
|
309
309
|
<div
|
|
310
310
|
ref={ref}
|
|
311
|
+
{...restRecord}
|
|
311
312
|
className={styles.root}
|
|
312
313
|
role="button"
|
|
313
314
|
aria-label={browseLabel}
|
|
@@ -323,7 +324,6 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
|
|
|
323
324
|
onDragLeave={interactive ? handleDragLeave : undefined}
|
|
324
325
|
onDragOver={interactive ? handleDragOver : undefined}
|
|
325
326
|
onDrop={interactive ? handleDrop : undefined}
|
|
326
|
-
{...restRecord}
|
|
327
327
|
>
|
|
328
328
|
<span className={styles.leadingIcon} aria-hidden>
|
|
329
329
|
{icon ?? <UploadIcon />}
|
|
@@ -267,10 +267,10 @@ export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(
|
|
|
267
267
|
>
|
|
268
268
|
<div
|
|
269
269
|
ref={ref}
|
|
270
|
+
{...restRecord}
|
|
270
271
|
className={styles.root}
|
|
271
272
|
data-disabled={disabled ? "true" : undefined}
|
|
272
273
|
data-error={effectiveError ? "true" : undefined}
|
|
273
|
-
{...restRecord}
|
|
274
274
|
>
|
|
275
275
|
{!readOnly && (
|
|
276
276
|
<div
|
|
@@ -32,6 +32,7 @@ export const FormControlLayout = React.forwardRef<
|
|
|
32
32
|
return (
|
|
33
33
|
<div
|
|
34
34
|
ref={ref}
|
|
35
|
+
{...sanitizedProps}
|
|
35
36
|
className={className ? `${styles.root} ${className}` : styles.root}
|
|
36
37
|
data-form-layout={formLayout}
|
|
37
38
|
style={
|
|
@@ -45,7 +46,6 @@ export const FormControlLayout = React.forwardRef<
|
|
|
45
46
|
: {}),
|
|
46
47
|
} as React.CSSProperties
|
|
47
48
|
}
|
|
48
|
-
{...sanitizedProps}
|
|
49
49
|
>
|
|
50
50
|
{/*
|
|
51
51
|
The left section strictly enforces the Label boundary sizes
|
|
@@ -94,6 +94,7 @@ export const FormControlWrapper = React.forwardRef<
|
|
|
94
94
|
return (
|
|
95
95
|
<FormControl
|
|
96
96
|
ref={ref}
|
|
97
|
+
{...(sanitizedProps as FormControlProps)}
|
|
97
98
|
error={!!error}
|
|
98
99
|
required={required}
|
|
99
100
|
disabled={disabled}
|
|
@@ -103,7 +104,6 @@ export const FormControlWrapper = React.forwardRef<
|
|
|
103
104
|
data-error={error ? "true" : undefined}
|
|
104
105
|
data-disabled={disabled ? "true" : undefined}
|
|
105
106
|
data-focused={focused ? "true" : undefined}
|
|
106
|
-
{...(sanitizedProps as FormControlProps)}
|
|
107
107
|
>
|
|
108
108
|
<FormControlLayout
|
|
109
109
|
formLayout={formLayout}
|