@recursica/mantine-adapter 0.42.1 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +93 -15
- package/dist/mantine-adapter.cjs +2 -2
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.js +3547 -3292
- 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.tsx +10 -18
- 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.tsx +25 -38
- 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.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
|
@@ -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";
|
|
@@ -28,6 +30,14 @@ export interface RecursicaDropdownProps
|
|
|
28
30
|
|
|
29
31
|
export type DropdownProps = RecursicaOverStyled<RecursicaDropdownProps>;
|
|
30
32
|
|
|
33
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
34
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
35
|
+
const UNSUPPORTED_PROPS = [
|
|
36
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
37
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
38
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
39
|
+
] as const satisfies readonly (keyof MantineSelectProps)[];
|
|
40
|
+
|
|
31
41
|
export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
32
42
|
function Dropdown(props, ref) {
|
|
33
43
|
const {
|
|
@@ -60,46 +70,23 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
60
70
|
data,
|
|
61
71
|
...rest
|
|
62
72
|
} = props;
|
|
63
|
-
const sanitizedProps =
|
|
73
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
74
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
75
|
+
UNSUPPORTED_PROPS,
|
|
76
|
+
);
|
|
64
77
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
65
78
|
|
|
66
|
-
// Delete prohibited sizing hooks from bypassing variables natively
|
|
67
|
-
delete restRecord["size"];
|
|
68
|
-
delete restRecord["variant"];
|
|
69
|
-
delete restRecord["radius"];
|
|
70
|
-
|
|
71
79
|
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
72
|
-
const mergedClassNames
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
classNamesProp &&
|
|
83
|
-
typeof classNamesProp === "object" &&
|
|
84
|
-
!Array.isArray(classNamesProp)
|
|
85
|
-
) {
|
|
86
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
87
|
-
mergedClassNames.wrapper = o.wrapper
|
|
88
|
-
? `${styles.root} ${o.wrapper}`
|
|
89
|
-
: styles.root;
|
|
90
|
-
mergedClassNames.input = o.input
|
|
91
|
-
? `${styles.input} ${o.input}`
|
|
92
|
-
: styles.input;
|
|
93
|
-
mergedClassNames.section = o.section
|
|
94
|
-
? `${styles.section} ${o.section}`
|
|
95
|
-
: styles.section;
|
|
96
|
-
mergedClassNames.dropdown = o.dropdown
|
|
97
|
-
? `${styles.dropdown} ${o.dropdown}`
|
|
98
|
-
: styles.dropdown;
|
|
99
|
-
mergedClassNames.option = o.option
|
|
100
|
-
? `${styles.option} ${o.option}`
|
|
101
|
-
: styles.option;
|
|
102
|
-
}
|
|
80
|
+
const mergedClassNames = mergeClassNames(
|
|
81
|
+
{
|
|
82
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
83
|
+
input: styles.input,
|
|
84
|
+
section: styles.section,
|
|
85
|
+
dropdown: styles.dropdown,
|
|
86
|
+
option: styles.option,
|
|
87
|
+
},
|
|
88
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
89
|
+
);
|
|
103
90
|
|
|
104
91
|
const injectedStyles = {
|
|
105
92
|
...((style as React.CSSProperties) || {}),
|
|
@@ -146,6 +133,7 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
146
133
|
/* Naked Select execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
|
|
147
134
|
<MantineSelect
|
|
148
135
|
ref={ref}
|
|
136
|
+
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
149
137
|
classNames={mergedClassNames}
|
|
150
138
|
disabled={disabled}
|
|
151
139
|
value={value}
|
|
@@ -169,7 +157,6 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
169
157
|
"data-error": error ? "true" : undefined,
|
|
170
158
|
},
|
|
171
159
|
}}
|
|
172
|
-
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
173
160
|
/>
|
|
174
161
|
}
|
|
175
162
|
/>
|
|
@@ -309,6 +309,7 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
|
|
|
309
309
|
>
|
|
310
310
|
<div
|
|
311
311
|
ref={ref}
|
|
312
|
+
{...restRecord}
|
|
312
313
|
className={styles.root}
|
|
313
314
|
role="button"
|
|
314
315
|
aria-label={browseLabel}
|
|
@@ -324,7 +325,6 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
|
|
|
324
325
|
onDragLeave={interactive ? handleDragLeave : undefined}
|
|
325
326
|
onDragOver={interactive ? handleDragOver : undefined}
|
|
326
327
|
onDrop={interactive ? handleDrop : undefined}
|
|
327
|
-
{...restRecord}
|
|
328
328
|
>
|
|
329
329
|
<span className={styles.leadingIcon} aria-hidden>
|
|
330
330
|
{icon ?? <UploadIcon />}
|
|
@@ -268,10 +268,10 @@ export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(
|
|
|
268
268
|
>
|
|
269
269
|
<div
|
|
270
270
|
ref={ref}
|
|
271
|
+
{...restRecord}
|
|
271
272
|
className={styles.root}
|
|
272
273
|
data-disabled={disabled ? "true" : undefined}
|
|
273
274
|
data-error={effectiveError ? "true" : undefined}
|
|
274
|
-
{...restRecord}
|
|
275
275
|
>
|
|
276
276
|
{!readOnly && (
|
|
277
277
|
<div
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@mantine/core";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
mergeClassNames,
|
|
9
10
|
type RecursicaOverStyled,
|
|
10
11
|
} from "../../utils/filterStylingProps";
|
|
11
12
|
import styles from "./HoverCard.module.css";
|
|
@@ -42,54 +43,37 @@ export type HoverCardProps = RecursicaOverStyled<
|
|
|
42
43
|
const HoverCardBase = function HoverCard({
|
|
43
44
|
overStyled = false,
|
|
44
45
|
withBeak = true,
|
|
46
|
+
position = "top", // Recursica default; Mantine defaults to "bottom". Consumers can still override.
|
|
47
|
+
// arrowSize must be a JS number prop — Mantine uses it for inline width/height
|
|
48
|
+
// and positioning offset (-arrowSize/2) calculations that cannot be CSS-driven.
|
|
49
|
+
// Default to 16 to match the Recursica beak-size token (16px). Consumers can still override.
|
|
50
|
+
arrowSize = 16,
|
|
51
|
+
withArrow,
|
|
45
52
|
...rest
|
|
46
53
|
}: HoverCardProps) {
|
|
47
54
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
55
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
48
56
|
|
|
49
57
|
// Bind CSS module classes to Mantine's internal classNames API
|
|
50
|
-
const mergedClassNames
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
classNamesProp &&
|
|
58
|
-
typeof classNamesProp === "object" &&
|
|
59
|
-
!Array.isArray(classNamesProp)
|
|
60
|
-
) {
|
|
61
|
-
const o = classNamesProp as Record<string, string>;
|
|
62
|
-
Object.keys(o).forEach((key) => {
|
|
63
|
-
if (mergedClassNames[key]) {
|
|
64
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
65
|
-
} else {
|
|
66
|
-
mergedClassNames[key] = o[key];
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// arrowSize must be a JS number prop — Mantine uses it for inline width/height
|
|
72
|
-
// and positioning offset (-arrowSize/2) calculations that cannot be CSS-driven.
|
|
73
|
-
// Default to 16 to match the Recursica beak-size token (16px).
|
|
74
|
-
const arrowSize =
|
|
75
|
-
((sanitizedProps as Record<string, unknown>).arrowSize as
|
|
76
|
-
| number
|
|
77
|
-
| undefined) ?? 16;
|
|
58
|
+
const mergedClassNames = mergeClassNames(
|
|
59
|
+
{
|
|
60
|
+
dropdown: styles.dropdown,
|
|
61
|
+
arrow: styles.arrow,
|
|
62
|
+
},
|
|
63
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
64
|
+
);
|
|
78
65
|
|
|
79
66
|
// Resolve withBeak (Recursica) vs withArrow (Mantine).
|
|
80
67
|
// withBeak takes precedence when both are provided.
|
|
81
|
-
const withArrow = (sanitizedProps as Record<string, unknown>).withArrow as
|
|
82
|
-
| boolean
|
|
83
|
-
| undefined;
|
|
84
68
|
const resolvedWithArrow = withBeak ?? withArrow;
|
|
85
69
|
|
|
86
70
|
return (
|
|
87
71
|
<MantineHoverCard
|
|
88
|
-
|
|
72
|
+
{...(sanitizedProps as unknown as MantineHoverCardProps)}
|
|
73
|
+
position={position}
|
|
89
74
|
arrowSize={arrowSize}
|
|
90
75
|
withArrow={resolvedWithArrow}
|
|
91
76
|
classNames={mergedClassNames}
|
|
92
|
-
{...(sanitizedProps as unknown as MantineHoverCardProps)}
|
|
93
77
|
/>
|
|
94
78
|
);
|
|
95
79
|
};
|
|
@@ -128,8 +112,8 @@ const HoverCardDropdown = function HoverCardDropdown({
|
|
|
128
112
|
|
|
129
113
|
return (
|
|
130
114
|
<MantineHoverCard.Dropdown
|
|
131
|
-
className={classNameProp}
|
|
132
115
|
{...(sanitizedProps as unknown as MantineHoverCardDropdownProps)}
|
|
116
|
+
className={classNameProp}
|
|
133
117
|
/>
|
|
134
118
|
);
|
|
135
119
|
};
|
|
@@ -2,6 +2,8 @@ import React, { forwardRef } from "react";
|
|
|
2
2
|
import { Input, type InputLabelProps } from "@mantine/core";
|
|
3
3
|
import {
|
|
4
4
|
filterStylingProps,
|
|
5
|
+
omitUnsupportedProps,
|
|
6
|
+
mergeClassNames,
|
|
5
7
|
type RecursicaOverStyled,
|
|
6
8
|
} from "../../utils/filterStylingProps";
|
|
7
9
|
import styles from "./Label.module.css";
|
|
@@ -35,28 +37,25 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
|
35
37
|
else if (labelOptionalText) resolvedOptionalText = labelOptionalText;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
41
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
42
|
+
const UNSUPPORTED_PROPS = [
|
|
43
|
+
"size", // Recursica controls label sizing via `labelSize` + design tokens, not Mantine's native `size`.
|
|
44
|
+
] as const satisfies readonly (keyof InputLabelProps)[];
|
|
40
45
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
47
|
+
filterStylingProps(rest, overStyled),
|
|
48
|
+
UNSUPPORTED_PROPS,
|
|
49
|
+
);
|
|
50
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
45
51
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
mergedClassNames.label = o.label
|
|
54
|
-
? `${styles.root} ${o.label}`
|
|
55
|
-
: styles.root;
|
|
56
|
-
mergedClassNames.required = o.required
|
|
57
|
-
? `${styles.required} ${o.required}`
|
|
58
|
-
: styles.required;
|
|
59
|
-
}
|
|
52
|
+
const mergedClassNames = mergeClassNames(
|
|
53
|
+
{
|
|
54
|
+
label: styles.root,
|
|
55
|
+
required: styles.required,
|
|
56
|
+
},
|
|
57
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
58
|
+
);
|
|
60
59
|
|
|
61
60
|
const classNameProp = restRecord.className as string | undefined;
|
|
62
61
|
const finalClass = classNameProp
|
|
@@ -66,12 +65,12 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
|
66
65
|
return (
|
|
67
66
|
<Input.Label
|
|
68
67
|
ref={ref}
|
|
68
|
+
{...sanitizedProps}
|
|
69
69
|
className={finalClass}
|
|
70
70
|
classNames={mergedClassNames}
|
|
71
71
|
data-size={labelSize}
|
|
72
72
|
data-alignment={resolvedAlignment}
|
|
73
73
|
required={required && !labelWithEditIcon}
|
|
74
|
-
{...sanitizedProps}
|
|
75
74
|
>
|
|
76
75
|
<span className={styles.labelText}>{children}</span>
|
|
77
76
|
{resolvedOptionalText && (
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@mantine/core";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
mergeClassNames,
|
|
9
10
|
type RecursicaOverStyled,
|
|
10
11
|
} from "../../utils/filterStylingProps";
|
|
11
12
|
import styles from "./Link.module.css";
|
|
@@ -23,19 +24,10 @@ const _Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
|
|
|
23
24
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
24
25
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
25
26
|
|
|
26
|
-
const mergedClassNames
|
|
27
|
-
root: styles.root,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const classNamesProp = restRecord.classNames;
|
|
31
|
-
if (
|
|
32
|
-
classNamesProp &&
|
|
33
|
-
typeof classNamesProp === "object" &&
|
|
34
|
-
!Array.isArray(classNamesProp)
|
|
35
|
-
) {
|
|
36
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
37
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
38
|
-
}
|
|
27
|
+
const mergedClassNames = mergeClassNames(
|
|
28
|
+
{ root: styles.root },
|
|
29
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
30
|
+
);
|
|
39
31
|
|
|
40
32
|
const classNameProp = restRecord.className as string | undefined;
|
|
41
33
|
const finalClass = classNameProp
|
|
@@ -45,11 +37,11 @@ const _Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
|
|
|
45
37
|
return (
|
|
46
38
|
<MantineAnchor
|
|
47
39
|
ref={ref}
|
|
40
|
+
{...(sanitizedProps as Record<string, unknown>)}
|
|
48
41
|
className={finalClass}
|
|
49
42
|
classNames={mergedClassNames}
|
|
50
43
|
underline="never"
|
|
51
44
|
{...(icon ? { "data-has-icon": "" } : {})}
|
|
52
|
-
{...(sanitizedProps as Record<string, unknown>)}
|
|
53
45
|
>
|
|
54
46
|
{icon && (
|
|
55
47
|
<span className={styles.iconWrapper} aria-hidden>
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "@mantine/core";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
8
9
|
type RecursicaOverStyled,
|
|
9
10
|
} from "../../utils/filterStylingProps";
|
|
10
11
|
import styles from "./Loader.module.css";
|
|
@@ -33,23 +34,14 @@ export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
|
|
|
33
34
|
// Strip all visual override injections unless developer has specifically opted into overStyling.
|
|
34
35
|
// External layout props like margins are safely preserved.
|
|
35
36
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
37
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
36
38
|
|
|
37
|
-
const mergedClassNames
|
|
38
|
-
root: styles.root,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
|
|
42
|
-
if (
|
|
43
|
-
classNamesProp &&
|
|
44
|
-
typeof classNamesProp === "object" &&
|
|
45
|
-
!Array.isArray(classNamesProp)
|
|
46
|
-
) {
|
|
47
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
48
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
49
|
-
}
|
|
39
|
+
const mergedClassNames = mergeClassNames(
|
|
40
|
+
{ root: styles.root },
|
|
41
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
42
|
+
);
|
|
50
43
|
|
|
51
|
-
const classNameProp =
|
|
52
|
-
.className as string | undefined;
|
|
44
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
53
45
|
const finalClass = classNameProp
|
|
54
46
|
? `${styles.root} ${classNameProp}`
|
|
55
47
|
: styles.root;
|
|
@@ -57,10 +49,10 @@ export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
|
|
|
57
49
|
return (
|
|
58
50
|
<MantineLoader
|
|
59
51
|
ref={ref}
|
|
52
|
+
{...sanitizedProps}
|
|
60
53
|
type={variant}
|
|
61
54
|
data-variant={variant}
|
|
62
55
|
data-size={resolvedSize}
|
|
63
|
-
{...sanitizedProps}
|
|
64
56
|
className={finalClass}
|
|
65
57
|
classNames={mergedClassNames}
|
|
66
58
|
/>
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
} from "@mantine/core";
|
|
15
15
|
import {
|
|
16
16
|
filterStylingProps,
|
|
17
|
+
omitUnsupportedProps,
|
|
18
|
+
mergeClassNames,
|
|
17
19
|
type RecursicaOverStyled,
|
|
18
20
|
} from "../../utils/filterStylingProps";
|
|
19
21
|
import styles from "./Menu.module.css";
|
|
@@ -46,38 +48,26 @@ export type MenuProps = RecursicaOverStyled<
|
|
|
46
48
|
|
|
47
49
|
const MenuBase = function Menu({ overStyled = false, ...rest }: MenuProps) {
|
|
48
50
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
51
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
49
52
|
|
|
50
53
|
// Bind CSS module classes to Mantine's internal classNames API
|
|
51
|
-
const mergedClassNames
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
classNamesProp &&
|
|
64
|
-
typeof classNamesProp === "object" &&
|
|
65
|
-
!Array.isArray(classNamesProp)
|
|
66
|
-
) {
|
|
67
|
-
const o = classNamesProp as Record<string, string>;
|
|
68
|
-
Object.keys(o).forEach((key) => {
|
|
69
|
-
if (mergedClassNames[key]) {
|
|
70
|
-
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
71
|
-
} else {
|
|
72
|
-
mergedClassNames[key] = o[key];
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
54
|
+
const mergedClassNames = mergeClassNames(
|
|
55
|
+
{
|
|
56
|
+
dropdown: styles.dropdown,
|
|
57
|
+
item: styles.item,
|
|
58
|
+
itemLabel: styles.itemLabel,
|
|
59
|
+
itemSection: styles.itemSection,
|
|
60
|
+
divider: styles.divider,
|
|
61
|
+
label: styles.label,
|
|
62
|
+
chevron: styles.chevron,
|
|
63
|
+
},
|
|
64
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
65
|
+
);
|
|
76
66
|
|
|
77
67
|
return (
|
|
78
68
|
<MantineMenu
|
|
79
|
-
classNames={mergedClassNames}
|
|
80
69
|
{...(sanitizedProps as unknown as MantineMenuProps)}
|
|
70
|
+
classNames={mergedClassNames}
|
|
81
71
|
/>
|
|
82
72
|
);
|
|
83
73
|
};
|
|
@@ -91,10 +81,19 @@ MenuBase.displayName = "Menu";
|
|
|
91
81
|
* Wrapper for the element that triggers the menu.
|
|
92
82
|
* Requires a single child element that supports ref forwarding.
|
|
93
83
|
*/
|
|
94
|
-
export type MenuTargetProps = MantineMenuTargetProps
|
|
84
|
+
export type MenuTargetProps = RecursicaOverStyled<MantineMenuTargetProps>;
|
|
95
85
|
|
|
96
|
-
const MenuTarget = function MenuTarget(
|
|
97
|
-
|
|
86
|
+
const MenuTarget = function MenuTarget({
|
|
87
|
+
overStyled = false,
|
|
88
|
+
...rest
|
|
89
|
+
}: MenuTargetProps) {
|
|
90
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<MantineMenu.Target
|
|
94
|
+
{...(sanitizedProps as unknown as MantineMenuTargetProps)}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
98
97
|
};
|
|
99
98
|
MenuTarget.displayName = "MenuTarget";
|
|
100
99
|
|
|
@@ -113,9 +112,9 @@ const MenuDropdown = forwardRef<HTMLDivElement, MenuDropdownProps>(
|
|
|
113
112
|
|
|
114
113
|
return (
|
|
115
114
|
<MantineMenu.Dropdown
|
|
115
|
+
{...(sanitizedProps as unknown as MantineMenuDropdownProps)}
|
|
116
116
|
ref={ref}
|
|
117
117
|
className={classNameProp}
|
|
118
|
-
{...(sanitizedProps as unknown as MantineMenuDropdownProps)}
|
|
119
118
|
/>
|
|
120
119
|
);
|
|
121
120
|
},
|
|
@@ -129,8 +128,7 @@ MenuDropdown.displayName = "MenuDropdown";
|
|
|
129
128
|
/**
|
|
130
129
|
* An individual actionable item within the menu dropdown.
|
|
131
130
|
*
|
|
132
|
-
* **Note:** Mantine's `color` prop is stripped
|
|
133
|
-
* design token adherence. Use `overStyled={true}` if you need to bypass.
|
|
131
|
+
* **Note:** Mantine's `color` prop is stripped to enforce design token adherence.
|
|
134
132
|
*/
|
|
135
133
|
export type MenuItemProps = RecursicaOverStyled<
|
|
136
134
|
Omit<MantineMenuItemProps, "color">
|
|
@@ -138,21 +136,25 @@ export type MenuItemProps = RecursicaOverStyled<
|
|
|
138
136
|
|
|
139
137
|
const _MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(
|
|
140
138
|
function MenuItem({ overStyled = false, ...rest }, ref) {
|
|
141
|
-
|
|
139
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
140
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
141
|
+
const UNSUPPORTED_PROPS = [
|
|
142
|
+
"color", // Colors are token-driven; the library's native palette isn't exposed.
|
|
143
|
+
] as const satisfies readonly (keyof MantineMenuItemProps)[];
|
|
144
|
+
|
|
145
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
146
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
147
|
+
UNSUPPORTED_PROPS,
|
|
148
|
+
);
|
|
142
149
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
143
150
|
|
|
144
|
-
// Strip Mantine's `color` prop to enforce token-driven styling
|
|
145
|
-
if (!overStyled) {
|
|
146
|
-
delete restRecord["color"];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
151
|
const classNameProp = restRecord.className as string | undefined;
|
|
150
152
|
|
|
151
153
|
return (
|
|
152
154
|
<MantineMenu.Item
|
|
155
|
+
{...(sanitizedProps as unknown as MantineMenuItemProps)}
|
|
153
156
|
ref={ref}
|
|
154
157
|
className={classNameProp}
|
|
155
|
-
{...(sanitizedProps as unknown as MantineMenuItemProps)}
|
|
156
158
|
/>
|
|
157
159
|
);
|
|
158
160
|
},
|
|
@@ -185,9 +187,9 @@ const MenuDivider = forwardRef<HTMLHRElement, MenuDividerProps>(
|
|
|
185
187
|
|
|
186
188
|
return (
|
|
187
189
|
<MantineMenu.Divider
|
|
190
|
+
{...(sanitizedProps as unknown as MantineMenuDividerProps)}
|
|
188
191
|
ref={ref}
|
|
189
192
|
className={classNameProp}
|
|
190
|
-
{...(sanitizedProps as unknown as MantineMenuDividerProps)}
|
|
191
193
|
/>
|
|
192
194
|
);
|
|
193
195
|
},
|
|
@@ -211,9 +213,9 @@ const MenuLabel = forwardRef<HTMLDivElement, MenuLabelProps>(function MenuLabel(
|
|
|
211
213
|
|
|
212
214
|
return (
|
|
213
215
|
<MantineMenu.Label
|
|
216
|
+
{...(sanitizedProps as unknown as MantineMenuLabelProps)}
|
|
214
217
|
ref={ref}
|
|
215
218
|
className={classNameProp}
|
|
216
|
-
{...(sanitizedProps as unknown as MantineMenuLabelProps)}
|
|
217
219
|
/>
|
|
218
220
|
);
|
|
219
221
|
});
|
|
@@ -257,20 +259,25 @@ export type MenuSubItemProps = RecursicaOverStyled<
|
|
|
257
259
|
|
|
258
260
|
const _MenuSubItem = forwardRef<HTMLButtonElement, MenuSubItemProps>(
|
|
259
261
|
function MenuSubItem({ overStyled = false, ...rest }, ref) {
|
|
260
|
-
|
|
262
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
263
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
264
|
+
const UNSUPPORTED_PROPS = [
|
|
265
|
+
"color", // Colors are token-driven; the library's native palette isn't exposed.
|
|
266
|
+
] as const satisfies readonly (keyof MantineMenuItemProps)[];
|
|
267
|
+
|
|
268
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
269
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
270
|
+
UNSUPPORTED_PROPS,
|
|
271
|
+
);
|
|
261
272
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
262
273
|
|
|
263
|
-
if (!overStyled) {
|
|
264
|
-
delete restRecord["color"];
|
|
265
|
-
}
|
|
266
|
-
|
|
267
274
|
const classNameProp = restRecord.className as string | undefined;
|
|
268
275
|
|
|
269
276
|
return (
|
|
270
277
|
<MantineMenu.Sub.Item
|
|
278
|
+
{...(sanitizedProps as unknown as MantineMenuItemProps)}
|
|
271
279
|
ref={ref}
|
|
272
280
|
className={classNameProp}
|
|
273
|
-
{...(sanitizedProps as unknown as MantineMenuItemProps)}
|
|
274
281
|
/>
|
|
275
282
|
);
|
|
276
283
|
},
|
|
@@ -302,9 +309,9 @@ const MenuSubDropdown = forwardRef<HTMLDivElement, MenuSubDropdownProps>(
|
|
|
302
309
|
|
|
303
310
|
return (
|
|
304
311
|
<MantineMenu.Sub.Dropdown
|
|
312
|
+
{...(sanitizedProps as unknown as MantineMenuSubDropdownProps)}
|
|
305
313
|
ref={ref}
|
|
306
314
|
className={classNameProp}
|
|
307
|
-
{...(sanitizedProps as unknown as MantineMenuSubDropdownProps)}
|
|
308
315
|
/>
|
|
309
316
|
);
|
|
310
317
|
},
|