@recursica/mui-adapter 0.25.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 +21 -0
- package/dist/index.d.ts +105 -19
- package/dist/mui-adapter.cjs +77 -77
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +29867 -25282
- package/dist/mui-adapter.js.map +1 -1
- package/package.json +1 -1
- package/src/GlobalExemptions.modules.css +0 -6
- package/src/components/Accordion/Accordion.module.css +0 -8
- package/src/components/Autocomplete/Autocomplete.module.css +0 -8
- package/src/components/Autocomplete/Autocomplete.tsx +46 -34
- package/src/components/Avatar/Avatar.module.css +0 -8
- 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.module.css +0 -13
- 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.module.css +0 -27
- package/src/components/Chip/Chip.tsx +12 -19
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +29 -0
- package/src/components/DatePicker/DatePicker.icons.tsx +35 -0
- package/src/components/DatePicker/DatePicker.module.css +451 -42
- package/src/components/DatePicker/DatePicker.stories.tsx +23 -25
- package/src/components/DatePicker/DatePicker.tsx +229 -47
- package/src/components/DatePicker/USAGE.md +14 -1
- package/src/components/Dropdown/BareDropdown.tsx +14 -8
- package/src/components/Dropdown/Dropdown.module.css +0 -7
- package/src/components/Dropdown/Dropdown.tsx +13 -6
- package/src/components/FileInput/FileInput.module.css +0 -21
- package/src/components/FileInput/FileInput.tsx +1 -1
- package/src/components/FileUpload/FileUpload.module.css +0 -11
- 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 +1 -7
- package/src/components/HoverCard/HoverCard.tsx +13 -21
- package/src/components/Label/Label.module.css +0 -6
- package/src/components/Link/Link.module.css +0 -13
- package/src/components/Menu/Menu.module.css +0 -5
- package/src/components/Menu/Menu.tsx +14 -6
- package/src/components/Modal/Modal.module.css +0 -11
- package/src/components/Modal/Modal.tsx +12 -6
- package/src/components/NumberInput/NumberInput.module.css +0 -7
- package/src/components/NumberInput/NumberInput.tsx +30 -15
- package/src/components/Pagination/Pagination.module.css +0 -81
- package/src/components/Pagination/Pagination.tsx +5 -15
- package/src/components/Panel/Panel.tsx +17 -25
- package/src/components/Popover/Popover.module.css +0 -5
- 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.module.css +0 -8
- 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.module.css +0 -7
- package/src/components/TextArea/TextArea.tsx +30 -7
- package/src/components/TextField/TextField.module.css +0 -8
- package/src/components/TextField/TextField.tsx +30 -26
- package/src/components/TimePicker/TimePicker.module.css +0 -16
- package/src/components/Timeline/Timeline.module.css +0 -6
- package/src/components/Timeline/Timeline.tsx +9 -1
- package/src/components/Timeline/TimelineItem.tsx +2 -2
- package/src/components/Toast/Toast.module.css +0 -7
- package/src/components/Toast/Toast.tsx +14 -23
- package/src/components/Tooltip/Tooltip.module.css +0 -9
- package/src/components/Tooltip/Tooltip.tsx +14 -27
- package/src/components/TransferList/TransferList.module.css +0 -8
- package/src/components/TransferList/TransferList.tsx +1 -1
- package/src/utils/filterStylingProps.ts +4 -0
|
@@ -1,47 +1,229 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import "
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
import React, { forwardRef, useEffect, useState } from "react";
|
|
2
|
+
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
|
3
|
+
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
|
4
|
+
import {
|
|
5
|
+
DatePicker as MuiDatePicker,
|
|
6
|
+
type DatePickerProps as MuiDatePickerProps,
|
|
7
|
+
type DatePickerSlotProps,
|
|
8
|
+
type DatePickerSlots,
|
|
9
|
+
} from "@mui/x-date-pickers/DatePicker";
|
|
10
|
+
import dayjs, { type Dayjs } from "dayjs";
|
|
11
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
12
|
+
import {
|
|
13
|
+
filterStylingProps,
|
|
14
|
+
type RecursicaOverStyled,
|
|
15
|
+
} from "../../utils/filterStylingProps";
|
|
16
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
17
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
18
|
+
import { CalendarIcon } from "./DatePicker.icons";
|
|
19
|
+
import styles from "./DatePicker.module.css";
|
|
20
|
+
|
|
21
|
+
import { type RecursicaDatePickerProps as BaseRecursicaDatePickerProps } from "@recursica/adapter-common";
|
|
22
|
+
|
|
23
|
+
// Default display/entry format (dayjs format string), matching the mantine-adapter's DatePicker.
|
|
24
|
+
const DATE_FORMAT = "MM/DD/YY";
|
|
25
|
+
|
|
26
|
+
function toDayjs(value: Date | null | undefined): Dayjs | null {
|
|
27
|
+
return value ? dayjs(value) : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RecursicaDatePickerProps
|
|
31
|
+
extends Omit<
|
|
32
|
+
MuiDatePickerProps,
|
|
33
|
+
| "value"
|
|
34
|
+
| "defaultValue"
|
|
35
|
+
| "onChange"
|
|
36
|
+
| "minDate"
|
|
37
|
+
| "maxDate"
|
|
38
|
+
| "format"
|
|
39
|
+
| "slots"
|
|
40
|
+
| "slotProps"
|
|
41
|
+
>,
|
|
42
|
+
Pick<
|
|
43
|
+
RecursicaFormControlWrapperProps,
|
|
44
|
+
| "label"
|
|
45
|
+
| "error"
|
|
46
|
+
| "required"
|
|
47
|
+
| "id"
|
|
48
|
+
| "assistiveText"
|
|
49
|
+
| "assistiveWithIcon"
|
|
50
|
+
| "formLayout"
|
|
51
|
+
| "labelSize"
|
|
52
|
+
| "labelAlignment"
|
|
53
|
+
| "labelOptionalText"
|
|
54
|
+
| "labelWithEditIcon"
|
|
55
|
+
| "onLabelEditClick"
|
|
56
|
+
>,
|
|
57
|
+
ReadOnlyControlProps,
|
|
58
|
+
BaseRecursicaDatePickerProps {
|
|
59
|
+
/** Selected date, matching the mantine-adapter convention of a plain `Date` value. */
|
|
60
|
+
value?: Date | null;
|
|
61
|
+
/** Uncontrolled initial date. */
|
|
62
|
+
defaultValue?: Date | null;
|
|
63
|
+
/** Fires with the new date, or `null` if cleared. */
|
|
64
|
+
onChange?: (value: Date | null) => void;
|
|
65
|
+
minDate?: Date;
|
|
66
|
+
maxDate?: Date;
|
|
67
|
+
/** Overridable component slots — e.g. `slots={{ openPickerIcon: MyIcon }}` to replace the default calendar icon. */
|
|
68
|
+
slots?: DatePickerSlots;
|
|
69
|
+
slotProps?: DatePickerSlotProps;
|
|
70
|
+
/** Caller-provided inline style, passed through to the FormControlWrapper root. */
|
|
71
|
+
style?: React.CSSProperties;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type DatePickerProps = RecursicaOverStyled<RecursicaDatePickerProps>;
|
|
75
|
+
|
|
76
|
+
export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
77
|
+
function DatePicker(props, ref) {
|
|
78
|
+
const {
|
|
79
|
+
overStyled = false,
|
|
80
|
+
formLayout = "stacked",
|
|
81
|
+
|
|
82
|
+
// Label & Wrapper Maps
|
|
83
|
+
labelSize,
|
|
84
|
+
labelAlignment,
|
|
85
|
+
labelOptionalText,
|
|
86
|
+
labelWithEditIcon,
|
|
87
|
+
onLabelEditClick,
|
|
88
|
+
|
|
89
|
+
label,
|
|
90
|
+
assistiveText,
|
|
91
|
+
assistiveWithIcon,
|
|
92
|
+
error,
|
|
93
|
+
required,
|
|
94
|
+
id,
|
|
95
|
+
className,
|
|
96
|
+
style,
|
|
97
|
+
disabled,
|
|
98
|
+
readOnly,
|
|
99
|
+
readOnlyComponent,
|
|
100
|
+
emptyValueComponent,
|
|
101
|
+
value,
|
|
102
|
+
defaultValue,
|
|
103
|
+
onChange,
|
|
104
|
+
minDate,
|
|
105
|
+
maxDate,
|
|
106
|
+
slots,
|
|
107
|
+
slotProps,
|
|
108
|
+
...rest
|
|
109
|
+
} = props;
|
|
110
|
+
|
|
111
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
112
|
+
|
|
113
|
+
// Internal Dayjs value: MUI X's DatePicker only speaks Dayjs (via AdapterDayjs); the public
|
|
114
|
+
// API stays plain `Date` to match the mantine-adapter convention.
|
|
115
|
+
const [internalValue, setInternalValue] = useState<Dayjs | null>(
|
|
116
|
+
() => toDayjs(value) ?? toDayjs(defaultValue) ?? null,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (value !== undefined) {
|
|
121
|
+
setInternalValue(toDayjs(value));
|
|
122
|
+
}
|
|
123
|
+
}, [value]);
|
|
124
|
+
|
|
125
|
+
const wrapperClass = className
|
|
126
|
+
? `${styles.layoutOverride} ${className}`
|
|
127
|
+
: styles.layoutOverride;
|
|
128
|
+
|
|
129
|
+
const handleChange = (next: Dayjs | null) => {
|
|
130
|
+
setInternalValue(next);
|
|
131
|
+
onChange?.(next ? next.toDate() : null);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const readOnlyValue = (() => {
|
|
135
|
+
const source = value !== undefined ? value : defaultValue;
|
|
136
|
+
return toDayjs(source)?.format(DATE_FORMAT);
|
|
137
|
+
})();
|
|
138
|
+
|
|
139
|
+
// slotProps.field/desktopPaper are typed to also allow a `(ownerState) => props` function
|
|
140
|
+
// form; this adapter only ever merges plain objects (same assumption TextField/DatePicker's
|
|
141
|
+
// mantine counterpart make for consumer-supplied classNames), so narrow to that shape here.
|
|
142
|
+
const consumerFieldProps =
|
|
143
|
+
slotProps?.field && typeof slotProps.field === "object"
|
|
144
|
+
? (slotProps.field as Record<string, unknown>)
|
|
145
|
+
: undefined;
|
|
146
|
+
const consumerDesktopPaperProps =
|
|
147
|
+
slotProps?.desktopPaper && typeof slotProps.desktopPaper === "object"
|
|
148
|
+
? (slotProps.desktopPaper as Record<string, unknown>)
|
|
149
|
+
: undefined;
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<WithReadOnlyWrapper
|
|
153
|
+
ref={ref}
|
|
154
|
+
className={wrapperClass}
|
|
155
|
+
style={style}
|
|
156
|
+
controlMaxWidth={undefined}
|
|
157
|
+
controlMinWidth={undefined}
|
|
158
|
+
overStyled={overStyled as true}
|
|
159
|
+
formLayout={formLayout}
|
|
160
|
+
labelSize={labelSize}
|
|
161
|
+
labelAlignment={labelAlignment}
|
|
162
|
+
labelOptionalText={labelOptionalText}
|
|
163
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
164
|
+
onLabelEditClick={onLabelEditClick}
|
|
165
|
+
label={label}
|
|
166
|
+
assistiveText={assistiveText}
|
|
167
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
168
|
+
error={error}
|
|
169
|
+
required={required}
|
|
170
|
+
id={id}
|
|
171
|
+
readOnly={readOnly}
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
+
readOnlyComponent={readOnlyComponent as any}
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
|
+
emptyValueComponent={(emptyValueComponent as any) || undefined}
|
|
176
|
+
readOnlyType="text"
|
|
177
|
+
readOnlyValue={readOnlyValue}
|
|
178
|
+
readOnlyNativeProps={props}
|
|
179
|
+
activeComponent={
|
|
180
|
+
/* Naked field execution safely decoupled from MUI X's own label/error macro handling,
|
|
181
|
+
same convention as TimePicker.tsx. Unlike TimePicker, the popup calendar IS used here
|
|
182
|
+
(and token-styled below) — see DATEPICKER_IMPLEMENTATION_NOTES.md. */
|
|
183
|
+
<div className={styles.root} data-error={error ? "true" : undefined}>
|
|
184
|
+
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
185
|
+
<MuiDatePicker
|
|
186
|
+
{...(sanitizedProps as unknown as Partial<MuiDatePickerProps>)}
|
|
187
|
+
value={internalValue}
|
|
188
|
+
onChange={handleChange}
|
|
189
|
+
disabled={disabled}
|
|
190
|
+
format={DATE_FORMAT}
|
|
191
|
+
minDate={toDayjs(minDate) ?? undefined}
|
|
192
|
+
maxDate={toDayjs(maxDate) ?? undefined}
|
|
193
|
+
slots={{ openPickerIcon: CalendarIcon, ...slots }}
|
|
194
|
+
slotProps={
|
|
195
|
+
{
|
|
196
|
+
...slotProps,
|
|
197
|
+
field: {
|
|
198
|
+
// Calendar icon opens the picker from the left side of the field, mirroring
|
|
199
|
+
// the mantine-adapter's default `leftSection`; overridable via
|
|
200
|
+
// `slots.openPickerIcon`. Only settable per-field, not at the picker's own
|
|
201
|
+
// top level (MUI X has no top-level `openPickerButtonPosition` prop).
|
|
202
|
+
openPickerButtonPosition: "start",
|
|
203
|
+
...consumerFieldProps,
|
|
204
|
+
className: consumerFieldProps?.className
|
|
205
|
+
? `${styles.field} ${consumerFieldProps.className as string}`
|
|
206
|
+
: styles.field,
|
|
207
|
+
},
|
|
208
|
+
// The popup surface is portaled to document.body, outside this component's own
|
|
209
|
+
// DOM subtree, so — same constraint mantine-adapter's `popoverProps.classNames`
|
|
210
|
+
// documents — it can only be reached by handing it a className directly, not a
|
|
211
|
+
// descendant CSS selector.
|
|
212
|
+
desktopPaper: {
|
|
213
|
+
...consumerDesktopPaperProps,
|
|
214
|
+
className: consumerDesktopPaperProps?.className
|
|
215
|
+
? `${styles.dropdown} ${consumerDesktopPaperProps.className as string}`
|
|
216
|
+
: styles.dropdown,
|
|
217
|
+
},
|
|
218
|
+
} as DatePickerSlotProps
|
|
219
|
+
}
|
|
220
|
+
/>
|
|
221
|
+
</LocalizationProvider>
|
|
222
|
+
</div>
|
|
223
|
+
}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
DatePicker.displayName = "DatePicker";
|
|
@@ -19,7 +19,7 @@ import React from "react";
|
|
|
19
19
|
import { DatePicker } from "@recursica/mui-adapter";
|
|
20
20
|
|
|
21
21
|
export default function Demo() {
|
|
22
|
-
return <DatePicker label="Select Date"
|
|
22
|
+
return <DatePicker label="Select Date" />;
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -34,3 +34,16 @@ All Recursica components in the `@recursica/mui-adapter` package adhere strictly
|
|
|
34
34
|
> - **Anti-override protection**: Rogues style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
|
|
35
35
|
> - **No Direct Layers**: Do not pass a `layer` prop to this component. To place it on a specific visual layer, wrap it in a `<Layer layer={0|1|2|3}>` component natively.
|
|
36
36
|
> - **Variables and Theming**: Styling is entirely determined by local CSS variables defined in `recursica_variables_scoped.css` and mapped in the component's CSS module.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 4. Key Integration Features & Constraints
|
|
41
|
+
|
|
42
|
+
## Structural Constraints
|
|
43
|
+
|
|
44
|
+
1. **Value Type**: `value`/`defaultValue`/`onChange` all speak plain `Date` objects (not MUI X's native `Dayjs`), matching the mantine-adapter's DatePicker convention — conversion to/from `Dayjs` happens internally.
|
|
45
|
+
2. **Read-Only Rendering**: When rendered in read-only mode, the selected date is displayed formatted as `MM/DD/YY`. If you need custom date formatting, pass a `readOnlyComponent` prop to control how the value is rendered.
|
|
46
|
+
3. **Calendar Popover Styling**: The UI Kit exports no calendar-specific tokens (surface, selected day, hover, today, in-range), so the calendar reuses the closest existing tokens — see `DATEPICKER_IMPLEMENTATION_NOTES.md` for the exact mapping, mirrored 1:1 from the mantine-adapter. The header's prev/next arrows and month-view toggle are styled as Recursica `text`-variant buttons.
|
|
47
|
+
4. **Typing vs. Popover Entry**: Unlike the mantine-adapter (popover-only selection), MUI X's field supports typing a date directly into its masked `MM/DD/YY` segments, in addition to picking from the popup calendar.
|
|
48
|
+
5. **Default Display/Entry Format**: `MM/DD/YY` (e.g. `08/19/26`), matching the mantine-adapter; pass your own `format` (a `dayjs` format string) via the standard MUI X `DatePicker` props to override.
|
|
49
|
+
6. **Default Leading Icon**: A calendar icon opens the picker from the start of the field by default; pass `slots={{ openPickerIcon: MyIcon }}` to override it.
|
|
@@ -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>
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- state-specific border-size variables are ignored because a uniform 1px border is applied globally to prevent
|
|
3
|
-
unexpected layout shift or flickering during focus, disabled, or error state transitions. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_properties_border-size */
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_variants_states_disabled_properties_border-size */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_variants_states_error_properties_border-size */
|
|
7
|
-
|
|
8
1
|
/* LAYOUT SPACING OVERRIDES:
|
|
9
2
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
10
3
|
- Also sets the --dropdown-control-{max,min}-width hooks consumed inline in Dropdown.tsx, since
|
|
@@ -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>
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- The disabled/error states' own border-size variables are ignored so the border never changes
|
|
3
|
-
thickness across states and shifts the layout — same house policy as TextField/DatePicker.
|
|
4
|
-
The flat, state-agnostic properties_border-size token below is applied uniformly instead.
|
|
5
|
-
- There is no forge-defined focus state for file-input (no `states.focus` axis, unlike its
|
|
6
|
-
disabled/error siblings) — the generic recursica_brand_states_focus_* ring is used instead,
|
|
7
|
-
same fallback TextField/DatePicker use for the same reason.
|
|
8
|
-
- properties_icon-text-gap doubles as the gap between chips: file-input has no dedicated
|
|
9
|
-
chip-gap token (unlike file-upload's properties_item-gap).
|
|
10
|
-
- The trailing clear-all affordance is now a real shared `Button` (icon-only, "text" variant)
|
|
11
|
-
rather than a bespoke span, so it gets its own real button semantics/keyboard handling —
|
|
12
|
-
same pattern as Tree's expand/collapse button. It renders through Button's own tokened
|
|
13
|
-
color states (including disabled), so file-input's own trailing-icon color tokens — which
|
|
14
|
-
have no "clear button is disabled/errored" equivalent state of their own anyway — are no
|
|
15
|
-
longer consumed here. */
|
|
16
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_disabled_properties_border-size */
|
|
17
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_error_properties_border-size */
|
|
18
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-input_properties_colors_trailing-icon */
|
|
19
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_error_properties_colors_trailing-icon */
|
|
20
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_disabled_properties_colors_trailing-icon */
|
|
21
|
-
|
|
22
1
|
/* LAYOUT SPACING OVERRIDES:
|
|
23
2
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
24
3
|
- Also sets the --file-input-control-{max,min}-width hooks consumed inline in FileInput.tsx,
|
|
@@ -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 />}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- border-style has no usable var(...) form: the Figma-exported token's value is the literal
|
|
3
|
-
string `"dashed"` (quotes included), which is not a valid CSS <line-style> keyword — the same
|
|
4
|
-
class of "structural, not tokenized" property the canonical guide already treats border-style
|
|
5
|
-
as (see .root's own HARDCODED VALUES note below and TimePicker/DatePicker's identical
|
|
6
|
-
hardcoded `border-style: solid`). Hardcoded to the keyword `dashed` instead.
|
|
7
|
-
- properties_icon-size has no equivalent: unlike TextField/DatePicker, file-upload has no
|
|
8
|
-
dedicated icon-size token. Sized to visually match the Figma reference instead (see
|
|
9
|
-
HARDCODED VALUES below). */
|
|
10
|
-
/* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_border-style */
|
|
11
|
-
|
|
12
1
|
/* HARDCODED VALUES:
|
|
13
2
|
- .root: display: flex; flex-direction: column; width: 100% — structural layout, not a design
|
|
14
3
|
token concern (matches how other block-level components like Layer are laid out).
|
|
@@ -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}
|
|
@@ -5,13 +5,7 @@
|
|
|
5
5
|
and positioning (-arrowSize/2) calculations that cannot be CSS-driven. The default matches
|
|
6
6
|
the Recursica beak-size token (16px). See COMPONENT_ISSUES.md for details.
|
|
7
7
|
- All structural layout (display, position, overflow) is deferred to Mantine's native
|
|
8
|
-
behavior. We only override visual design tokens (colors, typography, spacing, borders).
|
|
9
|
-
/* EXEMPTIONS:
|
|
10
|
-
- beak-size is ignored here because Mantine's popover arrows compute their sizing and positional offsets
|
|
11
|
-
dynamically within the React rendering engine (requires inline pixel number rather than standard CSS variables).
|
|
12
|
-
The beak size is checked in JS against a fixed value of 16px. If this variable changes, the TSX components
|
|
13
|
-
need to be reviewed/re-compiled. */
|
|
14
|
-
/* recursica-ignore: --recursica_ui-kit_components_hover-card-popover_properties_beak-size = 16px */
|
|
8
|
+
behavior. We only override visual design tokens (colors, typography, spacing, borders). */
|
|
15
9
|
|
|
16
10
|
/* ======================================
|
|
17
11
|
DROPDOWN CONTAINER
|
|
@@ -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;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- layouts_stacked sizes width variables are ignored because stacked labels naturally occupy 100% width block-level
|
|
3
|
-
real estate in HTML rendering, making explicit width properties redundant. Mirrors the mantine-adapter reference. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_default_properties_width */
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_small_properties_width */
|
|
6
|
-
|
|
7
1
|
.root {
|
|
8
2
|
/* Prevent default MUI absolute positioning logic */
|
|
9
3
|
position: relative;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- text_text-transform is ignored because links naturally inherit casing directly from text
|
|
3
|
-
children rather than requiring custom CSS transforms. Mirrors the mantine-adapter reference. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_link_properties_text_text-transform */
|
|
5
|
-
|
|
6
1
|
/*
|
|
7
2
|
HARDCODED VALUES:
|
|
8
3
|
- display: inline-flex; (Base layout to align icon and text correctly)
|
|
@@ -79,11 +74,3 @@ HARDCODED VALUES:
|
|
|
79
74
|
/* HARDCODE: allows text truncation if bounded */
|
|
80
75
|
min-width: 0;
|
|
81
76
|
}
|
|
82
|
-
|
|
83
|
-
/* EXEMPTIONS:
|
|
84
|
-
text_text-transform across multiple link states (default, hover, visited, visited-hover) is ignored
|
|
85
|
-
because links naturally inherit casing directly from text children rather than requiring custom CSS transforms. */
|
|
86
|
-
/* recursica-ignore: --recursica_ui-kit_components_link_variants_states_default_properties_text_text-transform */
|
|
87
|
-
/* recursica-ignore: --recursica_ui-kit_components_link_variants_states_hover_properties_text_text-transform */
|
|
88
|
-
/* recursica-ignore: --recursica_ui-kit_components_link_variants_states_visited_properties_text_text-transform */
|
|
89
|
-
/* recursica-ignore: --recursica_ui-kit_components_link_variants_states_visited-hover_properties_text_text-transform */
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- colors_supporting-text-color (selected variant) is ignored because selected menu items do not render
|
|
3
|
-
supporting secondary text, making the selected-state override for supporting text colors obsolete. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_supporting-text-color */
|
|
5
|
-
|
|
6
1
|
/* HARDCODED VALUES:
|
|
7
2
|
- border-style: solid. Structural rendering rule for the dropdown border (Mantine uses Paper which may not set border natively).
|
|
8
3
|
- NOTE: overflow is intentionally NOT set on .dropdown. Mantine renders sub-menu dropdowns
|