@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,220 @@
1
+ /**
2
+ * Radio.module.css
3
+ *
4
+ * HARDCODED VALUES:
5
+ * - margin / padding: 0 — Structural resets; consider token if spacing needed.
6
+ * - cursor: pointer / not-allowed — Baseline interactive indications.
7
+ * - appearance / box-sizing / border-style — necessary normalization for inputs.
8
+ * - transition: all 0.2s ease — Animation curve; consider a transition token.
9
+ * - min-width: 0 — Forces text wrap boundary in flex children.
10
+ */
11
+
12
+ /* --- GROUP STYLES --- */
13
+ .groupRoot {
14
+ padding: var(
15
+ --recursica_ui-kit_components_radio-button-group_properties_padding,
16
+ 0
17
+ );
18
+ margin: 0; /* HARDCODE: structural reset; consider token if layout needs change */
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(
22
+ --recursica_ui-kit_components_radio-button-group_properties_item-gap,
23
+ 16px
24
+ );
25
+ }
26
+
27
+ /* SideBySide Variant */
28
+ .groupRoot[data-layout="side-by-side"] {
29
+ flex-direction: row;
30
+ flex-wrap: wrap;
31
+ gap: var(
32
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_gutter
33
+ );
34
+ padding-top: var(
35
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_vertical-padding
36
+ );
37
+ padding-bottom: var(
38
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_vertical-padding
39
+ );
40
+ margin-top: var(
41
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_top-bottom-margin
42
+ );
43
+ margin-bottom: var(
44
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_top-bottom-margin
45
+ );
46
+ }
47
+
48
+ /* Stacked Variant */
49
+ .groupRoot[data-layout="stacked"] {
50
+ flex-direction: column;
51
+ margin-top: var(
52
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_label-field-gap,
53
+ var(
54
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_top-bottom-margin
55
+ )
56
+ );
57
+ margin-bottom: var(
58
+ --recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_top-bottom-margin
59
+ );
60
+ }
61
+
62
+ /* --- PRIMITIVE RADIO STYLES --- */
63
+
64
+ .root {
65
+ max-width: var(
66
+ --recursica_ui-kit_components_radio-button-item_properties_max-width
67
+ );
68
+ }
69
+
70
+ .body {
71
+ display: flex;
72
+ align-items: flex-start;
73
+ gap: var(
74
+ --recursica_ui-kit_components_radio-button-item_properties_label-gap
75
+ );
76
+ }
77
+
78
+ .root .inner {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ margin: 0; /* HARDCODE: structural reset */
83
+ width: var(--recursica_ui-kit_components_radio-button_properties_size);
84
+ height: var(--recursica_ui-kit_components_radio-button_properties_size);
85
+ /* Apply font-size to this element so that an 'em' based line-height token evaluates to the correct exact pixel value. */
86
+ font-size: var(
87
+ --recursica_ui-kit_components_radio-button-item_properties_text_font-size
88
+ );
89
+ /* Mathematically syncs the physical input radio bounding block visually with the center of the text's first line.
90
+ NOTE: Since the Figma line-height token resolves to an 'em' length (e.g. 1.05em), we CANNOT multiply it by font-size in CSS.
91
+ Instead, it naturally evaluates to the correct height because we applied the font-size above. */
92
+ margin-top: calc(
93
+ (
94
+ var(
95
+ --recursica_ui-kit_components_radio-button-item_properties_text_line-height
96
+ ) -
97
+ var(--recursica_ui-kit_components_radio-button_properties_size)
98
+ ) /
99
+ 2
100
+ );
101
+ }
102
+
103
+ /* Base explicit reset masking underlying framework defaults */
104
+ .root .radio {
105
+ cursor: pointer; /* HARDCODE: core interaction behavior */
106
+ appearance: none; /* HARDCODE: reset native widget */
107
+ box-sizing: border-box; /* HARDCODE: ensures size includes borders */
108
+ width: var(--recursica_ui-kit_components_radio-button_properties_size);
109
+ height: var(--recursica_ui-kit_components_radio-button_properties_size);
110
+ border-radius: var(
111
+ --recursica_ui-kit_components_radio-button_properties_border-radius
112
+ );
113
+ border-width: var(
114
+ --recursica_ui-kit_components_radio-button_properties_border-size
115
+ );
116
+ border-style: solid; /* HARDCODE: normalize primitive border-style */
117
+
118
+ /* Standard unchecked state */
119
+ background-color: var(
120
+ --recursica_ui-kit_components_radio-button_properties_colors_background-unselected
121
+ );
122
+ border-color: var(
123
+ --recursica_ui-kit_components_radio-button_properties_colors_border-unselected
124
+ );
125
+ transition: all 0.2s ease; /* HARDCODE: consider global transition token */
126
+ }
127
+
128
+ /* Checked State */
129
+ .root .radio:checked {
130
+ background-color: var(
131
+ --recursica_ui-kit_components_radio-button_properties_colors_background-selected
132
+ );
133
+ border-color: var(
134
+ --recursica_ui-kit_components_radio-button_properties_colors_border-selected
135
+ );
136
+ }
137
+
138
+ /* Disabled State */
139
+ .root .radio:disabled {
140
+ cursor: not-allowed; /* HARDCODE: disabled state default */
141
+ opacity: var(
142
+ --recursica_ui-kit_components_radio-button_properties_disabled-opacity
143
+ );
144
+ background-color: var(
145
+ --recursica_ui-kit_components_radio-button_properties_colors_disabled-background
146
+ );
147
+ border-color: var(
148
+ --recursica_ui-kit_components_radio-button_properties_colors_disabled-border
149
+ );
150
+ }
151
+
152
+ .root .icon {
153
+ position: absolute;
154
+ inset: 0;
155
+ margin: auto;
156
+ pointer-events: none;
157
+ width: var(--recursica_ui-kit_components_radio-button_properties_icon-size);
158
+ height: var(--recursica_ui-kit_components_radio-button_properties_icon-size);
159
+ color: var(
160
+ --recursica_ui-kit_components_radio-button_properties_colors_icon-color
161
+ );
162
+ }
163
+ .root .radio:disabled + .iconWrapper .icon,
164
+ .root .radio:disabled + .icon {
165
+ color: var(
166
+ --recursica_ui-kit_components_radio-button_properties_colors_disabled-icon
167
+ );
168
+ }
169
+
170
+ .root .labelWrapper {
171
+ padding: 0; /* HARDCODE: structural layout reset */
172
+ margin: 0; /* HARDCODE: structural layout reset */
173
+ flex: 1;
174
+ min-width: 0; /* HARDCODE: forces text wrap boundary in flex child */
175
+ }
176
+
177
+ .root .label {
178
+ cursor: pointer; /* HARDCODE: interactive affordance */
179
+ padding: 0; /* HARDCODE: reset layout */
180
+ margin: 0; /* HARDCODE: reset layout */
181
+ word-wrap: break-word;
182
+ white-space: normal;
183
+ color: var(
184
+ --recursica_ui-kit_components_radio-button-item_properties_colors_text
185
+ );
186
+ font-family: var(
187
+ --recursica_ui-kit_components_radio-button-item_properties_text_font-family
188
+ );
189
+ font-size: var(
190
+ --recursica_ui-kit_components_radio-button-item_properties_text_font-size
191
+ );
192
+ font-weight: var(
193
+ --recursica_ui-kit_components_radio-button-item_properties_text_font-weight
194
+ );
195
+ line-height: var(
196
+ --recursica_ui-kit_components_radio-button-item_properties_text_line-height
197
+ );
198
+ font-style: var(
199
+ --recursica_ui-kit_components_radio-button-item_properties_text_font-style
200
+ );
201
+ letter-spacing: var(
202
+ --recursica_ui-kit_components_radio-button-item_properties_text_letter-spacing
203
+ );
204
+ text-decoration: var(
205
+ --recursica_ui-kit_components_radio-button-item_properties_text_text-decoration
206
+ );
207
+ text-transform: var(
208
+ --recursica_ui-kit_components_radio-button-item_properties_text_text-transform
209
+ );
210
+ }
211
+ .root:has(input:disabled) .label {
212
+ cursor: not-allowed; /* HARDCODE: disabled interaction */
213
+ color: var(
214
+ --recursica_ui-kit_components_radio-button-item_properties_colors_disabled-text
215
+ );
216
+ opacity: var(
217
+ --recursica_ui-kit_components_radio-button-item_properties_disabled-opacity,
218
+ var(--recursica_ui-kit_components_radio-button_properties_disabled-opacity)
219
+ );
220
+ }
@@ -1,35 +1,93 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Radio } from "./Radio";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
3
 
5
4
  const meta: Meta<typeof Radio> = {
6
- title: "UI-Kit/🚧 Radio",
5
+ title: "UI-Kit/Radio",
7
6
  component: Radio,
8
7
  tags: ["autodocs"],
9
8
  parameters: {
10
- 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
- ],
9
+ docs: {
10
+ description: {
11
+ component: `
12
+ The \`Radio\` component represents a fundamental standalone boolean selection node mapping to the native HTML \`<input type="radio">\`.
13
+
14
+ > [!WARNING]
15
+ > This component represents the isolated primitive structure. If you need macro form configurations, standard vertical alignments, side-by-side array wrappers, or integrated assistive text and error borders natively, you MUST use the \`<Radio.Group>\` orchestrator instead!
16
+ `,
17
+ },
18
+ },
19
+ },
20
+ argTypes: {
21
+ disabled: {
22
+ control: "boolean",
23
+ },
24
+ checked: {
25
+ control: "boolean",
26
+ },
27
+ readOnly: {
28
+ control: "boolean",
29
+ },
30
+ error: {
31
+ control: "boolean",
26
32
  },
33
+ defaultChecked: {
34
+ control: "boolean",
35
+ },
36
+ controlMaxWidth: { table: { disable: true } },
37
+ controlMinWidth: { table: { disable: true } },
27
38
  },
28
39
  };
29
40
 
30
41
  export default meta;
42
+
31
43
  type Story = StoryObj<typeof Radio>;
32
44
 
33
45
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Radio" />,
46
+ args: {
47
+ label: "Standard Radio Primitive",
48
+ },
49
+ };
50
+
51
+ export const SideBySideLayout: Story = {
52
+ args: {
53
+ label: "Opt-in form alignment",
54
+ formLayout: "side-by-side",
55
+ },
56
+ };
57
+
58
+ export const CheckedState: Story = {
59
+ args: {
60
+ ...Default.args,
61
+ defaultChecked: true,
62
+ },
63
+ };
64
+
65
+ export const DisabledUnchecked: Story = {
66
+ args: {
67
+ ...Default.args,
68
+ disabled: true,
69
+ },
70
+ };
71
+
72
+ export const DisabledChecked: Story = {
73
+ args: {
74
+ ...Default.args,
75
+ disabled: true,
76
+ defaultChecked: true,
77
+ },
78
+ };
79
+
80
+ export const ReadOnly: Story = {
81
+ args: {},
82
+ render: () => (
83
+ <div
84
+ style={{
85
+ display: "flex",
86
+ flexDirection: "column",
87
+ gap: "24px",
88
+ }}
89
+ >
90
+ <Radio label="Account Type" defaultChecked readOnly />
91
+ </div>
92
+ ),
35
93
  };
@@ -1,7 +1,181 @@
1
- import React from "react";
1
+ import { forwardRef } from "react";
2
+ import {
3
+ Radio as MuiRadio,
4
+ type RadioProps as MuiRadioProps,
5
+ FormControlLabel,
6
+ } from "@mui/material";
7
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
8
+ import { RadioGroup } from "./RadioGroup";
9
+ import {
10
+ filterStylingProps,
11
+ type RecursicaOverStyled,
12
+ } from "../../utils/filterStylingProps";
13
+ import { type RequireAccessibleLabel } from "../../utils/RequireAccessibleLabel";
14
+ import {
15
+ FormControlLayout,
16
+ type FormControlLayoutProps,
17
+ } from "../FormControlLayout/FormControlLayout";
2
18
 
3
- export type RadioProps = React.HTMLAttributes<HTMLDivElement>;
19
+ import styles from "./Radio.module.css";
4
20
 
5
- export const Radio: React.FC<RadioProps> = (props) => {
6
- return <div {...props}>Radio</div>;
21
+ const RadioIcon: React.FC<{
22
+ className?: string;
23
+ style?: React.CSSProperties;
24
+ }> = ({ className, style }) => (
25
+ <svg
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ viewBox="0 0 16 16"
28
+ fill="currentColor"
29
+ className={className}
30
+ style={style}
31
+ >
32
+ <circle cx="8" cy="8" r="5" />
33
+ </svg>
34
+ );
35
+
36
+ export type RecursicaRadioProps = RequireAccessibleLabel<
37
+ Omit<MuiRadioProps, "size" | "color"> &
38
+ ReadOnlyControlProps &
39
+ Pick<
40
+ FormControlLayoutProps,
41
+ "formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
42
+ >
43
+ >;
44
+
45
+ export type RadioProps = RecursicaOverStyled<RecursicaRadioProps>;
46
+
47
+ type RadioComponent = React.ForwardRefExoticComponent<
48
+ RadioProps & React.RefAttributes<HTMLInputElement>
49
+ > & {
50
+ Group: typeof RadioGroup;
7
51
  };
52
+
53
+ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
54
+ function Radio(props, ref) {
55
+ const {
56
+ overStyled = false,
57
+ readOnly,
58
+ readOnlyComponent,
59
+ disabled,
60
+ formLayout,
61
+ labelSize,
62
+ controlMaxWidth,
63
+ controlMinWidth,
64
+ ...rest
65
+ } = props;
66
+ const sanitizedProps = filterStylingProps(rest, overStyled);
67
+ const restRecord = sanitizedProps as Record<string, unknown>;
68
+
69
+ // Actively delete dimension bindings that bypass the abstraction
70
+ delete restRecord["size"];
71
+ delete restRecord["color"];
72
+
73
+ const mergedClassNames: Partial<Record<string, string>> = {
74
+ root: styles.root,
75
+ body: styles.body,
76
+ inner: styles.inner,
77
+ radio: styles.radio,
78
+ icon: styles.icon,
79
+ labelWrapper: styles.labelWrapper,
80
+ label: styles.label,
81
+ };
82
+
83
+ const classNamesProp = restRecord.classNames;
84
+ if (
85
+ classNamesProp &&
86
+ typeof classNamesProp === "object" &&
87
+ !Array.isArray(classNamesProp)
88
+ ) {
89
+ const o = classNamesProp as Partial<Record<string, string>>;
90
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
91
+ mergedClassNames.body = o.body ? `${styles.body} ${o.body}` : styles.body;
92
+ mergedClassNames.inner = o.inner
93
+ ? `${styles.inner} ${o.inner}`
94
+ : styles.inner;
95
+ mergedClassNames.radio = o.radio
96
+ ? `${styles.radio} ${o.radio}`
97
+ : styles.radio;
98
+ mergedClassNames.icon = o.icon ? `${styles.icon} ${o.icon}` : styles.icon;
99
+ mergedClassNames.labelWrapper = o.labelWrapper
100
+ ? `${styles.labelWrapper} ${o.labelWrapper}`
101
+ : styles.labelWrapper;
102
+ mergedClassNames.label = o.label
103
+ ? `${styles.label} ${o.label}`
104
+ : styles.label;
105
+ }
106
+
107
+ const classNameProp = restRecord.className as string | undefined;
108
+ const finalClass = classNameProp
109
+ ? `${styles.root} ${classNameProp}`
110
+ : styles.root;
111
+
112
+ if (readOnly && !!readOnlyComponent) {
113
+ const isChecked = !!(restRecord.checked ?? restRecord.defaultChecked);
114
+ const ReadOnlyComp = readOnlyComponent;
115
+ const roNode = (
116
+ <ReadOnlyComp
117
+ {...props}
118
+ checked={isChecked}
119
+ label={restRecord.label as React.ReactNode}
120
+ />
121
+ );
122
+
123
+ if (formLayout) {
124
+ return (
125
+ <FormControlLayout
126
+ formLayout={formLayout}
127
+ labelSize={labelSize}
128
+ controlMaxWidth={controlMaxWidth}
129
+ controlMinWidth={controlMinWidth}
130
+ >
131
+ {roNode}
132
+ </FormControlLayout>
133
+ );
134
+ }
135
+
136
+ return <>{roNode}</>;
137
+ }
138
+
139
+ // We omit Mui's sizing/coloring so we rely strictly on variables from Radio.module.css
140
+ const radioNode = (
141
+ <MuiRadio
142
+ ref={ref}
143
+ icon={<RadioIcon />}
144
+ checkedIcon={<RadioIcon className={styles.checked} />}
145
+ className={finalClass}
146
+ classes={mergedClassNames}
147
+ disabled={readOnly || disabled}
148
+ {...(sanitizedProps as unknown as MuiRadioProps)}
149
+ />
150
+ );
151
+
152
+ const finalNode = restRecord.label ? (
153
+ <FormControlLabel
154
+ control={radioNode}
155
+ label={restRecord.label as React.ReactNode}
156
+ className={styles.labelWrapper}
157
+ classes={{ label: styles.label }}
158
+ />
159
+ ) : (
160
+ radioNode
161
+ );
162
+
163
+ if (formLayout) {
164
+ return (
165
+ <FormControlLayout
166
+ formLayout={formLayout}
167
+ labelSize={labelSize}
168
+ controlMaxWidth={controlMaxWidth}
169
+ controlMinWidth={controlMinWidth}
170
+ >
171
+ {finalNode}
172
+ </FormControlLayout>
173
+ );
174
+ }
175
+
176
+ return finalNode;
177
+ },
178
+ ) as RadioComponent;
179
+
180
+ Radio.displayName = "Radio";
181
+ Radio.Group = RadioGroup;
@@ -0,0 +1,111 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { forwardRef } from "react";
3
+ import {
4
+ Radio as MuiRadio,
5
+ RadioGroup as MuiRadioGroup,
6
+ type RadioGroupProps as MuiRadioGroupProps,
7
+ } from "@mui/material";
8
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
9
+ import {
10
+ filterStylingProps,
11
+ type RecursicaOverStyled,
12
+ } from "../../utils/filterStylingProps";
13
+ import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
14
+ import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
15
+ import styles from "./Radio.module.css";
16
+
17
+ export interface RecursicaRadioGroupProps
18
+ extends Omit<MuiRadioGroupProps, "size" | "labelProps">,
19
+ Omit<
20
+ RecursicaFormControlWrapperProps,
21
+ "controlMaxWidth" | "controlMinWidth"
22
+ >,
23
+ ReadOnlyControlProps {}
24
+
25
+ export type RadioGroupProps = RecursicaOverStyled<RecursicaRadioGroupProps>;
26
+
27
+ export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
28
+ function RadioGroup(props, ref) {
29
+ const {
30
+ overStyled = false,
31
+ formLayout = "stacked",
32
+
33
+ // Label Wrappers
34
+ labelSize,
35
+ labelAlignment,
36
+ labelOptionalText,
37
+ labelWithEditIcon,
38
+ onLabelEditClick,
39
+
40
+ // Base Mui Extracted Attributes
41
+ label,
42
+ description,
43
+ assistiveText,
44
+ assistiveWithIcon,
45
+ error,
46
+ required,
47
+ withAsterisk,
48
+ id,
49
+ className,
50
+ style,
51
+ children,
52
+ readOnly,
53
+ readOnlyComponent,
54
+ emptyValueComponent,
55
+ value,
56
+ defaultValue,
57
+ ...rest
58
+ } = props;
59
+ const sanitizedProps = filterStylingProps(rest, overStyled);
60
+ const restRecord = sanitizedProps as Record<string, unknown>;
61
+
62
+ // Delete prohibited sizing hooks from bypassing the variables
63
+ delete restRecord["size"];
64
+
65
+ return (
66
+ <WithReadOnlyWrapper
67
+ className={className}
68
+ style={style as React.CSSProperties}
69
+ controlMaxWidth="var(--recursica_ui-kit_components_radio-button-item_properties_max-width)"
70
+ controlMinWidth={undefined}
71
+ overStyled={overStyled as true}
72
+ labelElement="div" // Strictly override. ARIA grouping prohibits interactive radios nested natively inside <label>.
73
+ formLayout={formLayout}
74
+ labelSize={labelSize}
75
+ labelAlignment={labelAlignment}
76
+ labelOptionalText={labelOptionalText}
77
+ labelWithEditIcon={labelWithEditIcon}
78
+ onLabelEditClick={onLabelEditClick}
79
+ label={label}
80
+ description={description}
81
+ assistiveText={assistiveText}
82
+ assistiveWithIcon={assistiveWithIcon}
83
+ error={error}
84
+ required={required}
85
+ withAsterisk={withAsterisk}
86
+ id={id}
87
+ readOnly={readOnly && !!readOnlyComponent}
88
+ readOnlyComponent={readOnlyComponent}
89
+ emptyValueComponent={emptyValueComponent}
90
+ readOnlyType="text"
91
+ readOnlyValue={value !== undefined ? value : defaultValue}
92
+ readOnlyNativeProps={props}
93
+ activeComponent={
94
+ <MuiRadioGroup
95
+ ref={ref}
96
+ /* Natively bind local disabled lock dynamically */
97
+ {...(sanitizedProps as unknown as MuiRadioGroupProps)}
98
+ value={value}
99
+ defaultValue={defaultValue}
100
+ >
101
+ <div className={styles.groupRoot} data-layout={formLayout}>
102
+ {children}
103
+ </div>
104
+ </MuiRadioGroup>
105
+ }
106
+ />
107
+ );
108
+ },
109
+ );
110
+
111
+ RadioGroup.displayName = "RadioGroup";
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { Box } from "@mui/material";
3
+ import styles from "./ReadOnlyField.module.css";
4
+
5
+ export const ReadOnlyBooleanField: React.FC<{
6
+ checked: boolean;
7
+ label?: React.ReactNode;
8
+ }> = ({ checked, label }) => {
9
+ return (
10
+ <Box
11
+ component="span"
12
+ className={styles.textField}
13
+ style={{ display: "inline-flex", gap: "8px", alignItems: "baseline" }}
14
+ >
15
+ {label && <span style={{ fontWeight: 500 }}>{label}:</span>}
16
+ <span>{checked ? "True" : "False"}</span>
17
+ </Box>
18
+ );
19
+ };
20
+
21
+ ReadOnlyBooleanField.displayName = "ReadOnlyBooleanField";
@@ -0,0 +1,54 @@
1
+ /* LAYOUT SPACING OVERRIDES:
2
+ - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
3
+ .root {
4
+ width: 100%;
5
+ --form-control-margin-bottom: var(
6
+ --recursica_ui-kit_components_read-only-field_variants_layouts_stacked_properties_top-bottom-margin,
7
+ 0px
8
+ );
9
+ }
10
+
11
+ .root[data-form-layout="side-by-side"] {
12
+ --form-control-margin-bottom: var(
13
+ --recursica_ui-kit_components_read-only-field_variants_layouts_side-by-side_properties_top-bottom-margin,
14
+ 0px
15
+ );
16
+ }
17
+
18
+ .text {
19
+ display: flex;
20
+ align-items: center;
21
+
22
+ font-family: var(
23
+ --recursica_ui-kit_components_read-only-field_properties_text_font-family
24
+ );
25
+ font-size: var(
26
+ --recursica_ui-kit_components_read-only-field_properties_text_font-size
27
+ );
28
+ font-style: var(
29
+ --recursica_ui-kit_components_read-only-field_properties_text_font-style
30
+ );
31
+ font-weight: var(
32
+ --recursica_ui-kit_components_read-only-field_properties_text_font-weight
33
+ );
34
+ letter-spacing: var(
35
+ --recursica_ui-kit_components_read-only-field_properties_text_letter-spacing
36
+ );
37
+ line-height: var(
38
+ --recursica_ui-kit_components_read-only-field_properties_text_line-height
39
+ );
40
+ text-decoration: var(
41
+ --recursica_ui-kit_components_read-only-field_properties_text_text-decoration
42
+ );
43
+ text-transform: var(
44
+ --recursica_ui-kit_components_read-only-field_properties_text_text-transform
45
+ );
46
+ color: var(
47
+ --recursica_ui-kit_components_read-only-field_properties_colors_text
48
+ );
49
+
50
+ min-height: var(
51
+ --recursica_ui-kit_components_read-only-field_properties_min-height,
52
+ 40px
53
+ );
54
+ }