@recursica/mui-adapter 0.26.0 → 0.28.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 +17 -0
- package/dist/index.d.ts +83 -1
- package/dist/mui-adapter.cjs +73 -73
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +9400 -9255
- 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.module.css +10 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -6
- package/src/components/Breadcrumb/Breadcrumb.tsx +13 -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.icons.tsx +33 -0
- package/src/components/Dropdown/Dropdown.module.css +117 -15
- package/src/components/Dropdown/Dropdown.stories.tsx +21 -0
- package/src/components/Dropdown/Dropdown.tsx +102 -17
- package/src/components/Dropdown/USAGE.md +7 -0
- 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.module.css +25 -3
- package/src/components/HoverCard/HoverCard.tsx +13 -21
- package/src/components/Menu/Menu.module.css +3 -0
- package/src/components/Menu/Menu.stories.tsx +128 -5
- package/src/components/Menu/Menu.tsx +15 -7
- 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;
|
|
@@ -3,18 +3,21 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
.root {
|
|
6
|
-
/* HARDCODE: Basic structural flex layout for breadcrumbs */
|
|
7
|
-
display: flex;
|
|
8
|
-
align-items: center;
|
|
9
|
-
|
|
10
6
|
/* Base style resets */
|
|
11
7
|
background-color: transparent;
|
|
12
8
|
color: inherit;
|
|
13
9
|
|
|
14
10
|
/* Apply global breadcrumb properties */
|
|
15
11
|
padding: var(--recursica_ui-kit_components_breadcrumb_properties_padding);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* MUI renders items/separators inside the <ol>, not directly under <nav> (.root) — the
|
|
15
|
+
gap must live here to land uniformly between item/separator/item, matching Mantine. */
|
|
16
|
+
.ol {
|
|
17
|
+
/* HARDCODE: Basic structural flex layout for breadcrumbs */
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
16
20
|
|
|
17
|
-
/* Mantine's breadcrumbs uses gap on the root or pseudo classes, but standard gap is safest since flex is used */
|
|
18
21
|
gap: var(--recursica_ui-kit_components_breadcrumb_properties_item-gap);
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -23,4 +26,6 @@
|
|
|
23
26
|
display: flex;
|
|
24
27
|
align-items: center;
|
|
25
28
|
justify-content: center;
|
|
29
|
+
/* Neutralize MUI's built-in 8px marginLeft/marginRight so .ol's gap alone controls spacing */
|
|
30
|
+
margin: 0;
|
|
26
31
|
}
|
|
@@ -56,12 +56,6 @@ export const Default: Story = {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export const StaticExample: Story = {
|
|
60
|
-
args: {
|
|
61
|
-
items: ["Store", "Electronics", "Computers", "Laptops"],
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
59
|
export const CustomSeparator: Story = {
|
|
66
60
|
args: {
|
|
67
61
|
items: ["Root", "Branch", "Leaf"],
|
|
@@ -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,18 @@ 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
|
+
ol: styles.ol,
|
|
32
|
+
separator: styles.separator,
|
|
33
|
+
},
|
|
34
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
35
|
+
| Partial<Record<string, string>>
|
|
36
|
+
| undefined,
|
|
37
|
+
);
|
|
43
38
|
|
|
44
39
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
45
40
|
.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>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Chevron rendered in the right section alongside the clear button (item 3a). Only used for that
|
|
5
|
+
* combined case — MUI's own default `IconComponent` (`ArrowDropDown`) is left untouched everywhere
|
|
6
|
+
* else, so this is a hand-rolled copy of the exact same MUI glyph path, not a new icon design.
|
|
7
|
+
*/
|
|
8
|
+
export function ChevronIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
9
|
+
return (
|
|
10
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden {...props}>
|
|
11
|
+
<path d="M7 10l5 5 5-5z" />
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Clear ("x") icon for the clearable Dropdown's clear button (item 3a). */
|
|
17
|
+
export function ClearIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
18
|
+
return (
|
|
19
|
+
<svg
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
fill="none"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
strokeWidth="2"
|
|
24
|
+
strokeLinecap="round"
|
|
25
|
+
strokeLinejoin="round"
|
|
26
|
+
aria-hidden
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
30
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
}
|