@recursica/mui-adapter 0.4.0 → 0.6.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.
Files changed (177) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/mui-adapter.cjs +74 -1
  3. package/dist/mui-adapter.cjs.map +1 -1
  4. package/dist/mui-adapter.css +1 -1
  5. package/dist/mui-adapter.js +6651 -201
  6. package/dist/mui-adapter.js.map +1 -1
  7. package/dist/src/components/Accordion/Accordion.d.ts +63 -2
  8. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +9 -2
  9. package/dist/src/components/Autocomplete/Autocomplete.d.ts +17 -0
  10. package/dist/src/components/Autocomplete/index.d.ts +1 -0
  11. package/dist/src/components/Avatar/Avatar.d.ts +14 -2
  12. package/dist/src/components/Badge/Badge.d.ts +13 -3
  13. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
  14. package/dist/src/components/Button/Button.d.ts +1 -1
  15. package/dist/src/components/Card/Card.d.ts +40 -3
  16. package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
  17. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
  18. package/dist/src/components/Checkbox/index.d.ts +5 -1
  19. package/dist/src/components/Chip/Chip.d.ts +18 -2
  20. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
  21. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
  22. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
  23. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  24. package/dist/src/components/Label/Label.d.ts +13 -2
  25. package/dist/src/components/Link/Link.d.ts +1 -1
  26. package/dist/src/components/Menu/Menu.d.ts +99 -3
  27. package/dist/src/components/Modal/Modal.d.ts +36 -2
  28. package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
  29. package/dist/src/components/Pagination/Pagination.d.ts +44 -2
  30. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  31. package/dist/src/components/Panel/Panel.d.ts +65 -3
  32. package/dist/src/components/Radio/Radio.d.ts +13 -3
  33. package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
  34. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  35. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
  36. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  37. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  38. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  39. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  40. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
  41. package/dist/src/components/Slider/Slider.d.ts +28 -2
  42. package/dist/src/components/Stepper/Stepper.d.ts +21 -2
  43. package/dist/src/components/Switch/Switch.d.ts +10 -3
  44. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  45. package/dist/src/components/Tabs/Tabs.d.ts +35 -3
  46. package/dist/src/components/Text/Text.d.ts +1 -1
  47. package/dist/src/components/TextArea/TextArea.d.ts +18 -2
  48. package/dist/src/components/TextField/TextField.d.ts +16 -2
  49. package/dist/src/components/Timeline/Timeline.d.ts +14 -2
  50. package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
  51. package/dist/src/components/Title/Title.d.ts +1 -1
  52. package/dist/src/components/Toast/Toast.d.ts +25 -2
  53. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  54. package/dist/src/components/Typography/Typography.d.ts +1 -1
  55. package/dist/src/components/index.d.ts +1 -0
  56. package/package.json +12 -2
  57. package/src/GlobalExemptions.modules.css +13 -0
  58. package/src/components/Accordion/Accordion.module.css +295 -0
  59. package/src/components/Accordion/Accordion.stories.tsx +149 -21
  60. package/src/components/Accordion/Accordion.tsx +264 -4
  61. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  62. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
  63. package/src/components/AssistiveElement/AssistiveElement.tsx +85 -4
  64. package/src/components/Autocomplete/Autocomplete.module.css +325 -0
  65. package/src/components/Autocomplete/Autocomplete.stories.tsx +196 -0
  66. package/src/components/Autocomplete/Autocomplete.tsx +201 -0
  67. package/src/components/Autocomplete/index.ts +1 -0
  68. package/src/components/Avatar/Avatar.module.css +344 -0
  69. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  70. package/src/components/Avatar/Avatar.tsx +110 -5
  71. package/src/components/Badge/Badge.module.css +127 -0
  72. package/src/components/Badge/Badge.stories.tsx +65 -22
  73. package/src/components/Badge/Badge.tsx +70 -5
  74. package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
  75. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  76. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -5
  77. package/src/components/Button/Button.module.css +113 -20
  78. package/src/components/Button/Button.stories.tsx +3 -0
  79. package/src/components/Button/Button.tsx +10 -3
  80. package/src/components/Card/Card.module.css +118 -0
  81. package/src/components/Card/Card.stories.tsx +117 -19
  82. package/src/components/Card/Card.tsx +179 -4
  83. package/src/components/Checkbox/Checkbox.module.css +232 -0
  84. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  85. package/src/components/Checkbox/Checkbox.tsx +146 -4
  86. package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
  87. package/src/components/Checkbox/index.ts +6 -1
  88. package/src/components/Chip/Chip.module.css +236 -0
  89. package/src/components/Chip/Chip.stories.tsx +101 -21
  90. package/src/components/Chip/Chip.tsx +138 -5
  91. package/src/components/DatePicker/DatePicker.module.css +42 -0
  92. package/src/components/DatePicker/DatePicker.tsx +1 -0
  93. package/src/components/Dropdown/Dropdown.module.css +296 -0
  94. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  95. package/src/components/Dropdown/Dropdown.tsx +182 -5
  96. package/src/components/FileInput/FileInput.module.css +47 -0
  97. package/src/components/FileInput/FileInput.tsx +1 -0
  98. package/src/components/FileUpload/FileUpload.module.css +40 -0
  99. package/src/components/FileUpload/FileUpload.tsx +1 -0
  100. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  101. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
  102. package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
  103. package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
  104. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
  105. package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
  106. package/src/components/HoverCard/HoverCard.module.css +91 -0
  107. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  108. package/src/components/HoverCard/HoverCard.tsx +150 -4
  109. package/src/components/Label/Label.module.css +136 -0
  110. package/src/components/Label/Label.stories.tsx +98 -17
  111. package/src/components/Label/Label.tsx +88 -4
  112. package/src/components/Link/Link.module.css +8 -0
  113. package/src/components/Menu/Menu.module.css +287 -0
  114. package/src/components/Menu/Menu.stories.tsx +307 -21
  115. package/src/components/Menu/Menu.tsx +318 -4
  116. package/src/components/Modal/Modal.module.css +172 -0
  117. package/src/components/Modal/Modal.stories.tsx +66 -22
  118. package/src/components/Modal/Modal.tsx +216 -3
  119. package/src/components/NumberInput/NumberInput.module.css +281 -0
  120. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  121. package/src/components/NumberInput/NumberInput.tsx +148 -5
  122. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  123. package/src/components/Pagination/Pagination.module.css +307 -0
  124. package/src/components/Pagination/Pagination.stories.tsx +42 -21
  125. package/src/components/Pagination/Pagination.tsx +193 -4
  126. package/src/components/Panel/Panel.module.css +206 -0
  127. package/src/components/Panel/Panel.stories.tsx +214 -22
  128. package/src/components/Panel/Panel.tsx +153 -4
  129. package/src/components/Radio/Radio.module.css +220 -0
  130. package/src/components/Radio/Radio.stories.tsx +77 -19
  131. package/src/components/Radio/Radio.tsx +178 -4
  132. package/src/components/Radio/RadioGroup.tsx +111 -0
  133. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  134. package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
  135. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
  136. package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -4
  137. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  138. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  139. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  140. package/src/components/ReadOnlyField/index.ts +1 -0
  141. package/src/components/SegmentedControl/SegmentedControl.module.css +203 -0
  142. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  143. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -5
  144. package/src/components/Slider/Slider.module.css +542 -0
  145. package/src/components/Slider/Slider.stories.tsx +157 -19
  146. package/src/components/Slider/Slider.tsx +282 -4
  147. package/src/components/Stepper/Stepper.module.css +410 -0
  148. package/src/components/Stepper/Stepper.stories.tsx +132 -19
  149. package/src/components/Stepper/Stepper.tsx +145 -5
  150. package/src/components/Switch/Switch.module.css +238 -0
  151. package/src/components/Switch/Switch.stories.tsx +98 -19
  152. package/src/components/Switch/Switch.tsx +153 -5
  153. package/src/components/Switch/SwitchGroup.tsx +111 -0
  154. package/src/components/Tabs/Tabs.module.css +422 -0
  155. package/src/components/Tabs/Tabs.stories.tsx +138 -19
  156. package/src/components/Tabs/Tabs.tsx +103 -5
  157. package/src/components/TextArea/TextArea.module.css +160 -0
  158. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  159. package/src/components/TextArea/TextArea.tsx +156 -5
  160. package/src/components/TextField/TextField.module.css +266 -0
  161. package/src/components/TextField/TextField.stories.tsx +168 -15
  162. package/src/components/TextField/TextField.tsx +171 -5
  163. package/src/components/TimePicker/TimePicker.module.css +41 -0
  164. package/src/components/TimePicker/TimePicker.tsx +1 -0
  165. package/src/components/Timeline/Timeline.module.css +367 -0
  166. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  167. package/src/components/Timeline/Timeline.tsx +80 -4
  168. package/src/components/Timeline/TimelineItem.tsx +101 -0
  169. package/src/components/Toast/Toast.module.css +170 -0
  170. package/src/components/Toast/Toast.stories.tsx +54 -22
  171. package/src/components/Toast/Toast.tsx +90 -4
  172. package/src/components/Tooltip/Tooltip.module.css +90 -0
  173. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  174. package/src/components/Tooltip/Tooltip.tsx +114 -4
  175. package/src/components/TransferList/TransferList.module.css +38 -0
  176. package/src/components/TransferList/TransferList.tsx +1 -0
  177. package/src/components/index.ts +1 -0
@@ -0,0 +1,40 @@
1
+ /* EXEMPTIONS:
2
+ This component is a placeholder stub. Once fully implemented, its specific
3
+ tokens will be fully wired up to direct styles. */
4
+
5
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_border-radius */
6
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_border-style */
7
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_item-gap */
8
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_list-spacing */
9
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_padding */
10
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_font-family */
11
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_font-size */
12
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_font-style */
13
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_font-weight */
14
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_letter-spacing */
15
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_line-height */
16
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_text-decoration */
17
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_text_text-transform */
18
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_vertical-element-gap */
19
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_layouts_side-by-side_properties_top-bottom-margin */
20
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_layouts_stacked_properties_top-bottom-margin */
21
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_default_properties_border-size */
22
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_default_properties_colors_background */
23
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_default_properties_colors_border-color */
24
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_default_properties_colors_text */
25
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_default_properties_colors_upload-icon */
26
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_disabled_properties_border-size */
27
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_disabled_properties_colors_background */
28
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_disabled_properties_colors_border-color */
29
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_disabled_properties_colors_text */
30
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_disabled_properties_colors_upload-icon */
31
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_error_properties_border-size */
32
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_error_properties_colors_background */
33
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_error_properties_colors_border-color */
34
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_error_properties_colors_text */
35
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_error_properties_colors_upload-icon */
36
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_focus_properties_border-size */
37
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_focus_properties_colors_background */
38
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_focus_properties_colors_border-color */
39
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_focus_properties_colors_text */
40
+ /* recursica-ignore: --recursica_ui-kit_components_file-upload_variants_states_focus_properties_colors_upload-icon */
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import "./FileUpload.module.css";
2
3
 
3
4
  export type FileUploadProps = React.HTMLAttributes<HTMLDivElement>;
4
5
 
@@ -0,0 +1,92 @@
1
+ .root {
2
+ /* Recursica Reset Boundary */
3
+ box-sizing: border-box;
4
+ display: flex;
5
+ margin: 0; /* Hardcoded structural reset */
6
+ width: 100%;
7
+
8
+ /* Recursica Layout Overrides mapped down dynamically from components */
9
+ padding: 0; /* Hardcoded structural reset */
10
+ max-width: var(--form-control-max-width);
11
+ min-width: var(--form-control-min-width);
12
+ }
13
+
14
+ /* =========================================
15
+ LAYOUT MODES
16
+ ========================================= */
17
+
18
+ .root[data-form-layout="stacked"] {
19
+ flex-direction: column;
20
+ }
21
+
22
+ .root[data-form-layout="side-by-side"] {
23
+ /* Aligns the Input/Component identically horizontally beside the label mapping */
24
+ flex-direction: row;
25
+ align-items: flex-start;
26
+ flex-wrap: wrap; /* Allows collapse if screen is too thin, though standard flex width should hold */
27
+ }
28
+
29
+ /* =========================================
30
+ GRID SECTIONS
31
+ ========================================= */
32
+
33
+ .leftSection {
34
+ flex-shrink: 0;
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+
39
+ /*
40
+ Stacked margins natively apply spacing *below* the label
41
+ */
42
+ .root[data-form-layout="stacked"] .leftSection {
43
+ margin-bottom: var(
44
+ --recursica_ui-kit_globals_form_properties_label-field-gap-vertical
45
+ );
46
+ }
47
+
48
+ /*
49
+ Side-by-side margins natively apply spacing *right* of the label
50
+ and forces vertical padding to align the label perfectly with 1st line of input text
51
+ */
52
+ .root[data-form-layout="side-by-side"] .leftSection {
53
+ margin-right: var(
54
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_gutter
55
+ );
56
+ padding-top: var(
57
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_vertical-padding
58
+ );
59
+ min-height: var(
60
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_min-height
61
+ );
62
+ }
63
+
64
+ /* Label column sizing hooks (Only applies functionally in side-by-side mode) */
65
+ .root[data-form-layout="side-by-side"] .leftSection[data-label-size="small"] {
66
+ width: var(
67
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_small_properties_width
68
+ );
69
+ }
70
+
71
+ .root[data-form-layout="side-by-side"] .leftSection[data-label-size="default"] {
72
+ width: var(
73
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_default_properties_width
74
+ );
75
+ }
76
+
77
+ .root[data-form-layout="side-by-side"] .leftSection[data-label-size="md"] {
78
+ /* No 'medium' defined explicitly in generated token scope, mapped fallback to default */
79
+ width: var(
80
+ --recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_default_properties_width
81
+ );
82
+ }
83
+
84
+ /*
85
+ Right Section dynamically wraps the input element natively
86
+ */
87
+ .rightSection {
88
+ display: flex; /* Groups internal field alongside subsequent errors/descriptions */
89
+ flex-direction: column;
90
+ flex: 1; /* Consumes the remaining x-axis in grid columns natively */
91
+ min-width: 0; /* Break boundary preventing internal flex explosion */
92
+ }
@@ -1,28 +1,40 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from "react";
1
3
  import type { Meta, StoryObj } from "@storybook/react";
2
4
  import { FormControlLayout } from "./FormControlLayout";
3
- import { ComingSoon } from "@recursica/storybook-template";
5
+ import { Switch } from "../Switch/Switch";
4
6
 
5
7
  const meta: Meta<typeof FormControlLayout> = {
6
- title: "UI-Kit/🚧 FormControlLayout",
8
+ title: "UI-Kit/FormControlLayout",
7
9
  component: FormControlLayout,
8
- tags: ["autodocs"],
9
10
  parameters: {
11
+ layout: "padded",
12
+ docs: {
13
+ description: {
14
+ component:
15
+ "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.",
16
+ },
17
+ },
10
18
  controls: {
11
- include: [
12
- "layer",
13
- "withLayer",
14
- "children",
15
- "component",
16
- "variant",
17
- "size",
18
- "icon",
19
- "disabled",
20
- "href",
21
- "onClick",
22
- "onChange",
23
- "value",
24
- "checked",
25
- ],
19
+ include: ["formLayout", "labelSize"],
20
+ },
21
+ },
22
+ tags: ["autodocs"],
23
+ argTypes: {
24
+ formLayout: {
25
+ control: "radio",
26
+ options: ["stacked", "side-by-side"],
27
+ },
28
+ labelSize: {
29
+ control: "radio",
30
+ options: ["default", "small", "md"],
31
+ description: "Dictates the physical width of the left column.",
32
+ },
33
+ children: {
34
+ table: { disable: true },
35
+ },
36
+ leftSection: {
37
+ table: { disable: true },
26
38
  },
27
39
  },
28
40
  };
@@ -30,6 +42,49 @@ const meta: Meta<typeof FormControlLayout> = {
30
42
  export default meta;
31
43
  type Story = StoryObj<typeof FormControlLayout>;
32
44
 
45
+ /**
46
+ * The Default layout demonstrates wrapping a standalone primitive without a Label.
47
+ * In a stacked layout, an omitted leftSection naturally results in no structural padding.
48
+ */
33
49
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="FormControlLayout" />,
50
+ args: {
51
+ formLayout: "stacked",
52
+ labelSize: "default",
53
+ leftSection: (
54
+ <div
55
+ style={{ padding: 8, border: "1px dashed #ccc", background: "#fafafa" }}
56
+ >
57
+ Left Section Boundary
58
+ </div>
59
+ ),
60
+ children: <Switch label="Input area content" />,
61
+ },
62
+ render: ({ ...args }: any) => <FormControlLayout {...args} />,
63
+ };
64
+
65
+ /**
66
+ * Demonstrates the stacked layout without a left section.
67
+ * The input should natively pull flush to the top left since there is no left column.
68
+ */
69
+ export const StackedLayout: Story = {
70
+ args: {
71
+ formLayout: "stacked",
72
+ labelSize: "default",
73
+ children: <Switch label="Flush stacked switch" />,
74
+ },
75
+ render: ({ ...args }: any) => <FormControlLayout {...args} />,
76
+ };
77
+
78
+ /**
79
+ * Demonstrates how a naked boolean primitive perfectly aligns in a side-by-side layout
80
+ * by utilizing the layout wrapper. The left column maintains its precise design system width
81
+ * even when `leftSection` is undefined, forcing the component into the correct form column!
82
+ */
83
+ export const SideBySideLayout: Story = {
84
+ args: {
85
+ formLayout: "side-by-side",
86
+ labelSize: "default",
87
+ children: <Switch label="Offset switch aligning with grid" />,
88
+ },
89
+ render: ({ ...args }: any) => <FormControlLayout {...args} />,
35
90
  };
@@ -1,7 +1,61 @@
1
1
  import React from "react";
2
+ import { filterStylingProps } from "../../utils/filterStylingProps";
3
+ import styles from "./FormControlLayout.module.css";
2
4
 
3
- export type FormControlLayoutProps = React.HTMLAttributes<HTMLDivElement>;
5
+ export interface FormControlLayoutProps
6
+ extends React.HTMLAttributes<HTMLDivElement> {
7
+ overStyled?: boolean;
8
+ formLayout?: "stacked" | "side-by-side";
9
+ labelSize?: "small" | "default" | "md";
10
+ controlMaxWidth?: string;
11
+ controlMinWidth?: string;
12
+ leftSection?: React.ReactNode;
13
+ children: React.ReactNode;
14
+ }
4
15
 
5
- export const FormControlLayout: React.FC<FormControlLayoutProps> = (props) => {
6
- return <div {...props}>FormControlLayout</div>;
7
- };
16
+ export const FormControlLayout = React.forwardRef<
17
+ HTMLDivElement,
18
+ FormControlLayoutProps
19
+ >(function FormControlLayout(props, ref) {
20
+ const {
21
+ overStyled = false,
22
+ formLayout = "stacked",
23
+ labelSize = "default",
24
+ leftSection,
25
+ className,
26
+ children,
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
+ {...sanitizedProps}
38
+ >
39
+ {/*
40
+ The left section strictly enforces the Label boundary sizes
41
+ We render it unconditionally if side-by-side to preserve the grid column layout
42
+ */}
43
+ {formLayout === "side-by-side" ? (
44
+ <div className={styles.leftSection} data-label-size={labelSize}>
45
+ {leftSection}
46
+ </div>
47
+ ) : (
48
+ leftSection && (
49
+ <div className={styles.leftSection} data-label-size={labelSize}>
50
+ {leftSection}
51
+ </div>
52
+ )
53
+ )}
54
+
55
+ {/* The main content area natively flows into the remaining space */}
56
+ <div className={styles.rightSection}>{children}</div>
57
+ </div>
58
+ );
59
+ });
60
+
61
+ FormControlLayout.displayName = "FormControlLayout";
@@ -0,0 +1,96 @@
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
+ .inputContainer {
78
+ display: flex;
79
+ flex-direction: column;
80
+ flex: 1;
81
+ min-width: 0;
82
+
83
+ /* Constrain the input natively to custom properties */
84
+ max-width: var(--form-control-max-width);
85
+ min-width: var(--form-control-min-width);
86
+
87
+ /* Hook to allow child components to add margin below themselves dynamically
88
+ via CSS variables (e.g. padding beneath TextField before AssistiveText renders) */
89
+ margin-bottom: var(--form-control-margin-bottom);
90
+ }
91
+
92
+ .assistive {
93
+ margin-top: var(
94
+ --recursica_ui-kit_globals_form_properties_label-field-gap-vertical
95
+ );
96
+ }
@@ -1,35 +1,138 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from "react";
1
3
  import type { Meta, StoryObj } from "@storybook/react";
2
- import { FormControlWrapper } from "./FormControlWrapper";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
+ import {
5
+ FormControlWrapper,
6
+ type FormControlWrapperProps,
7
+ } from "./FormControlWrapper";
8
+ import { TextField } from "../TextField/TextField";
9
+ import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
4
10
 
5
- const meta: Meta<typeof FormControlWrapper> = {
6
- title: "UI-Kit/🚧 FormControlWrapper",
11
+ type WrapperStoryProps = FormControlWrapperProps;
12
+
13
+ const meta: Meta<WrapperStoryProps> = {
14
+ title: "UI-Kit/FormControlWrapper",
7
15
  component: FormControlWrapper,
8
16
  tags: ["autodocs"],
9
17
  parameters: {
18
+ docs: {
19
+ description: {
20
+ component:
21
+ "The `FormControlWrapper` is the ultimate structural replacement for built-in wrapper layouts. By centralizing label tracking, error rendering, ARIA generation, and grid layouts natively inside this single component, we achieve pixel-perfect layout compliance while retaining MUI's internal `useFormControl` Context natively.\n\n### Usage with Naked Primitives\nThis component wraps 'naked' elements directly. The demonstration stories below utilize `<TextField>` as a native display vehicle, since `<TextField>` natively pipes all its properties structurally back into this wrapper.",
22
+ },
23
+ },
10
24
  controls: {
11
25
  include: [
12
- "layer",
13
- "withLayer",
14
- "children",
15
- "component",
16
- "variant",
17
- "size",
18
- "icon",
19
- "disabled",
20
- "href",
21
- "onClick",
22
- "onChange",
23
- "value",
24
- "checked",
26
+ "error",
27
+ "assistiveText",
28
+ "assistiveWithIcon",
29
+ "required",
30
+ "formLayout",
31
+ "labelSize",
32
+ "labelAlignment",
33
+ "labelOptionalText",
34
+ "labelWithEditIcon",
35
+ "labelActionArea",
25
36
  ],
26
37
  },
27
38
  },
39
+ argTypes: {
40
+ ...formControlArgTypes,
41
+ error: {
42
+ control: "text",
43
+ description:
44
+ "Error string driving native assistive component and validation markers.",
45
+ },
46
+ assistiveText: {
47
+ control: "text",
48
+ description:
49
+ "Helper instructions safely dynamically anchored below the input box.",
50
+ },
51
+ assistiveWithIcon: {
52
+ control: "boolean",
53
+ },
54
+ required: {
55
+ control: "boolean",
56
+ },
57
+ },
28
58
  };
29
59
 
30
60
  export default meta;
31
- type Story = StoryObj<typeof FormControlWrapper>;
61
+
62
+ type Story = StoryObj<WrapperStoryProps>;
63
+
64
+ const renderWithTextField = ({ ...args }: any) => (
65
+ // We explicitly cast args to 'any' here because MUI wrapper defines Wrapper anatomy,
66
+ // but TextField expects them for the full TextInput anatomy, causing a TS intersection mismatch.
67
+ <TextField placeholder="Form Control primitive mapped..." {...args} />
68
+ );
32
69
 
33
70
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="FormControlWrapper" />,
71
+ args: {
72
+ label: "Account Username",
73
+ formLayout: "stacked",
74
+ assistiveText: "Validation occurs immediately natively.",
75
+ },
76
+ render: renderWithTextField,
77
+ };
78
+
79
+ export const VisualErrorState: Story = {
80
+ args: {
81
+ label: "Encryption Protocol",
82
+ formLayout: "stacked",
83
+ error: "Strict validation limits reached. Handshake rejected securely.",
84
+ },
85
+ render: renderWithTextField,
86
+ };
87
+
88
+ export const RequiredArchitecture: Story = {
89
+ args: {
90
+ label: "Root Password",
91
+ formLayout: "side-by-side",
92
+ required: true,
93
+ assistiveText: "Bypass string structure required to initiate protocol.",
94
+ },
95
+ render: renderWithTextField,
96
+ };
97
+
98
+ export const WithoutAssistiveIcons: Story = {
99
+ args: {
100
+ label: "Server Domain",
101
+ assistiveText:
102
+ "A standard text boundary without default native icon parameters bounding.",
103
+ assistiveWithIcon: false,
104
+ },
105
+ render: renderWithTextField,
106
+ };
107
+
108
+ export const NativeChildrenDirectly: Story = {
109
+ parameters: {
110
+ docs: {
111
+ description: {
112
+ story:
113
+ "Bypassing the TextField map to show exactly how native `<input>` hooks execute inside the raw wrapper natively perfectly.",
114
+ },
115
+ },
116
+ },
117
+ args: {
118
+ label: "Raw HTML Checkbox",
119
+ formLayout: "side-by-side",
120
+ assistiveText: "This wraps a raw HTML input tag mapping correctly.",
121
+ },
122
+ render: ({ ...args }: any) => (
123
+ <div
124
+ style={{
125
+ display: "flex",
126
+ gap: "10px",
127
+ alignItems: "center",
128
+ }}
129
+ >
130
+ <FormControlWrapper {...args}>
131
+ <input
132
+ type="checkbox"
133
+ style={{ margin: 0, width: "16px", height: "16px" }}
134
+ />
135
+ </FormControlWrapper>
136
+ </div>
137
+ ),
35
138
  };