@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
|
@@ -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}`
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "@mui/material";
|
|
15
15
|
import {
|
|
16
16
|
filterStylingProps,
|
|
17
|
+
mergeClassNames,
|
|
17
18
|
type RecursicaOverStyled,
|
|
18
19
|
} from "../../utils/filterStylingProps";
|
|
19
20
|
import styles from "./Stepper.module.css";
|
|
@@ -83,6 +84,13 @@ export const Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
|
83
84
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
84
85
|
const isHorizontal = orientation === "horizontal";
|
|
85
86
|
|
|
87
|
+
const mergedClassNames = mergeClassNames(
|
|
88
|
+
{ root: styles.steps },
|
|
89
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
90
|
+
| Partial<Record<string, string>>
|
|
91
|
+
| undefined,
|
|
92
|
+
);
|
|
93
|
+
|
|
86
94
|
return (
|
|
87
95
|
<div
|
|
88
96
|
className={`${styles.root} ${isHorizontal ? styles.horizontal : styles.vertical} ${size === "large" ? styles.large : styles.small} ${className || ""}`}
|
|
@@ -117,9 +125,7 @@ export const Stepper = forwardRef<HTMLDivElement, StepperProps>(
|
|
|
117
125
|
<></>
|
|
118
126
|
)
|
|
119
127
|
}
|
|
120
|
-
classes={
|
|
121
|
-
root: styles.steps,
|
|
122
|
-
}}
|
|
128
|
+
classes={mergedClassNames}
|
|
123
129
|
/>
|
|
124
130
|
</div>
|
|
125
131
|
);
|
|
@@ -133,11 +139,18 @@ export type StepProps = RecursicaOverStyled<MuiStepProps>;
|
|
|
133
139
|
export const Step = forwardRef<HTMLDivElement, StepProps>(
|
|
134
140
|
function Step(props, ref) {
|
|
135
141
|
const { overStyled = false, className, ...rest } = props;
|
|
142
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
143
|
+
const mergedClassNames = mergeClassNames(
|
|
144
|
+
{ root: styles.step },
|
|
145
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
146
|
+
| Partial<Record<string, string>>
|
|
147
|
+
| undefined,
|
|
148
|
+
);
|
|
136
149
|
return (
|
|
137
150
|
<MuiStep
|
|
138
151
|
ref={ref}
|
|
139
|
-
{...(
|
|
140
|
-
classes={
|
|
152
|
+
{...(sanitizedProps as MuiStepProps)}
|
|
153
|
+
classes={mergedClassNames}
|
|
141
154
|
className={className || ""}
|
|
142
155
|
/>
|
|
143
156
|
);
|
|
@@ -173,21 +186,28 @@ export const StepLabel = forwardRef<HTMLDivElement, StepLabelProps>(
|
|
|
173
186
|
overStyled = false,
|
|
174
187
|
className,
|
|
175
188
|
description,
|
|
176
|
-
StepIconComponent,
|
|
189
|
+
StepIconComponent = RecursicaStepIcon,
|
|
177
190
|
...rest
|
|
178
191
|
} = props;
|
|
192
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
193
|
+
const mergedClassNames = mergeClassNames(
|
|
194
|
+
{
|
|
195
|
+
root: styles.stepLabelRoot,
|
|
196
|
+
label: styles.stepLabel,
|
|
197
|
+
iconContainer: styles.stepIcon,
|
|
198
|
+
labelContainer: styles.stepBody,
|
|
199
|
+
},
|
|
200
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
201
|
+
| Partial<Record<string, string>>
|
|
202
|
+
| undefined,
|
|
203
|
+
);
|
|
179
204
|
return (
|
|
180
205
|
<MuiStepLabel
|
|
181
206
|
ref={ref}
|
|
182
|
-
{...(
|
|
207
|
+
{...(sanitizedProps as MuiStepLabelProps)}
|
|
183
208
|
className={className || ""}
|
|
184
|
-
classes={
|
|
185
|
-
|
|
186
|
-
label: styles.stepLabel,
|
|
187
|
-
iconContainer: styles.stepIcon,
|
|
188
|
-
labelContainer: styles.stepBody,
|
|
189
|
-
}}
|
|
190
|
-
StepIconComponent={StepIconComponent ?? RecursicaStepIcon}
|
|
209
|
+
classes={mergedClassNames}
|
|
210
|
+
StepIconComponent={StepIconComponent}
|
|
191
211
|
optional={
|
|
192
212
|
description ? (
|
|
193
213
|
<div className={styles.stepDescription}>{description}</div>
|
|
@@ -7,6 +7,8 @@ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import {
|
|
@@ -100,14 +102,19 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
100
102
|
style,
|
|
101
103
|
...rest
|
|
102
104
|
} = props;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
106
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above. We
|
|
107
|
+
// rely strictly on variables from Switch.module.css instead of MUI's own dimension/palette props.
|
|
108
|
+
const UNSUPPORTED_PROPS = [
|
|
109
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
110
|
+
"color", // Recursica hardcodes color="default" below; MUI's palette-driven colors are unused
|
|
111
|
+
] as const satisfies readonly (keyof MuiSwitchProps)[];
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
114
|
+
filterStylingProps(rest, overStyled),
|
|
115
|
+
UNSUPPORTED_PROPS,
|
|
116
|
+
);
|
|
117
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
111
118
|
|
|
112
119
|
// NOTE: classes.root must stay keyed "root" (MUI's own slot name), but it targets a
|
|
113
120
|
// dedicated .switchRoot CSS class, not .root — .root is this component's own outer
|
|
@@ -119,47 +126,24 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
119
126
|
// replaces that slot outright (confirmed against MUI's SwitchBase/Switch source: icon and
|
|
120
127
|
// checkedIcon substitute the whole default thumb, they don't add to it), so the .thumb
|
|
121
128
|
// class is applied directly to our own node instead.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
classesProp &&
|
|
142
|
-
typeof classesProp === "object" &&
|
|
143
|
-
!Array.isArray(classesProp)
|
|
144
|
-
) {
|
|
145
|
-
const o = classesProp as Partial<Record<string, string>>;
|
|
146
|
-
mergedClassNames.root = o.root
|
|
147
|
-
? `${styles.switchRoot} ${o.root}`
|
|
148
|
-
: styles.switchRoot;
|
|
149
|
-
mergedClassNames.body = o.body ? `${styles.body} ${o.body}` : styles.body;
|
|
150
|
-
mergedClassNames.track = o.track
|
|
151
|
-
? `${styles.track} ${o.track}`
|
|
152
|
-
: styles.track;
|
|
153
|
-
mergedClassNames.trackLabel = o.trackLabel
|
|
154
|
-
? `${styles.trackLabel} ${o.trackLabel}`
|
|
155
|
-
: styles.trackLabel;
|
|
156
|
-
mergedClassNames.labelWrapper = o.labelWrapper
|
|
157
|
-
? `${styles.labelWrapper} ${o.labelWrapper}`
|
|
158
|
-
: styles.labelWrapper;
|
|
159
|
-
mergedClassNames.label = o.label
|
|
160
|
-
? `${styles.label} ${o.label}`
|
|
161
|
-
: styles.label;
|
|
162
|
-
}
|
|
129
|
+
// NOTE: MUI's actual prop is "classes", not "classNames" (that's Mantine's naming). Also
|
|
130
|
+
// note "body"/"trackLabel"/"labelWrapper"/"label" below aren't real MUI `classes` slots at
|
|
131
|
+
// all (MUI only recognizes root/switchBase/track/thumb/input/checked/disabled/colorX/sizeX/
|
|
132
|
+
// edgeX) — those four are this component's own hardcoded elements, styled directly in the
|
|
133
|
+
// JSX below, not through MUI's classes mechanism. Only root/track/switchBase actually take
|
|
134
|
+
// effect via this merge; the rest are pre-existing dead weight, unrelated to the merge itself.
|
|
135
|
+
const mergedClassNames = mergeClassNames(
|
|
136
|
+
{
|
|
137
|
+
root: styles.switchRoot,
|
|
138
|
+
body: styles.body,
|
|
139
|
+
track: styles.track,
|
|
140
|
+
switchBase: styles.switchBase,
|
|
141
|
+
trackLabel: styles.trackLabel,
|
|
142
|
+
labelWrapper: styles.labelWrapper,
|
|
143
|
+
label: styles.label,
|
|
144
|
+
},
|
|
145
|
+
restRecord.classes as Partial<Record<string, string>> | undefined,
|
|
146
|
+
);
|
|
163
147
|
|
|
164
148
|
const classNameProp = restRecord.className as string | undefined;
|
|
165
149
|
const finalClass = classNameProp
|
|
@@ -237,6 +221,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
237
221
|
<MuiSwitch
|
|
238
222
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
239
223
|
ref={ref as any}
|
|
224
|
+
{...(sanitizedProps as unknown as MuiSwitchProps)}
|
|
240
225
|
className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
|
|
241
226
|
classes={mergedClassNames}
|
|
242
227
|
color="default"
|
|
@@ -244,7 +229,6 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
244
229
|
data-disabled={isSwitchDisabled || undefined}
|
|
245
230
|
icon={thumbNode}
|
|
246
231
|
checkedIcon={thumbNode}
|
|
247
|
-
{...(sanitizedProps as unknown as MuiSwitchProps)}
|
|
248
232
|
{...(isGrouped
|
|
249
233
|
? { checked: isChecked, onChange: handleGroupChange }
|
|
250
234
|
: {})}
|
|
@@ -80,12 +80,12 @@ export const SwitchGroup = forwardRef<HTMLDivElement, SwitchGroupProps>(
|
|
|
80
80
|
onChange,
|
|
81
81
|
...rest
|
|
82
82
|
} = props;
|
|
83
|
+
// NOTE: MuiFormGroupProps has no native "size" (or other Recursica-unsupported) prop to
|
|
84
|
+
// leak through here — nothing to add to an UNSUPPORTED_PROPS list beyond what
|
|
85
|
+
// filterStylingProps already blocks.
|
|
83
86
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
84
87
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
85
88
|
|
|
86
|
-
// Delete prohibited sizing hooks from bypassing the variables
|
|
87
|
-
delete restRecord["size"];
|
|
88
|
-
|
|
89
89
|
const handleChange = (
|
|
90
90
|
_event: React.SyntheticEvent,
|
|
91
91
|
childValue: string[],
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@mui/material";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
mergeClassNames,
|
|
10
11
|
type RecursicaOverStyled,
|
|
11
12
|
} from "../../utils/filterStylingProps";
|
|
12
13
|
import styles from "./Tabs.module.css";
|
|
@@ -32,6 +33,16 @@ export const Tabs = forwardRef<HTMLDivElement, TabsProps>(
|
|
|
32
33
|
|
|
33
34
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
34
35
|
|
|
36
|
+
const mergedClassNames = mergeClassNames(
|
|
37
|
+
{
|
|
38
|
+
flexContainer: styles.list,
|
|
39
|
+
indicator: styles.indicator,
|
|
40
|
+
},
|
|
41
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
42
|
+
| Partial<Record<string, string>>
|
|
43
|
+
| undefined,
|
|
44
|
+
);
|
|
45
|
+
|
|
35
46
|
return (
|
|
36
47
|
<MuiTabs
|
|
37
48
|
ref={ref}
|
|
@@ -41,10 +52,7 @@ export const Tabs = forwardRef<HTMLDivElement, TabsProps>(
|
|
|
41
52
|
data-variant={variant}
|
|
42
53
|
data-orientation={orientation}
|
|
43
54
|
data-inverted={inverted || undefined}
|
|
44
|
-
classes={
|
|
45
|
-
flexContainer: styles.list,
|
|
46
|
-
indicator: styles.indicator,
|
|
47
|
-
}}
|
|
55
|
+
classes={mergedClassNames}
|
|
48
56
|
/>
|
|
49
57
|
);
|
|
50
58
|
},
|
|
@@ -7,6 +7,8 @@ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
|
7
7
|
// Removed unused TextField import
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -69,13 +71,32 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
69
71
|
autosize,
|
|
70
72
|
...rest
|
|
71
73
|
} = props;
|
|
72
|
-
|
|
74
|
+
|
|
75
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
76
|
+
// through even if a caller forces them via plain JavaScript, bypassing the Omit<> above.
|
|
77
|
+
const UNSUPPORTED_PROPS = [
|
|
78
|
+
"size", // Recursica controls sizing via design tokens, not MUI's native small/medium size
|
|
79
|
+
"variant", // Recursica styles the naked textarea directly; MUI's standard/filled/outlined unused
|
|
80
|
+
] as const satisfies readonly (keyof MuiTextareaProps)[];
|
|
81
|
+
|
|
82
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
83
|
+
filterStylingProps(rest, overStyled),
|
|
84
|
+
UNSUPPORTED_PROPS,
|
|
85
|
+
);
|
|
73
86
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
74
87
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
// The `slotProps` prop is NOT omitted from this component's public type, so a caller can
|
|
89
|
+
// legitimately supply it — merge it (rather than letting our own `slotProps.input` spread
|
|
90
|
+
// after theirs and clobber `disableUnderline`/`classes`/`data-autosize` wholesale).
|
|
91
|
+
const callerSlotProps = restRecord.slotProps as
|
|
92
|
+
| { input?: Record<string, unknown> }
|
|
93
|
+
| undefined;
|
|
94
|
+
const mergedInputClasses = mergeClassNames(
|
|
95
|
+
{ root: styles.root, input: styles.input },
|
|
96
|
+
callerSlotProps?.input?.classes as
|
|
97
|
+
| Partial<Record<string, string>>
|
|
98
|
+
| undefined,
|
|
99
|
+
);
|
|
79
100
|
|
|
80
101
|
const wrapperClass = className
|
|
81
102
|
? `${styles.layoutOverride} ${className}`
|
|
@@ -109,6 +130,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
109
130
|
activeComponent={
|
|
110
131
|
/* Naked Input execution safely decoupled from Mui's macro Input.Wrapper DOM hooks */
|
|
111
132
|
<MuiTextarea
|
|
133
|
+
{...(sanitizedProps as unknown as MuiTextareaProps)}
|
|
112
134
|
multiline
|
|
113
135
|
variant="standard"
|
|
114
136
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -120,15 +142,16 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
120
142
|
error={!!error}
|
|
121
143
|
required={undefined}
|
|
122
144
|
slotProps={{
|
|
145
|
+
...callerSlotProps,
|
|
123
146
|
input: {
|
|
147
|
+
...callerSlotProps?.input,
|
|
124
148
|
disableUnderline: true,
|
|
125
|
-
classes:
|
|
149
|
+
classes: mergedInputClasses,
|
|
126
150
|
...({
|
|
127
151
|
"data-autosize": autosize ? "true" : undefined,
|
|
128
152
|
} as Record<string, unknown>),
|
|
129
153
|
},
|
|
130
154
|
}}
|
|
131
|
-
{...(sanitizedProps as unknown as MuiTextareaProps)}
|
|
132
155
|
/>
|
|
133
156
|
}
|
|
134
157
|
/>
|
|
@@ -3,6 +3,8 @@ import { InputBase, type InputBaseProps } from "@mui/material";
|
|
|
3
3
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
4
4
|
import {
|
|
5
5
|
filterStylingProps,
|
|
6
|
+
omitUnsupportedProps,
|
|
7
|
+
mergeClassNames,
|
|
6
8
|
type RecursicaOverStyled,
|
|
7
9
|
} from "../../utils/filterStylingProps";
|
|
8
10
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -77,34 +79,36 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
77
79
|
...rest
|
|
78
80
|
} = props;
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
"color", // Colors are token-driven; MUI's native palette isn't exposed
|
|
87
|
+
] as const satisfies readonly (keyof InputBaseProps)[];
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
89
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
90
|
+
filterStylingProps(rest, overStyled),
|
|
91
|
+
UNSUPPORTED_PROPS,
|
|
92
|
+
);
|
|
93
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: styles.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely. The
|
|
96
|
+
// caller-facing "root"/"input" slot names (MUI's own InputBase `classes` shape) are
|
|
97
|
+
// translated to this component's internal "wrapper"/"input" naming below.
|
|
98
|
+
const callerClasses = restRecord.classes as
|
|
99
|
+
| Partial<Record<string, string>>
|
|
100
|
+
| undefined;
|
|
101
|
+
const mergedClassNames = mergeClassNames(
|
|
102
|
+
{
|
|
103
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
104
|
+
input: styles.input,
|
|
105
|
+
section: styles.section,
|
|
106
|
+
},
|
|
107
|
+
callerClasses && {
|
|
108
|
+
wrapper: callerClasses.root,
|
|
109
|
+
input: callerClasses.input,
|
|
110
|
+
},
|
|
111
|
+
);
|
|
108
112
|
|
|
109
113
|
const wrapperClass = className
|
|
110
114
|
? `${styles.layoutOverride} ${className}`
|
|
@@ -4,6 +4,7 @@ import MuiTimeline, {
|
|
|
4
4
|
} from "@mui/lab/Timeline";
|
|
5
5
|
import {
|
|
6
6
|
filterStylingProps,
|
|
7
|
+
mergeClassNames,
|
|
7
8
|
type RecursicaOverStyled,
|
|
8
9
|
} from "../../utils/filterStylingProps";
|
|
9
10
|
import styles from "./Timeline.module.css";
|
|
@@ -51,6 +52,13 @@ const TimelineInner = React.forwardRef<HTMLUListElement, TimelineProps>(
|
|
|
51
52
|
) {
|
|
52
53
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
53
54
|
|
|
55
|
+
const mergedClassNames = mergeClassNames(
|
|
56
|
+
{ root: styles.root },
|
|
57
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
58
|
+
| Partial<Record<string, string>>
|
|
59
|
+
| undefined,
|
|
60
|
+
);
|
|
61
|
+
|
|
54
62
|
const items = React.Children.toArray(children);
|
|
55
63
|
const decoratedItems = items.map((item, index) =>
|
|
56
64
|
React.isValidElement(item)
|
|
@@ -68,7 +76,7 @@ const TimelineInner = React.forwardRef<HTMLUListElement, TimelineProps>(
|
|
|
68
76
|
MuiTimelineProps,
|
|
69
77
|
"color" | "radius" | "bulletSize" | "lineWidth"
|
|
70
78
|
>)}
|
|
71
|
-
classes={
|
|
79
|
+
classes={mergedClassNames}
|
|
72
80
|
>
|
|
73
81
|
{decoratedItems}
|
|
74
82
|
</MuiTimeline>
|
|
@@ -69,12 +69,12 @@ export const TimelineItem = React.forwardRef<HTMLLIElement, TimelineItemProps>(
|
|
|
69
69
|
return (
|
|
70
70
|
<MuiTimelineItem
|
|
71
71
|
ref={ref}
|
|
72
|
-
data-variant={bulletVariant}
|
|
73
|
-
data-active={__active || undefined}
|
|
74
72
|
{...(restSanitizedProps as unknown as Omit<
|
|
75
73
|
MuiTimelineItemProps,
|
|
76
74
|
"radius" | "color" | "lineVariant" | "title"
|
|
77
75
|
>)}
|
|
76
|
+
data-variant={bulletVariant}
|
|
77
|
+
data-active={__active || undefined}
|
|
78
78
|
className={
|
|
79
79
|
userClassName ? `${styles.item} ${userClassName}` : styles.item
|
|
80
80
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@mui/material";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
mergeClassNames,
|
|
10
11
|
type RecursicaOverStyled,
|
|
11
12
|
} from "../../utils/filterStylingProps";
|
|
12
13
|
import styles from "./Toast.module.css";
|
|
@@ -63,29 +64,19 @@ export const Toast = React.forwardRef<HTMLDivElement, ToastProps>(
|
|
|
63
64
|
) {
|
|
64
65
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
) {
|
|
80
|
-
const o = classNamesProp as Record<string, string>;
|
|
81
|
-
Object.keys(o).forEach((key) => {
|
|
82
|
-
if (mergedClassNames[key]) {
|
|
83
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
84
|
-
} else {
|
|
85
|
-
mergedClassNames[key] = o[key];
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
67
|
+
// Note MUI's actual prop is "classes", not "classNames" (that's Mantine's naming) — this
|
|
68
|
+
// used to read the wrong key, silently no-op-ing any caller-supplied classes.
|
|
69
|
+
const mergedClassNames = mergeClassNames(
|
|
70
|
+
{
|
|
71
|
+
root: styles.root,
|
|
72
|
+
message: styles.body,
|
|
73
|
+
icon: styles.icon,
|
|
74
|
+
action: styles.closeButton,
|
|
75
|
+
},
|
|
76
|
+
(sanitizedProps as Record<string, unknown>).classes as
|
|
77
|
+
| Partial<Record<string, string>>
|
|
78
|
+
| undefined,
|
|
79
|
+
);
|
|
89
80
|
|
|
90
81
|
const handleClose = () => {
|
|
91
82
|
if (onClose) onClose();
|