@recursica/mui-adapter 0.6.0 → 0.7.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/ARCHITECTURE.md +15 -0
- package/CHANGELOG.md +6 -0
- package/SETUP.md +77 -0
- package/USAGE.md +55 -0
- package/dist/mui-adapter.cjs +26 -26
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +3721 -3511
- package/dist/mui-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +9 -29
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +2 -5
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +4 -8
- package/dist/src/components/Avatar/Avatar.d.ts +1 -6
- package/dist/src/components/Badge/Badge.d.ts +1 -4
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +2 -3
- package/dist/src/components/Button/Button.d.ts +2 -17
- package/dist/src/components/Card/Card.d.ts +2 -4
- package/dist/src/components/Checkbox/Checkbox.d.ts +4 -6
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +8 -6
- package/dist/src/components/Chip/Chip.d.ts +2 -11
- package/dist/src/components/Container/Container.d.ts +1 -5
- package/dist/src/components/DatePicker/DatePicker.d.ts +2 -1
- package/dist/src/components/Dropdown/Dropdown.d.ts +4 -14
- package/dist/src/components/Flex/Flex.d.ts +2 -22
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +2 -7
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +3 -18
- package/dist/src/components/Group/Group.d.ts +2 -7
- package/dist/src/components/HoverCard/HoverCard.d.ts +13 -39
- package/dist/src/components/Label/Label.d.ts +2 -9
- package/dist/src/components/Link/Link.d.ts +2 -6
- package/dist/src/components/Loader/Loader.d.ts +2 -7
- package/dist/src/components/Menu/Menu.d.ts +24 -98
- package/dist/src/components/Modal/Modal.d.ts +32 -31
- package/dist/src/components/NumberInput/NumberInput.d.ts +3 -5
- package/dist/src/components/Pagination/Pagination.d.ts +8 -41
- package/dist/src/components/Panel/Panel.d.ts +5 -18
- package/dist/src/components/Radio/Radio.d.ts +4 -4
- package/dist/src/components/Radio/RadioGroup.d.ts +3 -7
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +2 -5
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +5 -15
- package/dist/src/components/Slider/Slider.d.ts +4 -14
- package/dist/src/components/Stack/Stack.d.ts +2 -5
- package/dist/src/components/Stepper/Stepper.d.ts +36 -16
- package/dist/src/components/Switch/Switch.d.ts +4 -4
- package/dist/src/components/Switch/SwitchGroup.d.ts +4 -4
- package/dist/src/components/Table/Table.d.ts +2 -1
- package/dist/src/components/Tabs/Tabs.d.ts +11 -26
- package/dist/src/components/Text/Text.d.ts +2 -16
- package/dist/src/components/TextArea/TextArea.d.ts +3 -9
- package/dist/src/components/TextField/TextField.d.ts +3 -7
- package/dist/src/components/TimePicker/TimePicker.d.ts +2 -1
- package/dist/src/components/Timeline/Timeline.d.ts +2 -1
- package/dist/src/components/Timeline/TimelineItem.d.ts +2 -13
- package/dist/src/components/Title/Title.d.ts +2 -15
- package/dist/src/components/Toast/Toast.d.ts +11 -16
- package/dist/src/components/Tooltip/Tooltip.d.ts +7 -21
- package/dist/src/components/TransferList/TransferList.d.ts +2 -1
- package/dist/src/components/Typography/Typography.d.ts +1 -1
- package/dist/src/utils/RequireAccessibleLabel.d.ts +1 -18
- package/dist/src/utils/filterStylingProps.d.ts +1 -11
- package/llms.txt +16 -0
- package/package.json +6 -2
- package/src/components/Accordion/Accordion.module.css +1 -2
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.tsx +19 -23
- package/src/components/AssistiveElement/AssistiveElement.module.css +32 -29
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +10 -27
- package/src/components/AssistiveElement/AssistiveElement.tsx +11 -15
- package/src/components/Autocomplete/Autocomplete.module.css +17 -18
- package/src/components/Autocomplete/Autocomplete.stories.tsx +7 -16
- package/src/components/Autocomplete/Autocomplete.tsx +27 -45
- package/src/components/Avatar/Avatar.tsx +2 -13
- package/src/components/Badge/Badge.tsx +2 -5
- package/src/components/Breadcrumb/Breadcrumb.tsx +1 -4
- package/src/components/Button/Button.module.css +19 -61
- package/src/components/Button/Button.stories.tsx +24 -23
- package/src/components/Button/Button.tsx +7 -18
- package/src/components/Card/Card.module.css +36 -0
- package/src/components/Card/Card.tsx +6 -4
- package/src/components/Checkbox/Checkbox.module.css +27 -10
- package/src/components/Checkbox/Checkbox.tsx +124 -24
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +43 -23
- package/src/components/Chip/Chip.module.css +52 -34
- package/src/components/Chip/Chip.tsx +45 -47
- package/src/components/Container/Container.stories.tsx +103 -20
- package/src/components/Container/Container.tsx +1 -13
- package/src/components/DatePicker/DatePicker.stories.tsx +125 -19
- package/src/components/DatePicker/DatePicker.tsx +3 -1
- package/src/components/Dropdown/Dropdown.tsx +7 -9
- package/src/components/FileInput/FileInput.stories.tsx +2 -19
- package/src/components/FileUpload/FileUpload.stories.tsx +2 -19
- package/src/components/Flex/Flex.stories.tsx +113 -20
- package/src/components/Flex/Flex.tsx +16 -26
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +0 -8
- package/src/components/FormControlLayout/FormControlLayout.tsx +18 -7
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +1 -10
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +12 -21
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +11 -30
- package/src/components/Group/Group.stories.tsx +6 -30
- package/src/components/Group/Group.tsx +14 -9
- package/src/components/HoverCard/HoverCard.tsx +72 -75
- package/src/components/Label/Label.module.css +4 -0
- package/src/components/Label/Label.stories.tsx +18 -12
- package/src/components/Label/Label.tsx +3 -9
- package/src/components/Link/Link.module.css +4 -3
- package/src/components/Link/Link.stories.tsx +2 -8
- package/src/components/Link/Link.tsx +1 -5
- package/src/components/Loader/Loader.module.css +1 -2
- package/src/components/Loader/Loader.tsx +1 -7
- package/src/components/Menu/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Menu/Menu.stories.tsx +106 -188
- package/src/components/Menu/Menu.tsx +34 -280
- package/src/components/Modal/Modal.tsx +158 -160
- package/src/components/NumberInput/NumberInput.tsx +6 -18
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +3 -0
- package/src/components/Pagination/Pagination.module.css +12 -24
- package/src/components/Pagination/Pagination.stories.tsx +0 -8
- package/src/components/Pagination/Pagination.tsx +20 -140
- package/src/components/Panel/Panel.tsx +7 -30
- package/src/components/Radio/Radio.module.css +25 -10
- package/src/components/Radio/Radio.tsx +51 -19
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +26 -12
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +6 -6
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +5 -36
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +9 -13
- package/src/components/SegmentedControl/SegmentedControl.module.css +23 -47
- package/src/components/SegmentedControl/SegmentedControl.tsx +44 -14
- package/src/components/Slider/Slider.module.css +1 -4
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +26 -25
- package/src/components/Stack/Stack.stories.tsx +4 -25
- package/src/components/Stack/Stack.tsx +1 -5
- package/src/components/Stepper/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Stepper/Stepper.stories.tsx +56 -43
- package/src/components/Stepper/Stepper.tsx +89 -87
- package/src/components/Switch/Switch.module.css +21 -0
- package/src/components/Switch/Switch.tsx +63 -16
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +27 -18
- package/src/components/Table/Table.stories.tsx +1 -19
- package/src/components/Table/Table.tsx +3 -1
- package/src/components/Tabs/IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Tabs/Tabs.stories.tsx +41 -24
- package/src/components/Tabs/Tabs.tsx +49 -66
- package/src/components/Text/Text.stories.tsx +5 -17
- package/src/components/Text/Text.tsx +1 -15
- package/src/components/TextArea/TextArea.tsx +7 -17
- package/src/components/TextField/TextField.stories.tsx +1 -27
- package/src/components/TextField/TextField.tsx +4 -6
- package/src/components/TimePicker/TimePicker.stories.tsx +2 -19
- package/src/components/TimePicker/TimePicker.tsx +3 -1
- package/src/components/Timeline/Timeline.tsx +4 -1
- package/src/components/Timeline/TimelineItem.tsx +1 -12
- package/src/components/Title/Title.stories.tsx +0 -18
- package/src/components/Title/Title.tsx +1 -7
- package/src/components/Toast/Toast.stories.tsx +1 -1
- package/src/components/Toast/Toast.tsx +62 -28
- package/src/components/Tooltip/Tooltip.tsx +11 -37
- package/src/components/TransferList/TransferList.stories.tsx +1 -19
- package/src/components/TransferList/TransferList.tsx +3 -1
- package/src/utils/RequireAccessibleLabel.ts +1 -12
- package/src/utils/filterStylingProps.ts +7 -20
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef } from "react";
|
|
2
2
|
import { Loader } from "../Loader/Loader";
|
|
3
|
-
import type { RecursicaLoaderProps } from "../Loader/Loader";
|
|
4
3
|
import {
|
|
5
4
|
Button as MuiButton,
|
|
6
5
|
type ButtonProps as MuiButtonProps,
|
|
@@ -11,21 +10,7 @@ import {
|
|
|
11
10
|
} from "../../utils/filterStylingProps";
|
|
12
11
|
import styles from "./Button.module.css";
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
variant?: "solid" | "outline" | "text";
|
|
16
|
-
size?: "default" | "small";
|
|
17
|
-
icon?: React.ReactNode;
|
|
18
|
-
/**
|
|
19
|
-
* For polymorphic support (e.g., `component="a"`), native to MUI.
|
|
20
|
-
*/
|
|
21
|
-
component?: React.ElementType;
|
|
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;
|
|
28
|
-
}
|
|
13
|
+
import { type RecursicaButtonProps } from "@recursica/adapter-common";
|
|
29
14
|
|
|
30
15
|
export type ButtonProps = RecursicaOverStyled<
|
|
31
16
|
Omit<MuiButtonProps, "variant" | "size" | "color" | "fullWidth"> &
|
|
@@ -73,7 +58,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
73
58
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
74
59
|
|
|
75
60
|
// Explicitly delete blocked semantic expansion dimension props that MUI uses natively
|
|
76
|
-
delete restRecord["fullWidth"];
|
|
77
61
|
delete restRecord["color"]; // Recursica handles colors internally via tokens
|
|
78
62
|
|
|
79
63
|
const hasStartIcon = !!icon || !!restRecord["startIcon"];
|
|
@@ -127,8 +111,13 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
127
111
|
className={finalClass}
|
|
128
112
|
loading={!!restRecord.loading}
|
|
129
113
|
loadingIndicator={loadingIndicator}
|
|
114
|
+
fullWidth={!!restRecord.fullWidth}
|
|
130
115
|
startIcon={
|
|
131
|
-
|
|
116
|
+
restRecord.loading ? (
|
|
117
|
+
<span className={styles.iconWrapper} aria-hidden>
|
|
118
|
+
{loadingIndicator}
|
|
119
|
+
</span>
|
|
120
|
+
) : icon != null ? (
|
|
132
121
|
<span className={styles.iconWrapper} aria-hidden>
|
|
133
122
|
{icon}
|
|
134
123
|
</span>
|
|
@@ -65,6 +65,18 @@
|
|
|
65
65
|
text-decoration: var(
|
|
66
66
|
--recursica_ui-kit_components_card_properties_header-style_textDecoration
|
|
67
67
|
);
|
|
68
|
+
|
|
69
|
+
/* Edge-to-edge behavior */
|
|
70
|
+
margin-left: calc(var(--card-padding) * -1) !important;
|
|
71
|
+
margin-right: calc(var(--card-padding) * -1) !important;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.header:first-child {
|
|
75
|
+
margin-top: calc(var(--card-padding) * -1) !important;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.header:last-child {
|
|
79
|
+
margin-bottom: calc(var(--card-padding) * -1) !important;
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
.footer {
|
|
@@ -75,6 +87,18 @@
|
|
|
75
87
|
border-top: var(--recursica_ui-kit_components_card_properties_divider-size)
|
|
76
88
|
solid
|
|
77
89
|
var(--recursica_ui-kit_components_card_properties_colors_divider-color);
|
|
90
|
+
|
|
91
|
+
/* Edge-to-edge behavior */
|
|
92
|
+
margin-left: calc(var(--card-padding) * -1) !important;
|
|
93
|
+
margin-right: calc(var(--card-padding) * -1) !important;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.footer:first-child {
|
|
97
|
+
margin-top: calc(var(--card-padding) * -1) !important;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.footer:last-child {
|
|
101
|
+
margin-bottom: calc(var(--card-padding) * -1) !important;
|
|
78
102
|
}
|
|
79
103
|
|
|
80
104
|
.section {
|
|
@@ -82,6 +106,18 @@
|
|
|
82
106
|
margin-top: var(
|
|
83
107
|
--recursica_ui-kit_components_card_properties_vertical-gutter
|
|
84
108
|
);
|
|
109
|
+
|
|
110
|
+
/* Edge-to-edge behavior */
|
|
111
|
+
margin-left: calc(var(--card-padding) * -1) !important;
|
|
112
|
+
margin-right: calc(var(--card-padding) * -1) !important;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.section:first-child {
|
|
116
|
+
margin-top: calc(var(--card-padding) * -1) !important;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.section:last-child {
|
|
120
|
+
margin-bottom: calc(var(--card-padding) * -1) !important;
|
|
85
121
|
}
|
|
86
122
|
|
|
87
123
|
.content {
|
|
@@ -6,8 +6,13 @@ import {
|
|
|
6
6
|
} from "../../utils/filterStylingProps";
|
|
7
7
|
import styles from "./Card.module.css";
|
|
8
8
|
|
|
9
|
+
import {
|
|
10
|
+
type RecursicaCardProps,
|
|
11
|
+
type RecursicaCardSectionProps,
|
|
12
|
+
} from "@recursica/adapter-common";
|
|
13
|
+
|
|
9
14
|
// ==== CARD CONTAINER ====
|
|
10
|
-
export type CardProps = RecursicaOverStyled<MuiCardProps>;
|
|
15
|
+
export type CardProps = RecursicaOverStyled<MuiCardProps & RecursicaCardProps>;
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* 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.
|
|
@@ -53,9 +58,6 @@ const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
|
53
58
|
CardBase.displayName = "Card";
|
|
54
59
|
|
|
55
60
|
// ==== NATIVE MANTINE CARD.SECTION ====
|
|
56
|
-
export type RecursicaCardSectionProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
57
|
-
children?: React.ReactNode;
|
|
58
|
-
};
|
|
59
61
|
export type CardSectionProps = RecursicaOverStyled<RecursicaCardSectionProps>;
|
|
60
62
|
|
|
61
63
|
/**
|
|
@@ -11,17 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
/* --- GROUP STYLES --- */
|
|
13
13
|
.groupRoot {
|
|
14
|
-
padding: var(
|
|
15
|
-
--recursica_ui-kit_components_checkbox-group_properties_padding,
|
|
16
|
-
0
|
|
17
|
-
);
|
|
14
|
+
padding: var(--recursica_ui-kit_components_checkbox-group_properties_padding);
|
|
18
15
|
margin: 0; /* HARDCODE: structural reset; consider token if layout needs change */
|
|
19
16
|
display: flex;
|
|
20
17
|
flex-direction: column;
|
|
21
|
-
gap: var(
|
|
22
|
-
--recursica_ui-kit_components_checkbox-group_properties_item-gap,
|
|
23
|
-
16px
|
|
24
|
-
);
|
|
18
|
+
gap: var(--recursica_ui-kit_components_checkbox-group_properties_item-gap);
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
/* SideBySide Variant */
|
|
@@ -118,7 +112,8 @@
|
|
|
118
112
|
}
|
|
119
113
|
|
|
120
114
|
/* Checked/Indeterminate State */
|
|
121
|
-
.input:checked
|
|
115
|
+
.input:checked,
|
|
116
|
+
.inputChecked {
|
|
122
117
|
background-color: var(
|
|
123
118
|
--recursica_ui-kit_components_checkbox_properties_colors_background-checked
|
|
124
119
|
);
|
|
@@ -126,7 +121,8 @@
|
|
|
126
121
|
--recursica_ui-kit_components_checkbox_properties_colors_border-checked
|
|
127
122
|
);
|
|
128
123
|
}
|
|
129
|
-
.input[data-indeterminate]
|
|
124
|
+
.input[data-indeterminate],
|
|
125
|
+
.inputIndeterminate {
|
|
130
126
|
background-color: var(
|
|
131
127
|
--recursica_ui-kit_components_checkbox_properties_colors_background-indeterminate
|
|
132
128
|
);
|
|
@@ -213,6 +209,27 @@
|
|
|
213
209
|
);
|
|
214
210
|
}
|
|
215
211
|
|
|
212
|
+
.description {
|
|
213
|
+
margin-top: 5px; /* HARDCODE: mantine default */
|
|
214
|
+
color: #868e96; /* HARDCODE: mantine default dimmed */
|
|
215
|
+
font-size: 12px; /* HARDCODE: mantine default xs */
|
|
216
|
+
line-height: 1.2;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.error {
|
|
220
|
+
margin-top: 5px; /* HARDCODE: mantine default */
|
|
221
|
+
color: #fa5252; /* HARDCODE: mantine default error */
|
|
222
|
+
font-size: 12px; /* HARDCODE: mantine default xs */
|
|
223
|
+
line-height: 1.2;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.description[data-disabled="true"],
|
|
227
|
+
.error[data-disabled="true"] {
|
|
228
|
+
opacity: var(
|
|
229
|
+
--recursica_ui-kit_components_checkbox-item_properties_disabled-opacity
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
216
233
|
/* CheckboxGroup Layouts */
|
|
217
234
|
.groupRoot {
|
|
218
235
|
display: flex;
|
|
@@ -2,15 +2,17 @@ import React, { forwardRef } from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
Checkbox as MuiCheckbox,
|
|
4
4
|
type CheckboxProps as MuiCheckboxProps,
|
|
5
|
-
FormControlLabel,
|
|
6
5
|
} from "@mui/material";
|
|
7
6
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
8
|
-
import { CheckboxGroup } from "./CheckboxGroup";
|
|
7
|
+
import { CheckboxGroup, CheckboxGroupContext } from "./CheckboxGroup";
|
|
9
8
|
import {
|
|
10
9
|
filterStylingProps,
|
|
11
10
|
type RecursicaOverStyled,
|
|
12
11
|
} from "../../utils/filterStylingProps";
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type RequireAccessibleLabel,
|
|
14
|
+
type RecursicaCheckboxProps,
|
|
15
|
+
} from "@recursica/adapter-common";
|
|
14
16
|
import {
|
|
15
17
|
FormControlLayout,
|
|
16
18
|
type FormControlLayoutProps,
|
|
@@ -18,17 +20,18 @@ import {
|
|
|
18
20
|
|
|
19
21
|
import styles from "./Checkbox.module.css";
|
|
20
22
|
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export type CheckboxWrapperProps = Omit<MuiCheckboxProps, "size" | "color"> &
|
|
24
|
+
RecursicaCheckboxProps &
|
|
25
|
+
ReadOnlyControlProps &
|
|
26
|
+
Pick<
|
|
27
|
+
FormControlLayoutProps,
|
|
28
|
+
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export type RecursicaCheckboxPropsAlias =
|
|
32
|
+
RequireAccessibleLabel<CheckboxWrapperProps>;
|
|
30
33
|
|
|
31
|
-
export type CheckboxProps = RecursicaOverStyled<
|
|
34
|
+
export type CheckboxProps = RecursicaOverStyled<RecursicaCheckboxPropsAlias>;
|
|
32
35
|
|
|
33
36
|
type CheckboxComponent = React.ForwardRefExoticComponent<
|
|
34
37
|
CheckboxProps & React.RefAttributes<HTMLButtonElement>
|
|
@@ -48,6 +51,9 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
48
51
|
controlMaxWidth,
|
|
49
52
|
controlMinWidth,
|
|
50
53
|
label,
|
|
54
|
+
description,
|
|
55
|
+
error,
|
|
56
|
+
style,
|
|
51
57
|
...rest
|
|
52
58
|
} = props;
|
|
53
59
|
|
|
@@ -84,11 +90,21 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
84
90
|
? `${styles.root} ${classNameProp}`
|
|
85
91
|
: styles.root;
|
|
86
92
|
|
|
93
|
+
const groupContext = React.useContext(CheckboxGroupContext);
|
|
94
|
+
const isGrouped = groupContext !== null;
|
|
95
|
+
const isGroupReadOnly = isGrouped ? groupContext.readOnly : false;
|
|
96
|
+
const isChecked = isGrouped
|
|
97
|
+
? (groupContext.value || []).includes(restRecord.value)
|
|
98
|
+
: !!(restRecord.checked ?? restRecord.defaultChecked);
|
|
99
|
+
|
|
87
100
|
if (readOnly && !!readOnlyComponent) {
|
|
88
|
-
const isChecked = !!(restRecord.checked ?? restRecord.defaultChecked);
|
|
89
101
|
const ReadOnlyComp = readOnlyComponent;
|
|
90
102
|
const roNode = (
|
|
91
|
-
<ReadOnlyComp
|
|
103
|
+
<ReadOnlyComp
|
|
104
|
+
{...props}
|
|
105
|
+
checked={isChecked}
|
|
106
|
+
label={label as React.ReactNode}
|
|
107
|
+
/>
|
|
92
108
|
);
|
|
93
109
|
|
|
94
110
|
if (formLayout) {
|
|
@@ -107,25 +123,109 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
107
123
|
return <>{roNode}</>;
|
|
108
124
|
}
|
|
109
125
|
|
|
126
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
127
|
+
if (isGrouped) {
|
|
128
|
+
const newValue = e.target.checked
|
|
129
|
+
? [...(groupContext.value || []), restRecord.value]
|
|
130
|
+
: (groupContext.value || []).filter((v) => v !== restRecord.value);
|
|
131
|
+
if (groupContext.onChange) {
|
|
132
|
+
groupContext.onChange(e, newValue);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (restRecord.onChange) {
|
|
136
|
+
(restRecord.onChange as any)(e, e.target.checked);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const CheckIcon = (props: React.ComponentProps<"svg">) => (
|
|
142
|
+
<svg
|
|
143
|
+
viewBox="0 0 10 7"
|
|
144
|
+
fill="none"
|
|
145
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
146
|
+
{...props}
|
|
147
|
+
>
|
|
148
|
+
<path
|
|
149
|
+
d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z"
|
|
150
|
+
fill="currentColor"
|
|
151
|
+
fillRule="evenodd"
|
|
152
|
+
clipRule="evenodd"
|
|
153
|
+
/>
|
|
154
|
+
</svg>
|
|
155
|
+
);
|
|
156
|
+
|
|
110
157
|
const checkboxNode = (
|
|
111
158
|
<MuiCheckbox
|
|
112
159
|
ref={ref}
|
|
113
|
-
className={finalClass}
|
|
160
|
+
className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
|
|
114
161
|
classes={mergedClassNames}
|
|
115
|
-
disabled={readOnly || disabled}
|
|
162
|
+
disabled={readOnly || disabled || isGroupReadOnly}
|
|
116
163
|
{...(sanitizedProps as unknown as MuiCheckboxProps)}
|
|
164
|
+
checked={isChecked as boolean}
|
|
165
|
+
onChange={handleChange}
|
|
166
|
+
sx={label ? { padding: 0 } : undefined}
|
|
167
|
+
icon={<div className={styles.input} />}
|
|
168
|
+
checkedIcon={
|
|
169
|
+
<div className={`${styles.input} ${styles.inputChecked}`}>
|
|
170
|
+
<CheckIcon className={styles.icon} />
|
|
171
|
+
</div>
|
|
172
|
+
}
|
|
173
|
+
indeterminateIcon={
|
|
174
|
+
<div className={`${styles.input} ${styles.inputIndeterminate}`}>
|
|
175
|
+
<svg
|
|
176
|
+
className={styles.icon}
|
|
177
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
178
|
+
fill="none"
|
|
179
|
+
viewBox="0 0 32 32"
|
|
180
|
+
>
|
|
181
|
+
<rect x="6" y="14" width="20" height="4" fill="currentColor" />
|
|
182
|
+
</svg>
|
|
183
|
+
</div>
|
|
184
|
+
}
|
|
117
185
|
/>
|
|
118
186
|
);
|
|
119
187
|
|
|
120
188
|
const finalNode = label ? (
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
189
|
+
<div className={finalClass} style={style as React.CSSProperties}>
|
|
190
|
+
<div className={styles.body}>
|
|
191
|
+
{checkboxNode}
|
|
192
|
+
<div className={styles.labelWrapper}>
|
|
193
|
+
<label
|
|
194
|
+
className={styles.label}
|
|
195
|
+
htmlFor={restRecord.id as string}
|
|
196
|
+
data-disabled={
|
|
197
|
+
readOnly || disabled || isGroupReadOnly ? true : undefined
|
|
198
|
+
}
|
|
199
|
+
>
|
|
200
|
+
{label as React.ReactNode}
|
|
201
|
+
</label>
|
|
202
|
+
{description && (
|
|
203
|
+
<div
|
|
204
|
+
className={styles.description}
|
|
205
|
+
data-disabled={
|
|
206
|
+
readOnly || disabled || isGroupReadOnly ? true : undefined
|
|
207
|
+
}
|
|
208
|
+
>
|
|
209
|
+
{description}
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
{error && (
|
|
213
|
+
<div
|
|
214
|
+
className={styles.error}
|
|
215
|
+
data-disabled={
|
|
216
|
+
readOnly || disabled || isGroupReadOnly ? true : undefined
|
|
217
|
+
}
|
|
218
|
+
>
|
|
219
|
+
{error}
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
127
225
|
) : (
|
|
128
|
-
|
|
226
|
+
<div className={finalClass} style={style as React.CSSProperties}>
|
|
227
|
+
{checkboxNode}
|
|
228
|
+
</div>
|
|
129
229
|
);
|
|
130
230
|
|
|
131
231
|
if (formLayout) {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Checkbox } from "./Checkbox";
|
|
3
|
+
import { CheckboxGroup } from "./CheckboxGroup";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof CheckboxGroup> = {
|
|
8
|
+
title: "UI-Kit/CheckboxGroup",
|
|
9
|
+
component: CheckboxGroup,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
The \`CheckboxGroup\` component is the mandatory organizational wrapper aggregating multiple \`Checkbox\` primitives into structurally bound unified arrays. It inherently leverages the \`FormControlWrapper\` granting native access to macroscopic layout structuring, assistive descriptions, and strict flex arrays.
|
|
16
|
+
|
|
17
|
+
We exclusively utilize the \`formLayout\` parameter to control macro-level form flow:
|
|
18
|
+
- **\`formLayout="stacked"\`**: Top-to-bottom layout cascading the Label bounding box down vertically into a standard stacked checkbox column array.
|
|
19
|
+
- **\`formLayout="side-by-side"\`**: Flow architecture pulling the grouping Label dynamically to the left while structurally organizing the internal checkboxes cleanly alongside it horizontally.
|
|
20
|
+
|
|
21
|
+
\`\`\`tsx
|
|
22
|
+
<Checkbox.Group
|
|
23
|
+
label="Execution Targets"
|
|
24
|
+
assistiveText="Strictly mapped structural grouping dynamically applied natively."
|
|
25
|
+
formLayout="stacked"
|
|
26
|
+
>
|
|
27
|
+
<Checkbox value="react" label="Browser Execution Context" />
|
|
28
|
+
<Checkbox value="svelte" label="Edge Nodes" />
|
|
29
|
+
</Checkbox.Group>
|
|
30
|
+
\`\`\`
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
argTypes: {
|
|
36
|
+
...formControlArgTypes,
|
|
37
|
+
readOnly: {
|
|
38
|
+
control: "boolean",
|
|
39
|
+
description:
|
|
40
|
+
"Toggles structural read-only data presentation bypassing interaction boundaries completely.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default meta;
|
|
46
|
+
|
|
47
|
+
type Story = StoryObj<typeof CheckboxGroup>;
|
|
48
|
+
|
|
49
|
+
export const Default: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
formLayout: "stacked",
|
|
52
|
+
label: "Standard Group",
|
|
53
|
+
},
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
55
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
56
|
+
const [value, setValue] = useState<string[]>([]);
|
|
57
|
+
return (
|
|
58
|
+
<Checkbox.Group {...args} value={value} onChange={setValue}>
|
|
59
|
+
<Checkbox value="1" label="Option 1" />
|
|
60
|
+
<Checkbox value="2" label="Option 2" />
|
|
61
|
+
</Checkbox.Group>
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const SideBySideLayout: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
formLayout: "side-by-side",
|
|
69
|
+
labelOptionalText: "Recommended",
|
|
70
|
+
labelWithEditIcon: true,
|
|
71
|
+
label: "Frontend Frameworks",
|
|
72
|
+
assistiveText:
|
|
73
|
+
"Select all libraries currently in use for this specific workspace configuration.",
|
|
74
|
+
},
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
76
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
77
|
+
const [value, setValue] = useState<string[]>(["react"]);
|
|
78
|
+
return (
|
|
79
|
+
<Checkbox.Group {...args} value={value} onChange={setValue}>
|
|
80
|
+
<Checkbox value="react" label="React (Standard Build)" />
|
|
81
|
+
<Checkbox
|
|
82
|
+
value="svelte"
|
|
83
|
+
label="The Svelte architecture which provides a highly optimized, completely compiler-driven framework avoiding virtual DOM boundaries. This massively extended text explicitly guarantees accurate wrapper constraint checking and multi-line flex alignment."
|
|
84
|
+
/>
|
|
85
|
+
<Checkbox value="vue" label="Vue Configuration Map" />
|
|
86
|
+
</Checkbox.Group>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const StackedLayout: Story = {
|
|
92
|
+
args: {
|
|
93
|
+
formLayout: "stacked",
|
|
94
|
+
required: true,
|
|
95
|
+
label: "Execution Targets",
|
|
96
|
+
error: "You must select at least one deployment target to compile.",
|
|
97
|
+
},
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
99
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
100
|
+
const [value, setValue] = useState<string[]>(["react"]);
|
|
101
|
+
return (
|
|
102
|
+
<Checkbox.Group {...args} value={value} onChange={setValue}>
|
|
103
|
+
<Checkbox value="react" label="Browser Execution Context" />
|
|
104
|
+
<Checkbox
|
|
105
|
+
value="svelte"
|
|
106
|
+
label="Highly distributed Edge computing environments seamlessly bridging local runtime boundaries."
|
|
107
|
+
/>
|
|
108
|
+
<Checkbox value="vue" label="Serverless Cloud Providers" />
|
|
109
|
+
</Checkbox.Group>
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const ReadOnly: Story = {
|
|
115
|
+
args: {
|
|
116
|
+
readOnly: true,
|
|
117
|
+
formLayout: "stacked",
|
|
118
|
+
required: true,
|
|
119
|
+
label: "Static ReadOnly Execution Locks",
|
|
120
|
+
assistiveText:
|
|
121
|
+
"This structure explicitly validates native component-level DOM preservation natively mapping lock bounds safely over interaction.",
|
|
122
|
+
},
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
124
|
+
render: function StoryRender({ withLayer, layer, ...args }: any) {
|
|
125
|
+
const [value, setValue] = useState<string[]>(["disabledNode"]);
|
|
126
|
+
return (
|
|
127
|
+
<Checkbox.Group {...args} value={value} onChange={setValue}>
|
|
128
|
+
<Checkbox
|
|
129
|
+
value="disabledNode"
|
|
130
|
+
label="Structurally Checked Node natively"
|
|
131
|
+
/>
|
|
132
|
+
<Checkbox
|
|
133
|
+
value="disabledNodeEmpty"
|
|
134
|
+
label="Unchecked Configuration Limit"
|
|
135
|
+
/>
|
|
136
|
+
</Checkbox.Group>
|
|
137
|
+
);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import React, { forwardRef } from "react";
|
|
3
|
-
import { FormGroup, type FormGroupProps } from "@mui/material";
|
|
4
3
|
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
5
4
|
import {
|
|
6
5
|
filterStylingProps,
|
|
@@ -10,17 +9,29 @@ import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/For
|
|
|
10
9
|
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
11
10
|
import styles from "./Checkbox.module.css";
|
|
12
11
|
|
|
12
|
+
import { FormGroup as MuiFormGroup } from "@mui/material";
|
|
13
|
+
|
|
14
|
+
export const CheckboxGroupContext = React.createContext<{
|
|
15
|
+
value?: any[];
|
|
16
|
+
onChange?: (event: React.SyntheticEvent, value: any) => void;
|
|
17
|
+
name?: string;
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
} | null>(null);
|
|
20
|
+
|
|
21
|
+
import { type RecursicaCheckboxGroupProps as BaseRecursicaCheckboxGroupProps } from "@recursica/adapter-common";
|
|
22
|
+
|
|
13
23
|
export interface RecursicaCheckboxGroupProps
|
|
14
|
-
extends Omit<
|
|
24
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
|
|
15
25
|
Omit<
|
|
16
26
|
RecursicaFormControlWrapperProps,
|
|
17
|
-
|
|
27
|
+
| "controlMaxWidth"
|
|
28
|
+
| "controlMinWidth"
|
|
29
|
+
| "onChange"
|
|
30
|
+
| "classes"
|
|
31
|
+
| "withAsterisk"
|
|
18
32
|
>,
|
|
19
|
-
ReadOnlyControlProps
|
|
20
|
-
|
|
21
|
-
defaultValue?: any[];
|
|
22
|
-
onChange?: (value: any[]) => void;
|
|
23
|
-
}
|
|
33
|
+
ReadOnlyControlProps,
|
|
34
|
+
BaseRecursicaCheckboxGroupProps {}
|
|
24
35
|
|
|
25
36
|
export type CheckboxGroupProps =
|
|
26
37
|
RecursicaOverStyled<RecursicaCheckboxGroupProps>;
|
|
@@ -45,7 +56,7 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
45
56
|
assistiveWithIcon,
|
|
46
57
|
error,
|
|
47
58
|
required,
|
|
48
|
-
withAsterisk
|
|
59
|
+
// removed withAsterisk
|
|
49
60
|
id,
|
|
50
61
|
className,
|
|
51
62
|
style,
|
|
@@ -56,6 +67,7 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
56
67
|
value,
|
|
57
68
|
defaultValue,
|
|
58
69
|
onChange,
|
|
70
|
+
row,
|
|
59
71
|
...rest
|
|
60
72
|
} = props;
|
|
61
73
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
@@ -64,11 +76,10 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
64
76
|
// Delete prohibited sizing hooks
|
|
65
77
|
delete restRecord["size"];
|
|
66
78
|
|
|
67
|
-
const handleChange = (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// For now, we pass down props or rely on the user to handle state.
|
|
79
|
+
const handleChange = (_event: React.SyntheticEvent, childValue: any) => {
|
|
80
|
+
if (onChange) {
|
|
81
|
+
onChange(childValue);
|
|
82
|
+
}
|
|
72
83
|
};
|
|
73
84
|
|
|
74
85
|
return (
|
|
@@ -78,7 +89,7 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
78
89
|
controlMaxWidth="var(--recursica_ui-kit_components_checkbox-item_properties_max-width)"
|
|
79
90
|
controlMinWidth={undefined}
|
|
80
91
|
overStyled={overStyled as true}
|
|
81
|
-
|
|
92
|
+
// strictly override
|
|
82
93
|
formLayout={formLayout}
|
|
83
94
|
labelSize={labelSize}
|
|
84
95
|
labelAlignment={labelAlignment}
|
|
@@ -91,7 +102,6 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
91
102
|
assistiveWithIcon={assistiveWithIcon}
|
|
92
103
|
error={error}
|
|
93
104
|
required={required}
|
|
94
|
-
withAsterisk={withAsterisk}
|
|
95
105
|
id={id}
|
|
96
106
|
readOnly={readOnly && !!readOnlyComponent}
|
|
97
107
|
readOnlyComponent={readOnlyComponent}
|
|
@@ -100,14 +110,24 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
100
110
|
readOnlyValue={value !== undefined ? value : defaultValue}
|
|
101
111
|
readOnlyNativeProps={props}
|
|
102
112
|
activeComponent={
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
<CheckboxGroupContext.Provider
|
|
114
|
+
value={{
|
|
115
|
+
value: value !== undefined ? value : defaultValue,
|
|
116
|
+
onChange: handleChange,
|
|
117
|
+
name: restRecord.name as string | undefined,
|
|
118
|
+
readOnly: readOnly || !!(restRecord as any).disabled,
|
|
119
|
+
}}
|
|
108
120
|
>
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
<MuiFormGroup
|
|
122
|
+
ref={ref}
|
|
123
|
+
row={row}
|
|
124
|
+
{...(sanitizedProps as any)}
|
|
125
|
+
className={`${styles.groupRoot} ${(sanitizedProps as any).className || ""}`.trim()}
|
|
126
|
+
data-layout={formLayout}
|
|
127
|
+
>
|
|
128
|
+
{children}
|
|
129
|
+
</MuiFormGroup>
|
|
130
|
+
</CheckboxGroupContext.Provider>
|
|
111
131
|
}
|
|
112
132
|
/>
|
|
113
133
|
);
|