@recursica/mantine-adapter 0.42.1 → 0.44.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 +93 -15
- package/dist/mantine-adapter.cjs +2 -2
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +3534 -3266
- package/dist/mantine-adapter.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.tsx +32 -33
- package/src/components/AssistiveElement/AssistiveElement.tsx +1 -1
- package/src/components/AutoComplete/AutoComplete.tsx +26 -38
- package/src/components/Avatar/Avatar.tsx +22 -23
- package/src/components/Badge/Badge.tsx +12 -18
- package/src/components/Breadcrumb/Breadcrumb.module.css +2 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -6
- package/src/components/Breadcrumb/Breadcrumb.tsx +25 -17
- package/src/components/Button/Button.tsx +31 -30
- package/src/components/Card/Card.tsx +48 -27
- package/src/components/Checkbox/Checkbox.tsx +29 -42
- package/src/components/Checkbox/CheckboxGroup.tsx +12 -5
- package/src/components/Chip/Chip.tsx +34 -27
- package/src/components/DatePicker/DatePicker.tsx +46 -47
- package/src/components/Dropdown/BareDropdown.tsx +25 -13
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +2 -0
- package/src/components/Dropdown/Dropdown.module.css +61 -4
- package/src/components/Dropdown/Dropdown.stories.tsx +21 -0
- package/src/components/Dropdown/Dropdown.tsx +41 -37
- package/src/components/Dropdown/USAGE.md +1 -0
- package/src/components/FileInput/FileInput.tsx +1 -1
- package/src/components/FileUpload/FileUpload.tsx +1 -1
- package/src/components/HoverCard/HoverCard.tsx +18 -34
- package/src/components/Label/Label.tsx +20 -21
- package/src/components/Link/Link.tsx +6 -14
- package/src/components/Loader/Loader.tsx +8 -16
- package/src/components/Menu/Menu.stories.tsx +9 -1
- package/src/components/Menu/Menu.tsx +55 -48
- package/src/components/Modal/Modal.tsx +134 -96
- package/src/components/NumberInput/NumberInput.tsx +14 -7
- package/src/components/Pagination/Pagination.tsx +25 -34
- package/src/components/Panel/Panel.tsx +19 -26
- package/src/components/Popover/Popover.tsx +19 -31
- package/src/components/Radio/Radio.tsx +32 -43
- package/src/components/Radio/RadioGroup.tsx +11 -4
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +1 -1
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +1 -1
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +1 -1
- package/src/components/SegmentedControl/SegmentedControl.tsx +29 -33
- package/src/components/Slider/Slider.tsx +30 -43
- package/src/components/Stepper/Stepper.tsx +20 -13
- package/src/components/Switch/Switch.tsx +45 -45
- package/src/components/Switch/SwitchGroup.tsx +11 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +13 -6
- package/src/components/Text/Text.tsx +1 -1
- package/src/components/TextArea/TextArea.tsx +21 -25
- package/src/components/TextField/TextField.tsx +23 -30
- package/src/components/TimePicker/TimePicker.tsx +26 -12
- package/src/components/Timeline/Timeline.tsx +7 -21
- package/src/components/Timeline/TimelineItem.tsx +14 -26
- package/src/components/Title/Title.tsx +12 -2
- package/src/components/Toast/Toast.tsx +32 -31
- package/src/components/Tooltip/Tooltip.tsx +18 -34
- package/src/components/TransferList/TransferList.tsx +1 -1
- package/src/components/Tree/Tree.tsx +8 -8
- package/src/utils/filterStylingProps.ts +4 -0
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
} from "@mantine/core";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import styles from "./Card.module.css";
|
|
@@ -32,11 +34,23 @@ export type CardProps = RecursicaOverStyled<
|
|
|
32
34
|
/**
|
|
33
35
|
* The root Card elevation box. It establishes the global background color, border radius, nested component gap, and outer shadow governed by the current `Layer` context.
|
|
34
36
|
*/
|
|
37
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
38
|
+
// through even if a caller forces them via plain JavaScript. `Card.module.css` unconditionally
|
|
39
|
+
// drives these from Recursica UI Kit tokens on `.root`; the native prop is never consulted.
|
|
40
|
+
const UNSUPPORTED_PROPS = [
|
|
41
|
+
"radius", // Border radius is token-driven (`--recursica_..._borders_border-radius`), not caller-set.
|
|
42
|
+
"withBorder", // Border is always applied via tokens on `.root`; not an optional caller toggle.
|
|
43
|
+
"padding", // Padding is token-driven (`--card-padding`), not Mantine's spacing-scale prop.
|
|
44
|
+
] as const satisfies readonly (keyof MantineCardProps)[];
|
|
45
|
+
|
|
35
46
|
const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
36
47
|
{ overStyled = false, ...rest },
|
|
37
48
|
ref,
|
|
38
49
|
) {
|
|
39
|
-
const sanitizedProps =
|
|
50
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
51
|
+
filterStylingProps(rest, overStyled),
|
|
52
|
+
UNSUPPORTED_PROPS,
|
|
53
|
+
);
|
|
40
54
|
|
|
41
55
|
// Expose outer layout/flexbox properties on Card to allow structuring alongside siblings
|
|
42
56
|
const safeCardLayoutKeys = [
|
|
@@ -56,25 +70,14 @@ const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
|
56
70
|
}
|
|
57
71
|
});
|
|
58
72
|
|
|
59
|
-
const mergedClassNames
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
!Array.isArray(classNamesProp)
|
|
68
|
-
) {
|
|
69
|
-
const o = classNamesProp as Record<string, string>;
|
|
70
|
-
Object.keys(o).forEach((key) => {
|
|
71
|
-
if (mergedClassNames[key]) {
|
|
72
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
73
|
-
} else {
|
|
74
|
-
mergedClassNames[key] = o[key];
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
|
73
|
+
const mergedClassNames = mergeClassNames(
|
|
74
|
+
{
|
|
75
|
+
root: styles.root,
|
|
76
|
+
},
|
|
77
|
+
(sanitizedProps as Record<string, unknown>).classNames as
|
|
78
|
+
| Partial<Record<string, string>>
|
|
79
|
+
| undefined,
|
|
80
|
+
);
|
|
78
81
|
|
|
79
82
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
80
83
|
.className as string | undefined;
|
|
@@ -82,9 +85,9 @@ const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
|
82
85
|
return (
|
|
83
86
|
<MantineCard
|
|
84
87
|
ref={ref}
|
|
88
|
+
{...(sanitizedProps as unknown as MantineCardProps)}
|
|
85
89
|
className={classNameProp}
|
|
86
90
|
classNames={mergedClassNames}
|
|
87
|
-
{...(sanitizedProps as unknown as MantineCardProps)}
|
|
88
91
|
/>
|
|
89
92
|
);
|
|
90
93
|
});
|
|
@@ -106,10 +109,10 @@ export const CardSection = forwardRef<HTMLDivElement, CardSectionProps>(
|
|
|
106
109
|
return (
|
|
107
110
|
<MantineCard.Section
|
|
108
111
|
ref={ref}
|
|
112
|
+
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
109
113
|
className={
|
|
110
114
|
classNameProp ? `${styles.section} ${classNameProp}` : styles.section
|
|
111
115
|
}
|
|
112
|
-
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
113
116
|
/>
|
|
114
117
|
);
|
|
115
118
|
},
|
|
@@ -122,19 +125,32 @@ export type CardHeaderProps = RecursicaOverStyled<RecursicaCardSectionProps>;
|
|
|
122
125
|
/**
|
|
123
126
|
* Replaces standard generic Mantine `<Card.Section>` wrappers by directly injecting the strict Recursica design tokens for header sizing, background drops, and intrinsic padding limits.
|
|
124
127
|
*/
|
|
128
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
129
|
+
// through even if a caller forces them via plain JavaScript. `CardHeaderProps` already omits all
|
|
130
|
+
// native Mantine section props from the public type, but that alone doesn't stop a forced-typed
|
|
131
|
+
// caller from smuggling one through `...rest`; `.header` in Card.module.css unconditionally
|
|
132
|
+
// drives background/padding/border from Recursica tokens, so these native toggles are meaningless.
|
|
133
|
+
const HEADER_FOOTER_UNSUPPORTED_PROPS = [
|
|
134
|
+
"withBorder", // The divider border is always applied via tokens on `.header`/`.footer`.
|
|
135
|
+
"inheritPadding", // Padding is fixed by the `.header`/`.footer` token-driven padding, not Mantine's default.
|
|
136
|
+
] as const satisfies readonly (keyof MantineCardSectionProps)[];
|
|
137
|
+
|
|
125
138
|
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
|
|
126
139
|
function CardHeader({ overStyled = false, ...rest }, ref) {
|
|
127
|
-
const sanitizedProps =
|
|
140
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
141
|
+
filterStylingProps(rest, overStyled),
|
|
142
|
+
HEADER_FOOTER_UNSUPPORTED_PROPS,
|
|
143
|
+
);
|
|
128
144
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
129
145
|
.className as string | undefined;
|
|
130
146
|
|
|
131
147
|
return (
|
|
132
148
|
<MantineCard.Section
|
|
133
149
|
ref={ref}
|
|
150
|
+
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
134
151
|
className={
|
|
135
152
|
classNameProp ? `${styles.header} ${classNameProp}` : styles.header
|
|
136
153
|
}
|
|
137
|
-
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
138
154
|
/>
|
|
139
155
|
);
|
|
140
156
|
},
|
|
@@ -149,17 +165,22 @@ export type CardFooterProps = RecursicaOverStyled<RecursicaCardSectionProps>;
|
|
|
149
165
|
*/
|
|
150
166
|
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
|
|
151
167
|
function CardFooter({ overStyled = false, ...rest }, ref) {
|
|
152
|
-
|
|
168
|
+
// Reuses HEADER_FOOTER_UNSUPPORTED_PROPS (declared above with CardHeader) — same rationale
|
|
169
|
+
// applies: `.footer` in Card.module.css unconditionally drives padding/border from tokens.
|
|
170
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
171
|
+
filterStylingProps(rest, overStyled),
|
|
172
|
+
HEADER_FOOTER_UNSUPPORTED_PROPS,
|
|
173
|
+
);
|
|
153
174
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
154
175
|
.className as string | undefined;
|
|
155
176
|
|
|
156
177
|
return (
|
|
157
178
|
<MantineCard.Section
|
|
158
179
|
ref={ref}
|
|
180
|
+
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
159
181
|
className={
|
|
160
182
|
classNameProp ? `${styles.footer} ${classNameProp}` : styles.footer
|
|
161
183
|
}
|
|
162
|
-
{...(sanitizedProps as unknown as MantineCardSectionProps)}
|
|
163
184
|
/>
|
|
164
185
|
);
|
|
165
186
|
},
|
|
@@ -183,10 +204,10 @@ export const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
|
|
|
183
204
|
return (
|
|
184
205
|
<div
|
|
185
206
|
ref={ref}
|
|
207
|
+
{...sanitizedProps}
|
|
186
208
|
className={
|
|
187
209
|
classNameProp ? `${styles.content} ${classNameProp}` : styles.content
|
|
188
210
|
}
|
|
189
|
-
{...sanitizedProps}
|
|
190
211
|
/>
|
|
191
212
|
);
|
|
192
213
|
},
|
|
@@ -7,6 +7,8 @@ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
|
7
7
|
import { CheckboxGroup } from "./CheckboxGroup";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import {
|
|
@@ -56,49 +58,34 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
56
58
|
...rest
|
|
57
59
|
} = props;
|
|
58
60
|
// Checkbox doesn't use Label onLabelEditClick natively since it isn't mapped inside FormControlWrapper intrinsically.
|
|
59
|
-
|
|
61
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
62
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
63
|
+
const UNSUPPORTED_PROPS = [
|
|
64
|
+
"size", // Recursica controls sizing via Checkbox.module.css variables, not Mantine's native size scale.
|
|
65
|
+
"color", // Colors are token-driven via Checkbox.module.css; Mantine's native palette isn't exposed.
|
|
66
|
+
"radius", // Corner radius is fixed by Recursica tokens in Checkbox.module.css, not caller-configurable.
|
|
67
|
+
"variant", // Checkbox has a single Recursica-defined visual treatment; Mantine's variant isn't exposed.
|
|
68
|
+
"iconColor", // Icon color is token-driven via Checkbox.module.css, not a native Mantine override.
|
|
69
|
+
] as const satisfies readonly (keyof MantineCheckboxProps)[];
|
|
70
|
+
|
|
71
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
72
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
73
|
+
UNSUPPORTED_PROPS,
|
|
74
|
+
) as Partial<typeof rest>;
|
|
60
75
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
61
76
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
icon: styles.icon,
|
|
75
|
-
labelWrapper: styles.labelWrapper,
|
|
76
|
-
label: styles.label,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const classNamesProp = restRecord.classNames;
|
|
80
|
-
if (
|
|
81
|
-
classNamesProp &&
|
|
82
|
-
typeof classNamesProp === "object" &&
|
|
83
|
-
!Array.isArray(classNamesProp)
|
|
84
|
-
) {
|
|
85
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
86
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
87
|
-
mergedClassNames.body = o.body ? `${styles.body} ${o.body}` : styles.body;
|
|
88
|
-
mergedClassNames.inner = o.inner
|
|
89
|
-
? `${styles.inner} ${o.inner}`
|
|
90
|
-
: styles.inner;
|
|
91
|
-
mergedClassNames.input = o.input
|
|
92
|
-
? `${styles.input} ${o.input}`
|
|
93
|
-
: styles.input;
|
|
94
|
-
mergedClassNames.icon = o.icon ? `${styles.icon} ${o.icon}` : styles.icon;
|
|
95
|
-
mergedClassNames.labelWrapper = o.labelWrapper
|
|
96
|
-
? `${styles.labelWrapper} ${o.labelWrapper}`
|
|
97
|
-
: styles.labelWrapper;
|
|
98
|
-
mergedClassNames.label = o.label
|
|
99
|
-
? `${styles.label} ${o.label}`
|
|
100
|
-
: styles.label;
|
|
101
|
-
}
|
|
77
|
+
const mergedClassNames = mergeClassNames(
|
|
78
|
+
{
|
|
79
|
+
root: styles.root,
|
|
80
|
+
body: styles.body,
|
|
81
|
+
inner: styles.inner,
|
|
82
|
+
input: styles.input,
|
|
83
|
+
icon: styles.icon,
|
|
84
|
+
labelWrapper: styles.labelWrapper,
|
|
85
|
+
label: styles.label,
|
|
86
|
+
},
|
|
87
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
88
|
+
);
|
|
102
89
|
|
|
103
90
|
const classNameProp = restRecord.className as string | undefined;
|
|
104
91
|
const finalClass = classNameProp
|
|
@@ -136,10 +123,10 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
136
123
|
const checkboxNode = (
|
|
137
124
|
<MantineCheckbox
|
|
138
125
|
ref={ref}
|
|
126
|
+
{...(sanitizedProps as unknown as MantineCheckboxProps)}
|
|
139
127
|
className={finalClass}
|
|
140
128
|
classNames={mergedClassNames}
|
|
141
129
|
disabled={readOnly || disabled}
|
|
142
|
-
{...(sanitizedProps as unknown as MantineCheckboxProps)}
|
|
143
130
|
/>
|
|
144
131
|
);
|
|
145
132
|
|
|
@@ -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";
|
|
@@ -62,11 +63,17 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
62
63
|
defaultValue,
|
|
63
64
|
...rest
|
|
64
65
|
} = props;
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
67
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
68
|
+
const UNSUPPORTED_PROPS = [
|
|
69
|
+
"size", // Recursica controls sizing via Checkbox.module.css variables, not Mantine's native size scale.
|
|
70
|
+
] as const satisfies readonly (keyof MantineCheckboxGroupProps)[];
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
73
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
74
|
+
UNSUPPORTED_PROPS,
|
|
75
|
+
) as Partial<typeof rest>;
|
|
76
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
70
77
|
|
|
71
78
|
// Mantine's own `Checkbox.Group` always establishes a context whose `value` defaults to
|
|
72
79
|
// `[]` (see @mantine/core's `useUncontrolled({ finalValue: [] })`), and its `Checkbox` spreads
|
|
@@ -123,8 +130,8 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
123
130
|
) : (
|
|
124
131
|
<div
|
|
125
132
|
ref={ref}
|
|
126
|
-
role="group"
|
|
127
133
|
{...(sanitizedProps as Record<string, unknown>)}
|
|
134
|
+
role="group"
|
|
128
135
|
className={styles.groupRoot}
|
|
129
136
|
data-layout={formLayout}
|
|
130
137
|
>
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
} from "@mantine/core";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
9
|
+
withCallerOverride,
|
|
8
10
|
type RecursicaOverStyled,
|
|
9
11
|
} from "../../utils/filterStylingProps";
|
|
10
12
|
import styles from "./Chip.module.css";
|
|
@@ -45,6 +47,9 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(function Chip(
|
|
|
45
47
|
removeIconRef,
|
|
46
48
|
children,
|
|
47
49
|
overStyled = false,
|
|
50
|
+
wrapperProps,
|
|
51
|
+
tabIndex,
|
|
52
|
+
"aria-hidden": ariaHidden,
|
|
48
53
|
...rest
|
|
49
54
|
},
|
|
50
55
|
ref,
|
|
@@ -52,26 +57,16 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(function Chip(
|
|
|
52
57
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
53
58
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
54
59
|
|
|
55
|
-
const mergedClassNames
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
classNamesProp &&
|
|
66
|
-
typeof classNamesProp === "object" &&
|
|
67
|
-
!Array.isArray(classNamesProp)
|
|
68
|
-
) {
|
|
69
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
70
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
71
|
-
mergedClassNames.label = o.label
|
|
72
|
-
? `${styles.label} ${o.label}`
|
|
73
|
-
: styles.label;
|
|
74
|
-
}
|
|
60
|
+
const mergedClassNames = mergeClassNames(
|
|
61
|
+
{
|
|
62
|
+
root: styles.root,
|
|
63
|
+
label: styles.label,
|
|
64
|
+
input: styles.input,
|
|
65
|
+
iconWrapper: styles.mantineIconWrapper,
|
|
66
|
+
checkIcon: styles.checkIcon,
|
|
67
|
+
},
|
|
68
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
69
|
+
);
|
|
75
70
|
|
|
76
71
|
const classNameProp = restRecord.className as string | undefined;
|
|
77
72
|
const finalClass = classNameProp
|
|
@@ -90,18 +85,30 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(function Chip(
|
|
|
90
85
|
restRecord.onClick !== undefined ||
|
|
91
86
|
restRecord.onChange !== undefined;
|
|
92
87
|
|
|
88
|
+
// Our own computed default for Mantine's `wrapperProps` — flags the internal error/interactive
|
|
89
|
+
// CSS hooks. This is a real Mantine `ChipProps` slot a caller could legitimately pass their own
|
|
90
|
+
// value for, so it must merge via `withCallerOverride`, not silently clobber theirs.
|
|
91
|
+
const computedWrapperProps = {
|
|
92
|
+
...(dataError !== undefined ? { "data-error": "" } : {}),
|
|
93
|
+
...(isInteractive ? { "data-interactive": "" } : {}),
|
|
94
|
+
} as NonNullable<MantineChipProps["wrapperProps"]>;
|
|
95
|
+
|
|
93
96
|
return (
|
|
94
97
|
<MantineChip
|
|
95
98
|
ref={ref}
|
|
99
|
+
{...sanitizedProps}
|
|
96
100
|
className={finalClass}
|
|
97
101
|
classNames={mergedClassNames}
|
|
98
|
-
wrapperProps={
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{
|
|
102
|
+
wrapperProps={withCallerOverride(computedWrapperProps, wrapperProps)}
|
|
103
|
+
data-icon-only={isIconOnly ? "" : undefined}
|
|
104
|
+
tabIndex={withCallerOverride<number | undefined>(
|
|
105
|
+
isInteractive ? undefined : -1,
|
|
106
|
+
tabIndex,
|
|
107
|
+
)}
|
|
108
|
+
aria-hidden={withCallerOverride<boolean | "true" | "false" | undefined>(
|
|
109
|
+
isInteractive ? undefined : true,
|
|
110
|
+
ariaHidden,
|
|
111
|
+
)}
|
|
105
112
|
>
|
|
106
113
|
<span className={styles.innerWrapper}>
|
|
107
114
|
{icon && (
|
|
@@ -3,6 +3,9 @@ import { DatePickerInput, type DatePickerInputProps } from "@mantine/dates";
|
|
|
3
3
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
4
4
|
import {
|
|
5
5
|
filterStylingProps,
|
|
6
|
+
omitUnsupportedProps,
|
|
7
|
+
withCallerOverride,
|
|
8
|
+
mergeClassNames,
|
|
6
9
|
type RecursicaOverStyled,
|
|
7
10
|
} from "../../utils/filterStylingProps";
|
|
8
11
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -39,6 +42,15 @@ export interface RecursicaDatePickerProps
|
|
|
39
42
|
|
|
40
43
|
export type DatePickerProps = RecursicaOverStyled<RecursicaDatePickerProps>;
|
|
41
44
|
|
|
45
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
46
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
47
|
+
const UNSUPPORTED_PROPS = [
|
|
48
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
49
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
50
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
51
|
+
"description", // Managed by FormControlWrapper via assistiveText.
|
|
52
|
+
] as const satisfies readonly (keyof DatePickerInputProps)[];
|
|
53
|
+
|
|
42
54
|
export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
43
55
|
function DatePicker(props, ref) {
|
|
44
56
|
const {
|
|
@@ -67,48 +79,34 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
67
79
|
emptyValueComponent,
|
|
68
80
|
value,
|
|
69
81
|
defaultValue,
|
|
82
|
+
highlightToday = true, // Default on so today is visually marked; consumers can still override.
|
|
83
|
+
valueFormat = "MM/DD/YY", // Default display format; consumers can still override.
|
|
84
|
+
placeholder = "MM / DD / YY", // Default placeholder; consumers can still override.
|
|
85
|
+
leftSection, // Default leading icon computed below; consumers can still override.
|
|
70
86
|
...rest
|
|
71
87
|
} = props;
|
|
72
88
|
|
|
73
|
-
const sanitizedProps =
|
|
89
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
90
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
91
|
+
UNSUPPORTED_PROPS,
|
|
92
|
+
);
|
|
74
93
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
75
94
|
|
|
76
|
-
// Delete prohibited sizing hooks from bypassing variables natively
|
|
77
|
-
delete restRecord["size"];
|
|
78
|
-
delete restRecord["variant"];
|
|
79
|
-
delete restRecord["radius"];
|
|
80
|
-
delete restRecord["description"]; // Managed by FormControlWrapper via assistiveText
|
|
81
|
-
|
|
82
95
|
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
83
|
-
const mergedClassNames
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
classNamesProp &&
|
|
98
|
-
typeof classNamesProp === "object" &&
|
|
99
|
-
!Array.isArray(classNamesProp)
|
|
100
|
-
) {
|
|
101
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
102
|
-
mergedClassNames.wrapper = o.wrapper
|
|
103
|
-
? `${styles.root} ${o.wrapper}`
|
|
104
|
-
: styles.root;
|
|
105
|
-
mergedClassNames.input = o.input
|
|
106
|
-
? `${styles.input} ${o.input}`
|
|
107
|
-
: styles.input;
|
|
108
|
-
mergedClassNames.section = o.section
|
|
109
|
-
? `${styles.section} ${o.section}`
|
|
110
|
-
: styles.section;
|
|
111
|
-
}
|
|
96
|
+
const mergedClassNames = mergeClassNames(
|
|
97
|
+
{
|
|
98
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
99
|
+
input: styles.input,
|
|
100
|
+
section: styles.section,
|
|
101
|
+
day: styles.day,
|
|
102
|
+
weekday: styles.weekday,
|
|
103
|
+
calendarHeader: styles.calendarHeader,
|
|
104
|
+
calendarHeaderControl: styles.calendarHeaderControl,
|
|
105
|
+
calendarHeaderLevel: styles.calendarHeaderLevel,
|
|
106
|
+
calendarHeaderControlIcon: styles.calendarHeaderControlIcon,
|
|
107
|
+
},
|
|
108
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
109
|
+
);
|
|
112
110
|
|
|
113
111
|
// The calendar popover surface (.dropdown) is styled by Mantine's underlying `Popover`
|
|
114
112
|
// component, not `DatePickerInput` itself — it only accepts classNames via `popoverProps`,
|
|
@@ -129,12 +127,10 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
129
127
|
delete restRecord["popoverProps"];
|
|
130
128
|
const mergedPopoverProps = {
|
|
131
129
|
...consumerPopoverProps,
|
|
132
|
-
classNames:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
: styles.dropdown,
|
|
137
|
-
},
|
|
130
|
+
classNames: mergeClassNames(
|
|
131
|
+
{ dropdown: styles.dropdown },
|
|
132
|
+
consumerPopoverClassNames,
|
|
133
|
+
),
|
|
138
134
|
};
|
|
139
135
|
|
|
140
136
|
const wrapperClass = className
|
|
@@ -177,6 +173,7 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
177
173
|
/* Naked Input execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
|
|
178
174
|
<DatePickerInput
|
|
179
175
|
ref={ref}
|
|
176
|
+
{...(sanitizedProps as unknown as DatePickerInputProps)}
|
|
180
177
|
classNames={mergedClassNames}
|
|
181
178
|
disabled={disabled}
|
|
182
179
|
value={value}
|
|
@@ -185,16 +182,18 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
185
182
|
description={undefined} // Disable Mantine's native description
|
|
186
183
|
error={undefined} // Disable Mantine's native error text (handled by wrapper)
|
|
187
184
|
withAsterisk={false} // Handled by wrapper
|
|
185
|
+
highlightToday={highlightToday}
|
|
186
|
+
valueFormat={valueFormat}
|
|
187
|
+
placeholder={placeholder}
|
|
188
|
+
leftSection={withCallerOverride<React.ReactNode>(
|
|
189
|
+
<CalendarIcon />,
|
|
190
|
+
leftSection,
|
|
191
|
+
)}
|
|
188
192
|
wrapperProps={{
|
|
189
193
|
"data-disabled": disabled ? "true" : undefined,
|
|
190
194
|
"data-error": error ? "true" : undefined,
|
|
191
195
|
}}
|
|
192
|
-
highlightToday // Default on so today is visually marked; consumers can still override via rest
|
|
193
|
-
valueFormat="MM/DD/YY" // Default display format; consumers can still override via rest
|
|
194
|
-
placeholder="MM / DD / YY" // Default placeholder; consumers can still override via rest
|
|
195
|
-
leftSection={<CalendarIcon />} // Default leading icon; consumers can still override via rest
|
|
196
196
|
popoverProps={mergedPopoverProps}
|
|
197
|
-
{...(sanitizedProps as unknown as DatePickerInputProps)}
|
|
198
197
|
/>
|
|
199
198
|
}
|
|
200
199
|
/>
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
} from "@mantine/core";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
omitUnsupportedProps,
|
|
9
|
+
mergeClassNames,
|
|
8
10
|
type RecursicaOverStyled,
|
|
9
11
|
} from "../../utils/filterStylingProps";
|
|
10
12
|
import styles from "./Dropdown.module.css";
|
|
@@ -37,29 +39,40 @@ export interface BareDropdownProps
|
|
|
37
39
|
|
|
38
40
|
export type BareDropdownComponentProps = RecursicaOverStyled<BareDropdownProps>;
|
|
39
41
|
|
|
42
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
43
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
44
|
+
const UNSUPPORTED_PROPS = [
|
|
45
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
46
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
47
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
48
|
+
] as const satisfies readonly (keyof MantineSelectProps)[];
|
|
49
|
+
|
|
40
50
|
export const BareDropdown = forwardRef<
|
|
41
51
|
HTMLInputElement,
|
|
42
52
|
BareDropdownComponentProps
|
|
43
53
|
>(function BareDropdown(props, ref) {
|
|
44
54
|
const { overStyled = false, className, disabled, error, ...rest } = props;
|
|
45
|
-
const sanitizedProps =
|
|
55
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
56
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
57
|
+
UNSUPPORTED_PROPS,
|
|
58
|
+
);
|
|
46
59
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
61
|
+
const mergedClassNames = mergeClassNames(
|
|
62
|
+
{
|
|
63
|
+
wrapper: className ? `${styles.root} ${className}` : styles.root,
|
|
64
|
+
input: styles.input,
|
|
65
|
+
section: styles.section,
|
|
66
|
+
dropdown: styles.dropdown,
|
|
67
|
+
option: styles.option,
|
|
68
|
+
},
|
|
69
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
70
|
+
);
|
|
59
71
|
|
|
60
72
|
return (
|
|
61
73
|
<MantineSelect
|
|
62
74
|
ref={ref}
|
|
75
|
+
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
63
76
|
classNames={mergedClassNames}
|
|
64
77
|
disabled={disabled}
|
|
65
78
|
label={undefined}
|
|
@@ -77,7 +90,6 @@ export const BareDropdown = forwardRef<
|
|
|
77
90
|
"data-error": error ? "true" : undefined,
|
|
78
91
|
},
|
|
79
92
|
}}
|
|
80
|
-
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
81
93
|
/>
|
|
82
94
|
);
|
|
83
95
|
});
|
|
@@ -5,3 +5,5 @@ The `Dropdown` component is mapped explicitly to Mantine's `<Select>` following
|
|
|
5
5
|
1. **Naked Primitive Mapping:** Mantine's `Select` natively executes macro-label generation. To decouple it, we explicitly disable internal labels (`label={undefined}`) and inject it purely inside our generic `FormControlWrapper`.
|
|
6
6
|
2. **Strict Dropdown Design Tokens:** The adapter implements strictly sandboxed styling utilizing only `--recursica_ui-kit_components_dropdown_...` variables. It explicitly does NOT inherit general `text-field` tokens despite geometric similarities, ensuring dropdown menus can be themed independently.
|
|
7
7
|
3. **Dropdown Appendages:** To correctly map Mantine's detached Popover `.dropdown` and list `.option` items, we targeted focus and geometric bindings appending standard padding structures matched to the dropdown height overrides dynamically into our `Dropdown.module.css`.
|
|
8
|
+
4. **Popup Vertical Padding:** The gap above/below the option list inside `.dropdown` is explicitly set from the same `--recursica_..._dropdown_properties_vertical-padding` token the closed control uses — it was previously left to Mantine's own untokenized `--combobox-padding` (4px) default.
|
|
9
|
+
5. **Clear Button Styling:** Mantine's native clear button (`clearButtonProps`, shown when `clearable` + a value are both present) is a bare `CloseButton` with its own hardcoded gray icon/hover styling by default. `Dropdown.tsx` merges in a `.clearButton` class (Dropdown.module.css) that overrides it with the same icon-size/trailing-icon-color/focus-ring tokens as the rest of the right section, using the double-class-selector specificity trick (same as Chip.module.css's `.root.root`) to beat Mantine's own CSS module rule without `!important`.
|