@recursica/mantine-adapter 0.20.0 → 0.21.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 +6 -0
- package/dist/mantine-adapter.cjs +1 -1
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +958 -947
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Button/Button.d.ts +11 -1
- package/package.json +1 -1
- package/src/components/Button/Button.module.css +5 -1
- package/src/components/Button/Button.stories.tsx +41 -0
- package/src/components/Button/Button.tsx +32 -0
- package/src/components/Button/IMPLEMENTATION_NOTES.md +16 -0
- package/src/components/Toast/Toast.module.css +6 -0
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { RecursicaLoaderProps } from '../Loader/Loader';
|
|
2
3
|
import { ButtonProps as MantineButtonProps } from '@mantine/core';
|
|
3
4
|
import { RecursicaOverStyled } from '../../utils/filterStylingProps';
|
|
4
5
|
export interface RecursicaButtonProps {
|
|
6
|
+
/** The visual style variant of the button */
|
|
5
7
|
variant?: "solid" | "outline" | "text";
|
|
8
|
+
/** The size of the button */
|
|
6
9
|
size?: "default" | "small";
|
|
10
|
+
/** An optional icon element to display to the left of the button text. Replaces Mantine's leftSection. */
|
|
7
11
|
icon?: React.ReactNode;
|
|
12
|
+
/** Which Recursica Loader variant to use */
|
|
13
|
+
loaderVariant?: RecursicaLoaderProps["variant"];
|
|
14
|
+
/** The size variant for the loader */
|
|
15
|
+
loaderSize?: RecursicaLoaderProps["size"];
|
|
16
|
+
/** Whether to use the Recursica loader or fallback to the Mantine loader */
|
|
17
|
+
useRecursicaLoader?: boolean;
|
|
8
18
|
}
|
|
9
19
|
export type ButtonProps = RecursicaOverStyled<Omit<MantineButtonProps, "variant" | "size" | "leftSection" | "fullWidth"> & RecursicaButtonProps>;
|
|
10
20
|
/**
|
|
@@ -25,7 +35,7 @@ export type ButtonProps = RecursicaOverStyled<Omit<MantineButtonProps, "variant"
|
|
|
25
35
|
*/
|
|
26
36
|
export declare const Button: (<C = "button">(props: import('@mantine/core').PolymorphicComponentProps<C, ButtonProps>) => React.ReactElement) & Omit<React.FunctionComponent<((ButtonProps & {
|
|
27
37
|
component?: any;
|
|
28
|
-
}) & Omit<Omit<any, "ref">, "className" | "classNames" | "style" | "styles" | "vars" | "p" | "px" | "py" | "pt" | "pb" | "pl" | "pr" | "bg" | "c" | "opacity" | "ff" | "fz" | "fw" | "lts" | "ta" | "lh" | "fs" | "tt" | "td" | "bd" | "w" | "miw" | "maw" | "h" | "mih" | "mah" | "m" | "my" | "mx" | "mt" | "mb" | "ml" | "mr" | "top" | "left" | "bottom" | "right" | "color" | "children" | "radius" | "__vars" | "__size" | "hiddenFrom" | "visibleFrom" | "lightHidden" | "darkHidden" | "mod" | "ms" | "me" | "ps" | "pe" | "bdrs" | "bgsz" | "bgp" | "bgr" | "bga" | "pos" | "inset" | "display" | "flex" | "unstyled" | "variant" | "attributes" | "overStyled" | "disabled" | "icon" | "justify" | "size" | "gradient" | "autoContrast" | "component" | "loading" | "rightSection" | "data-disabled" | "loaderProps"> & {
|
|
38
|
+
}) & Omit<Omit<any, "ref">, "className" | "classNames" | "style" | "styles" | "vars" | "p" | "px" | "py" | "pt" | "pb" | "pl" | "pr" | "bg" | "c" | "opacity" | "ff" | "fz" | "fw" | "lts" | "ta" | "lh" | "fs" | "tt" | "td" | "bd" | "w" | "miw" | "maw" | "h" | "mih" | "mah" | "m" | "my" | "mx" | "mt" | "mb" | "ml" | "mr" | "top" | "left" | "bottom" | "right" | "color" | "children" | "radius" | "__vars" | "__size" | "hiddenFrom" | "visibleFrom" | "lightHidden" | "darkHidden" | "mod" | "ms" | "me" | "ps" | "pe" | "bdrs" | "bgsz" | "bgp" | "bgr" | "bga" | "pos" | "inset" | "display" | "flex" | "unstyled" | "variant" | "attributes" | "overStyled" | "disabled" | "icon" | "justify" | "size" | "gradient" | "autoContrast" | "component" | "loading" | "rightSection" | "data-disabled" | "loaderProps" | "loaderVariant" | "loaderSize" | "useRecursicaLoader"> & {
|
|
29
39
|
ref?: any;
|
|
30
40
|
renderRoot?: (props: any) => any;
|
|
31
41
|
}) | (ButtonProps & {
|
package/package.json
CHANGED
|
@@ -36,12 +36,16 @@
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/* Internal Mantine reset & layout logic for Truncation */
|
|
39
|
-
.root >
|
|
39
|
+
.root > *:not(.loader) {
|
|
40
40
|
min-width: 0;
|
|
41
41
|
position: relative;
|
|
42
42
|
z-index: 1;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
.loader {
|
|
46
|
+
/* Mantine absolutely positions this; we just need a hook to exclude it from the child reset */
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
.label {
|
|
46
50
|
display: flex;
|
|
47
51
|
align-items: center;
|
|
@@ -20,6 +20,30 @@ const meta: Meta<ButtonStoryProps> = {
|
|
|
20
20
|
options: ["default", "small"],
|
|
21
21
|
description: "The size of the button",
|
|
22
22
|
},
|
|
23
|
+
loading: {
|
|
24
|
+
control: "boolean",
|
|
25
|
+
description: "Sets the button to a loading state",
|
|
26
|
+
},
|
|
27
|
+
useRecursicaLoader: {
|
|
28
|
+
control: "boolean",
|
|
29
|
+
description:
|
|
30
|
+
"Use the Recursica Loader component instead of the default Mantine loader",
|
|
31
|
+
},
|
|
32
|
+
loaderVariant: {
|
|
33
|
+
control: "select",
|
|
34
|
+
options: ["oval", "bars", "dots"],
|
|
35
|
+
description: "The visual variant of the Recursica Loader",
|
|
36
|
+
},
|
|
37
|
+
loaderSize: {
|
|
38
|
+
control: "select",
|
|
39
|
+
options: [undefined, "sm", "md", "lg", "small", "default", "large"],
|
|
40
|
+
description: "The size variant for the loader",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
args: {
|
|
44
|
+
useRecursicaLoader: true,
|
|
45
|
+
loaderVariant: "oval",
|
|
46
|
+
loaderSize: undefined,
|
|
23
47
|
},
|
|
24
48
|
};
|
|
25
49
|
|
|
@@ -126,3 +150,20 @@ export const TruncatedLabel: Story = {
|
|
|
126
150
|
</div>
|
|
127
151
|
),
|
|
128
152
|
};
|
|
153
|
+
|
|
154
|
+
export const Loading: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
children: "Saving Changes",
|
|
157
|
+
variant: "solid",
|
|
158
|
+
size: "default",
|
|
159
|
+
loading: true,
|
|
160
|
+
},
|
|
161
|
+
parameters: {
|
|
162
|
+
docs: {
|
|
163
|
+
description: {
|
|
164
|
+
story:
|
|
165
|
+
"When `loading={true}` is applied, the Button injects the Recursica `<Loader />` component. Per Recursica design rules, placing a Button in a loading state automatically forces the `disabled={true}` state on the underlying element. This ensures the button immediately receives the brand theme disabled opacities without relying solely on semantic logic.",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef } from "react";
|
|
2
|
+
import { Loader } from "../Loader/Loader";
|
|
3
|
+
import type { RecursicaLoaderProps } from "../Loader/Loader";
|
|
2
4
|
import {
|
|
3
5
|
Button as MantineButton,
|
|
4
6
|
type ButtonProps as MantineButtonProps,
|
|
@@ -11,9 +13,18 @@ import {
|
|
|
11
13
|
import styles from "./Button.module.css";
|
|
12
14
|
|
|
13
15
|
export interface RecursicaButtonProps {
|
|
16
|
+
/** The visual style variant of the button */
|
|
14
17
|
variant?: "solid" | "outline" | "text";
|
|
18
|
+
/** The size of the button */
|
|
15
19
|
size?: "default" | "small";
|
|
20
|
+
/** An optional icon element to display to the left of the button text. Replaces Mantine's leftSection. */
|
|
16
21
|
icon?: React.ReactNode;
|
|
22
|
+
/** Which Recursica Loader variant to use */
|
|
23
|
+
loaderVariant?: RecursicaLoaderProps["variant"];
|
|
24
|
+
/** The size variant for the loader */
|
|
25
|
+
loaderSize?: RecursicaLoaderProps["size"];
|
|
26
|
+
/** Whether to use the Recursica loader or fallback to the Mantine loader */
|
|
27
|
+
useRecursicaLoader?: boolean;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
export type ButtonProps = RecursicaOverStyled<
|
|
@@ -34,6 +45,9 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
34
45
|
icon,
|
|
35
46
|
children,
|
|
36
47
|
overStyled = false,
|
|
48
|
+
loaderVariant = "oval",
|
|
49
|
+
loaderSize,
|
|
50
|
+
useRecursicaLoader = true,
|
|
37
51
|
...rest
|
|
38
52
|
},
|
|
39
53
|
ref,
|
|
@@ -75,6 +89,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
75
89
|
root: styles.root,
|
|
76
90
|
section: styles.section,
|
|
77
91
|
label: styles.label,
|
|
92
|
+
loader: styles.loader,
|
|
78
93
|
};
|
|
79
94
|
|
|
80
95
|
const classNamesProp = restRecord.classNames;
|
|
@@ -94,6 +109,21 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
94
109
|
? `${styles.root} ${classNameProp}`
|
|
95
110
|
: styles.root;
|
|
96
111
|
|
|
112
|
+
const userLoaderProps = restRecord.loaderProps as
|
|
113
|
+
| Record<string, any>
|
|
114
|
+
| undefined;
|
|
115
|
+
|
|
116
|
+
const resolvedLoaderSize =
|
|
117
|
+
loaderSize ?? (size === "small" ? "small" : "default");
|
|
118
|
+
|
|
119
|
+
let mergedLoaderProps = userLoaderProps;
|
|
120
|
+
if (useRecursicaLoader) {
|
|
121
|
+
mergedLoaderProps = {
|
|
122
|
+
children: <Loader variant={loaderVariant} size={resolvedLoaderSize} />,
|
|
123
|
+
...userLoaderProps,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
97
127
|
return (
|
|
98
128
|
<MantineButton
|
|
99
129
|
ref={ref}
|
|
@@ -101,6 +131,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
101
131
|
classNames={mergedClassNames}
|
|
102
132
|
variant={mapVariant[variant]}
|
|
103
133
|
size={mapSize[size]}
|
|
134
|
+
loaderProps={mergedLoaderProps}
|
|
104
135
|
leftSection={
|
|
105
136
|
icon != null ? (
|
|
106
137
|
<span className={styles.iconWrapper} aria-hidden>
|
|
@@ -112,6 +143,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
112
143
|
data-size={size}
|
|
113
144
|
{...(isIconOnly ? { "data-icon-only": "" } : {})}
|
|
114
145
|
{...sanitizedProps}
|
|
146
|
+
disabled={!!restRecord.disabled || !!restRecord.loading}
|
|
115
147
|
>
|
|
116
148
|
<span className={styles.labelText}>{children}</span>
|
|
117
149
|
</MantineButton>
|
|
@@ -43,3 +43,19 @@ Mantine's `.mantine-Button-label` flex centering breaks primitive truncation log
|
|
|
43
43
|
## Disabled state: brand theme opacity (implicit)
|
|
44
44
|
|
|
45
45
|
**Decision:** The UI kit enforces global brand theme disabled opacities. The `.root:disabled` logic implicitly overrides visibility locally via `var(--recursica_brand_states_disabled)`.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Loader color contrast
|
|
50
|
+
|
|
51
|
+
**Decision:** When a Button is in a loading state, the `Recursica Loader` component is injected. The `Loader` component strictly defines its own colors and styles per variant, meaning it does not inherit the text color (`currentColor`) from the Button.
|
|
52
|
+
|
|
53
|
+
**Constraint:** This can lead to contrast issues (e.g., a blue dots loader inside a solid blue button). Design has explicitly decided not to address this at the moment. As such, developers using the `loading` prop must be aware that the loader's color is fixed by its internal tokens, not by the button's context.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Loading state enforces disabled state
|
|
58
|
+
|
|
59
|
+
**Decision:** When `loading={true}` is passed to the Button, the component explicitly forces `disabled={true}` natively on the underlying element.
|
|
60
|
+
|
|
61
|
+
**Implementation:** This ensures that loading buttons automatically inherit the brand theme disabled opacities (via the `:disabled` CSS pseudo-class) rather than relying solely on Mantine's native `data-disabled` dataset logic, which may not trigger the strict visual fade required by the Recursica design system.
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
);
|
|
24
24
|
gap: var(--recursica_ui-kit_components_toast_properties_spacing);
|
|
25
25
|
|
|
26
|
+
/* Hide Mantine's native colored vertical line */
|
|
27
|
+
&::before {
|
|
28
|
+
display: none;
|
|
29
|
+
content: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
& .icon {
|
|
27
33
|
width: var(--recursica_ui-kit_components_toast_properties_icon) !important;
|
|
28
34
|
height: var(--recursica_ui-kit_components_toast_properties_icon) !important;
|