@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
package/package.json
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
} from "@mantine/core";
|
|
9
9
|
import {
|
|
10
10
|
filterStylingProps,
|
|
11
|
+
omitUnsupportedProps,
|
|
12
|
+
mergeClassNames,
|
|
11
13
|
type RecursicaOverStyled,
|
|
12
14
|
} from "../../utils/filterStylingProps";
|
|
13
15
|
import styles from "./Accordion.module.css";
|
|
@@ -47,44 +49,31 @@ const AccordionBase = function Accordion({
|
|
|
47
49
|
: variant;
|
|
48
50
|
|
|
49
51
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
52
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
50
53
|
|
|
51
54
|
// Bind all deep CSS module references natively into the global class mapping schema
|
|
52
|
-
const mergedClassNames
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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 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
|
-
}
|
|
55
|
+
const mergedClassNames = mergeClassNames(
|
|
56
|
+
{
|
|
57
|
+
root: styles.root,
|
|
58
|
+
item: styles.item,
|
|
59
|
+
control: styles.control,
|
|
60
|
+
label: styles.label,
|
|
61
|
+
chevron: styles.chevron,
|
|
62
|
+
icon: styles.icon,
|
|
63
|
+
panel: styles.panel,
|
|
64
|
+
content: styles.content,
|
|
65
|
+
},
|
|
66
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
67
|
+
);
|
|
78
68
|
|
|
79
|
-
const classNameProp =
|
|
80
|
-
.className as string | undefined;
|
|
69
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
81
70
|
|
|
82
71
|
return (
|
|
83
72
|
<MantineAccordion
|
|
73
|
+
{...(sanitizedProps as unknown as MantineAccordionProps)}
|
|
84
74
|
variant={resolvedVariant as MantineAccordionProps["variant"]}
|
|
85
75
|
className={classNameProp}
|
|
86
76
|
classNames={mergedClassNames}
|
|
87
|
-
{...(sanitizedProps as unknown as MantineAccordionProps)}
|
|
88
77
|
/>
|
|
89
78
|
);
|
|
90
79
|
};
|
|
@@ -128,9 +117,9 @@ export const AccordionItem = forwardRef<
|
|
|
128
117
|
return (
|
|
129
118
|
<MantineAccordion.Item
|
|
130
119
|
ref={ref}
|
|
120
|
+
{...(sanitizedProps as unknown as AccordionItemProps)}
|
|
131
121
|
className={finalClass}
|
|
132
122
|
data-disabled={disabled || undefined}
|
|
133
|
-
{...(sanitizedProps as unknown as AccordionItemProps)}
|
|
134
123
|
>
|
|
135
124
|
{title ? (
|
|
136
125
|
<>
|
|
@@ -155,6 +144,13 @@ export type AccordionControlWrapperProps = RecursicaOverStyled<
|
|
|
155
144
|
Omit<AccordionControlProps, "icon"> & RecursicaAccordionControlProps
|
|
156
145
|
>;
|
|
157
146
|
|
|
147
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
148
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
149
|
+
const UNSUPPORTED_PROPS = [
|
|
150
|
+
"icon", // Mantine's native icon slot is fully computed here from `leftIcon`; the `Omit<>` on
|
|
151
|
+
// AccordionControlWrapperProps only stops a well-typed caller, this is the runtime backstop.
|
|
152
|
+
] as const satisfies readonly (keyof AccordionControlProps)[];
|
|
153
|
+
|
|
158
154
|
export const AccordionControl = forwardRef<
|
|
159
155
|
HTMLButtonElement,
|
|
160
156
|
AccordionControlWrapperProps
|
|
@@ -162,13 +158,17 @@ export const AccordionControl = forwardRef<
|
|
|
162
158
|
{ leftIcon, children, overStyled = false, ...rest },
|
|
163
159
|
ref,
|
|
164
160
|
) {
|
|
165
|
-
const sanitizedProps =
|
|
161
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
162
|
+
filterStylingProps(rest, overStyled),
|
|
163
|
+
UNSUPPORTED_PROPS,
|
|
164
|
+
);
|
|
166
165
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
167
166
|
.className as string | undefined;
|
|
168
167
|
|
|
169
168
|
return (
|
|
170
169
|
<MantineAccordion.Control
|
|
171
170
|
ref={ref}
|
|
171
|
+
{...(sanitizedProps as unknown as AccordionControlProps)}
|
|
172
172
|
className={classNameProp}
|
|
173
173
|
icon={
|
|
174
174
|
leftIcon ? (
|
|
@@ -177,7 +177,6 @@ export const AccordionControl = forwardRef<
|
|
|
177
177
|
</span>
|
|
178
178
|
) : undefined
|
|
179
179
|
}
|
|
180
|
-
{...(sanitizedProps as unknown as AccordionControlProps)}
|
|
181
180
|
>
|
|
182
181
|
{children}
|
|
183
182
|
</MantineAccordion.Control>
|
|
@@ -201,8 +200,8 @@ export const AccordionPanel = forwardRef<
|
|
|
201
200
|
return (
|
|
202
201
|
<MantineAccordion.Panel
|
|
203
202
|
ref={ref}
|
|
204
|
-
className={classNameProp}
|
|
205
203
|
{...(sanitizedProps as unknown as AccordionPanelProps)}
|
|
204
|
+
className={classNameProp}
|
|
206
205
|
/>
|
|
207
206
|
);
|
|
208
207
|
});
|
|
@@ -74,11 +74,11 @@ export const AssistiveElement = forwardRef<
|
|
|
74
74
|
return (
|
|
75
75
|
<div
|
|
76
76
|
ref={ref}
|
|
77
|
+
{...restRecord} // Spread standard HTML attributes (like id, aria-*) natively.
|
|
77
78
|
className={finalClass}
|
|
78
79
|
data-variant={assistiveVariant}
|
|
79
80
|
role={resolvedRole}
|
|
80
81
|
style={restRecord.style as React.CSSProperties}
|
|
81
|
-
{...restRecord} // Spread standard HTML attributes (like id, aria-*) natively.
|
|
82
82
|
>
|
|
83
83
|
{assistiveWithIcon && (
|
|
84
84
|
<span className={styles.iconWrapper}>
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
mergeClassNames,
|
|
10
12
|
type RecursicaOverStyled,
|
|
11
13
|
} from "../../utils/filterStylingProps";
|
|
12
14
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
@@ -42,6 +44,14 @@ export interface RecursicaAutoCompleteProps
|
|
|
42
44
|
|
|
43
45
|
export type AutoCompleteProps = RecursicaOverStyled<RecursicaAutoCompleteProps>;
|
|
44
46
|
|
|
47
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
48
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
49
|
+
const UNSUPPORTED_PROPS = [
|
|
50
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
51
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
52
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
53
|
+
] as const satisfies readonly (keyof MantineAutocompleteProps)[];
|
|
54
|
+
|
|
45
55
|
export const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
|
|
46
56
|
function AutoComplete(props, ref) {
|
|
47
57
|
const {
|
|
@@ -72,46 +82,24 @@ export const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
|
|
|
72
82
|
defaultValue,
|
|
73
83
|
...rest
|
|
74
84
|
} = props;
|
|
75
|
-
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
76
|
-
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
87
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
88
|
+
UNSUPPORTED_PROPS,
|
|
89
|
+
);
|
|
90
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
82
91
|
|
|
83
92
|
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
84
|
-
const mergedClassNames
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
classNamesProp &&
|
|
95
|
-
typeof classNamesProp === "object" &&
|
|
96
|
-
!Array.isArray(classNamesProp)
|
|
97
|
-
) {
|
|
98
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
99
|
-
mergedClassNames.wrapper = o.wrapper
|
|
100
|
-
? `${styles.root} ${o.wrapper}`
|
|
101
|
-
: styles.root;
|
|
102
|
-
mergedClassNames.input = o.input
|
|
103
|
-
? `${styles.input} ${o.input}`
|
|
104
|
-
: styles.input;
|
|
105
|
-
mergedClassNames.section = o.section
|
|
106
|
-
? `${styles.section} ${o.section}`
|
|
107
|
-
: styles.section;
|
|
108
|
-
mergedClassNames.dropdown = o.dropdown
|
|
109
|
-
? `${styles.dropdown} ${o.dropdown}`
|
|
110
|
-
: styles.dropdown;
|
|
111
|
-
mergedClassNames.option = o.option
|
|
112
|
-
? `${styles.option} ${o.option}`
|
|
113
|
-
: styles.option;
|
|
114
|
-
}
|
|
93
|
+
const mergedClassNames = mergeClassNames(
|
|
94
|
+
{
|
|
95
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
96
|
+
input: styles.input,
|
|
97
|
+
section: styles.section,
|
|
98
|
+
dropdown: styles.dropdown,
|
|
99
|
+
option: styles.option,
|
|
100
|
+
},
|
|
101
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
102
|
+
);
|
|
115
103
|
|
|
116
104
|
const wrapperClass = className
|
|
117
105
|
? `${styles.layoutOverride} ${className}`
|
|
@@ -147,12 +135,12 @@ export const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
|
|
|
147
135
|
/* Naked Input execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
|
|
148
136
|
<MantineAutocomplete
|
|
149
137
|
ref={ref}
|
|
138
|
+
{...(sanitizedProps as unknown as MantineAutocompleteProps)}
|
|
150
139
|
classNames={mergedClassNames}
|
|
151
140
|
disabled={disabled}
|
|
152
141
|
value={value as string | undefined}
|
|
153
142
|
defaultValue={defaultValue as string | undefined}
|
|
154
143
|
error={!!error}
|
|
155
|
-
{...(sanitizedProps as unknown as MantineAutocompleteProps)}
|
|
156
144
|
/>
|
|
157
145
|
}
|
|
158
146
|
/>
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
} from "@mantine/core";
|
|
7
7
|
import {
|
|
8
8
|
filterStylingProps,
|
|
9
|
+
omitUnsupportedProps,
|
|
10
|
+
mergeClassNames,
|
|
9
11
|
type RecursicaOverStyled,
|
|
10
12
|
} from "../../utils/filterStylingProps";
|
|
11
13
|
import styles from "./Avatar.module.css";
|
|
@@ -41,7 +43,17 @@ const _Avatar = forwardRef<HTMLDivElement, AvatarProps>(function Avatar(
|
|
|
41
43
|
large: "lg",
|
|
42
44
|
} as const;
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
47
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
48
|
+
const UNSUPPORTED_PROPS = [
|
|
49
|
+
"color", // Colors are token-driven via `data-variant`; Mantine's native palette isn't exposed.
|
|
50
|
+
"radius", // Avatar corner radius is controlled by design tokens, not a raw radius prop.
|
|
51
|
+
] as const satisfies readonly (keyof MantineAvatarProps)[];
|
|
52
|
+
|
|
53
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
54
|
+
filterStylingProps(rest, overStyled),
|
|
55
|
+
UNSUPPORTED_PROPS,
|
|
56
|
+
);
|
|
45
57
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
46
58
|
|
|
47
59
|
// Determine semantic style based on provided props
|
|
@@ -52,33 +64,21 @@ const _Avatar = forwardRef<HTMLDivElement, AvatarProps>(function Avatar(
|
|
|
52
64
|
computedStyle = "icon";
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
const mergedClassNames
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
classNamesProp &&
|
|
64
|
-
typeof classNamesProp === "object" &&
|
|
65
|
-
!Array.isArray(classNamesProp)
|
|
66
|
-
) {
|
|
67
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
68
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
69
|
-
mergedClassNames.image = o.image
|
|
70
|
-
? `${styles.image} ${o.image}`
|
|
71
|
-
: styles.image;
|
|
72
|
-
mergedClassNames.placeholder = o.placeholder
|
|
73
|
-
? `${styles.placeholder} ${o.placeholder}`
|
|
74
|
-
: styles.placeholder;
|
|
75
|
-
}
|
|
67
|
+
const mergedClassNames = mergeClassNames(
|
|
68
|
+
{
|
|
69
|
+
root: styles.root,
|
|
70
|
+
image: styles.image,
|
|
71
|
+
placeholder: styles.placeholder,
|
|
72
|
+
},
|
|
73
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
74
|
+
);
|
|
76
75
|
|
|
77
76
|
const classNameProp = restRecord.className as string | undefined;
|
|
78
77
|
|
|
79
78
|
return (
|
|
80
79
|
<MantineAvatar
|
|
81
80
|
ref={ref}
|
|
81
|
+
{...sanitizedProps}
|
|
82
82
|
className={classNameProp}
|
|
83
83
|
classNames={mergedClassNames}
|
|
84
84
|
variant={mapVariant[variant]}
|
|
@@ -87,7 +87,6 @@ const _Avatar = forwardRef<HTMLDivElement, AvatarProps>(function Avatar(
|
|
|
87
87
|
data-variant={variant}
|
|
88
88
|
data-size={size}
|
|
89
89
|
data-style={computedStyle}
|
|
90
|
-
{...sanitizedProps}
|
|
91
90
|
>
|
|
92
91
|
{icon != null ? (
|
|
93
92
|
<span className={styles.iconWrapper} aria-hidden>
|
|
@@ -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 "./Badge.module.css";
|
|
@@ -25,23 +26,16 @@ const _Badge = forwardRef<HTMLDivElement, BadgeProps>(function Badge(
|
|
|
25
26
|
// External layout props like margins are safely preserved.
|
|
26
27
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
27
28
|
|
|
28
|
-
const mergedClassNames
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
!Array.isArray(classNamesProp)
|
|
39
|
-
) {
|
|
40
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
41
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
42
|
-
mergedClassNames.section = o.section ?? styles.section;
|
|
43
|
-
mergedClassNames.label = o.label ?? styles.label;
|
|
44
|
-
}
|
|
29
|
+
const mergedClassNames = mergeClassNames(
|
|
30
|
+
{
|
|
31
|
+
root: styles.root,
|
|
32
|
+
section: styles.section,
|
|
33
|
+
label: styles.label,
|
|
34
|
+
},
|
|
35
|
+
(sanitizedProps as Record<string, unknown>).classNames as
|
|
36
|
+
| Partial<Record<string, string>>
|
|
37
|
+
| undefined,
|
|
38
|
+
);
|
|
45
39
|
|
|
46
40
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
47
41
|
.className as string | undefined;
|
|
@@ -52,9 +46,9 @@ const _Badge = forwardRef<HTMLDivElement, BadgeProps>(function Badge(
|
|
|
52
46
|
return (
|
|
53
47
|
<MantineBadge
|
|
54
48
|
ref={ref}
|
|
49
|
+
{...sanitizedProps}
|
|
55
50
|
variant="filled" // We manage visual style purely through our CSS variables mapping
|
|
56
51
|
data-variant={variant}
|
|
57
|
-
{...sanitizedProps}
|
|
58
52
|
className={finalClass}
|
|
59
53
|
classNames={mergedClassNames}
|
|
60
54
|
/>
|
|
@@ -56,12 +56,6 @@ export const Default: Story = {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export const StaticExample: Story = {
|
|
60
|
-
args: {
|
|
61
|
-
items: ["Store", "Electronics", "Computers", "Laptops"],
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
59
|
export const CustomSeparator: Story = {
|
|
66
60
|
args: {
|
|
67
61
|
items: ["Root", "Branch", "Leaf"],
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "@mantine/core";
|
|
6
6
|
import {
|
|
7
7
|
filterStylingProps,
|
|
8
|
+
mergeClassNames,
|
|
8
9
|
type RecursicaOverStyled,
|
|
9
10
|
} from "../../utils/filterStylingProps";
|
|
10
11
|
import styles from "./Breadcrumb.module.css";
|
|
@@ -24,24 +25,30 @@ export const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>(
|
|
|
24
25
|
overStyled,
|
|
25
26
|
);
|
|
26
27
|
|
|
27
|
-
const mergedClassNames
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const mergedClassNames = mergeClassNames(
|
|
29
|
+
{
|
|
30
|
+
root: styles.root,
|
|
31
|
+
separator: styles.separator,
|
|
32
|
+
},
|
|
33
|
+
(sanitizedProps as Record<string, unknown>).classNames as
|
|
34
|
+
| Partial<Record<string, string>>
|
|
35
|
+
| undefined,
|
|
36
|
+
);
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
// Mantine's Breadcrumbs, unlike MUI's, doesn't hide the separator from screen
|
|
39
|
+
// readers by default — it's a plain text node. Match MUI's built-in aria-hidden,
|
|
40
|
+
// letting a caller override it via attributes.separator["aria-hidden"] if needed.
|
|
41
|
+
const callerAttributes = (sanitizedProps as Record<string, unknown>)
|
|
42
|
+
.attributes as
|
|
43
|
+
| Partial<Record<string, Record<string, unknown>>>
|
|
44
|
+
| undefined;
|
|
45
|
+
const mergedAttributes = {
|
|
46
|
+
...callerAttributes,
|
|
47
|
+
separator: {
|
|
48
|
+
"aria-hidden": true,
|
|
49
|
+
...callerAttributes?.separator,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
45
52
|
|
|
46
53
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
47
54
|
.className as string | undefined;
|
|
@@ -55,6 +62,7 @@ export const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>(
|
|
|
55
62
|
{...(sanitizedProps as unknown as MantineBreadcrumbsProps)}
|
|
56
63
|
className={finalClass}
|
|
57
64
|
classNames={mergedClassNames}
|
|
65
|
+
attributes={mergedAttributes}
|
|
58
66
|
/>
|
|
59
67
|
);
|
|
60
68
|
},
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
} from "@mantine/core";
|
|
8
8
|
import {
|
|
9
9
|
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
withCallerOverride,
|
|
12
|
+
mergeClassNames,
|
|
10
13
|
type RecursicaOverStyled,
|
|
11
14
|
} from "../../utils/filterStylingProps";
|
|
12
15
|
import styles from "./Button.module.css";
|
|
@@ -49,11 +52,17 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
49
52
|
small: "sm",
|
|
50
53
|
} as const;
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
56
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
57
|
+
const UNSUPPORTED_PROPS = [
|
|
58
|
+
"fullWidth", // Recursica Button width comes from layout/token constraints, not Mantine's boolean fullWidth toggle.
|
|
59
|
+
] as const satisfies readonly (keyof MantineButtonProps)[];
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
62
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
63
|
+
UNSUPPORTED_PROPS,
|
|
64
|
+
) as Partial<typeof rest>;
|
|
65
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
57
66
|
|
|
58
67
|
const hasLeftSection = !!icon || !!restRecord["leftSection"];
|
|
59
68
|
const hasRightSection = !!restRecord["rightSection"];
|
|
@@ -78,41 +87,33 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
78
87
|
);
|
|
79
88
|
}
|
|
80
89
|
|
|
81
|
-
const mergedClassNames
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
classNamesProp &&
|
|
91
|
-
typeof classNamesProp === "object" &&
|
|
92
|
-
!Array.isArray(classNamesProp)
|
|
93
|
-
) {
|
|
94
|
-
const o = classNamesProp as Partial<Record<string, string>>;
|
|
95
|
-
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
96
|
-
mergedClassNames.section = o.section ?? styles.section;
|
|
97
|
-
mergedClassNames.label = o.label ?? styles.label;
|
|
98
|
-
}
|
|
90
|
+
const mergedClassNames = mergeClassNames(
|
|
91
|
+
{
|
|
92
|
+
root: styles.root,
|
|
93
|
+
section: styles.section,
|
|
94
|
+
label: styles.label,
|
|
95
|
+
loader: styles.loader,
|
|
96
|
+
},
|
|
97
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
98
|
+
);
|
|
99
99
|
|
|
100
100
|
const classNameProp = restRecord.className as string | undefined;
|
|
101
101
|
const finalClass = classNameProp
|
|
102
102
|
? `${styles.root} ${classNameProp}`
|
|
103
103
|
: styles.root;
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
delete restRecord["className"];
|
|
104
|
+
// Note: `finalClass` is safe from being clobbered by `{...sanitizedProps}` below purely
|
|
105
|
+
// because of spread order — `className` (like `classNames`, `variant`, `size`, `leftSection`,
|
|
106
|
+
// and the `data-*` attributes) is spread first, then re-asserted after, so our computed value
|
|
107
|
+
// always wins. See the canonical guide's §3.3 for why ordering (not deletion) is the fix here.
|
|
109
108
|
|
|
110
109
|
const userLoaderProps = restRecord.loaderProps as
|
|
111
110
|
| Record<string, unknown>
|
|
112
111
|
| undefined;
|
|
113
112
|
|
|
114
|
-
const resolvedLoaderSize =
|
|
115
|
-
|
|
113
|
+
const resolvedLoaderSize = withCallerOverride(
|
|
114
|
+
size === "small" ? "small" : "default",
|
|
115
|
+
loaderSize,
|
|
116
|
+
);
|
|
116
117
|
|
|
117
118
|
let mergedLoaderProps = userLoaderProps;
|
|
118
119
|
if (useRecursicaLoader) {
|
|
@@ -125,6 +126,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
125
126
|
return (
|
|
126
127
|
<MantineButton
|
|
127
128
|
ref={ref}
|
|
129
|
+
{...sanitizedProps}
|
|
128
130
|
className={finalClass}
|
|
129
131
|
classNames={mergedClassNames}
|
|
130
132
|
variant={mapVariant[variant]}
|
|
@@ -140,7 +142,6 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
140
142
|
data-variant={variant}
|
|
141
143
|
data-size={size}
|
|
142
144
|
data-content={contentType}
|
|
143
|
-
{...sanitizedProps}
|
|
144
145
|
disabled={!!restRecord.disabled || !!restRecord.loading}
|
|
145
146
|
>
|
|
146
147
|
<span className={styles.labelText}>{children}</span>
|