@recursica/mui-adapter 0.5.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 +15 -0
- package/SETUP.md +77 -0
- package/USAGE.md +55 -0
- package/dist/mui-adapter.cjs +74 -1
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +6863 -205
- package/dist/mui-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +43 -2
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +6 -2
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +12 -2
- package/dist/src/components/Avatar/Avatar.d.ts +10 -3
- package/dist/src/components/Badge/Badge.d.ts +10 -3
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +9 -3
- package/dist/src/components/Button/Button.d.ts +2 -17
- package/dist/src/components/Card/Card.d.ts +38 -3
- package/dist/src/components/Checkbox/Checkbox.d.ts +13 -2
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +18 -0
- package/dist/src/components/Checkbox/index.d.ts +5 -1
- package/dist/src/components/Chip/Chip.d.ts +9 -2
- 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 +12 -2
- package/dist/src/components/Flex/Flex.d.ts +2 -22
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +5 -2
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +9 -2
- package/dist/src/components/Group/Group.d.ts +2 -7
- package/dist/src/components/HoverCard/HoverCard.d.ts +28 -2
- package/dist/src/components/Label/Label.d.ts +6 -2
- 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 +25 -3
- package/dist/src/components/Modal/Modal.d.ts +37 -2
- package/dist/src/components/NumberInput/NumberInput.d.ts +12 -2
- package/dist/src/components/Pagination/Pagination.d.ts +12 -3
- package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
- package/dist/src/components/Panel/Panel.d.ts +52 -3
- package/dist/src/components/Radio/Radio.d.ts +13 -3
- package/dist/src/components/Radio/RadioGroup.d.ts +9 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +4 -2
- package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
- package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +21 -3
- package/dist/src/components/Slider/Slider.d.ts +18 -2
- package/dist/src/components/Stack/Stack.d.ts +2 -5
- package/dist/src/components/Stepper/Stepper.d.ts +41 -2
- package/dist/src/components/Switch/Switch.d.ts +10 -3
- package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
- package/dist/src/components/Table/Table.d.ts +2 -1
- package/dist/src/components/Tabs/Tabs.d.ts +20 -3
- package/dist/src/components/Text/Text.d.ts +2 -16
- package/dist/src/components/TextArea/TextArea.d.ts +12 -2
- package/dist/src/components/TextField/TextField.d.ts +12 -2
- package/dist/src/components/TimePicker/TimePicker.d.ts +2 -1
- package/dist/src/components/Timeline/Timeline.d.ts +15 -2
- package/dist/src/components/Timeline/TimelineItem.d.ts +18 -0
- package/dist/src/components/Title/Title.d.ts +2 -15
- package/dist/src/components/Toast/Toast.d.ts +20 -2
- package/dist/src/components/Tooltip/Tooltip.d.ts +28 -3
- 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 +14 -3
- package/src/components/Accordion/Accordion.module.css +294 -56
- package/src/components/Accordion/Accordion.stories.tsx +131 -21
- package/src/components/Accordion/Accordion.tsx +260 -5
- package/src/components/AssistiveElement/AssistiveElement.module.css +114 -20
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +60 -20
- package/src/components/AssistiveElement/AssistiveElement.tsx +81 -5
- package/src/components/Autocomplete/Autocomplete.module.css +319 -42
- package/src/components/Autocomplete/Autocomplete.stories.tsx +175 -23
- package/src/components/Autocomplete/Autocomplete.tsx +181 -6
- package/src/components/Avatar/Avatar.module.css +343 -73
- package/src/components/Avatar/Avatar.stories.tsx +94 -22
- package/src/components/Avatar/Avatar.tsx +99 -6
- package/src/components/Badge/Badge.module.css +127 -29
- package/src/components/Badge/Badge.stories.tsx +65 -22
- package/src/components/Badge/Badge.tsx +67 -6
- package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
- package/src/components/Breadcrumb/Breadcrumb.tsx +59 -6
- 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 +154 -38
- package/src/components/Card/Card.stories.tsx +117 -19
- package/src/components/Card/Card.tsx +181 -5
- package/src/components/Checkbox/Checkbox.module.css +249 -39
- package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
- package/src/components/Checkbox/Checkbox.tsx +246 -5
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +137 -0
- package/src/components/Checkbox/index.ts +6 -1
- package/src/components/Chip/Chip.module.css +246 -43
- package/src/components/Chip/Chip.stories.tsx +101 -21
- package/src/components/Chip/Chip.tsx +136 -6
- 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.module.css +291 -41
- package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
- package/src/components/Dropdown/Dropdown.tsx +180 -6
- 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.module.css +92 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +67 -20
- package/src/components/FormControlLayout/FormControlLayout.tsx +69 -4
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +87 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -22
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +126 -6
- package/src/components/Group/Group.stories.tsx +6 -30
- package/src/components/Group/Group.tsx +14 -9
- package/src/components/HoverCard/HoverCard.module.css +90 -22
- package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
- package/src/components/HoverCard/HoverCard.tsx +147 -5
- package/src/components/Label/Label.module.css +140 -35
- package/src/components/Label/Label.stories.tsx +108 -21
- package/src/components/Label/Label.tsx +82 -5
- 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.module.css +285 -51
- package/src/components/Menu/Menu.stories.tsx +229 -25
- package/src/components/Menu/Menu.tsx +73 -6
- package/src/components/Modal/Modal.module.css +171 -35
- package/src/components/Modal/Modal.stories.tsx +66 -22
- package/src/components/Modal/Modal.tsx +215 -5
- package/src/components/NumberInput/NumberInput.module.css +276 -42
- package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
- package/src/components/NumberInput/NumberInput.tsx +136 -6
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +3 -0
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +234 -20
- package/src/components/Pagination/Pagination.stories.tsx +34 -21
- package/src/components/Pagination/Pagination.tsx +74 -6
- package/src/components/Panel/Panel.module.css +206 -38
- package/src/components/Panel/Panel.stories.tsx +214 -22
- package/src/components/Panel/Panel.tsx +130 -5
- package/src/components/Radio/Radio.module.css +235 -37
- package/src/components/Radio/Radio.stories.tsx +77 -19
- package/src/components/Radio/Radio.tsx +210 -5
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +125 -0
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +147 -19
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +105 -5
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +1 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +179 -47
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
- package/src/components/SegmentedControl/SegmentedControl.tsx +140 -6
- package/src/components/Slider/Slider.module.css +539 -86
- package/src/components/Slider/Slider.stories.tsx +157 -19
- package/src/components/Slider/Slider.tsx +283 -5
- 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.module.css +410 -71
- package/src/components/Stepper/Stepper.stories.tsx +146 -20
- package/src/components/Stepper/Stepper.tsx +147 -6
- package/src/components/Switch/Switch.module.css +259 -39
- package/src/components/Switch/Switch.stories.tsx +98 -19
- package/src/components/Switch/Switch.tsx +200 -6
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +120 -0
- 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.module.css +422 -79
- package/src/components/Tabs/Tabs.stories.tsx +156 -20
- package/src/components/Tabs/Tabs.tsx +86 -6
- package/src/components/Text/Text.stories.tsx +5 -17
- package/src/components/Text/Text.tsx +1 -15
- package/src/components/TextArea/TextArea.module.css +155 -32
- package/src/components/TextArea/TextArea.stories.tsx +80 -21
- package/src/components/TextArea/TextArea.tsx +146 -6
- package/src/components/TextField/TextField.module.css +261 -42
- package/src/components/TextField/TextField.stories.tsx +147 -20
- package/src/components/TextField/TextField.tsx +169 -6
- package/src/components/TimePicker/TimePicker.stories.tsx +2 -19
- package/src/components/TimePicker/TimePicker.tsx +3 -1
- package/src/components/Timeline/Timeline.module.css +365 -76
- package/src/components/Timeline/Timeline.stories.tsx +114 -21
- package/src/components/Timeline/Timeline.tsx +83 -5
- package/src/components/Timeline/TimelineItem.tsx +90 -0
- package/src/components/Title/Title.stories.tsx +0 -18
- package/src/components/Title/Title.tsx +1 -7
- package/src/components/Toast/Toast.module.css +166 -28
- package/src/components/Toast/Toast.stories.tsx +54 -22
- package/src/components/Toast/Toast.tsx +124 -5
- package/src/components/Tooltip/Tooltip.module.css +89 -24
- package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
- package/src/components/Tooltip/Tooltip.tsx +88 -5
- 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,28 +1,35 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { FormControlLayout } from "./FormControlLayout";
|
|
3
|
-
import {
|
|
3
|
+
import { Switch } from "../Switch/Switch";
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof FormControlLayout> = {
|
|
6
|
-
title: "UI-Kit
|
|
6
|
+
title: "UI-Kit/FormControlLayout",
|
|
7
7
|
component: FormControlLayout,
|
|
8
|
-
tags: ["autodocs"],
|
|
9
8
|
parameters: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
layout: "padded",
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
"A layout component used to correctly position form inputs alongside their labels.\n\n**When to use this:**\nTypically, you should use `FormControlWrapper` instead, which uses this component under the hood to handle layout automatically.\n\nHowever, this component is useful when you need to align standalone inputs (like a `Switch` or `Checkbox` without a label) so they perfectly match the spacing and alignment of your other form fields in a `side-by-side` layout.",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
tags: ["autodocs"],
|
|
18
|
+
argTypes: {
|
|
19
|
+
formLayout: {
|
|
20
|
+
control: "radio",
|
|
21
|
+
options: ["stacked", "side-by-side"],
|
|
22
|
+
},
|
|
23
|
+
labelSize: {
|
|
24
|
+
control: "radio",
|
|
25
|
+
options: ["default", "small", "md"],
|
|
26
|
+
description: "Dictates the physical width of the left column.",
|
|
27
|
+
},
|
|
28
|
+
children: {
|
|
29
|
+
table: { disable: true },
|
|
30
|
+
},
|
|
31
|
+
leftSection: {
|
|
32
|
+
table: { disable: true },
|
|
26
33
|
},
|
|
27
34
|
},
|
|
28
35
|
};
|
|
@@ -30,6 +37,46 @@ const meta: Meta<typeof FormControlLayout> = {
|
|
|
30
37
|
export default meta;
|
|
31
38
|
type Story = StoryObj<typeof FormControlLayout>;
|
|
32
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The Default layout demonstrates wrapping a standalone primitive without a Label.
|
|
42
|
+
* In a stacked layout, an omitted leftSection naturally results in no structural padding.
|
|
43
|
+
*/
|
|
33
44
|
export const Default: Story = {
|
|
34
|
-
|
|
45
|
+
args: {
|
|
46
|
+
formLayout: "stacked",
|
|
47
|
+
labelSize: "default",
|
|
48
|
+
leftSection: (
|
|
49
|
+
<div
|
|
50
|
+
style={{ padding: 8, border: "1px dashed #ccc", background: "#fafafa" }}
|
|
51
|
+
>
|
|
52
|
+
Left Section Boundary
|
|
53
|
+
</div>
|
|
54
|
+
),
|
|
55
|
+
children: <Switch label="Input area content" />,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Demonstrates the stacked layout without a left section.
|
|
61
|
+
* The input should natively pull flush to the top left since there is no left column.
|
|
62
|
+
*/
|
|
63
|
+
export const StackedLayout: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
formLayout: "stacked",
|
|
66
|
+
labelSize: "default",
|
|
67
|
+
children: <Switch label="Flush stacked switch" />,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Demonstrates how a naked boolean primitive perfectly aligns in a side-by-side layout
|
|
73
|
+
* by utilizing the layout wrapper. The left column maintains its precise design system width
|
|
74
|
+
* even when `leftSection` is undefined, forcing the component into the correct form column!
|
|
75
|
+
*/
|
|
76
|
+
export const SideBySideLayout: Story = {
|
|
77
|
+
args: {
|
|
78
|
+
formLayout: "side-by-side",
|
|
79
|
+
labelSize: "default",
|
|
80
|
+
children: <Switch label="Offset switch aligning with grid" />,
|
|
81
|
+
},
|
|
35
82
|
};
|
|
@@ -1,7 +1,72 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { filterStylingProps } from "../../utils/filterStylingProps";
|
|
3
|
+
import styles from "./FormControlLayout.module.css";
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
import { type RecursicaFormControlLayoutProps } from "@recursica/adapter-common";
|
|
4
6
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export interface FormControlLayoutProps
|
|
8
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
9
|
+
RecursicaFormControlLayoutProps {
|
|
10
|
+
overStyled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const FormControlLayout = React.forwardRef<
|
|
14
|
+
HTMLDivElement,
|
|
15
|
+
FormControlLayoutProps
|
|
16
|
+
>(function FormControlLayout(props, ref) {
|
|
17
|
+
const {
|
|
18
|
+
overStyled = false,
|
|
19
|
+
formLayout = "stacked",
|
|
20
|
+
labelSize = "default",
|
|
21
|
+
controlMaxWidth,
|
|
22
|
+
controlMinWidth,
|
|
23
|
+
leftSection,
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
style,
|
|
27
|
+
...rest
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={className ? `${styles.root} ${className}` : styles.root}
|
|
36
|
+
data-form-layout={formLayout}
|
|
37
|
+
style={
|
|
38
|
+
{
|
|
39
|
+
...style,
|
|
40
|
+
...(controlMaxWidth
|
|
41
|
+
? { "--form-control-max-width": controlMaxWidth }
|
|
42
|
+
: {}),
|
|
43
|
+
...(controlMinWidth
|
|
44
|
+
? { "--form-control-min-width": controlMinWidth }
|
|
45
|
+
: {}),
|
|
46
|
+
} as React.CSSProperties
|
|
47
|
+
}
|
|
48
|
+
{...sanitizedProps}
|
|
49
|
+
>
|
|
50
|
+
{/*
|
|
51
|
+
The left section strictly enforces the Label boundary sizes
|
|
52
|
+
We render it unconditionally if side-by-side to preserve the grid column layout
|
|
53
|
+
*/}
|
|
54
|
+
{formLayout === "side-by-side" ? (
|
|
55
|
+
<div className={styles.leftSection} data-label-size={labelSize}>
|
|
56
|
+
{leftSection}
|
|
57
|
+
</div>
|
|
58
|
+
) : (
|
|
59
|
+
leftSection && (
|
|
60
|
+
<div className={styles.leftSection} data-label-size={labelSize}>
|
|
61
|
+
{leftSection}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{/* The main content area natively flows into the remaining space */}
|
|
67
|
+
<div className={styles.rightSection}>{children}</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
FormControlLayout.displayName = "FormControlLayout";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
FORM CONTROL WRAPPER
|
|
3
|
+
Strict architectural layout mapping handling grid bounds around MUI Form Controls natively.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.root {
|
|
7
|
+
/* Inherit MUI Reset overrides */
|
|
8
|
+
display: block;
|
|
9
|
+
width: 100%;
|
|
10
|
+
|
|
11
|
+
/* Global Form Field Colors */
|
|
12
|
+
--form-field-background: var(
|
|
13
|
+
--recursica_ui-kit_globals_form_field_colors_background
|
|
14
|
+
);
|
|
15
|
+
--form-field-background-read-only: var(
|
|
16
|
+
--recursica_ui-kit_globals_form_field_colors_background-read-only
|
|
17
|
+
);
|
|
18
|
+
--form-field-border: var(--recursica_ui-kit_globals_form_field_colors_border);
|
|
19
|
+
--form-field-border-error: var(
|
|
20
|
+
--recursica_ui-kit_globals_form_field_colors_border-error
|
|
21
|
+
);
|
|
22
|
+
--form-field-border-focus: var(
|
|
23
|
+
--recursica_ui-kit_globals_form_field_colors_border-focus
|
|
24
|
+
);
|
|
25
|
+
--form-field-border-selected: var(
|
|
26
|
+
--recursica_ui-kit_globals_form_field_colors_border-selected
|
|
27
|
+
);
|
|
28
|
+
--form-field-disabled-background: var(
|
|
29
|
+
--recursica_ui-kit_globals_form_field_colors_disabled-background
|
|
30
|
+
);
|
|
31
|
+
--form-field-disabled-text: var(
|
|
32
|
+
--recursica_ui-kit_globals_form_field_colors_disabled-text
|
|
33
|
+
);
|
|
34
|
+
--form-field-icon: var(--recursica_ui-kit_globals_form_field_colors_icon);
|
|
35
|
+
--form-field-placeholder-text-opacity: var(
|
|
36
|
+
--recursica_ui-kit_globals_form_field_colors_placeholder-text-opacity
|
|
37
|
+
);
|
|
38
|
+
--form-field-text-valued: var(
|
|
39
|
+
--recursica_ui-kit_globals_form_field_colors_text-valued
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/* Global Form Field Geometric Sizes */
|
|
43
|
+
--form-field-border-radius: var(
|
|
44
|
+
--recursica_ui-kit_globals_form_field_size_border-radius
|
|
45
|
+
);
|
|
46
|
+
--form-field-border-size-default: var(
|
|
47
|
+
--recursica_ui-kit_globals_form_field_size_border-size-default
|
|
48
|
+
);
|
|
49
|
+
--form-field-border-size-focus: var(
|
|
50
|
+
--recursica_ui-kit_globals_form_field_size_border-size-focus
|
|
51
|
+
);
|
|
52
|
+
--form-field-horizontal-padding: var(
|
|
53
|
+
--recursica_ui-kit_globals_form_field_size_horizontal-padding
|
|
54
|
+
);
|
|
55
|
+
--form-field-icon-size: var(--recursica_ui-kit_globals_form_field_size_icon);
|
|
56
|
+
--form-field-icon-text-gap: var(
|
|
57
|
+
--recursica_ui-kit_globals_form_field_size_icon-text-gap
|
|
58
|
+
);
|
|
59
|
+
--form-field-max-width: var(
|
|
60
|
+
--recursica_ui-kit_globals_form_field_size_max-width
|
|
61
|
+
);
|
|
62
|
+
--form-field-min-width: var(
|
|
63
|
+
--recursica_ui-kit_globals_form_field_size_min-width
|
|
64
|
+
);
|
|
65
|
+
--form-field-single-line-input-height: var(
|
|
66
|
+
--recursica_ui-kit_globals_form_field_size_single-line-input-height
|
|
67
|
+
);
|
|
68
|
+
--form-field-vertical-gutter: var(
|
|
69
|
+
--recursica_ui-kit_globals_form_field_size_vertical-gutter
|
|
70
|
+
);
|
|
71
|
+
--form-field-vertical-padding: var(
|
|
72
|
+
--recursica_ui-kit_globals_form_field_size_vertical-padding
|
|
73
|
+
);
|
|
74
|
+
color: var(--form-field-text-valued);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.inputSection {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
flex: 1;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
}
|
|
83
|
+
.assistive {
|
|
84
|
+
margin-top: var(
|
|
85
|
+
--recursica_ui-kit_globals_form_properties_label-field-gap-vertical
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -1,35 +1,129 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
FormControlWrapper,
|
|
4
|
+
type FormControlWrapperProps,
|
|
5
|
+
} from "./FormControlWrapper";
|
|
6
|
+
import { TextField } from "../TextField/TextField";
|
|
7
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
type WrapperStoryProps = FormControlWrapperProps;
|
|
10
|
+
|
|
11
|
+
const meta: Meta<WrapperStoryProps> = {
|
|
12
|
+
title: "UI-Kit/FormControlWrapper",
|
|
7
13
|
component: FormControlWrapper,
|
|
8
14
|
tags: ["autodocs"],
|
|
9
15
|
parameters: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
docs: {
|
|
17
|
+
description: {
|
|
18
|
+
component:
|
|
19
|
+
"The `FormControlWrapper` is the ultimate structural replacement for Mantine's built-in `Input.Wrapper`. By abandoning Mantine's opinionated wrappers entirely, we centralize all label tracking, error rendering, ARIA generation, and grid layouts natively inside this single component.\n\n### Usage with Naked Primitives\nThis component wraps 'naked' elements like `<Input>` directly. The demonstration stories below utilize `<TextField>` as a native display vehicle, since `<TextField>` natively pipes all its properties structurally back into this wrapper.",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
argTypes: {
|
|
24
|
+
...formControlArgTypes,
|
|
25
|
+
error: {
|
|
26
|
+
control: "text",
|
|
27
|
+
description:
|
|
28
|
+
"Error string driving native assistive component and validation markers.",
|
|
29
|
+
},
|
|
30
|
+
assistiveText: {
|
|
31
|
+
control: "text",
|
|
32
|
+
description:
|
|
33
|
+
"Helper instructions safely dynamically anchored below the input box.",
|
|
34
|
+
},
|
|
35
|
+
assistiveWithIcon: {
|
|
36
|
+
control: "boolean",
|
|
37
|
+
},
|
|
38
|
+
required: {
|
|
39
|
+
control: "boolean",
|
|
26
40
|
},
|
|
27
41
|
},
|
|
28
42
|
};
|
|
29
43
|
|
|
30
44
|
export default meta;
|
|
31
|
-
|
|
45
|
+
|
|
46
|
+
type Story = StoryObj<WrapperStoryProps>;
|
|
47
|
+
|
|
48
|
+
const renderWithTextField = (args: WrapperStoryProps) => (
|
|
49
|
+
// We explicitly cast args to 'any' here because Mantine enforces strict anatomy types for
|
|
50
|
+
// 'classNames', 'styles', and 'attributes'. WrapperStoryProps defines these for the Wrapper anatomy,
|
|
51
|
+
// but TextField expects them for the full TextInput anatomy, causing a TS intersection mismatch.
|
|
52
|
+
<TextField
|
|
53
|
+
placeholder="Form Control primitive mapped..."
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
{...(args as any)}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
32
58
|
|
|
33
59
|
export const Default: Story = {
|
|
34
|
-
|
|
60
|
+
args: {
|
|
61
|
+
label: "Account Username",
|
|
62
|
+
formLayout: "stacked",
|
|
63
|
+
assistiveText: "Validation occurs immediately natively.",
|
|
64
|
+
},
|
|
65
|
+
render: renderWithTextField,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const VisualErrorState: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
label: "Encryption Protocol",
|
|
71
|
+
formLayout: "stacked",
|
|
72
|
+
error: "Strict validation limits reached. Handshake rejected securely.",
|
|
73
|
+
},
|
|
74
|
+
render: renderWithTextField,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const RequiredArchitecture: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
label: "Root Password",
|
|
80
|
+
formLayout: "side-by-side",
|
|
81
|
+
required: true,
|
|
82
|
+
assistiveText: "Bypass string structure required to initiate protocol.",
|
|
83
|
+
},
|
|
84
|
+
render: renderWithTextField,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const WithoutAssistiveIcons: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
label: "Server Domain",
|
|
90
|
+
assistiveText:
|
|
91
|
+
"A standard text boundary without default native icon parameters bounding.",
|
|
92
|
+
assistiveWithIcon: false,
|
|
93
|
+
},
|
|
94
|
+
render: renderWithTextField,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const NativeChildrenDirectly: Story = {
|
|
98
|
+
parameters: {
|
|
99
|
+
docs: {
|
|
100
|
+
description: {
|
|
101
|
+
story:
|
|
102
|
+
"Bypassing the TextField map to show exactly how native `<input>` hooks execute inside the raw wrapper natively perfectly.",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
args: {
|
|
107
|
+
label: "Raw HTML Checkbox",
|
|
108
|
+
formLayout: "side-by-side",
|
|
109
|
+
assistiveText: "This wraps a raw HTML input tag mapping correctly.",
|
|
110
|
+
},
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
112
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
113
|
+
<div
|
|
114
|
+
style={{
|
|
115
|
+
display: "flex",
|
|
116
|
+
gap: "10px",
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{/* We can cleanly wrap even un-styled HTML primitives! */}
|
|
121
|
+
<FormControlWrapper {...args}>
|
|
122
|
+
<input
|
|
123
|
+
type="checkbox"
|
|
124
|
+
style={{ margin: 0, width: "16px", height: "16px" }}
|
|
125
|
+
/>
|
|
126
|
+
</FormControlWrapper>
|
|
127
|
+
</div>
|
|
128
|
+
),
|
|
35
129
|
};
|
|
@@ -1,9 +1,129 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { FormControl, type FormControlProps } from "@mui/material";
|
|
3
|
+
import { filterStylingProps } from "../../utils/filterStylingProps";
|
|
4
|
+
import { Label } from "../Label/Label";
|
|
5
|
+
import { AssistiveElement } from "../AssistiveElement/AssistiveElement";
|
|
6
|
+
import { FormControlLayout } from "../FormControlLayout/FormControlLayout";
|
|
7
|
+
import styles from "./FormControlWrapper.module.css";
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
import { type RecursicaFormControlWrapperProps as BaseRecursicaFormControlWrapperProps } from "@recursica/adapter-common";
|
|
4
10
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
export interface RecursicaFormControlWrapperProps
|
|
12
|
+
extends Omit<FormControlProps, "margin" | "variant" | "size" | "color">,
|
|
13
|
+
BaseRecursicaFormControlWrapperProps {
|
|
14
|
+
overStyled?: boolean;
|
|
15
|
+
label?: React.ReactNode;
|
|
16
|
+
focused?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type FormControlWrapperProps = RecursicaFormControlWrapperProps;
|
|
20
|
+
|
|
21
|
+
export const FormControlWrapper = React.forwardRef<
|
|
22
|
+
HTMLDivElement,
|
|
23
|
+
FormControlWrapperProps
|
|
24
|
+
>(function FormControlWrapper(props, ref) {
|
|
25
|
+
const {
|
|
26
|
+
overStyled = false,
|
|
27
|
+
label,
|
|
28
|
+
assistiveText,
|
|
29
|
+
description,
|
|
30
|
+
helperText,
|
|
31
|
+
assistiveWithIcon = true,
|
|
32
|
+
error,
|
|
33
|
+
required,
|
|
34
|
+
disabled,
|
|
35
|
+
focused,
|
|
36
|
+
id,
|
|
37
|
+
|
|
38
|
+
formLayout = "stacked",
|
|
39
|
+
labelSize = "default",
|
|
40
|
+
labelOptionalText,
|
|
41
|
+
labelWithEditIcon,
|
|
42
|
+
onLabelEditClick,
|
|
43
|
+
labelActionArea,
|
|
44
|
+
|
|
45
|
+
controlMaxWidth,
|
|
46
|
+
controlMinWidth,
|
|
47
|
+
|
|
48
|
+
className,
|
|
49
|
+
style,
|
|
50
|
+
children,
|
|
51
|
+
...rest
|
|
52
|
+
} = props;
|
|
53
|
+
|
|
54
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
55
|
+
|
|
56
|
+
// Map descriptions fallback exactly like Mantine
|
|
57
|
+
const finalAssistiveText = assistiveText || description || helperText;
|
|
58
|
+
|
|
59
|
+
// ARIA Bindings
|
|
60
|
+
const labelId = id ? `${id}-label` : undefined;
|
|
61
|
+
const descriptionId = id ? `${id}-description` : undefined;
|
|
62
|
+
|
|
63
|
+
// Wrap children safely cloning aria hooks down natively
|
|
64
|
+
const mappedChildren = React.Children.map(children, (child) => {
|
|
65
|
+
if (React.isValidElement(child)) {
|
|
66
|
+
return React.cloneElement(child, {
|
|
67
|
+
"aria-labelledby": labelId,
|
|
68
|
+
"aria-describedby": finalAssistiveText ? descriptionId : undefined,
|
|
69
|
+
} as React.HTMLAttributes<HTMLElement>);
|
|
70
|
+
}
|
|
71
|
+
return child;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<FormControl
|
|
76
|
+
ref={ref}
|
|
77
|
+
error={!!error}
|
|
78
|
+
required={required}
|
|
79
|
+
disabled={disabled}
|
|
80
|
+
focused={focused}
|
|
81
|
+
className={className ? `${styles.root} ${className}` : styles.root}
|
|
82
|
+
style={style}
|
|
83
|
+
data-error={error ? "true" : undefined}
|
|
84
|
+
data-disabled={disabled ? "true" : undefined}
|
|
85
|
+
data-focused={focused ? "true" : undefined}
|
|
86
|
+
{...(sanitizedProps as FormControlProps)}
|
|
87
|
+
>
|
|
88
|
+
<FormControlLayout
|
|
89
|
+
formLayout={formLayout}
|
|
90
|
+
labelSize={labelSize}
|
|
91
|
+
controlMaxWidth={controlMaxWidth}
|
|
92
|
+
controlMinWidth={controlMinWidth}
|
|
93
|
+
leftSection={
|
|
94
|
+
label && (
|
|
95
|
+
<Label
|
|
96
|
+
id={labelId}
|
|
97
|
+
htmlFor={id}
|
|
98
|
+
required={required}
|
|
99
|
+
labelOptionalText={labelOptionalText}
|
|
100
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
101
|
+
onLabelEditClick={onLabelEditClick}
|
|
102
|
+
labelActionArea={labelActionArea}
|
|
103
|
+
>
|
|
104
|
+
{label}
|
|
105
|
+
</Label>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
>
|
|
109
|
+
<div className={styles.inputSection}>
|
|
110
|
+
{/* Natively wrap children into flex box */}
|
|
111
|
+
{mappedChildren}
|
|
112
|
+
|
|
113
|
+
{/* Append native assistive block dynamically below the input */}
|
|
114
|
+
{(finalAssistiveText || error) && (
|
|
115
|
+
<AssistiveElement
|
|
116
|
+
id={descriptionId}
|
|
117
|
+
assistiveVariant={error ? "error" : "help"}
|
|
118
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
119
|
+
>
|
|
120
|
+
{error && typeof error === "string" ? error : finalAssistiveText}
|
|
121
|
+
</AssistiveElement>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</FormControlLayout>
|
|
125
|
+
</FormControl>
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
FormControlWrapper.displayName = "FormControlWrapper";
|
|
@@ -17,36 +17,12 @@ const meta: Meta<GroupStoryProps> = {
|
|
|
17
17
|
"Group is a flex horizontal layout container that maps directly to Mantine's Group component allowing safe layout property passing.",
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
|
-
controls: {
|
|
21
|
-
include: [
|
|
22
|
-
"layer",
|
|
23
|
-
"withLayer",
|
|
24
|
-
"gap",
|
|
25
|
-
"alignItems",
|
|
26
|
-
"justifyContent",
|
|
27
|
-
"flexWrap",
|
|
28
|
-
"defaultChecked",
|
|
29
|
-
"rowGap",
|
|
30
|
-
"columnGap",
|
|
31
|
-
"children",
|
|
32
|
-
"component",
|
|
33
|
-
"variant",
|
|
34
|
-
"size",
|
|
35
|
-
"icon",
|
|
36
|
-
"disabled",
|
|
37
|
-
"href",
|
|
38
|
-
"onClick",
|
|
39
|
-
"onChange",
|
|
40
|
-
"value",
|
|
41
|
-
"checked",
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
20
|
},
|
|
45
21
|
args: {
|
|
46
22
|
gap: "rec-default",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
align: "center",
|
|
24
|
+
justify: "flex-start",
|
|
25
|
+
wrap: "wrap",
|
|
50
26
|
},
|
|
51
27
|
argTypes: {
|
|
52
28
|
gap: {
|
|
@@ -67,12 +43,12 @@ const meta: Meta<GroupStoryProps> = {
|
|
|
67
43
|
],
|
|
68
44
|
description: "Gap between elements",
|
|
69
45
|
},
|
|
70
|
-
|
|
46
|
+
align: {
|
|
71
47
|
control: "select",
|
|
72
48
|
options: ["flex-start", "center", "flex-end", "stretch"],
|
|
73
49
|
description: "Align-items property",
|
|
74
50
|
},
|
|
75
|
-
|
|
51
|
+
justify: {
|
|
76
52
|
control: "select",
|
|
77
53
|
options: [
|
|
78
54
|
"flex-start",
|
|
@@ -83,7 +59,7 @@ const meta: Meta<GroupStoryProps> = {
|
|
|
83
59
|
],
|
|
84
60
|
description: "Justify-content property",
|
|
85
61
|
},
|
|
86
|
-
|
|
62
|
+
wrap: {
|
|
87
63
|
control: "select",
|
|
88
64
|
options: ["wrap", "nowrap", "wrap-reverse"],
|
|
89
65
|
description: "Flex-wrap property",
|
|
@@ -4,25 +4,28 @@ import {
|
|
|
4
4
|
type StackProps as MUIStackProps,
|
|
5
5
|
} from "@mui/material";
|
|
6
6
|
import {
|
|
7
|
-
type RecursicaSpacing,
|
|
8
7
|
SPACING_MAP,
|
|
9
8
|
type OmitSx,
|
|
10
9
|
filterSxProp,
|
|
11
10
|
} from "../../utils/filterStylingProps";
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
children?: React.ReactNode;
|
|
15
|
-
gap?: MUIStackProps["spacing"] | RecursicaSpacing;
|
|
16
|
-
rowGap?: MUIStackProps["spacing"] | RecursicaSpacing;
|
|
17
|
-
columnGap?: MUIStackProps["spacing"] | RecursicaSpacing;
|
|
18
|
-
}
|
|
12
|
+
import { type RecursicaGroupProps } from "@recursica/adapter-common";
|
|
19
13
|
|
|
20
14
|
export type GroupProps = OmitSx<
|
|
21
15
|
Omit<MUIStackProps, "spacing" | "direction"> & RecursicaGroupProps
|
|
22
16
|
>;
|
|
23
17
|
|
|
24
18
|
export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
25
|
-
{
|
|
19
|
+
{
|
|
20
|
+
children,
|
|
21
|
+
gap = "rec-default",
|
|
22
|
+
rowGap,
|
|
23
|
+
columnGap,
|
|
24
|
+
justify,
|
|
25
|
+
align,
|
|
26
|
+
wrap,
|
|
27
|
+
...rest
|
|
28
|
+
},
|
|
26
29
|
ref,
|
|
27
30
|
) {
|
|
28
31
|
const safeProps = filterSxProp(rest as Record<string, unknown>);
|
|
@@ -43,7 +46,9 @@ export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
|
43
46
|
<MUIStack
|
|
44
47
|
ref={ref}
|
|
45
48
|
direction="row"
|
|
46
|
-
flexWrap="wrap"
|
|
49
|
+
flexWrap={wrap || "wrap"}
|
|
50
|
+
justifyContent={justify}
|
|
51
|
+
alignItems={align}
|
|
47
52
|
spacing={resolvedGap}
|
|
48
53
|
sx={{
|
|
49
54
|
...(resolvedRowGap ? { rowGap: resolvedRowGap } : {}),
|